From 4c57f2d9f845209b5148ad85d71906c7b5d5f985 Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Fri, 27 Sep 2024 16:37:51 +0800 Subject: [PATCH 01/15] Fix 0.7 server favorites --- src/engine/client/favorites.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/engine/client/favorites.cpp b/src/engine/client/favorites.cpp index 9df4e42aff6..ab3c5822303 100644 --- a/src/engine/client/favorites.cpp +++ b/src/engine/client/favorites.cpp @@ -135,14 +135,16 @@ void CFavorites::Add(const NETADDR *pAddrs, int NumAddrs) // other favorite. for(int i = 0; i < NumAddrs; i++) { - CEntry *pEntry = Entry(pAddrs[i]); + NETADDR Addr = pAddrs[i]; + Addr.type &= ~(NETTYPE_TW7); + CEntry *pEntry = Entry(Addr); if(pEntry == nullptr) { continue; } for(int j = 0; j < pEntry->m_NumAddrs; j++) { - if(pEntry->m_aAddrs[j] == pAddrs[i]) + if(pEntry->m_aAddrs[j] == Addr) { pEntry->m_aAddrs[j] = pEntry->m_aAddrs[pEntry->m_NumAddrs - 1]; pEntry->m_NumAddrs -= 1; @@ -162,8 +164,10 @@ void CFavorites::Add(const NETADDR *pAddrs, int NumAddrs) NewEntry.m_NumAddrs = std::min(NumAddrs, (int)std::size(NewEntry.m_aAddrs)); for(int i = 0; i < NewEntry.m_NumAddrs; i++) { - NewEntry.m_aAddrs[i] = pAddrs[i]; - m_ByAddr[pAddrs[i]] = m_vEntries.size(); + NETADDR Addr = pAddrs[i]; + Addr.type &= ~(NETTYPE_TW7); + NewEntry.m_aAddrs[i] = Addr; + m_ByAddr[Addr] = m_vEntries.size(); } NewEntry.m_AllowPing = false; m_vEntries.push_back(NewEntry); @@ -207,7 +211,10 @@ void CFavorites::AllEntries(const CEntry **ppEntries, int *pNumEntries) CFavorites::CEntry *CFavorites::Entry(const NETADDR &Addr) { - auto Entry = m_ByAddr.find(Addr); + NETADDR AddrAnyProtocol = Addr; + AddrAnyProtocol.type &= ~(NETTYPE_TW7); + + auto Entry = m_ByAddr.find(AddrAnyProtocol); if(Entry == m_ByAddr.end()) { return nullptr; @@ -217,7 +224,10 @@ CFavorites::CEntry *CFavorites::Entry(const NETADDR &Addr) const CFavorites::CEntry *CFavorites::Entry(const NETADDR &Addr) const { - auto Entry = m_ByAddr.find(Addr); + NETADDR AddrAnyProtocol = Addr; + AddrAnyProtocol.type &= ~(NETTYPE_TW7); + + auto Entry = m_ByAddr.find(AddrAnyProtocol); if(Entry == m_ByAddr.end()) { return nullptr; From 3f676794f6393f438e42ea6fe7a467627a311dfe Mon Sep 17 00:00:00 2001 From: Pioooooo Date: Sat, 5 Oct 2024 20:13:45 +0800 Subject: [PATCH 02/15] Add rcon commands (un)infinite_jump, practice commands /(un)infjump --- src/game/server/ddracechat.cpp | 14 ++++++++++++++ src/game/server/ddracecommands.cpp | 16 ++++++++++++++++ src/game/server/entities/character.cpp | 5 +++++ src/game/server/entities/character.h | 1 + src/game/server/gamecontext.cpp | 4 ++++ src/game/server/gamecontext.h | 4 ++++ 6 files changed, 44 insertions(+) diff --git a/src/game/server/ddracechat.cpp b/src/game/server/ddracechat.cpp index 1741cbe4422..55c86721602 100644 --- a/src/game/server/ddracechat.cpp +++ b/src/game/server/ddracechat.cpp @@ -2166,6 +2166,13 @@ void CGameContext::ConPracticeJetpack(IConsole::IResult *pResult, void *pUserDat ConJetpack(pResult, pUserData); } +void CGameContext::ConPracticeEndlessJump(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(pSelf->GetPracticeCharacter(pResult)) + ConEndlessJump(pResult, pUserData); +} + void CGameContext::ConPracticeSetJumps(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; @@ -2208,6 +2215,13 @@ void CGameContext::ConPracticeUnJetpack(IConsole::IResult *pResult, void *pUserD ConUnJetpack(pResult, pUserData); } +void CGameContext::ConPracticeUnEndlessJump(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + if(pSelf->GetPracticeCharacter(pResult)) + ConUnEndlessJump(pResult, pUserData); +} + void CGameContext::ConPracticeUnWeapons(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; diff --git a/src/game/server/ddracecommands.cpp b/src/game/server/ddracecommands.cpp index e7e9b8251c2..26f817314d7 100644 --- a/src/game/server/ddracecommands.cpp +++ b/src/game/server/ddracecommands.cpp @@ -277,6 +277,14 @@ void CGameContext::ConJetpack(IConsole::IResult *pResult, void *pUserData) pChr->SetJetpack(true); } +void CGameContext::ConEndlessJump(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + CCharacter *pChr = pSelf->GetPlayerChar(pResult->m_ClientId); + if(pChr) + pChr->SetEndlessJump(true); +} + void CGameContext::ConSetJumps(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; @@ -317,6 +325,14 @@ void CGameContext::ConUnJetpack(IConsole::IResult *pResult, void *pUserData) pChr->SetJetpack(false); } +void CGameContext::ConUnEndlessJump(IConsole::IResult *pResult, void *pUserData) +{ + CGameContext *pSelf = (CGameContext *)pUserData; + CCharacter *pChr = pSelf->GetPlayerChar(pResult->m_ClientId); + if(pChr) + pChr->SetEndlessJump(false); +} + void CGameContext::ConUnWeapons(IConsole::IResult *pResult, void *pUserData) { CGameContext *pSelf = (CGameContext *)pUserData; diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index fe961f1e16e..eaeeacb11c2 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -161,6 +161,11 @@ void CCharacter::SetJetpack(bool Active) m_Core.m_Jetpack = Active; } +void CCharacter::SetEndlessJump(bool Active) +{ + m_Core.m_EndlessJump = Active; +} + void CCharacter::SetJumps(int Jumps) { m_Core.m_Jumps = Jumps; diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h index 59793707cde..0b2d61dd615 100644 --- a/src/game/server/entities/character.h +++ b/src/game/server/entities/character.h @@ -48,6 +48,7 @@ class CCharacter : public CEntity void SetWeapon(int W); void SetJetpack(bool Active); + void SetEndlessJump(bool Active); void SetJumps(int Jumps); void SetSolo(bool Solo); void SetSuper(bool Super); diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 1a2e24d1f8b..6d9537f0bd1 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -3692,6 +3692,8 @@ void CGameContext::RegisterDDRaceCommands() Console()->Register("super", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConSuper, this, "Makes you super"); Console()->Register("unsuper", "", CFGFLAG_SERVER, ConUnSuper, this, "Removes super from you"); Console()->Register("invincible", "?i['0'|'1']", CFGFLAG_SERVER | CMDFLAG_TEST, ConToggleInvincible, this, "Toggles invincible mode"); + Console()->Register("infinite_jump", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConEndlessJump, this, "Gives you infinite jump"); + Console()->Register("uninfinite_jump", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnEndlessJump, this, "Removes infinite jump from you"); Console()->Register("endless_hook", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConEndlessHook, this, "Gives you endless hook"); Console()->Register("unendless_hook", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConUnEndlessHook, this, "Removes endless hook from you"); Console()->Register("solo", "", CFGFLAG_SERVER | CMDFLAG_TEST, ConSolo, this, "Puts you into solo part"); @@ -3825,6 +3827,8 @@ void CGameContext::RegisterChatCommands() Console()->Register("unweapons", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeUnWeapons, this, "Removes all weapons from you"); Console()->Register("ninja", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeNinja, this, "Makes you a ninja"); Console()->Register("unninja", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeUnNinja, this, "Removes ninja from you"); + Console()->Register("infjump", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeEndlessJump, this, "Gives you infinite jump"); + Console()->Register("uninfjump", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeUnEndlessJump, this, "Removes infinite jump from you"); Console()->Register("endless", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeEndlessHook, this, "Gives you endless hook"); Console()->Register("unendless", "", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeUnEndlessHook, this, "Removes endless hook from you"); Console()->Register("invincible", "?i['0'|'1']", CFGFLAG_CHAT | CMDFLAG_PRACTICE, ConPracticeToggleInvincible, this, "Toggles invincible mode"); diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h index 4551fbc1f93..85055d7d38e 100644 --- a/src/game/server/gamecontext.h +++ b/src/game/server/gamecontext.h @@ -396,12 +396,14 @@ class CGameContext : public IGameServer static void ConGrenade(IConsole::IResult *pResult, void *pUserData); static void ConLaser(IConsole::IResult *pResult, void *pUserData); static void ConJetpack(IConsole::IResult *pResult, void *pUserData); + static void ConEndlessJump(IConsole::IResult *pResult, void *pUserData); static void ConSetJumps(IConsole::IResult *pResult, void *pUserData); static void ConWeapons(IConsole::IResult *pResult, void *pUserData); static void ConUnShotgun(IConsole::IResult *pResult, void *pUserData); static void ConUnGrenade(IConsole::IResult *pResult, void *pUserData); static void ConUnLaser(IConsole::IResult *pResult, void *pUserData); static void ConUnJetpack(IConsole::IResult *pResult, void *pUserData); + static void ConUnEndlessJump(IConsole::IResult *pResult, void *pUserData); static void ConUnWeapons(IConsole::IResult *pResult, void *pUserData); static void ConAddWeapon(IConsole::IResult *pResult, void *pUserData); static void ConRemoveWeapon(IConsole::IResult *pResult, void *pUserData); @@ -488,12 +490,14 @@ class CGameContext : public IGameServer static void ConPracticeGrenade(IConsole::IResult *pResult, void *pUserData); static void ConPracticeLaser(IConsole::IResult *pResult, void *pUserData); static void ConPracticeJetpack(IConsole::IResult *pResult, void *pUserData); + static void ConPracticeEndlessJump(IConsole::IResult *pResult, void *pUserData); static void ConPracticeSetJumps(IConsole::IResult *pResult, void *pUserData); static void ConPracticeWeapons(IConsole::IResult *pResult, void *pUserData); static void ConPracticeUnShotgun(IConsole::IResult *pResult, void *pUserData); static void ConPracticeUnGrenade(IConsole::IResult *pResult, void *pUserData); static void ConPracticeUnLaser(IConsole::IResult *pResult, void *pUserData); static void ConPracticeUnJetpack(IConsole::IResult *pResult, void *pUserData); + static void ConPracticeUnEndlessJump(IConsole::IResult *pResult, void *pUserData); static void ConPracticeUnWeapons(IConsole::IResult *pResult, void *pUserData); static void ConPracticeNinja(IConsole::IResult *pResult, void *pUserData); static void ConPracticeUnNinja(IConsole::IResult *pResult, void *pUserData); From f7955a50e6a7c163d45fc3bbe40884dc2d4b628e Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Wed, 9 Oct 2024 09:46:34 +0900 Subject: [PATCH 03/15] Fix empty vector element access in mysql prepare If the sql statement does not contain placeholders ``NumParameters`` can be empty. In that case accessing the first element will cause an asan error: ``` runtime error: reference binding to null pointer of type 'st_mysql_bind' ``` --- src/engine/server/databases/mysql.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/engine/server/databases/mysql.cpp b/src/engine/server/databases/mysql.cpp index 2479733f033..0943c0abcc5 100644 --- a/src/engine/server/databases/mysql.cpp +++ b/src/engine/server/databases/mysql.cpp @@ -331,8 +331,11 @@ bool CMysqlConnection::PrepareStatement(const char *pStmt, char *pError, int Err unsigned NumParameters = mysql_stmt_param_count(m_pStmt.get()); m_vStmtParameters.resize(NumParameters); m_vStmtParameterExtras.resize(NumParameters); - mem_zero(&m_vStmtParameters[0], sizeof(m_vStmtParameters[0]) * m_vStmtParameters.size()); - mem_zero(&m_vStmtParameterExtras[0], sizeof(m_vStmtParameterExtras[0]) * m_vStmtParameterExtras.size()); + if(NumParameters) + { + mem_zero(&m_vStmtParameters[0], sizeof(m_vStmtParameters[0]) * m_vStmtParameters.size()); + mem_zero(&m_vStmtParameterExtras[0], sizeof(m_vStmtParameterExtras[0]) * m_vStmtParameterExtras.size()); + } return false; } From 7e04b8c52151f18f02ed57e28b552b3fe92ef453 Mon Sep 17 00:00:00 2001 From: heinrich5991 Date: Wed, 9 Oct 2024 20:47:20 +0200 Subject: [PATCH 04/15] =?UTF-8?q?Allow=202048=20bans,=20we=20hit=20the=20l?= =?UTF-8?q?imit=20of=201024=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/engine/shared/netban.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/engine/shared/netban.h b/src/engine/shared/netban.h index f51afc07c37..72c88f44371 100644 --- a/src/engine/shared/netban.h +++ b/src/engine/shared/netban.h @@ -131,7 +131,7 @@ class CNetBan private: enum { - MAX_BANS = 1024, + MAX_BANS = 2048, }; CBan *m_aapHashList[HashCount][256]; From 1bf350c6f8bb6b1cd8f8984e61ca773616d65982 Mon Sep 17 00:00:00 2001 From: ChillerDragon Date: Wed, 9 Oct 2024 09:32:21 +0900 Subject: [PATCH 05/15] Use .data() in mysql code for better readability Fixes https://clang.llvm.org/extra/clang-tidy/checks/readability/container-data-pointer.html --- src/engine/server/databases/mysql.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/engine/server/databases/mysql.cpp b/src/engine/server/databases/mysql.cpp index 0943c0abcc5..9b5bcbe1854 100644 --- a/src/engine/server/databases/mysql.cpp +++ b/src/engine/server/databases/mysql.cpp @@ -333,8 +333,8 @@ bool CMysqlConnection::PrepareStatement(const char *pStmt, char *pError, int Err m_vStmtParameterExtras.resize(NumParameters); if(NumParameters) { - mem_zero(&m_vStmtParameters[0], sizeof(m_vStmtParameters[0]) * m_vStmtParameters.size()); - mem_zero(&m_vStmtParameterExtras[0], sizeof(m_vStmtParameterExtras[0]) * m_vStmtParameterExtras.size()); + mem_zero(m_vStmtParameters.data(), sizeof(m_vStmtParameters[0]) * m_vStmtParameters.size()); + mem_zero(m_vStmtParameterExtras.data(), sizeof(m_vStmtParameterExtras[0]) * m_vStmtParameterExtras.size()); } return false; } @@ -446,7 +446,7 @@ bool CMysqlConnection::Step(bool *pEnd, char *pError, int ErrorSize) if(m_NewQuery) { m_NewQuery = false; - if(mysql_stmt_bind_param(m_pStmt.get(), &m_vStmtParameters[0])) + if(mysql_stmt_bind_param(m_pStmt.get(), m_vStmtParameters.data())) { StoreErrorStmt("bind_param"); str_copy(pError, m_aErrorDetail, ErrorSize); @@ -477,7 +477,7 @@ bool CMysqlConnection::ExecuteUpdate(int *pNumUpdated, char *pError, int ErrorSi if(m_NewQuery) { m_NewQuery = false; - if(mysql_stmt_bind_param(m_pStmt.get(), &m_vStmtParameters[0])) + if(mysql_stmt_bind_param(m_pStmt.get(), m_vStmtParameters.data())) { StoreErrorStmt("bind_param"); str_copy(pError, m_aErrorDetail, ErrorSize); From a2fedd05894e6858d87eab28855596b9f3963079 Mon Sep 17 00:00:00 2001 From: KebsCS Date: Mon, 16 Sep 2024 03:15:41 +0200 Subject: [PATCH 06/15] Change editor teleport number behavior --- src/game/editor/editor.cpp | 29 +++--- src/game/editor/editor.h | 26 +++-- src/game/editor/mapitems/layer_tele.cpp | 31 +++--- src/game/editor/mapitems/layer_tele.h | 5 +- src/game/editor/mapitems/layer_tiles.cpp | 12 +-- src/game/editor/popups.cpp | 121 ++++++++++++----------- 6 files changed, 122 insertions(+), 102 deletions(-) diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 78538e45b66..85979e884f9 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -1268,6 +1268,7 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar) const char *pButtonName = nullptr; CUi::FPopupMenuFunction pfnPopupFunc = nullptr; int Rows = 0; + int ExtraWidth = 0; if(pS == m_Map.m_pSwitchLayer) { pButtonName = "Switch"; @@ -1290,7 +1291,8 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar) { pButtonName = "Tele"; pfnPopupFunc = PopupTele; - Rows = 3; + Rows = m_TeleNumbers.size() + 1; + ExtraWidth = 50; } if(pButtonName != nullptr) @@ -1305,7 +1307,7 @@ void CEditor::DoToolbarLayers(CUIRect ToolBar) static SPopupMenuId s_PopupModifierId; if(!Ui()->IsPopupOpen(&s_PopupModifierId)) { - Ui()->DoPopupMenu(&s_PopupModifierId, Button.x, Button.y + Button.h, 120, 10.0f + Rows * 13.0f, this, pfnPopupFunc); + Ui()->DoPopupMenu(&s_PopupModifierId, Button.x, Button.y + Button.h, 120 + ExtraWidth, 10.0f + Rows * 13.0f, this, pfnPopupFunc); } } TB_Bottom.VSplitLeft(5.0f, nullptr, &TB_Bottom); @@ -8050,7 +8052,7 @@ void CEditor::Render() return pLayer->m_Type == LAYERTYPE_TILES && std::static_pointer_cast(pLayer)->m_Tele; }); if(HasTeleTiles) - str_copy(m_aTooltip, "Use shift+mousewheel up/down to adjust the tele numbers. Use ctrl+f to change all tele numbers to the first unused number."); + str_copy(m_aTooltip, "Use shift+mousewheel up/down to adjust the tele number. Use ctrl+f to change current tele number to the first unused number."); if(Input()->ShiftIsPressed()) { @@ -8995,9 +8997,6 @@ void CEditor::AdjustBrushSpecialTiles(bool UseNextFree, int Adjust) // Only handle tele, switch and tune layers if(pLayerTiles->m_Tele) { - int NextFreeTeleNumber = FindNextFreeTeleNumber(); - int NextFreeCPNumber = FindNextFreeTeleNumber(true); - std::shared_ptr pTeleLayer = std::static_pointer_cast(pLayer); for(int y = 0; y < pTeleLayer->m_Height; y++) { @@ -9009,13 +9008,19 @@ void CEditor::AdjustBrushSpecialTiles(bool UseNextFree, int Adjust) if(UseNextFree) { - if(IsTeleTileCheckpoint(pTeleLayer->m_pTiles[i].m_Index)) - pTeleLayer->m_pTeleTile[i].m_Number = NextFreeCPNumber; - else - pTeleLayer->m_pTeleTile[i].m_Number = NextFreeTeleNumber; + pTeleLayer->m_pTeleTile[i].m_Number = FindNextFreeTeleNumber(pTeleLayer->m_pTiles[i].m_Index); } else AdjustNumber(pTeleLayer->m_pTeleTile[i].m_Number); + + if(IsTeleTileNumberUsedAny(pTeleLayer->m_pTiles[i].m_Index) && + m_TeleNumbers[pTeleLayer->m_pTiles[i].m_Index] != pTeleLayer->m_pTeleTile[i].m_Number) + { + if(UseNextFree || Adjust != 0) + m_TeleNumbers[pTeleLayer->m_pTiles[i].m_Index] = pTeleLayer->m_pTeleTile[i].m_Number; + else if(!UseNextFree && Adjust == 0) + pTeleLayer->m_pTeleTile[i].m_Number = m_TeleNumbers[pTeleLayer->m_pTiles[i].m_Index]; + } } } } @@ -9075,12 +9080,12 @@ int CEditor::FindNextFreeSwitchNumber() return Number; } -int CEditor::FindNextFreeTeleNumber(bool IsCheckpoint) +int CEditor::FindNextFreeTeleNumber(int Index) { int Number = -1; for(int i = 1; i <= 255; i++) { - if(!m_Map.m_pTeleLayer->ContainsElementWithId(i, IsCheckpoint)) + if(!m_Map.m_pTeleLayer->ContainsElementWithId(i, Index)) { Number = i; break; diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 6206cea25f3..b4a70be1df2 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -218,8 +218,15 @@ class CEditorMap void MakeTuneLayer(const std::shared_ptr &pLayer); }; -struct CProperty +class CProperty { +public: + CProperty(const char *pName, int Value, int Type, int Min, int Max) : + m_pName(pName), m_Value(Value), m_Type(Type), m_Min(Min), m_Max(Max) {} + + CProperty(std::nullptr_t) : + m_pName(nullptr), m_Value(0), m_Type(0), m_Min(0), m_Max(0) {} + const char *m_pName; int m_Value; int m_Type; @@ -428,8 +435,16 @@ class CEditor : public IEditor // DDRace - m_TeleNumber = 1; - m_TeleCheckpointNumber = 1; + m_ViewTeleNumber = 1; + m_TeleNumbers = { + {TILE_TELEINEVIL, 1}, + {TILE_TELEINWEAPON, 1}, + {TILE_TELEINHOOK, 1}, + {TILE_TELEIN, 1}, + {TILE_TELEOUT, 1}, + {TILE_TELECHECK, 1}, + {TILE_TELECHECKOUT, 1}}; + m_SwitchNum = 1; m_TuningNum = 1; m_SwitchDelay = 0; @@ -1127,9 +1142,8 @@ class CEditor : public IEditor IGraphics::CTextureHandle GetSwitchTexture(); IGraphics::CTextureHandle GetTuneTexture(); - unsigned char m_TeleNumber; - unsigned char m_TeleCheckpointNumber; unsigned char m_ViewTeleNumber; + std::map m_TeleNumbers; unsigned char m_TuningNum; @@ -1143,7 +1157,7 @@ class CEditor : public IEditor void AdjustBrushSpecialTiles(bool UseNextFree, int Adjust = 0); int FindNextFreeSwitchNumber(); - int FindNextFreeTeleNumber(bool IsCheckpoint = false); + int FindNextFreeTeleNumber(int Index); // Undo/Redo CEditorHistory m_EditorHistory; diff --git a/src/game/editor/mapitems/layer_tele.cpp b/src/game/editor/mapitems/layer_tele.cpp index 977344a58dc..28343632e7e 100644 --- a/src/game/editor/mapitems/layer_tele.cpp +++ b/src/game/editor/mapitems/layer_tele.cpp @@ -77,7 +77,7 @@ void CLayerTele::BrushDraw(std::shared_ptr pBrush, float wx, float wy) int sx = ConvertX(wx); int sy = ConvertY(wy); if(str_comp(pTeleLayer->m_aFileName, m_pEditor->m_aFileName)) - m_pEditor->m_TeleNumber = pTeleLayer->m_TeleNum; + m_pEditor->m_TeleNumbers = pTeleLayer->m_TeleNumbers; bool Destructive = m_pEditor->m_BrushDrawDestructive || IsEmpty(pTeleLayer); @@ -99,22 +99,19 @@ void CLayerTele::BrushDraw(std::shared_ptr pBrush, float wx, float wy) m_pTeleTile[Index].m_Type, m_pTiles[Index].m_Index}; - if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidTeleTile(pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index)) && pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index != TILE_AIR) + unsigned char TgtIndex = pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index; + if((m_pEditor->m_AllowPlaceUnusedTiles || IsValidTeleTile(TgtIndex)) && TgtIndex != TILE_AIR) { - bool IsCheckpoint = IsTeleTileCheckpoint(pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index); - if(!IsCheckpoint && !IsTeleTileNumberUsed(pTeleLayer->m_pTiles[y * pTeleLayer->m_Width + x].m_Index, false)) + bool IsCheckpoint = IsTeleTileCheckpoint(TgtIndex); + if(!IsCheckpoint && !IsTeleTileNumberUsed(TgtIndex, false)) { // Tele tile number is unused. Set a known value which is not 0, // as tiles with number 0 would be ignored by previous versions. m_pTeleTile[Index].m_Number = 255; } - else if(!IsCheckpoint && m_pEditor->m_TeleNumber != pTeleLayer->m_TeleNum) + else if(m_pEditor->m_TeleNumbers[TgtIndex] != pTeleLayer->m_TeleNumbers[TgtIndex]) { - m_pTeleTile[Index].m_Number = m_pEditor->m_TeleNumber; - } - else if(IsCheckpoint && m_pEditor->m_TeleCheckpointNumber != pTeleLayer->m_TeleCheckpointNum) - { - m_pTeleTile[Index].m_Number = m_pEditor->m_TeleCheckpointNumber; + m_pTeleTile[Index].m_Number = m_pEditor->m_TeleNumbers[TgtIndex]; } else if(pTeleLayer->m_pTeleTile[y * pTeleLayer->m_Width + x].m_Number) { @@ -122,7 +119,7 @@ void CLayerTele::BrushDraw(std::shared_ptr pBrush, float wx, float wy) } else { - if((!IsCheckpoint && !m_pEditor->m_TeleNumber) || (IsCheckpoint && !m_pEditor->m_TeleCheckpointNumber)) + if(!m_pEditor->m_TeleNumbers[TgtIndex]) { m_pTeleTile[Index].m_Number = 0; m_pTeleTile[Index].m_Type = 0; @@ -138,7 +135,7 @@ void CLayerTele::BrushDraw(std::shared_ptr pBrush, float wx, float wy) } else { - m_pTeleTile[Index].m_Number = IsCheckpoint ? m_pEditor->m_TeleCheckpointNumber : m_pEditor->m_TeleNumber; + m_pTeleTile[Index].m_Number = m_pEditor->m_TeleNumbers[TgtIndex]; } } @@ -283,10 +280,8 @@ void CLayerTele::FillSelection(bool Empty, std::shared_ptr pBrush, CUIRe // as tiles with number 0 would be ignored by previous versions. m_pTeleTile[TgtIndex].m_Number = 255; } - else if(!IsCheckpoint && ((pLt->m_pTeleTile[SrcIndex].m_Number == 0 && m_pEditor->m_TeleNumber) || m_pEditor->m_TeleNumber != pLt->m_TeleNum)) - m_pTeleTile[TgtIndex].m_Number = m_pEditor->m_TeleNumber; - else if(IsCheckpoint && ((pLt->m_pTeleTile[SrcIndex].m_Number == 0 && m_pEditor->m_TeleCheckpointNumber) || m_pEditor->m_TeleCheckpointNumber != pLt->m_TeleCheckpointNum)) - m_pTeleTile[TgtIndex].m_Number = m_pEditor->m_TeleCheckpointNumber; + else if((pLt->m_pTeleTile[SrcIndex].m_Number == 0 && m_pEditor->m_TeleNumbers[m_pTiles[TgtIndex].m_Index]) || m_pEditor->m_TeleNumbers[m_pTiles[TgtIndex].m_Index] != pLt->m_TeleNumbers[m_pTiles[TgtIndex].m_Index]) + m_pTeleTile[TgtIndex].m_Number = m_pEditor->m_TeleNumbers[m_pTiles[TgtIndex].m_Index]; else m_pTeleTile[TgtIndex].m_Number = pLt->m_pTeleTile[SrcIndex].m_Number; } @@ -303,13 +298,13 @@ void CLayerTele::FillSelection(bool Empty, std::shared_ptr pBrush, CUIRe FlagModified(sx, sy, w, h); } -bool CLayerTele::ContainsElementWithId(int Id, bool Checkpoint) +bool CLayerTele::ContainsElementWithId(int Id, int Index) { for(int y = 0; y < m_Height; ++y) { for(int x = 0; x < m_Width; ++x) { - if(IsTeleTileNumberUsed(m_pTeleTile[y * m_Width + x].m_Type, Checkpoint) && m_pTeleTile[y * m_Width + x].m_Number == Id) + if(m_pTeleTile[y * m_Width + x].m_Type == Index && m_pTeleTile[y * m_Width + x].m_Number == Id) { return true; } diff --git a/src/game/editor/mapitems/layer_tele.h b/src/game/editor/mapitems/layer_tele.h index 8d1c5570ea0..267dd2f56e9 100644 --- a/src/game/editor/mapitems/layer_tele.h +++ b/src/game/editor/mapitems/layer_tele.h @@ -22,8 +22,7 @@ class CLayerTele : public CLayerTiles ~CLayerTele(); CTeleTile *m_pTeleTile; - unsigned char m_TeleNum; - unsigned char m_TeleCheckpointNum; + std::map m_TeleNumbers; void Resize(int NewW, int NewH) override; void Shift(int Direction) override; @@ -33,7 +32,7 @@ class CLayerTele : public CLayerTiles void BrushFlipY() override; void BrushRotate(float Amount) override; void FillSelection(bool Empty, std::shared_ptr pBrush, CUIRect Rect) override; - virtual bool ContainsElementWithId(int Id, bool Checkpoint); + virtual bool ContainsElementWithId(int Id, int Index); virtual void GetPos(int Number, int Offset, int &TeleX, int &TeleY); int m_GotoTeleOffset; diff --git a/src/game/editor/mapitems/layer_tiles.cpp b/src/game/editor/mapitems/layer_tiles.cpp index 665a0fec97b..58cfc823a36 100644 --- a/src/game/editor/mapitems/layer_tiles.cpp +++ b/src/game/editor/mapitems/layer_tiles.cpp @@ -310,17 +310,15 @@ int CLayerTiles::BrushGrab(std::shared_ptr pBrush, CUIRect Rect) for(int x = 0; x < r.w; x++) { pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x] = static_cast(this)->m_pTeleTile[(r.y + y) * m_Width + (r.x + x)]; - if(IsValidTeleTile(pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Type)) + unsigned char TgtIndex = pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Type; + if(IsValidTeleTile(TgtIndex)) { - if(IsTeleTileNumberUsed(pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Type, false)) - m_pEditor->m_TeleNumber = pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Number; - else if(IsTeleTileNumberUsed(pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Type, true)) - m_pEditor->m_TeleCheckpointNumber = pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Number; + if(IsTeleTileNumberUsedAny(TgtIndex)) + m_pEditor->m_TeleNumbers[TgtIndex] = pGrabbed->m_pTeleTile[y * pGrabbed->m_Width + x].m_Number; } } - pGrabbed->m_TeleNum = m_pEditor->m_TeleNumber; - pGrabbed->m_TeleCheckpointNum = m_pEditor->m_TeleCheckpointNumber; + pGrabbed->m_TeleNumbers = m_pEditor->m_TeleNumbers; str_copy(pGrabbed->m_aFileName, m_pEditor->m_aFileName); } diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index d1f0d8ab329..65fbb685a99 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -2432,24 +2432,29 @@ int CEditor::PopupSelectConfigAutoMapResult() CUi::EPopupMenuFunctionResult CEditor::PopupTele(void *pContext, CUIRect View, bool Active) { CEditor *pEditor = static_cast(pContext); + static const int s_NumTeleTiles = 7; - static int s_PreviousTeleNumber; - static int s_PreviousCheckpointNumber; + static int s_aPreviousTeleNumbers[s_NumTeleTiles]; static int s_PreviousViewTeleNumber; CUIRect NumberPicker; CUIRect FindEmptySlot; - CUIRect FindFreeTeleSlot, FindFreeCheckpointSlot, FindFreeViewSlot; + CUIRect FindFreeViewSlot; + CUIRect aFindFreeTeleSlots[s_NumTeleTiles]; View.VSplitRight(15.f, &NumberPicker, &FindEmptySlot); NumberPicker.VSplitRight(2.f, &NumberPicker, nullptr); - FindEmptySlot.HSplitTop(13.0f, &FindFreeTeleSlot, &FindEmptySlot); - FindEmptySlot.HSplitTop(13.0f, &FindFreeCheckpointSlot, &FindEmptySlot); + for(auto &Slot : aFindFreeTeleSlots) + { + FindEmptySlot.HSplitTop(13.0f, &Slot, &FindEmptySlot); + } FindEmptySlot.HSplitTop(13.0f, &FindFreeViewSlot, &FindEmptySlot); - FindFreeTeleSlot.HMargin(1.0f, &FindFreeTeleSlot); - FindFreeCheckpointSlot.HMargin(1.0f, &FindFreeCheckpointSlot); + for(auto &Slot : aFindFreeTeleSlots) + { + Slot.HMargin(1.0f, &Slot); + } FindFreeViewSlot.HMargin(1.0f, &FindFreeViewSlot); auto ViewTele = [](CEditor *pEd) -> bool { @@ -2465,79 +2470,83 @@ CUi::EPopupMenuFunctionResult CEditor::PopupTele(void *pContext, CUIRect View, b return false; }; - static std::vector s_vColors = { - ColorRGBA(0.5f, 1, 0.5f, 0.5f), - ColorRGBA(0.5f, 1, 0.5f, 0.5f), - ColorRGBA(0.5f, 1, 0.5f, 0.5f), - }; - enum - { - PROP_TELE = 0, - PROP_TELE_CP, - PROP_TELE_VIEW, - NUM_PROPS, - }; + static std::vector s_vColors(s_NumTeleTiles + 1, ColorRGBA(0.5f, 1, 0.5f, 0.5f)); // find next free numbers buttons { - // Pressing ctrl+f will find next free numbers for both tele and checkpoints - - static int s_NextFreeTelePid = 0; - if(pEditor->DoButton_Editor(&s_NextFreeTelePid, "F", 0, &FindFreeTeleSlot, 0, "[ctrl+f] Find next free tele number") || (Active && pEditor->Input()->ModifierIsPressed() && pEditor->Input()->KeyPress(KEY_F))) - { - int TeleNumber = pEditor->FindNextFreeTeleNumber(); - - if(TeleNumber != -1) - pEditor->m_TeleNumber = TeleNumber; - } - - static int s_NextFreeCheckpointPid = 0; - if(pEditor->DoButton_Editor(&s_NextFreeCheckpointPid, "F", 0, &FindFreeCheckpointSlot, 0, "[ctrl+f] Find next free checkpoint number") || (Active && pEditor->Input()->ModifierIsPressed() && pEditor->Input()->KeyPress(KEY_F))) + static int s_aNextFreeTeleButtonIds[s_NumTeleTiles] = {0}; + for(int i = 0; i < s_NumTeleTiles; i++) { - int CPNumber = pEditor->FindNextFreeTeleNumber(true); + if(pEditor->DoButton_Editor(&s_aNextFreeTeleButtonIds[i], "F", 0, &aFindFreeTeleSlots[i], 0, "[ctrl+f] Find next free tele number") || + (Active && pEditor->Input()->ModifierIsPressed() && pEditor->Input()->KeyPress(KEY_F))) + { + int Tile = (*std::next(pEditor->m_TeleNumbers.begin(), i)).first; + int TeleNumber = pEditor->FindNextFreeTeleNumber(Tile); - if(CPNumber != -1) - pEditor->m_TeleCheckpointNumber = CPNumber; + if(TeleNumber != -1) + { + pEditor->m_TeleNumbers[Tile] = TeleNumber; + pEditor->AdjustBrushSpecialTiles(false); + } + } } static int s_NextFreeViewPid = 0; int btn = pEditor->DoButton_Editor(&s_NextFreeViewPid, "N", 0, &FindFreeViewSlot, 0, "[n] Show next tele with this number"); if(btn || (Active && pEditor->Input()->KeyPress(KEY_N))) - s_vColors[PROP_TELE_VIEW] = ViewTele(pEditor) ? ColorRGBA(0.5f, 1, 0.5f, 0.5f) : ColorRGBA(1, 0.5f, 0.5f, 0.5f); + s_vColors[s_NumTeleTiles] = ViewTele(pEditor) ? ColorRGBA(0.5f, 1, 0.5f, 0.5f) : ColorRGBA(1, 0.5f, 0.5f, 0.5f); } // number picker { - CProperty aProps[] = { - {"Number", pEditor->m_TeleNumber, PROPTYPE_INT, 1, 255}, - {"Checkpoint", pEditor->m_TeleCheckpointNumber, PROPTYPE_INT, 1, 255}, - {"View", pEditor->m_ViewTeleNumber, PROPTYPE_INT, 1, 255}, - {nullptr}, + static const char *s_apTeleLabelText[] = { + "Red Tele", // TILE_TELEINEVIL + "Weapon Tele", // TILE_TELEINWEAPON + "Hook Tele", // TILE_TELEINHOOK + "Blue Tele", // TILE_TELEIN + "Tele To", // TILE_TELEOUT + "CP Tele", // TILE_TELECHECK + "CP Tele To", // TILE_TELECHECKOUT }; - static int s_aIds[NUM_PROPS] = {0}; + std::vector vProps; + for(int i = 0; i < s_NumTeleTiles; i++) + { + unsigned char TeleNumber = (*std::next(pEditor->m_TeleNumbers.begin(), i)).second; + vProps.emplace_back(s_apTeleLabelText[i], TeleNumber, PROPTYPE_INT, 1, 255); + } + vProps.emplace_back("View", pEditor->m_ViewTeleNumber, PROPTYPE_INT, 1, 255); + vProps.emplace_back(nullptr); + + static int s_aIds[s_NumTeleTiles + 2] = {0}; int NewVal = 0; - int Prop = pEditor->DoProperties(&NumberPicker, aProps, s_aIds, &NewVal, s_vColors); - if(Prop == PROP_TELE) - pEditor->m_TeleNumber = (NewVal - 1 + 255) % 255 + 1; - else if(Prop == PROP_TELE_CP) - pEditor->m_TeleCheckpointNumber = (NewVal - 1 + 255) % 255 + 1; - else if(Prop == PROP_TELE_VIEW) - pEditor->m_ViewTeleNumber = (NewVal - 1 + 255) % 255 + 1; + int Prop = pEditor->DoProperties(&NumberPicker, vProps.data(), s_aIds, &NewVal, s_vColors); - if(s_PreviousTeleNumber == 1 || s_PreviousTeleNumber != pEditor->m_TeleNumber) - s_vColors[PROP_TELE] = pEditor->m_Map.m_pTeleLayer->ContainsElementWithId(pEditor->m_TeleNumber, false) ? ColorRGBA(1, 0.5f, 0.5f, 0.5f) : ColorRGBA(0.5f, 1, 0.5f, 0.5f); + if(Prop >= 0 && Prop < s_NumTeleTiles) + { + auto Tele = (*std::next(pEditor->m_TeleNumbers.begin(), Prop)); + pEditor->m_TeleNumbers[Tele.first] = (NewVal - 1 + 255) % 255 + 1; + pEditor->AdjustBrushSpecialTiles(false); + } + else if(Prop == s_NumTeleTiles) + pEditor->m_ViewTeleNumber = (NewVal - 1 + 255) % 255 + 1; - if(s_PreviousCheckpointNumber == 1 || s_PreviousCheckpointNumber != pEditor->m_TeleCheckpointNumber) - s_vColors[PROP_TELE_CP] = pEditor->m_Map.m_pTeleLayer->ContainsElementWithId(pEditor->m_TeleCheckpointNumber, true) ? ColorRGBA(1, 0.5f, 0.5f, 0.5f) : ColorRGBA(0.5f, 1, 0.5f, 0.5f); + for(int i = 0; i < s_NumTeleTiles; i++) + { + auto Tele = (*std::next(pEditor->m_TeleNumbers.begin(), i)); + if(s_aPreviousTeleNumbers[i] == 1 || s_aPreviousTeleNumbers[i] != Tele.second) + s_vColors[i] = pEditor->m_Map.m_pTeleLayer->ContainsElementWithId(Tele.second, Tele.first) ? ColorRGBA(1, 0.5f, 0.5f, 0.5f) : ColorRGBA(0.5f, 1, 0.5f, 0.5f); + } if(s_PreviousViewTeleNumber != pEditor->m_ViewTeleNumber) - s_vColors[PROP_TELE_VIEW] = ViewTele(pEditor) ? ColorRGBA(0.5f, 1, 0.5f, 0.5f) : ColorRGBA(1, 0.5f, 0.5f, 0.5f); + s_vColors[s_NumTeleTiles] = ViewTele(pEditor) ? ColorRGBA(0.5f, 1, 0.5f, 0.5f) : ColorRGBA(1, 0.5f, 0.5f, 0.5f); } - s_PreviousTeleNumber = pEditor->m_TeleNumber; - s_PreviousCheckpointNumber = pEditor->m_TeleCheckpointNumber; + for(int i = 0; i < s_NumTeleTiles; i++) + { + s_aPreviousTeleNumbers[i] = (*std::next(pEditor->m_TeleNumbers.begin(), i)).second; + } s_PreviousViewTeleNumber = pEditor->m_ViewTeleNumber; return CUi::POPUP_KEEP_OPEN; From a4bb1ec0dce239b7e6aebf12a148fc3830e53d3e Mon Sep 17 00:00:00 2001 From: furo Date: Thu, 10 Oct 2024 15:39:47 +0200 Subject: [PATCH 07/15] Don't check `sv_rescue_delay` in practice --- src/game/server/entities/character.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp index 44363e91533..f73d5c2a7e3 100644 --- a/src/game/server/entities/character.cpp +++ b/src/game/server/entities/character.cpp @@ -2368,7 +2368,7 @@ void CCharacter::Rescue() { if(m_SetSavePos[GetPlayer()->m_RescueMode] && !m_Core.m_Super) { - if(m_LastRescue + (int64_t)g_Config.m_SvRescueDelay * Server()->TickSpeed() > Server()->Tick()) + if(m_LastRescue + (int64_t)g_Config.m_SvRescueDelay * Server()->TickSpeed() > Server()->Tick() && !Teams()->IsPractice(Team())) { char aBuf[256]; str_format(aBuf, sizeof(aBuf), "You have to wait %d seconds until you can rescue yourself", (int)((m_LastRescue + (int64_t)g_Config.m_SvRescueDelay * Server()->TickSpeed() - Server()->Tick()) / Server()->TickSpeed())); From bfe2e4dc8068f9aa9bc7561bf3308b693ce92a02 Mon Sep 17 00:00:00 2001 From: KebsCS Date: Thu, 10 Oct 2024 16:10:50 +0200 Subject: [PATCH 08/15] Add hook collisions preview --- src/game/client/components/menus_settings.cpp | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 95a62cc4462..2150ec4dfa9 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -2858,6 +2858,97 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) DoLine_ColorPicker(&s_HookCollNoCollResetId, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &LeftView, Localize("Nothing hookable"), &g_Config.m_ClHookCollColorNoColl, ColorRGBA(1.0f, 0.0f, 0.0f, 1.0f), false); DoLine_ColorPicker(&s_HookCollHookableCollResetId, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &LeftView, Localize("Something hookable"), &g_Config.m_ClHookCollColorHookableColl, ColorRGBA(130.0f / 255.0f, 232.0f / 255.0f, 160.0f / 255.0f, 1.0f), false); DoLine_ColorPicker(&s_HookCollTeeCollResetId, ColorPickerLineSize, ColorPickerLabelSize, ColorPickerLineSpacing, &LeftView, Localize("A Tee"), &g_Config.m_ClHookCollColorTeeColl, ColorRGBA(1.0f, 1.0f, 0.0f, 1.0f), false); + + // ***** Hook collisions preview ***** // + RightView.HSplitTop(HeadlineHeight, &Label, &RightView); + Ui()->DoLabel(&Label, Localize("Preview"), HeadlineFontSize, TEXTALIGN_ML); + RightView.HSplitTop(2 * MarginSmall, nullptr, &RightView); + + auto DoHookCollision = [this](const vec2 &Pos, const float &Length, const ColorRGBA &Color) { + Graphics()->TextureClear(); + if(g_Config.m_ClHookCollSize > 0) + { + Graphics()->QuadsBegin(); + Graphics()->SetColor(Color.WithAlpha((float)g_Config.m_ClHookCollAlpha / 100)); + float LineWidth = 0.5f + (float)(g_Config.m_ClHookCollSize - 1) * 0.25f; + IGraphics::CQuadItem QuadItem(Pos.x, Pos.y - LineWidth, Length, LineWidth * 2.f); + Graphics()->QuadsDrawTL(&QuadItem, 1); + Graphics()->QuadsEnd(); + } + else + { + Graphics()->LinesBegin(); + Graphics()->SetColor(Color.WithAlpha((float)g_Config.m_ClHookCollAlpha / 100)); + IGraphics::CLineItem LineItem(Pos.x, Pos.y, Pos.x + Length, Pos.y); + Graphics()->LinesDraw(&LineItem, 1); + Graphics()->LinesEnd(); + } + }; + + CTeeRenderInfo OwnSkinInfo; + OwnSkinInfo.Apply(m_pClient->m_Skins.Find(g_Config.m_ClPlayerSkin)); + OwnSkinInfo.m_CustomColoredSkin = g_Config.m_ClPlayerUseCustomColor; + if(g_Config.m_ClPlayerUseCustomColor) + { + OwnSkinInfo.m_ColorBody = color_cast(ColorHSLA(g_Config.m_ClPlayerColorBody).UnclampLighting(ColorHSLA::DARKEST_LGT)); + OwnSkinInfo.m_ColorFeet = color_cast(ColorHSLA(g_Config.m_ClPlayerColorFeet).UnclampLighting(ColorHSLA::DARKEST_LGT)); + } + else + { + OwnSkinInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); + OwnSkinInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); + } + OwnSkinInfo.m_Size = 50.0f; + + CTeeRenderInfo DummySkinInfo; + DummySkinInfo.Apply(m_pClient->m_Skins.Find(g_Config.m_ClDummySkin)); + DummySkinInfo.m_CustomColoredSkin = g_Config.m_ClDummyUseCustomColor; + if(g_Config.m_ClDummyUseCustomColor) + { + DummySkinInfo.m_ColorBody = color_cast(ColorHSLA(g_Config.m_ClDummyColorBody).UnclampLighting(ColorHSLA::DARKEST_LGT)); + DummySkinInfo.m_ColorFeet = color_cast(ColorHSLA(g_Config.m_ClDummyColorFeet).UnclampLighting(ColorHSLA::DARKEST_LGT)); + } + else + { + DummySkinInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); + DummySkinInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); + } + DummySkinInfo.m_Size = 50.0f; + + const float LineLength = 150.f; + const float LeftMargin = 30.f; + + CUIRect PreviewNoColl; + RightView.HSplitTop(50.0f, &PreviewNoColl, &RightView); + RightView.HSplitTop(4 * MarginSmall, nullptr, &RightView); + vec2 TeeRenderPos = vec2(PreviewNoColl.x + LeftMargin, PreviewNoColl.y + PreviewNoColl.h / 2.0f); + DoHookCollision(TeeRenderPos, PreviewNoColl.w - LineLength, color_cast(ColorHSLA(g_Config.m_ClHookCollColorNoColl))); + RenderTools()->RenderTee(CAnimState::GetIdle(), &OwnSkinInfo, 0, vec2(1.0f, 0.0f), TeeRenderPos); + + CUIRect PreviewColl; + RightView.HSplitTop(50.0f, &PreviewColl, &RightView); + RightView.HSplitTop(4 * MarginSmall, nullptr, &RightView); + TeeRenderPos = vec2(PreviewColl.x + LeftMargin, PreviewColl.y + PreviewColl.h / 2.0f); + DoHookCollision(TeeRenderPos, PreviewColl.w - LineLength - LeftMargin, color_cast(ColorHSLA(g_Config.m_ClHookCollColorHookableColl))); + RenderTools()->RenderTee(CAnimState::GetIdle(), &OwnSkinInfo, 0, vec2(1.0f, 0.0f), TeeRenderPos); + + CUIRect TileRect; + PreviewColl.VSplitRight(LineLength + LeftMargin, &PreviewColl, &TileRect); + TileRect.VSplitLeft(50.0f, &TileRect, nullptr); + TileRect.Margin(10.0f, &TileRect); + CUIRect Rect; + TileRect.Margin(3.0f, &Rect); + TileRect.Draw(ColorRGBA(.451f, .435f, .349f), IGraphics::CORNER_ALL, 4.0f); + Rect.Draw(ColorRGBA(.6f, .573f, .451f), IGraphics::CORNER_ALL, 4.0f); + + CUIRect PreviewCollTee; + RightView.HSplitTop(50.0f, &PreviewCollTee, &RightView); + RightView.HSplitTop(4 * MarginSmall, nullptr, &RightView); + TeeRenderPos = vec2(PreviewCollTee.x + LeftMargin, PreviewCollTee.y + PreviewCollTee.h / 2.0f); + const vec2 DummyRenderPos = vec2(PreviewCollTee.x + PreviewCollTee.w - LineLength - 10.f, PreviewCollTee.y + PreviewCollTee.h / 2.0f); + RenderTools()->RenderTee(CAnimState::GetIdle(), &DummySkinInfo, 0, vec2(1.0f, 0.0f), DummyRenderPos); + DoHookCollision(TeeRenderPos, PreviewCollTee.w - LineLength - LeftMargin - 20.f, color_cast(ColorHSLA(g_Config.m_ClHookCollColorTeeColl))); + RenderTools()->RenderTee(CAnimState::GetIdle(), &OwnSkinInfo, 0, vec2(1.0f, 0.0f), TeeRenderPos); } else if(s_CurTab == APPEARANCE_TAB_INFO_MESSAGES) { From 3f829b4ac25f3b36f0bde0e7b7d8d8f89c95f301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 10 Oct 2024 22:28:25 +0200 Subject: [PATCH 09/15] Refactor `CTeeRenderInfo` usage - Add `CTeeRenderInfo::ApplyColors` function to reduce duplicate code. - Use `CTeeRenderInfo::Apply` function in more cases. - Use `CSkins::Find` function instead of implementing default skin handling manually with the `FindOrNullptr` function for chat settings preview. - Remove redundant initialization of `CTeeRenderInfo::m_CustomColoredSkin` member. - Replace empty client skin with `default` instead of checking for empty skin names later. - Remove unnecessary check for empty skin name for ghost rendering. The `CSkins::Find` function will return the default skin for an empty skin name, which is more correct than invalidating the ghost skin render info. --- src/game/client/components/ghost.cpp | 25 +------- src/game/client/components/menus_browser.cpp | 12 +--- src/game/client/components/menus_settings.cpp | 58 ++++--------------- src/game/client/components/players.cpp | 1 - src/game/client/gameclient.cpp | 33 +++-------- src/game/client/render.h | 15 +++++ 6 files changed, 36 insertions(+), 108 deletions(-) diff --git a/src/game/client/components/ghost.cpp b/src/game/client/components/ghost.cpp index 175cfe3a328..a6fe5ab3020 100644 --- a/src/game/client/components/ghost.cpp +++ b/src/game/client/components/ghost.cpp @@ -388,20 +388,8 @@ void CGhost::InitRenderInfos(CGhostItem *pGhost) char aSkinName[MAX_SKIN_LENGTH]; IntsToStr(&pGhost->m_Skin.m_Skin0, 6, aSkinName, std::size(aSkinName)); CTeeRenderInfo *pRenderInfo = &pGhost->m_RenderInfo; - pRenderInfo->Apply(m_pClient->m_Skins.Find(aSkinName)); - pRenderInfo->m_CustomColoredSkin = pGhost->m_Skin.m_UseCustomColor; - if(pGhost->m_Skin.m_UseCustomColor) - { - pRenderInfo->m_ColorBody = color_cast(ColorHSLA(pGhost->m_Skin.m_ColorBody).UnclampLighting(ColorHSLA::DARKEST_LGT)); - pRenderInfo->m_ColorFeet = color_cast(ColorHSLA(pGhost->m_Skin.m_ColorFeet).UnclampLighting(ColorHSLA::DARKEST_LGT)); - } - else - { - pRenderInfo->m_ColorBody = ColorRGBA(1, 1, 1); - pRenderInfo->m_ColorFeet = ColorRGBA(1, 1, 1); - } - + pRenderInfo->ApplyColors(pGhost->m_Skin.m_UseCustomColor, pGhost->m_Skin.m_ColorBody, pGhost->m_Skin.m_ColorFeet); pRenderInfo->m_Size = 64; } @@ -687,16 +675,7 @@ void CGhost::OnRefreshSkins() return; char aSkinName[MAX_SKIN_LENGTH]; IntsToStr(&Ghost.m_Skin.m_Skin0, 6, aSkinName, std::size(aSkinName)); - CTeeRenderInfo *pRenderInfo = &Ghost.m_RenderInfo; - if(aSkinName[0] != '\0') - { - pRenderInfo->Apply(m_pClient->m_Skins.Find(aSkinName)); - } - else - { - pRenderInfo->m_OriginalRenderSkin.Reset(); - pRenderInfo->m_ColorableRenderSkin.Reset(); - } + Ghost.m_RenderInfo.Apply(m_pClient->m_Skins.Find(aSkinName)); }; for(auto &Ghost : m_aActiveGhosts) diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index ac5e3328be7..608975e4660 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -1846,17 +1846,7 @@ CTeeRenderInfo CMenus::GetTeeRenderInfo(vec2 Size, const char *pSkinName, bool C { CTeeRenderInfo TeeInfo; TeeInfo.Apply(m_pClient->m_Skins.Find(pSkinName)); - TeeInfo.m_CustomColoredSkin = CustomSkinColors; - if(CustomSkinColors) - { - TeeInfo.m_ColorBody = color_cast(ColorHSLA(CustomSkinColorBody).UnclampLighting(ColorHSLA::DARKEST_LGT)); - TeeInfo.m_ColorFeet = color_cast(ColorHSLA(CustomSkinColorFeet).UnclampLighting(ColorHSLA::DARKEST_LGT)); - } - else - { - TeeInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); - TeeInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); - } + TeeInfo.ApplyColors(CustomSkinColors, CustomSkinColorBody, CustomSkinColorFeet); TeeInfo.m_Size = minimum(Size.x, Size.y); return TeeInfo; } diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 2150ec4dfa9..2c97ccaba87 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -593,17 +593,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) // which invalidates the skin. CTeeRenderInfo OwnSkinInfo; OwnSkinInfo.Apply(m_pClient->m_Skins.Find(pSkinName)); - OwnSkinInfo.m_CustomColoredSkin = *pUseCustomColor; - if(*pUseCustomColor) - { - OwnSkinInfo.m_ColorBody = color_cast(ColorHSLA(*pColorBody).UnclampLighting(ColorHSLA::DARKEST_LGT)); - OwnSkinInfo.m_ColorFeet = color_cast(ColorHSLA(*pColorFeet).UnclampLighting(ColorHSLA::DARKEST_LGT)); - } - else - { - OwnSkinInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); - OwnSkinInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); - } + OwnSkinInfo.ApplyColors(*pUseCustomColor, *pColorBody, *pColorFeet); OwnSkinInfo.m_Size = 50.0f; // Tee @@ -786,10 +776,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView) Item.m_Rect.VSplitLeft(60.0f, &Button, &Label); CTeeRenderInfo Info = OwnSkinInfo; - Info.m_CustomColoredSkin = *pUseCustomColor; - Info.m_OriginalRenderSkin = pSkinToBeDraw->m_OriginalSkin; - Info.m_ColorableRenderSkin = pSkinToBeDraw->m_ColorableSkin; - Info.m_SkinMetrics = pSkinToBeDraw->m_Metrics; + Info.Apply(pSkinToBeDraw); vec2 OffsetToMid; CRenderTools::GetRenderTeeOffsetToRenderedTee(CAnimState::GetIdle(), &Info, OffsetToMid); @@ -2514,7 +2501,6 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) static std::vector s_vLines; - const auto *pDefaultSkin = GameClient()->m_Skins.Find("default"); enum ELineFlag { FLAG_TEAM = 1 << 0, @@ -2552,15 +2538,11 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) str_copy(pLine->m_aName, pName); str_copy(pLine->m_aText, pText); }; - auto &&SetLineSkin = [RealTeeSize, &pDefaultSkin](int Index, const CSkin *pSkin) { + auto &&SetLineSkin = [RealTeeSize](int Index, const CSkin *pSkin) { if(Index >= (int)s_vLines.size()) return; s_vLines[Index].m_RenderInfo.m_Size = RealTeeSize; - s_vLines[Index].m_RenderInfo.m_CustomColoredSkin = false; - if(pSkin != nullptr) - s_vLines[Index].m_RenderInfo.m_OriginalRenderSkin = pSkin->m_OriginalSkin; - else if(pDefaultSkin != nullptr) - s_vLines[Index].m_RenderInfo.m_OriginalRenderSkin = pDefaultSkin->m_OriginalSkin; + s_vLines[Index].m_RenderInfo.Apply(pSkin); }; auto &&RenderPreview = [&](int LineIndex, int x, int y, bool Render = true) { @@ -2666,10 +2648,10 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) SetPreviewLine(PREVIEW_CLIENT, -1, "— ", "Echo command executed", FLAG_CLIENT, 0); } - SetLineSkin(1, GameClient()->m_Skins.FindOrNullptr("pinky")); - SetLineSkin(2, pDefaultSkin); - SetLineSkin(3, GameClient()->m_Skins.FindOrNullptr("cammostripes")); - SetLineSkin(4, GameClient()->m_Skins.FindOrNullptr("beast")); + SetLineSkin(1, GameClient()->m_Skins.Find("pinky")); + SetLineSkin(2, GameClient()->m_Skins.Find("default")); + SetLineSkin(3, GameClient()->m_Skins.Find("cammostripes")); + SetLineSkin(4, GameClient()->m_Skins.Find("beast")); // Backgrounds first if(!g_Config.m_ClChatOld) @@ -2887,32 +2869,12 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) CTeeRenderInfo OwnSkinInfo; OwnSkinInfo.Apply(m_pClient->m_Skins.Find(g_Config.m_ClPlayerSkin)); - OwnSkinInfo.m_CustomColoredSkin = g_Config.m_ClPlayerUseCustomColor; - if(g_Config.m_ClPlayerUseCustomColor) - { - OwnSkinInfo.m_ColorBody = color_cast(ColorHSLA(g_Config.m_ClPlayerColorBody).UnclampLighting(ColorHSLA::DARKEST_LGT)); - OwnSkinInfo.m_ColorFeet = color_cast(ColorHSLA(g_Config.m_ClPlayerColorFeet).UnclampLighting(ColorHSLA::DARKEST_LGT)); - } - else - { - OwnSkinInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); - OwnSkinInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); - } + OwnSkinInfo.ApplyColors(g_Config.m_ClPlayerUseCustomColor, g_Config.m_ClPlayerColorBody, g_Config.m_ClPlayerColorFeet); OwnSkinInfo.m_Size = 50.0f; CTeeRenderInfo DummySkinInfo; DummySkinInfo.Apply(m_pClient->m_Skins.Find(g_Config.m_ClDummySkin)); - DummySkinInfo.m_CustomColoredSkin = g_Config.m_ClDummyUseCustomColor; - if(g_Config.m_ClDummyUseCustomColor) - { - DummySkinInfo.m_ColorBody = color_cast(ColorHSLA(g_Config.m_ClDummyColorBody).UnclampLighting(ColorHSLA::DARKEST_LGT)); - DummySkinInfo.m_ColorFeet = color_cast(ColorHSLA(g_Config.m_ClDummyColorFeet).UnclampLighting(ColorHSLA::DARKEST_LGT)); - } - else - { - DummySkinInfo.m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f); - DummySkinInfo.m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f); - } + DummySkinInfo.ApplyColors(g_Config.m_ClDummyUseCustomColor, g_Config.m_ClDummyColorBody, g_Config.m_ClDummyColorFeet); DummySkinInfo.m_Size = 50.0f; const float LineLength = 150.f; diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp index 36891669595..46ec5e129f3 100644 --- a/src/game/client/components/players.cpp +++ b/src/game/client/components/players.cpp @@ -862,7 +862,6 @@ void CPlayers::OnRender() } CTeeRenderInfo RenderInfoSpec; RenderInfoSpec.Apply(m_pClient->m_Skins.Find("x_spec")); - RenderInfoSpec.m_CustomColoredSkin = false; RenderInfoSpec.m_Size = 64.0f; const int LocalClientId = m_pClient->m_Snap.m_LocalClientId; diff --git a/src/game/client/gameclient.cpp b/src/game/client/gameclient.cpp index 9e501159514..cedda4f7773 100644 --- a/src/game/client/gameclient.cpp +++ b/src/game/client/gameclient.cpp @@ -1542,30 +1542,21 @@ void CGameClient::OnNewSnapshot() } IntsToStr(&pInfo->m_Clan0, 3, pClient->m_aClan, std::size(pClient->m_aClan)); pClient->m_Country = pInfo->m_Country; + IntsToStr(&pInfo->m_Skin0, 6, pClient->m_aSkinName, std::size(pClient->m_aSkinName)); + if(pClient->m_aSkinName[0] == '\0' || + (!m_GameInfo.m_AllowXSkins && (pClient->m_aSkinName[0] == 'x' && pClient->m_aSkinName[1] == '_'))) + { + str_copy(pClient->m_aSkinName, "default"); + } pClient->m_UseCustomColor = pInfo->m_UseCustomColor; pClient->m_ColorBody = pInfo->m_ColorBody; pClient->m_ColorFeet = pInfo->m_ColorFeet; - // prepare the info - if(!m_GameInfo.m_AllowXSkins && (pClient->m_aSkinName[0] == 'x' && pClient->m_aSkinName[1] == '_')) - str_copy(pClient->m_aSkinName, "default"); - - pClient->m_SkinInfo.m_ColorBody = color_cast(ColorHSLA(pClient->m_ColorBody).UnclampLighting(ColorHSLA::DARKEST_LGT)); - pClient->m_SkinInfo.m_ColorFeet = color_cast(ColorHSLA(pClient->m_ColorFeet).UnclampLighting(ColorHSLA::DARKEST_LGT)); pClient->m_SkinInfo.m_Size = 64; - - // find new skin pClient->m_SkinInfo.Apply(m_Skins.Find(pClient->m_aSkinName)); - pClient->m_SkinInfo.m_CustomColoredSkin = pClient->m_UseCustomColor; - - if(!pClient->m_UseCustomColor) - { - pClient->m_SkinInfo.m_ColorBody = ColorRGBA(1, 1, 1); - pClient->m_SkinInfo.m_ColorFeet = ColorRGBA(1, 1, 1); - } - + pClient->m_SkinInfo.ApplyColors(pClient->m_UseCustomColor, pClient->m_ColorBody, pClient->m_ColorFeet); pClient->UpdateRenderInfo(IsTeamPlay()); } } @@ -3755,15 +3746,7 @@ void CGameClient::RefreshSkins() for(auto &Client : m_aClients) { - if(Client.m_aSkinName[0] != '\0') - { - Client.m_SkinInfo.Apply(m_Skins.Find(Client.m_aSkinName)); - } - else - { - Client.m_SkinInfo.m_OriginalRenderSkin.Reset(); - Client.m_SkinInfo.m_ColorableRenderSkin.Reset(); - } + Client.m_SkinInfo.Apply(m_Skins.Find(Client.m_aSkinName)); Client.UpdateRenderInfo(IsTeamPlay()); } diff --git a/src/game/client/render.h b/src/game/client/render.h index 11b7255b96a..44dd2766f11 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -64,6 +64,21 @@ class CTeeRenderInfo m_SkinMetrics = pSkin->m_Metrics; } + void ApplyColors(bool CustomColoredSkin, int ColorBody, int ColorFeet) + { + m_CustomColoredSkin = CustomColoredSkin; + if(CustomColoredSkin) + { + m_ColorBody = color_cast(ColorHSLA(ColorBody).UnclampLighting(ColorHSLA::DARKEST_LGT)); + m_ColorFeet = color_cast(ColorHSLA(ColorFeet).UnclampLighting(ColorHSLA::DARKEST_LGT)); + } + else + { + m_ColorBody = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); + m_ColorFeet = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f); + } + } + CSkin::SSkinTextures m_OriginalRenderSkin; CSkin::SSkinTextures m_ColorableRenderSkin; From 279b14cc44f4a60914d016fb904c797cb83a4d51 Mon Sep 17 00:00:00 2001 From: KebsCS Date: Thu, 10 Oct 2024 23:51:14 +0200 Subject: [PATCH 10/15] Add regional rankings to /top5team --- src/engine/shared/config_variables.h | 2 +- src/game/server/scoreworker.cpp | 130 ++++++++++++++++++--------- src/game/server/scoreworker.h | 2 + src/test/score.cpp | 45 +++++++--- 4 files changed, 123 insertions(+), 56 deletions(-) diff --git a/src/engine/shared/config_variables.h b/src/engine/shared/config_variables.h index 45859dec3ce..f41e2dfd54e 100644 --- a/src/engine/shared/config_variables.h +++ b/src/engine/shared/config_variables.h @@ -494,7 +494,7 @@ MACRO_CONFIG_INT(SvSpecFrequency, sv_pause_frequency, 1, 0, 9999, CFGFLAG_SERVER MACRO_CONFIG_INT(SvInvite, sv_invite, 1, 0, 1, CFGFLAG_SERVER, "Whether players can invite other players to teams") MACRO_CONFIG_INT(SvInviteFrequency, sv_invite_frequency, 1, 0, 9999, CFGFLAG_SERVER, "The minimum allowed delay between invites") MACRO_CONFIG_INT(SvTeleOthersAuthLevel, sv_tele_others_auth_level, 1, 1, 3, CFGFLAG_SERVER, "The auth level you need to tele others") -MACRO_CONFIG_INT(SvRegionalRankings, sv_regional_rankings, 1, 0, 1, CFGFLAG_SERVER, "Display regional rankings in /rank and /top5") +MACRO_CONFIG_INT(SvRegionalRankings, sv_regional_rankings, 1, 0, 1, CFGFLAG_SERVER, "Display regional rankings in /rank, /top5 and /top5team") MACRO_CONFIG_INT(SvEmotionalTees, sv_emotional_tees, 1, -1, 1, CFGFLAG_SERVER, "Whether eye change of tees is enabled with emoticons = 1, not = 0, -1 not at all") MACRO_CONFIG_INT(SvEmoticonMsDelay, sv_emoticon_ms_delay, 3000, 20, 999999999, CFGFLAG_SERVER, "The time in ms a player has to wait before allowing the next over-head emoticons") diff --git a/src/game/server/scoreworker.cpp b/src/game/server/scoreworker.cpp index 76d85f5a56d..50d7b952fc5 100644 --- a/src/game/server/scoreworker.cpp +++ b/src/game/server/scoreworker.cpp @@ -99,6 +99,48 @@ bool CTeamrank::SamePlayers(const std::vector *pvSortedNames) return true; } +bool CTeamrank::GetSqlTop5Team(IDbConnection *pSqlServer, bool *pEnd, char *pError, int ErrorSize, char (*paMessages)[512], int *Line, int Count) +{ + char aTime[32]; + int StartLine = *Line; + for(*Line = StartLine; *Line < StartLine + Count; (*Line)++) + { + bool Last = false; + float Time = pSqlServer->GetFloat(2); + str_time_float(Time, TIME_HOURS_CENTISECS, aTime, sizeof(aTime)); + int Rank = pSqlServer->GetInt(3); + int TeamSize = pSqlServer->GetInt(4); + + char aNames[2300] = {0}; + for(int i = 0; i < TeamSize; i++) + { + char aName[MAX_NAME_LENGTH]; + pSqlServer->GetString(1, aName, sizeof(aName)); + str_append(aNames, aName); + if(i < TeamSize - 2) + str_append(aNames, ", "); + else if(i == TeamSize - 2) + str_append(aNames, " & "); + if(pSqlServer->Step(&Last, pError, ErrorSize)) + { + return true; + } + if(Last) + { + break; + } + } + str_format(paMessages[*Line], sizeof(paMessages[*Line]), "%d. %s Team Time: %s", + Rank, aNames, aTime); + if(Last) + { + (*Line)++; + break; + } + } + return false; +} + bool CScoreWorker::LoadBestTime(IDbConnection *pSqlServer, const ISqlData *pGameData, char *pError, int ErrorSize) { const auto *pData = dynamic_cast(pGameData); @@ -1071,34 +1113,40 @@ bool CScoreWorker::ShowTeamTop5(IDbConnection *pSqlServer, const ISqlData *pGame int LimitStart = maximum(absolute(pData->m_Offset) - 1, 0); const char *pOrder = pData->m_Offset >= 0 ? "ASC" : "DESC"; + const char *pAny = "%"; // check sort method char aBuf[1024]; str_format(aBuf, sizeof(aBuf), "SELECT Name, Time, Ranking, TeamSize " - "FROM (" // limit to 5 - " SELECT TeamSize, Ranking, Id " + "FROM (" + " SELECT TeamSize, Ranking, Id, Server " " FROM (" // teamrank score board - " SELECT RANK() OVER w AS Ranking, COUNT(*) AS Teamsize, Id " - " FROM %s_teamrace " + " SELECT RANK() OVER w AS Ranking, COUNT(*) AS Teamsize, Id, Server " + " FROM (" + " SELECT * FROM %s_teamrace as tr " + " INNER JOIN %s_race as rr ON tr.Map = rr.Map AND tr.Name = rr.Name AND tr.Time = rr.Time AND tr.Timestamp = rr.Timestamp" + " ) " " WHERE Map = ? " " GROUP BY ID " " WINDOW w AS (ORDER BY Min(Time))" " ) as l1 " + " WHERE Server LIKE ? " " ORDER BY Ranking %s " - " LIMIT %d, 5" + " LIMIT %d, ?" ") as l2 " "INNER JOIN %s_teamrace as r ON l2.Id = r.Id " "ORDER BY Ranking %s, r.Id, Name ASC", - pSqlServer->GetPrefix(), pOrder, LimitStart, pSqlServer->GetPrefix(), pOrder); + pSqlServer->GetPrefix(), pSqlServer->GetPrefix(), pOrder, LimitStart, pSqlServer->GetPrefix(), pOrder); if(pSqlServer->PrepareStatement(aBuf, pError, ErrorSize)) { return true; } pSqlServer->BindString(1, pData->m_aMap); + pSqlServer->BindString(2, pAny); + pSqlServer->BindInt(3, 5); - // show teamtop5 int Line = 0; str_copy(paMessages[Line++], "------- Team Top 5 -------", sizeof(paMessages[Line])); @@ -1109,44 +1157,44 @@ bool CScoreWorker::ShowTeamTop5(IDbConnection *pSqlServer, const ISqlData *pGame } if(!End) { - for(Line = 1; Line < 6; Line++) // print + if(CTeamrank::GetSqlTop5Team(pSqlServer, &End, pError, ErrorSize, paMessages, &Line, 5)) { - bool Last = false; - float Time = pSqlServer->GetFloat(2); - str_time_float(Time, TIME_HOURS_CENTISECS, aBuf, sizeof(aBuf)); - int Rank = pSqlServer->GetInt(3); - int TeamSize = pSqlServer->GetInt(4); - - char aNames[2300] = {0}; - for(int i = 0; i < TeamSize; i++) - { - char aName[MAX_NAME_LENGTH]; - pSqlServer->GetString(1, aName, sizeof(aName)); - str_append(aNames, aName); - if(i < TeamSize - 2) - str_append(aNames, ", "); - else if(i == TeamSize - 2) - str_append(aNames, " & "); - if(pSqlServer->Step(&Last, pError, ErrorSize)) - { - return true; - } - if(Last) - { - break; - } - } - str_format(paMessages[Line], sizeof(paMessages[Line]), "%d. %s Team Time: %s", - Rank, aNames, aBuf); - if(Last) - { - Line++; - break; - } + return true; } } - str_copy(paMessages[Line], "---------------------------------", sizeof(paMessages[Line])); + if(!g_Config.m_SvRegionalRankings) + { + str_copy(paMessages[Line], "-------------------------------", sizeof(paMessages[Line])); + return false; + } + + char aServerLike[16]; + str_format(aServerLike, sizeof(aServerLike), "%%%s%%", pData->m_aServer); + + if(pSqlServer->PrepareStatement(aBuf, pError, ErrorSize)) + { + return true; + } + pSqlServer->BindString(1, pData->m_aMap); + pSqlServer->BindString(2, aServerLike); + pSqlServer->BindInt(3, 3); + + str_format(pResult->m_Data.m_aaMessages[Line], sizeof(pResult->m_Data.m_aaMessages[Line]), + "----- %s Team Top -----", pData->m_aServer); + Line++; + + if(pSqlServer->Step(&End, pError, ErrorSize)) + { + return true; + } + if(!End) + { + if(CTeamrank::GetSqlTop5Team(pSqlServer, &End, pError, ErrorSize, paMessages, &Line, 3)) + { + return true; + } + } return false; } diff --git a/src/game/server/scoreworker.h b/src/game/server/scoreworker.h index 3001bad2361..1969838dc17 100644 --- a/src/game/server/scoreworker.h +++ b/src/game/server/scoreworker.h @@ -280,6 +280,8 @@ struct CTeamrank bool NextSqlResult(IDbConnection *pSqlServer, bool *pEnd, char *pError, int ErrorSize); bool SamePlayers(const std::vector *pvSortedNames); + + static bool GetSqlTop5Team(IDbConnection *pSqlServer, bool *pEnd, char *pError, int ErrorSize, char (*paMessages)[512], int *StartLine, int Count); }; struct CScoreWorker diff --git a/src/test/score.cpp b/src/test/score.cpp index 4bd3dcdec6d..d24b52fb1dc 100644 --- a/src/test/score.cpp +++ b/src/test/score.cpp @@ -282,46 +282,63 @@ struct TeamScore : public Score { void SetUp() override { - CSqlTeamScoreData teamScoreData; - str_copy(teamScoreData.m_aMap, "Kobra 3", sizeof(teamScoreData.m_aMap)); - str_copy(teamScoreData.m_aGameUuid, "8d300ecf-5873-4297-bee5-95668fdff320", sizeof(teamScoreData.m_aGameUuid)); - teamScoreData.m_Size = 2; - str_copy(teamScoreData.m_aaNames[0], "nameless tee", sizeof(teamScoreData.m_aaNames[0])); - str_copy(teamScoreData.m_aaNames[1], "brainless tee", sizeof(teamScoreData.m_aaNames[1])); - teamScoreData.m_Time = 100.0; - str_copy(teamScoreData.m_aTimestamp, "2021-11-24 19:24:08", sizeof(teamScoreData.m_aTimestamp)); - ASSERT_FALSE(CScoreWorker::SaveTeamScore(m_pConn, &teamScoreData, Write::NORMAL, m_aError, sizeof(m_aError))) << m_aError; - - str_copy(m_PlayerRequest.m_aMap, "Kobra 3", sizeof(m_PlayerRequest.m_aMap)); - str_copy(m_PlayerRequest.m_aRequestingPlayer, "brainless tee", sizeof(m_PlayerRequest.m_aRequestingPlayer)); - m_PlayerRequest.m_Offset = 0; + InsertTeamRank(100.0); } void InsertTeamRank(float Time = 100.0) { + str_copy(g_Config.m_SvSqlServerName, "USA", sizeof(g_Config.m_SvSqlServerName)); CSqlTeamScoreData teamScoreData; + CSqlScoreData ScoreData(std::make_shared()); str_copy(teamScoreData.m_aMap, "Kobra 3", sizeof(teamScoreData.m_aMap)); + str_copy(ScoreData.m_aMap, "Kobra 3", sizeof(ScoreData.m_aMap)); str_copy(teamScoreData.m_aGameUuid, "8d300ecf-5873-4297-bee5-95668fdff320", sizeof(teamScoreData.m_aGameUuid)); + str_copy(ScoreData.m_aGameUuid, "8d300ecf-5873-4297-bee5-95668fdff320", sizeof(ScoreData.m_aGameUuid)); teamScoreData.m_Size = 2; str_copy(teamScoreData.m_aaNames[0], "nameless tee", sizeof(teamScoreData.m_aaNames[0])); str_copy(teamScoreData.m_aaNames[1], "brainless tee", sizeof(teamScoreData.m_aaNames[1])); teamScoreData.m_Time = Time; + ScoreData.m_Time = Time; str_copy(teamScoreData.m_aTimestamp, "2021-11-24 19:24:08", sizeof(teamScoreData.m_aTimestamp)); + str_copy(ScoreData.m_aTimestamp, "2021-11-24 19:24:08", sizeof(ScoreData.m_aTimestamp)); + for(int i = 0; i < NUM_CHECKPOINTS; i++) + ScoreData.m_aCurrentTimeCp[i] = 0; ASSERT_FALSE(CScoreWorker::SaveTeamScore(m_pConn, &teamScoreData, Write::NORMAL, m_aError, sizeof(m_aError))) << m_aError; str_copy(m_PlayerRequest.m_aMap, "Kobra 3", sizeof(m_PlayerRequest.m_aMap)); str_copy(m_PlayerRequest.m_aRequestingPlayer, "brainless tee", sizeof(m_PlayerRequest.m_aRequestingPlayer)); + str_copy(ScoreData.m_aRequestingPlayer, "brainless tee", sizeof(ScoreData.m_aRequestingPlayer)); + + str_copy(ScoreData.m_aName, "nameless tee", sizeof(ScoreData.m_aName)); + ScoreData.m_ClientId = 0; + ASSERT_FALSE(CScoreWorker::SaveScore(m_pConn, &ScoreData, Write::NORMAL, m_aError, sizeof(m_aError))) << m_aError; + str_copy(ScoreData.m_aName, "brainless tee", sizeof(ScoreData.m_aName)); + ScoreData.m_ClientId = 1; + ASSERT_FALSE(CScoreWorker::SaveScore(m_pConn, &ScoreData, Write::NORMAL, m_aError, sizeof(m_aError))) << m_aError; m_PlayerRequest.m_Offset = 0; } }; TEST_P(TeamScore, All) { + g_Config.m_SvRegionalRankings = false; ASSERT_FALSE(CScoreWorker::ShowTeamTop5(m_pConn, &m_PlayerRequest, m_aError, sizeof(m_aError))) << m_aError; ExpectLines(m_pPlayerResult, {"------- Team Top 5 -------", "1. brainless tee & nameless tee Team Time: 01:40.00", - "---------------------------------"}); + "-------------------------------"}); +} + +TEST_P(TeamScore, TeamTop5Regional) +{ + g_Config.m_SvRegionalRankings = true; + str_copy(m_PlayerRequest.m_aServer, "USA", sizeof(m_PlayerRequest.m_aServer)); + ASSERT_FALSE(CScoreWorker::ShowTeamTop5(m_pConn, &m_PlayerRequest, m_aError, sizeof(m_aError))) << m_aError; + ExpectLines(m_pPlayerResult, + {"------- Team Top 5 -------", + "1. brainless tee & nameless tee Team Time: 01:40.00", + "----- USA Team Top -----", + "1. brainless tee & nameless tee Team Time: 01:40.00"}); } TEST_P(TeamScore, PlayerExists) From 9fdb24672485e56b6353aa0f83269312aa3fe781 Mon Sep 17 00:00:00 2001 From: furo Date: Fri, 11 Oct 2024 01:09:38 +0200 Subject: [PATCH 11/15] Render hookable and unhookable tiles for hook collision preview --- src/game/client/components/menus_settings.cpp | 40 +++++++---- src/game/client/render.h | 2 + src/game/client/render_map.cpp | 67 +++++++++++++++++++ 3 files changed, 97 insertions(+), 12 deletions(-) diff --git a/src/game/client/components/menus_settings.cpp b/src/game/client/components/menus_settings.cpp index 2150ec4dfa9..a680185bd39 100644 --- a/src/game/client/components/menus_settings.cpp +++ b/src/game/client/components/menus_settings.cpp @@ -2842,7 +2842,7 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) } LeftView.HSplitTop(2 * LineSize, &Button, &LeftView); - Ui()->DoScrollbarOption(&g_Config.m_ClHookCollSize, &g_Config.m_ClHookCollSize, &Button, Localize("Hook collision line width"), 0, 20, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_MULTILINE); + Ui()->DoScrollbarOption(&g_Config.m_ClHookCollSize, &g_Config.m_ClHookCollSize, &Button, Localize("Hook collision line width"), 0, 10000, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_MULTILINE); LeftView.HSplitTop(2 * LineSize, &Button, &LeftView); Ui()->DoScrollbarOption(&g_Config.m_ClHookCollAlpha, &g_Config.m_ClHookCollAlpha, &Button, Localize("Hook collision line opacity"), 0, 100, &CUi::ms_LinearScrollbarScale, CUi::SCROLLBAR_OPTION_MULTILINE, "%"); @@ -2925,29 +2925,45 @@ void CMenus::RenderSettingsAppearance(CUIRect MainView) DoHookCollision(TeeRenderPos, PreviewNoColl.w - LineLength, color_cast(ColorHSLA(g_Config.m_ClHookCollColorNoColl))); RenderTools()->RenderTee(CAnimState::GetIdle(), &OwnSkinInfo, 0, vec2(1.0f, 0.0f), TeeRenderPos); + CUIRect NoHookTileRect; + PreviewNoColl.VSplitRight(LineLength, &PreviewNoColl, &NoHookTileRect); + NoHookTileRect.VSplitLeft(50.0f, &NoHookTileRect, nullptr); + NoHookTileRect.Margin(10.0f, &NoHookTileRect); + + // Render unhookable tile + int TileScale = 32.0f; + Graphics()->TextureClear(); + Graphics()->TextureSet(m_pClient->m_MapImages.GetEntities(MAP_IMAGE_ENTITY_LAYER_TYPE_ALL_EXCEPT_SWITCH)); + Graphics()->BlendNormal(); + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + RenderTools()->RenderTile(NoHookTileRect.x, NoHookTileRect.y, TILE_NOHOOK, TileScale, ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f)); + CUIRect PreviewColl; RightView.HSplitTop(50.0f, &PreviewColl, &RightView); RightView.HSplitTop(4 * MarginSmall, nullptr, &RightView); TeeRenderPos = vec2(PreviewColl.x + LeftMargin, PreviewColl.y + PreviewColl.h / 2.0f); - DoHookCollision(TeeRenderPos, PreviewColl.w - LineLength - LeftMargin, color_cast(ColorHSLA(g_Config.m_ClHookCollColorHookableColl))); + DoHookCollision(TeeRenderPos, PreviewColl.w - LineLength, color_cast(ColorHSLA(g_Config.m_ClHookCollColorHookableColl))); RenderTools()->RenderTee(CAnimState::GetIdle(), &OwnSkinInfo, 0, vec2(1.0f, 0.0f), TeeRenderPos); - CUIRect TileRect; - PreviewColl.VSplitRight(LineLength + LeftMargin, &PreviewColl, &TileRect); - TileRect.VSplitLeft(50.0f, &TileRect, nullptr); - TileRect.Margin(10.0f, &TileRect); - CUIRect Rect; - TileRect.Margin(3.0f, &Rect); - TileRect.Draw(ColorRGBA(.451f, .435f, .349f), IGraphics::CORNER_ALL, 4.0f); - Rect.Draw(ColorRGBA(.6f, .573f, .451f), IGraphics::CORNER_ALL, 4.0f); + CUIRect HookTileRect; + PreviewColl.VSplitRight(LineLength, &PreviewColl, &HookTileRect); + HookTileRect.VSplitLeft(50.0f, &HookTileRect, nullptr); + HookTileRect.Margin(10.0f, &HookTileRect); + + // Render hookable tile + Graphics()->TextureClear(); + Graphics()->TextureSet(m_pClient->m_MapImages.GetEntities(MAP_IMAGE_ENTITY_LAYER_TYPE_ALL_EXCEPT_SWITCH)); + Graphics()->BlendNormal(); + Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f); + RenderTools()->RenderTile(HookTileRect.x, HookTileRect.y, TILE_SOLID, TileScale, ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f)); CUIRect PreviewCollTee; RightView.HSplitTop(50.0f, &PreviewCollTee, &RightView); RightView.HSplitTop(4 * MarginSmall, nullptr, &RightView); TeeRenderPos = vec2(PreviewCollTee.x + LeftMargin, PreviewCollTee.y + PreviewCollTee.h / 2.0f); - const vec2 DummyRenderPos = vec2(PreviewCollTee.x + PreviewCollTee.w - LineLength - 10.f, PreviewCollTee.y + PreviewCollTee.h / 2.0f); + const vec2 DummyRenderPos = vec2(PreviewCollTee.x + PreviewCollTee.w - LineLength - 5.f + LeftMargin, PreviewCollTee.y + PreviewCollTee.h / 2.0f); + DoHookCollision(TeeRenderPos, PreviewCollTee.w - LineLength - 15.f, color_cast(ColorHSLA(g_Config.m_ClHookCollColorTeeColl))); RenderTools()->RenderTee(CAnimState::GetIdle(), &DummySkinInfo, 0, vec2(1.0f, 0.0f), DummyRenderPos); - DoHookCollision(TeeRenderPos, PreviewCollTee.w - LineLength - LeftMargin - 20.f, color_cast(ColorHSLA(g_Config.m_ClHookCollColorTeeColl))); RenderTools()->RenderTee(CAnimState::GetIdle(), &OwnSkinInfo, 0, vec2(1.0f, 0.0f), TeeRenderPos); } else if(s_CurTab == APPEARANCE_TAB_INFO_MESSAGES) diff --git a/src/game/client/render.h b/src/game/client/render.h index 11b7255b96a..3a37168d105 100644 --- a/src/game/client/render.h +++ b/src/game/client/render.h @@ -226,6 +226,8 @@ class CRenderTools // the rectangle include all tiles in [RectX, RectX+RectW-1] x [RectY, RectY+RectH-1] void RenderTileRectangle(int RectX, int RectY, int RectW, int RectH, unsigned char IndexIn, unsigned char IndexOut, float Scale, ColorRGBA Color, int RenderFlags) const; + void RenderTile(int x, int y, unsigned char Index, float Scale, ColorRGBA Color) const; + // helpers void CalcScreenParams(float Aspect, float Zoom, float *pWidth, float *pHeight); void MapScreenToWorld(float CenterX, float CenterY, float ParallaxX, float ParallaxY, diff --git a/src/game/client/render_map.cpp b/src/game/client/render_map.cpp index f096a773d2d..b438d8b1e3b 100644 --- a/src/game/client/render_map.cpp +++ b/src/game/client/render_map.cpp @@ -667,6 +667,73 @@ void CRenderTools::RenderTilemap(CTile *pTiles, int w, int h, float Scale, Color Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); } +void CRenderTools::RenderTile(int x, int y, unsigned char Index, float Scale, ColorRGBA Color) const +{ + if(Graphics()->HasTextureArraysSupport()) + Graphics()->QuadsTex3DBegin(); + else + Graphics()->QuadsBegin(); + + float ScreenX0, ScreenY0, ScreenX1, ScreenY1; + Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1); + + // calculate the final pixelsize for the tiles + float TilePixelSize = 1024 / Scale; + float FinalTileSize = Scale / (ScreenX1 - ScreenX0) * Graphics()->ScreenWidth(); + float FinalTilesetScale = FinalTileSize / TilePixelSize; + + float TexSize = 1024.0f; + float Frac = (1.25f / TexSize) * (1 / FinalTilesetScale); + float Nudge = (0.5f / TexSize) * (1 / FinalTilesetScale); + + int tx = Index % 16; + int ty = Index / 16; + int Px0 = tx * (1024 / 16); + int Py0 = ty * (1024 / 16); + int Px1 = Px0 + (1024 / 16) - 1; + int Py1 = Py0 + (1024 / 16) - 1; + + float x0 = Nudge + Px0 / TexSize + Frac; + float y0 = Nudge + Py0 / TexSize + Frac; + float x1 = Nudge + Px1 / TexSize - Frac; + float y1 = Nudge + Py0 / TexSize + Frac; + float x2 = Nudge + Px1 / TexSize - Frac; + float y2 = Nudge + Py1 / TexSize - Frac; + float x3 = Nudge + Px0 / TexSize + Frac; + float y3 = Nudge + Py1 / TexSize - Frac; + + if(Graphics()->HasTextureArraysSupport()) + { + x0 = 0; + y0 = 0; + x1 = x0 + 1; + y1 = y0; + x2 = x0 + 1; + y2 = y0 + 1; + x3 = x0; + y3 = y0 + 1; + } + + if(Graphics()->HasTextureArraysSupport()) + { + Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3, Index); + IGraphics::CQuadItem QuadItem(x, y, Scale, Scale); + Graphics()->QuadsTex3DDrawTL(&QuadItem, 1); + } + else + { + Graphics()->QuadsSetSubsetFree(x0, y0, x1, y1, x2, y2, x3, y3); + IGraphics::CQuadItem QuadItem(x, y, Scale, Scale); + Graphics()->QuadsDrawTL(&QuadItem, 1); + } + + if(Graphics()->HasTextureArraysSupport()) + Graphics()->QuadsTex3DEnd(); + else + Graphics()->QuadsEnd(); + Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1); +} + void CRenderTools::RenderTeleOverlay(CTeleTile *pTele, int w, int h, float Scale, float Alpha) const { if(!g_Config.m_ClTextEntities) From 3c9d92a527b7880dc63a116786b5605cdeacc6a0 Mon Sep 17 00:00:00 2001 From: KebsCS Date: Thu, 10 Oct 2024 21:27:55 +0200 Subject: [PATCH 12/15] Add Antarctica flag, update some existing flags --- CMakeLists.txt | 1 + data/countryflags/AQ.png | Bin 0 -> 1576 bytes data/countryflags/AZ.png | Bin 1188 -> 1420 bytes data/countryflags/BY.png | Bin 1706 -> 2907 bytes data/countryflags/HN.png | Bin 1118 -> 1316 bytes data/countryflags/MR.png | Bin 1430 -> 1673 bytes data/countryflags/SS.png | Bin 1333 -> 2406 bytes data/countryflags/index.txt | 4 ++-- 8 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 data/countryflags/AQ.png diff --git a/CMakeLists.txt b/CMakeLists.txt index 663cea024dc..d07f0f0db8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1146,6 +1146,7 @@ set(EXPECTED_DATA countryflags/AL.png countryflags/AM.png countryflags/AO.png + countryflags/AQ.png countryflags/AR.png countryflags/AS.png countryflags/AT.png diff --git a/data/countryflags/AQ.png b/data/countryflags/AQ.png new file mode 100644 index 0000000000000000000000000000000000000000..0cd28fd5af8575fe76d7e3cd20bb3c62e8543fc4 GIT binary patch literal 1576 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 IM6N<$f_*2ZEdT%j delta 1169 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 delta 1691 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 Hu0mjfxPE9Y delta 1097 zcmV-P1h)I63f>5iB!4AIL_t(|0qvawvn5v)g*!INDaOg)80#svGq!Epwr$(CXP$X& zKd)p5rx4xl)IE1z?Y*kLs_WA=>udM)GXVUBtgI|EAUz1JTel{RWC8FOM1TfLn>KCG zv}seqND?LjFd!o%gNjF!CQS$5(dPzpG|>Ahnl)=i#ecIDK zBOq90%|!=h?SHwR6pyR`x)X@kKtr@lLq$ae3JMC6fxQ4KefL!0mOYMi<(4guzPoMs zzlp_S$(Any)YJhtY;inrCGhry`}k&kDd_PXM*_M+W6_v2_FLocf*#+nC1~1Ar@p=( zWo2a|fSF7>;lfLUZ-X|R9_xD(uZ_He{SU&>mHiJwG=HYrc%x~Vble3ial74Ast8~< z8%FUW`4BoYY}78Z&CR+I0k{Oe!s6n_EIlwk)Qcy;J)wGl^;haIpZm8q+%gVX6$C5ixP&YEjJwHHB;Q(3&xt7bAOFA##=%fgW24jeV&shQCX7bu~O5kLh*22w)?9bN}tHL9Z2$il5>!t`f5`Q>guMM>O_xzw+czgUke7~U-^UrzJ zBA2$Z!{IP!(MtzfY&IL{Xd6*pUJimvBIrH4-44RxaDbq7waeuKeU}RLdcE-Zd?4ug zqriRpb4M_ti$+N6Yfz_6Dd-wQrqD z?tkazqt)b->h$be{SM*$&4=`!2%sxHc2?lY9_R36r$bnA_!j-cXl2EbTX>?~9uPk) zJg0MP5rB@OWzV{Zb2lDh{qft{3m|Ar8k?peW7+wF(CC{o}gz;zhy53(&<1n7K%ZOI}) z_wy{v6#>-$j@`?%U=iTDx+@k(M1amG*%m7Tbics1R1u)p6}Ibr5ui8i?;qep~e$ zbG-;aWM*cfRjXF&Pn&9hIq4Tb72mRDO9sU=5FjHX!whKAqD2EOUIh3BZdm`y{tMj; P00000NkvXXu0mjfYJv*D diff --git a/data/countryflags/MR.png b/data/countryflags/MR.png index 3de7c949df773355fc91bb24ed082511ac721756..b45ed9b42493a9ceee10a266d245353c9ea22c3f 100644 GIT binary patch delta 1657 zcmV-<28Q{T3yBSoBYy@^Nkl~!{&X;hqFW}e-)hx5J9$B66dA(P4a0{RCcARxdDXDMAhNPs~~U|=8) z9z1AW z18H+zNdU1n{dzup5SDStkK)eG#5&B<7_b6x@?Y2!;z$^{r-M5o1_6i*u`Fz{3P2nU7p(xq z#aM6&!J-v_IDa0&f+hhR%qV$0M(_-{zCR(D1aJnongVgRNitCTeiC4qg9|&qWIHm7 zw++;hKM4?^3v*au59fKe!37^Tx?r**)?wFKtctOG|HEML|OMZyA4{in2}JhTl@P3IW;(yIjjx6OB)rCt`=D zmwUe)4}UH>lmzH{pcaq!8$6Lww9{VaWb^*U%5|N>FDzS+8HzZ&T_?L}4_>RAXX~_& z)&a6hHsRZrvADqyWH$kts~2|?;H8>nb`#*wdKtcH8HK`1Uq z7~ZH`h%Le?l~w#9H55Nx4ZWgbH7d$CfXxQ@Yk$PsVc_SXjK#CnOW8Re$@J}c)Su96 z*d)LhMI1hCoW%;XsB|+%*|>D8FW8SSTgKshj<>msVT(;^`TOxr>j>xz_uAaL6qM{> z_xe%eTuf1PbFb%O`Vu}Z1)B=@v^UjRMY}NE+O>ZkQ^^MWGc~O9jKd& zFMrtEexLjdpzD24M)oyu5vRZ7-f=_1A zPS#bK@9*2zQ3%#XLO9xw6THHQ1n47~#jI&xKK%C4c=E{&o5gyi&VJa8Iw*&&2{>3iia#@d!a* zPXgG;gM8ROv4l&r@I&q{Ob{yk)1jBr;jelo? zaZ=Z7%GkiBkOP_(8}O>(FGJ$b#+)wQ4!0Cj^lI&S14N7EM*F@M#r?*YgBH z2&ghn#zpxWJa&F3eskO#OOqrR)qg`slM-auoGd~75kK5~W*5@bD^RAN$Xgt)fQzwRK5aUJ=cx~xh)0elP!Z6k9;7j#0aYeo!(MNnS(wWUY z2_SfjlugV|kl`1{z47#gZGTWJ*C0E^19isfXfaLT8F!V5s5eeSZi*}GMxNsLjXvgs z#fh?h3z-Bs8|~0SoSZ0yS5hFnk^?b4(UH?Q62OLc$BGXU;Bvg+$rRB*0totjQ9=To zjiG==E5HTn_XoWIH{BO#6*t@yfWKb1AAkPuUZQsft^l@w5Hev2Lvl)hTo-o=RdfPS z_#{9!g)dG4YTZ1=+UJu1m3;Ua6uvkGC~$R$p28D{095Nq0M^$RQmq%201T;A3V(n9 z_OCW|IK+SufCca8=SRU01py=yNne1kuP+5ZNCo&A+~A3{egSPr00000NkvXXu0mjf Df`A+x delta 1412 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 Z@C>x<)B!sE^jQD^002ovPDHLkV1g4{dCmX; diff --git a/data/countryflags/index.txt b/data/countryflags/index.txt index 6ea9d4e1278..28ece35085b 100644 --- a/data/countryflags/index.txt +++ b/data/countryflags/index.txt @@ -57,8 +57,8 @@ AO AI == 660 -#AQ -#== 10 +AQ +== 10 AG == 28 From e24b87adbdc4cf487c90fe5cca49f09922086228 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 11 Oct 2024 21:07:29 +0200 Subject: [PATCH 13/15] Make `CServer::GetMapName` function more efficient by caching Store the current map filename (without path) separately when loading a new map instead of determining it again each time that the `CServer::GetMapName` function is called. Use the `fs_filename` function for this. Avoid the usage of the `sv_map` config variable for this, which may have caused the returned map filename to be out-of-sync with the real map on the server due to the map specified by the config variable not being reloaded immediately. --- src/engine/server/server.cpp | 11 +++-------- src/engine/server/server.h | 1 + 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index d1179df5ab5..c8a7d735d33 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -246,6 +246,7 @@ CServer::CServer() m_SameMapReload = false; m_ReloadedWhenEmpty = false; m_aCurrentMap[0] = '\0'; + m_pCurrentMapName = m_aCurrentMap; m_RconClientId = IServer::RCON_CID_SERV; m_RconAuthLevel = AUTHED_ADMIN; @@ -2541,14 +2542,7 @@ void CServer::PumpNetwork(bool PacketWaiting) const char *CServer::GetMapName() const { - // get the name of the map without his path - const char *pMapShortName = &Config()->m_SvMap[0]; - for(int i = 0; i < str_length(Config()->m_SvMap) - 1; i++) - { - if(Config()->m_SvMap[i] == '/' || Config()->m_SvMap[i] == '\\') - pMapShortName = &Config()->m_SvMap[i + 1]; - } - return pMapShortName; + return m_pCurrentMapName; } void CServer::ChangeMap(const char *pMap) @@ -2587,6 +2581,7 @@ int CServer::LoadMap(const char *pMapName) Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBufMsg); str_copy(m_aCurrentMap, pMapName); + m_pCurrentMapName = fs_filename(m_aCurrentMap); // load complete map into memory for download { diff --git a/src/engine/server/server.h b/src/engine/server/server.h index 7a54efe36d6..ec4950716fd 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -242,6 +242,7 @@ class CServer : public IServer }; char m_aCurrentMap[IO_MAX_PATH_LENGTH]; + const char *m_pCurrentMapName; SHA256_DIGEST m_aCurrentMapSha256[NUM_MAP_TYPES]; unsigned m_aCurrentMapCrc[NUM_MAP_TYPES]; unsigned char *m_apCurrentMapData[NUM_MAP_TYPES]; From ab60d0bf702ddabdf50c5a00907397e0b2213997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Fri, 11 Oct 2024 21:10:16 +0200 Subject: [PATCH 14/15] Fix server-side demos with maps in folders When maps are loaded from folders on the server, the same folders were used for demos but recording would usually fail due to the folders not existing in the demos folder. Furthermore, the map name being written in the demo header also included the folder names, which causes the client to not find the map unless it also exists at that location. Closes #9033. --- src/engine/server/server.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index c8a7d735d33..13dcd93e235 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -3393,13 +3393,13 @@ void CServer::DemoRecorder_HandleAutoStart() char aTimestamp[20]; str_timestamp(aTimestamp, sizeof(aTimestamp)); char aFilename[IO_MAX_PATH_LENGTH]; - str_format(aFilename, sizeof(aFilename), "demos/auto/server/%s_%s.demo", m_aCurrentMap, aTimestamp); + str_format(aFilename, sizeof(aFilename), "demos/auto/server/%s_%s.demo", GetMapName(), aTimestamp); m_aDemoRecorder[RECORDER_AUTO].Start( Storage(), m_pConsole, aFilename, GameServer()->NetVersion(), - m_aCurrentMap, + GetMapName(), m_aCurrentMapSha256[MAP_TYPE_SIX], m_aCurrentMapCrc[MAP_TYPE_SIX], "server", @@ -3423,7 +3423,7 @@ void CServer::SaveDemo(int ClientId, float Time) if(IsRecording(ClientId)) { char aNewFilename[IO_MAX_PATH_LENGTH]; - str_format(aNewFilename, sizeof(aNewFilename), "demos/%s_%s_%05.2f.demo", m_aCurrentMap, m_aClients[ClientId].m_aName, Time); + str_format(aNewFilename, sizeof(aNewFilename), "demos/%s_%s_%05.2f.demo", GetMapName(), m_aClients[ClientId].m_aName, Time); m_aDemoRecorder[ClientId].Stop(IDemoRecorder::EStopMode::KEEP_FILE, aNewFilename); } } @@ -3433,13 +3433,13 @@ void CServer::StartRecord(int ClientId) if(Config()->m_SvPlayerDemoRecord) { char aFilename[IO_MAX_PATH_LENGTH]; - str_format(aFilename, sizeof(aFilename), "demos/%s_%d_%d_tmp.demo", m_aCurrentMap, m_NetServer.Address().port, ClientId); + str_format(aFilename, sizeof(aFilename), "demos/%s_%d_%d_tmp.demo", GetMapName(), m_NetServer.Address().port, ClientId); m_aDemoRecorder[ClientId].Start( Storage(), Console(), aFilename, GameServer()->NetVersion(), - m_aCurrentMap, + GetMapName(), m_aCurrentMapSha256[MAP_TYPE_SIX], m_aCurrentMapCrc[MAP_TYPE_SIX], "server", @@ -3501,7 +3501,7 @@ void CServer::ConRecord(IConsole::IResult *pResult, void *pUser) pServer->Console(), aFilename, pServer->GameServer()->NetVersion(), - pServer->m_aCurrentMap, + pServer->GetMapName(), pServer->m_aCurrentMapSha256[MAP_TYPE_SIX], pServer->m_aCurrentMapCrc[MAP_TYPE_SIX], "server", From 1828b5b29b4f01b9846b29acd79d75b6c10063d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20M=C3=BCller?= Date: Thu, 26 Sep 2024 11:10:30 +0200 Subject: [PATCH 15/15] Fix Android build when first building `x64` architecture The argument `x64` is an alias for `x86_64` but the name of the build folder always contains `x86_64`, so the `data` folder and certificate were not being found when using the `x64` argument, unless another architecture was previously built. --- scripts/android/cmake_android.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/android/cmake_android.sh b/scripts/android/cmake_android.sh index 643d016a579..d541250a366 100755 --- a/scripts/android/cmake_android.sh +++ b/scripts/android/cmake_android.sh @@ -26,6 +26,9 @@ if [ -z ${1+x} ]; then printf "${COLOR_RED}%s${COLOR_RESET}\n" "Did not pass Android build type" else ANDROID_BUILD=$1 + if [[ "${ANDROID_BUILD}" == "x64" ]]; then + ANDROID_BUILD="x86_64" + fi printf "${COLOR_YELLOW}%s${COLOR_RESET}\n" "Android build type: ${ANDROID_BUILD}" fi @@ -168,7 +171,7 @@ if [[ "${ANDROID_BUILD}" == "x86" || "${ANDROID_BUILD}" == "all" ]]; then PID_BUILD_X86=$! fi -if [[ "${ANDROID_BUILD}" == "x86_64" || "${ANDROID_BUILD}" == "x64" || "${ANDROID_BUILD}" == "all" ]]; then +if [[ "${ANDROID_BUILD}" == "x86_64" || "${ANDROID_BUILD}" == "all" ]]; then build_for_type x86_64 x86_64 x86_64-linux-android & PID_BUILD_X86_64=$! fi @@ -236,7 +239,7 @@ if [[ "${ANDROID_BUILD}" == "x86" || "${ANDROID_BUILD}" == "all" ]]; then copy_libs x86 x86 fi -if [[ "${ANDROID_BUILD}" == "x86_64" || "${ANDROID_BUILD}" == "x64" || "${ANDROID_BUILD}" == "all" ]]; then +if [[ "${ANDROID_BUILD}" == "x86_64" || "${ANDROID_BUILD}" == "all" ]]; then copy_libs x86_64 x86_64 fi