diff --git a/data/languages/bosnian.txt b/data/languages/bosnian.txt
index 0c87d4ba5..d2c645224 100644
--- a/data/languages/bosnian.txt
+++ b/data/languages/bosnian.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Prikaži chat
-Show friends
+Show friends only
== Prikaži prijatelje
Show ingame HUD
diff --git a/data/languages/bulgarian.txt b/data/languages/bulgarian.txt
index 89b95af72..403941dec 100644
--- a/data/languages/bulgarian.txt
+++ b/data/languages/bulgarian.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Показвай чата
-Show friends
+Show friends only
== Покажи приятели
Show ingame HUD
@@ -674,12 +674,3 @@ Strict gametype filter
##### old translations #####
-Page %d of %d
-== Страница %d/%d
-
-Next
-== Нататък
-
-Prev
-== Назад
-
diff --git a/data/languages/czech.txt b/data/languages/czech.txt
index 33aca462b..b132f398e 100644
--- a/data/languages/czech.txt
+++ b/data/languages/czech.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Zobrazit chat
-Show friends
+Show friends only
== Zobrazit přátele
Show ingame HUD
diff --git a/data/languages/danish.txt b/data/languages/danish.txt
index db69e06c2..c78d848f4 100644
--- a/data/languages/danish.txt
+++ b/data/languages/danish.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Vis chat
-Show friends
+Show friends only
== Vis venner
Show ingame HUD
@@ -674,6 +674,3 @@ Strict gametype filter
##### old translations #####
-##### translated strings #####
-==
-
diff --git a/data/languages/dutch.txt b/data/languages/dutch.txt
index fbd488a1d..bd03032a9 100644
--- a/data/languages/dutch.txt
+++ b/data/languages/dutch.txt
@@ -511,7 +511,7 @@ Shotgun
Show chat
== Laat chat zien
-Show friends
+Show friends only
== Laat vrienden zien
Show ingame HUD
diff --git a/data/languages/finnish.txt b/data/languages/finnish.txt
index 6f418c4bd..74f7d4f22 100644
--- a/data/languages/finnish.txt
+++ b/data/languages/finnish.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Näytä chatti
-Show friends
+Show friends only
== Näytä ystävät
Show ingame HUD
diff --git a/data/languages/french.txt b/data/languages/french.txt
index 137e36820..629a885c6 100644
--- a/data/languages/french.txt
+++ b/data/languages/french.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Montrer le chat
-Show friends
+Show friends only
== Montrer les amis
Show ingame HUD
diff --git a/data/languages/german.txt b/data/languages/german.txt
index c3e74bbf4..38fd30a62 100644
--- a/data/languages/german.txt
+++ b/data/languages/german.txt
@@ -514,7 +514,7 @@ Shotgun
Show chat
== Chat anzeigen
-Show friends
+Show friends only
== Nur Freunde zeigen
Show ingame HUD
diff --git a/data/languages/italian.txt b/data/languages/italian.txt
index 6ee030544..8c9fa07ee 100644
--- a/data/languages/italian.txt
+++ b/data/languages/italian.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Mostra chat
-Show friends
+Show friends only
== Mostra amici
Show ingame HUD
diff --git a/data/languages/norwegian.txt b/data/languages/norwegian.txt
index a17365d56..7f04e19db 100644
--- a/data/languages/norwegian.txt
+++ b/data/languages/norwegian.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Vis samtale
-Show friends
+Show friends only
== Vis venner
Show ingame HUD
diff --git a/data/languages/polish.txt b/data/languages/polish.txt
index c3223a9ce..b8e74a962 100644
--- a/data/languages/polish.txt
+++ b/data/languages/polish.txt
@@ -612,7 +612,7 @@ Server address:
Server filter
==
-Show friends
+Show friends only
==
Show ingame HUD
diff --git a/data/languages/portuguese.txt b/data/languages/portuguese.txt
index 144862475..1d2681adf 100644
--- a/data/languages/portuguese.txt
+++ b/data/languages/portuguese.txt
@@ -1,4 +1,4 @@
-
+
##### translated strings #####
%d Bytes
@@ -514,7 +514,7 @@ Shotgun
Show chat
== Mostrar conversa
-Show friends
+Show friends only
== Mostrar amigos
Show ingame HUD
@@ -672,9 +672,5 @@ no limit
##### needs translation #####
-
##### old translations #####
-
-== ## translated strings #####
-
diff --git a/data/languages/romanian.txt b/data/languages/romanian.txt
index c7bbb6950..6c6ce05f2 100644
--- a/data/languages/romanian.txt
+++ b/data/languages/romanian.txt
@@ -1,3 +1,4 @@
+
##### translated strings #####
%d Bytes
@@ -141,12 +142,12 @@ Delete
Delete demo
== Șterge demonstrația
-Demofile: %s
-== Fișier demo: %s
-
Demo details
== Detalii demo
+Demofile: %s
+== Fișier demo: %s
+
Demos
== Demo
@@ -513,7 +514,7 @@ Shotgun
Show chat
== Afișare chat
-Show friends
+Show friends only
== Arată prietenii
Show ingame HUD
diff --git a/data/languages/russian.txt b/data/languages/russian.txt
index 87cf31688..52e49eb04 100644
--- a/data/languages/russian.txt
+++ b/data/languages/russian.txt
@@ -511,7 +511,7 @@ Shotgun
Show chat
== Показать чат
-Show friends
+Show friends only
== Показывать друзей
Show ingame HUD
diff --git a/data/languages/serbian.txt b/data/languages/serbian.txt
index aaabeabdd..516d50c8f 100644
--- a/data/languages/serbian.txt
+++ b/data/languages/serbian.txt
@@ -612,7 +612,7 @@ Server address:
Server filter
==
-Show friends
+Show friends only
==
Show ingame HUD
diff --git a/data/languages/slovak.txt b/data/languages/slovak.txt
index 203940967..37754d857 100644
--- a/data/languages/slovak.txt
+++ b/data/languages/slovak.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Ukázať chat
-Show friends
+Show friends only
== Ukázať priateľov
Show ingame HUD
diff --git a/data/languages/spanish.txt b/data/languages/spanish.txt
index d587cf026..df5084432 100644
--- a/data/languages/spanish.txt
+++ b/data/languages/spanish.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Mostrar chat
-Show friends
+Show friends only
== Mostrar amigos
Show ingame HUD
@@ -674,12 +674,3 @@ Strict gametype filter
##### old translations #####
-Page %d of %d
-== Página %d de %d
-
-Next
-== Siguiente
-
-Prev
-== Anterior
-
diff --git a/data/languages/swedish.txt b/data/languages/swedish.txt
index 6dea5ed4b..6b1804aaa 100644
--- a/data/languages/swedish.txt
+++ b/data/languages/swedish.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Visa chatt
-Show friends
+Show friends only
== Visa kompisar
Show ingame HUD
diff --git a/data/languages/turkish.txt b/data/languages/turkish.txt
index a62e6073f..a3fa958b8 100644
--- a/data/languages/turkish.txt
+++ b/data/languages/turkish.txt
@@ -508,7 +508,7 @@ Shotgun
Show chat
== Sohbeti göster
-Show friends
+Show friends only
== Arkadaşları göster
Show ingame HUD
diff --git a/data/languages/ukrainian.txt b/data/languages/ukrainian.txt
index 5019714bf..f6e69f003 100644
--- a/data/languages/ukrainian.txt
+++ b/data/languages/ukrainian.txt
@@ -618,7 +618,7 @@ Server address:
Server filter
==
-Show friends
+Show friends only
==
Show ingame HUD
diff --git a/src/engine/client/friends.cpp b/src/engine/client/friends.cpp
index 9ef00ed1e..99f82b505 100644
--- a/src/engine/client/friends.cpp
+++ b/src/engine/client/friends.cpp
@@ -1,6 +1,7 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include
+#include
#include
#include
@@ -44,12 +45,35 @@ const CFriendInfo *CFriends::GetFriend(int Index) const
return &m_aFriends[max(0, Index%m_NumFriends)];
}
-bool CFriends::IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const
+int CFriends::GetFriendState(const char *pName, const char *pClan) const
{
+ int Result = FRIEND_NO;
+ unsigned NameHash = str_quickhash(pName);
+ unsigned ClanHash = str_quickhash(pClan);
for(int i = 0; i < m_NumFriends; ++i)
{
- if(!str_comp(m_aFriends[i].m_aClan, pClan) &&
- ((!PlayersOnly && m_aFriends[i].m_aName[0] == 0) || !str_comp(m_aFriends[i].m_aName, pName)))
+ if(m_aFriends[i].m_ClanHash == ClanHash)
+ {
+ if(m_aFriends[i].m_aName[0] == 0)
+ Result = FRIEND_CLAN;
+ else if(m_aFriends[i].m_NameHash == NameHash)
+ {
+ Result = FRIEND_PLAYER;
+ break;
+ }
+ }
+ }
+ return Result;
+}
+
+bool CFriends::IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const
+{
+ unsigned NameHash = str_quickhash(pName);
+ unsigned ClanHash = str_quickhash(pClan);
+ for(int i = 0; i < m_NumFriends; ++i)
+ {
+ if(m_aFriends[i].m_ClanHash == ClanHash &&
+ ((!PlayersOnly && m_aFriends[i].m_aName[0] == 0) || m_aFriends[i].m_NameHash == NameHash))
return true;
}
return false;
@@ -61,22 +85,28 @@ void CFriends::AddFriend(const char *pName, const char *pClan)
return;
// make sure we don't have the friend already
+ unsigned NameHash = str_quickhash(pName);
+ unsigned ClanHash = str_quickhash(pClan);
for(int i = 0; i < m_NumFriends; ++i)
{
- if(!str_comp(m_aFriends[i].m_aName, pName) && !str_comp(m_aFriends[i].m_aClan, pClan))
+ if(m_aFriends[i].m_NameHash == NameHash && m_aFriends[i].m_ClanHash == ClanHash)
return;
}
str_copy(m_aFriends[m_NumFriends].m_aName, pName, sizeof(m_aFriends[m_NumFriends].m_aName));
str_copy(m_aFriends[m_NumFriends].m_aClan, pClan, sizeof(m_aFriends[m_NumFriends].m_aClan));
+ m_aFriends[m_NumFriends].m_NameHash = NameHash;
+ m_aFriends[m_NumFriends].m_ClanHash = ClanHash;
++m_NumFriends;
}
void CFriends::RemoveFriend(const char *pName, const char *pClan)
{
+ unsigned NameHash = str_quickhash(pName);
+ unsigned ClanHash = str_quickhash(pClan);
for(int i = 0; i < m_NumFriends; ++i)
{
- if(!str_comp(m_aFriends[i].m_aName, pName) && !str_comp(m_aFriends[i].m_aClan, pClan))
+ if(m_aFriends[i].m_NameHash == NameHash && m_aFriends[i].m_ClanHash == ClanHash)
{
RemoveFriend(i);
return;
diff --git a/src/engine/client/friends.h b/src/engine/client/friends.h
index be0cfa498..d4c539e14 100644
--- a/src/engine/client/friends.h
+++ b/src/engine/client/friends.h
@@ -22,6 +22,7 @@ public:
int NumFriends() const { return m_NumFriends; }
const CFriendInfo *GetFriend(int Index) const;
+ int GetFriendState(const char *pName, const char *pClan) const;
bool IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const;
void AddFriend(const char *pName, const char *pClan);
diff --git a/src/engine/client/serverbrowser.cpp b/src/engine/client/serverbrowser.cpp
index 424acb22c..c3ada10e8 100644
--- a/src/engine/client/serverbrowser.cpp
+++ b/src/engine/client/serverbrowser.cpp
@@ -144,101 +144,98 @@ void CServerBrowser::Filter()
{
int Filtered = 0;
- if(g_Config.m_BrFilterFriends)
+ if(g_Config.m_BrFilterEmpty && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == 0) || m_ppServerlist[i]->m_Info.m_NumClients == 0))
+ Filtered = 1;
+ else if(g_Config.m_BrFilterFull && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers) ||
+ m_ppServerlist[i]->m_Info.m_NumClients == m_ppServerlist[i]->m_Info.m_MaxClients))
+ Filtered = 1;
+ else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD)
+ Filtered = 1;
+ else if(g_Config.m_BrFilterPure &&
+ (str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 &&
+ str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 &&
+ str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0))
{
Filtered = 1;
+ }
+ else if(g_Config.m_BrFilterPureMap &&
+ !(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf6") == 0 ||
+ str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf7") == 0)
+ )
+ {
+ Filtered = 1;
+ }
+ else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency)
+ Filtered = 1;
+ else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0)
+ Filtered = 1;
+ else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress))
+ Filtered = 1;
+ else if(g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && str_comp_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
+ Filtered = 1;
+ else if(!g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
+ Filtered = 1;
+ else if(g_Config.m_BrFilterString[0] != 0)
+ {
+ int MatchFound = 0;
+
+ m_ppServerlist[i]->m_Info.m_QuickSearchHit = 0;
+
+ // match against server name
+ if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrFilterString))
+ {
+ MatchFound = 1;
+ m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME;
+ }
+
+ // match against players
for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
{
- if(m_pFriends->IsFriend(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, false))
+ if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, g_Config.m_BrFilterString) ||
+ str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, g_Config.m_BrFilterString))
{
- Filtered = 0;
+ MatchFound = 1;
+ m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYER;
break;
}
}
- }
- else
- {
- if(g_Config.m_BrFilterEmpty && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == 0) || m_ppServerlist[i]->m_Info.m_NumClients == 0))
- Filtered = 1;
- else if(g_Config.m_BrFilterFull && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers) ||
- m_ppServerlist[i]->m_Info.m_NumClients == m_ppServerlist[i]->m_Info.m_MaxClients))
- Filtered = 1;
- else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD)
- Filtered = 1;
- else if(g_Config.m_BrFilterPure &&
- (str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 &&
- str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 &&
- str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0))
+
+ // match against map
+ if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrFilterString))
{
- Filtered = 1;
+ MatchFound = 1;
+ m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME;
}
- else if(g_Config.m_BrFilterPureMap &&
- !(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf6") == 0 ||
- str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf7") == 0)
- )
- {
- Filtered = 1;
- }
- else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency)
- Filtered = 1;
- else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0)
- Filtered = 1;
- else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress))
- Filtered = 1;
- else if(g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && str_comp_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
- Filtered = 1;
- else if(!g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
- Filtered = 1;
- else if(g_Config.m_BrFilterString[0] != 0)
- {
- int MatchFound = 0;
- m_ppServerlist[i]->m_Info.m_QuickSearchHit = 0;
-
- // match against server name
- if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrFilterString))
- {
- MatchFound = 1;
- m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME;
- }
-
- // match against players
- for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
- {
- if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, g_Config.m_BrFilterString) ||
- str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, g_Config.m_BrFilterString))
- {
- MatchFound = 1;
- m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYER;
- break;
- }
- }
-
- // match against map
- if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrFilterString))
- {
- MatchFound = 1;
- m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME;
- }
-
- if(!MatchFound)
- Filtered = 1;
- }
+ if(!MatchFound)
+ Filtered = 1;
}
if(Filtered == 0)
- m_pSortedServerlist[m_NumSortedServers++] = i;
+ {
+ // check for friend
+ m_ppServerlist[i]->m_Info.m_FriendState = IFriends::FRIEND_NO;
+ for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
+ {
+ m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState = m_pFriends->GetFriendState(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName,
+ m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan);
+ m_ppServerlist[i]->m_Info.m_FriendState = max(m_ppServerlist[i]->m_Info.m_FriendState, m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState);
+ }
+
+ if(!g_Config.m_BrFilterFriends || m_ppServerlist[i]->m_Info.m_FriendState != IFriends::FRIEND_NO)
+ m_pSortedServerlist[m_NumSortedServers++] = i;
+ }
}
}
diff --git a/src/engine/friends.h b/src/engine/friends.h
index bf9df904a..164e3461a 100644
--- a/src/engine/friends.h
+++ b/src/engine/friends.h
@@ -11,6 +11,8 @@ struct CFriendInfo
{
char m_aName[MAX_NAME_LENGTH];
char m_aClan[MAX_CLAN_LENGTH];
+ unsigned m_NameHash;
+ unsigned m_ClanHash;
};
class IFriends : public IInterface
@@ -19,6 +21,10 @@ class IFriends : public IInterface
public:
enum
{
+ FRIEND_NO=0,
+ FRIEND_CLAN,
+ FRIEND_PLAYER,
+
MAX_FRIENDS=128,
};
@@ -26,11 +32,11 @@ public:
virtual int NumFriends() const = 0;
virtual const CFriendInfo *GetFriend(int Index) const = 0;
+ virtual int GetFriendState(const char *pName, const char *pClan) const = 0;
virtual bool IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const = 0;
virtual void AddFriend(const char *pName, const char *pClan) = 0;
virtual void RemoveFriend(const char *pName, const char *pClan) = 0;
- virtual void RemoveFriend(int Index) = 0;
};
#endif
diff --git a/src/engine/serverbrowser.h b/src/engine/serverbrowser.h
index 3ca59f9cf..1a49eaf0e 100644
--- a/src/engine/serverbrowser.h
+++ b/src/engine/serverbrowser.h
@@ -24,7 +24,9 @@ public:
int m_Country;
int m_Score;
bool m_Player;
- } ;
+
+ int m_FriendState;
+ };
int m_SortedIndex;
int m_ServerIndex;
@@ -32,6 +34,7 @@ public:
NETADDR m_NetAddr;
int m_QuickSearchHit;
+ int m_FriendState;
int m_MaxClients;
int m_NumClients;
diff --git a/src/game/client/components/menus.cpp b/src/game/client/components/menus.cpp
index 64754c68c..6ec18ce15 100644
--- a/src/game/client/components/menus.cpp
+++ b/src/game/client/components/menus.cpp
@@ -734,6 +734,8 @@ void CMenus::OnInit()
Console()->Chain("add_favorite", ConchainServerbrowserUpdate, this);
Console()->Chain("remove_favorite", ConchainServerbrowserUpdate, this);
+ Console()->Chain("add_friend", ConchainFriendlistUpdate, this);
+ Console()->Chain("remove_friend", ConchainFriendlistUpdate, this);
// setup load amount
m_LoadCurrent = 0;
@@ -1205,7 +1207,9 @@ int CMenus::Render()
// remove friend
if(m_FriendlistSelectedIndex >= 0)
{
- m_pClient->Friends()->RemoveFriend(m_FriendlistSelectedIndex);
+ m_pClient->Friends()->RemoveFriend(m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aName,
+ m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aClan);
+ FriendlistOnUpdate();
Client()->ServerBrowserUpdate();
}
}
diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h
index 51b8a1f85..959c366e4 100644
--- a/src/game/client/components/menus.h
+++ b/src/game/client/components/menus.h
@@ -7,6 +7,7 @@
#include
#include
+#include
#include
#include
@@ -202,8 +203,34 @@ class CMenus : public CComponent
void DemolistPopulate();
static int DemolistFetchCallback(const char *pName, int IsDir, int StorageType, void *pUser);
+ // friends
+ struct CFriendItem
+ {
+ const CFriendInfo *m_pFriendInfo;
+ int m_NumFound;
+
+ bool operator<(const CFriendItem &Other)
+ {
+ if(m_NumFound && !Other.m_NumFound)
+ return true;
+ else if(!m_NumFound && Other.m_NumFound)
+ return false;
+ else
+ {
+ int Result = str_comp(m_pFriendInfo->m_aName, Other.m_pFriendInfo->m_aName);
+ if(Result)
+ return Result < 0;
+ else
+ return str_comp(m_pFriendInfo->m_aClan, Other.m_pFriendInfo->m_aClan) < 0;
+ }
+ }
+ };
+
+ sorted_array m_lFriends;
int m_FriendlistSelectedIndex;
+ void FriendlistOnUpdate();
+
// found in menus.cpp
int Render();
//void render_background();
@@ -225,11 +252,13 @@ class CMenus : public CComponent
// found in menus_browser.cpp
int m_SelectedIndex;
+ int m_ScrollOffset;
void RenderServerbrowserServerList(CUIRect View);
void RenderServerbrowserServerDetail(CUIRect View);
void RenderServerbrowserFilters(CUIRect View);
void RenderServerbrowserFriends(CUIRect View);
void RenderServerbrowser(CUIRect MainView);
+ static void ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
// found in menus_settings.cpp
diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp
index 09c8c4943..5a0ebfb62 100644
--- a/src/game/client/components/menus_browser.cpp
+++ b/src/game/client/components/menus_browser.cpp
@@ -154,6 +154,11 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
int ScrollNum = NumServers-Num+1;
if(ScrollNum > 0)
{
+ if(m_ScrollOffset)
+ {
+ s_ScrollValue = (float)(m_ScrollOffset)/ScrollNum;
+ m_ScrollOffset = 0;
+ }
if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && UI()->MouseInside(&View))
s_ScrollValue -= 3.0f/ScrollNum;
if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && UI()->MouseInside(&View))
@@ -213,16 +218,14 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
m_SelectedIndex = -1;
- for (int i = 0; i < NumServers; i++)
- {
- const CServerInfo *pItem = ServerBrowser()->SortedGet(i);
- NumPlayers += pItem->m_NumPlayers;
- }
+ // reset friend counter
+ for(int i = 0; i < m_lFriends.size(); m_lFriends[i++].m_NumFound = 0);
for (int i = 0; i < NumServers; i++)
{
int ItemIndex = i;
const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex);
+ NumPlayers += pItem->m_NumPlayers;
CUIRect Row;
CUIRect SelectHitBox;
@@ -234,6 +237,28 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
if(Selected)
m_SelectedIndex = i;
+ // update friend counter
+ if(pItem->m_FriendState != IFriends::FRIEND_NO)
+ {
+ for(int j = 0; j < pItem->m_NumClients; ++j)
+ {
+ if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO)
+ {
+ unsigned NameHash = str_quickhash(pItem->m_aClients[j].m_aName);
+ unsigned ClanHash = str_quickhash(pItem->m_aClients[j].m_aClan);
+ for(int f = 0; f < m_lFriends.size(); ++f)
+ {
+ if(ClanHash == m_lFriends[f].m_pFriendInfo->m_ClanHash &&
+ (!m_lFriends[f].m_pFriendInfo->m_aName[0] || NameHash == m_lFriends[f].m_pFriendInfo->m_NameHash))
+ {
+ m_lFriends[f].m_NumFound++;
+ break;
+ }
+ }
+ }
+ }
+ }
+
// make sure that only those in view can be selected
if(Row.y+Row.h > OriginalView.y && Row.y < OriginalView.y+OriginalView.h)
{
@@ -353,6 +378,15 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
}
else if(ID == COL_PLAYERS)
{
+ CUIRect Icon;
+ Button.VMargin(4.0f, &Button);
+ if(pItem->m_FriendState != IFriends::FRIEND_NO)
+ {
+ Button.VSplitLeft(Button.h, &Icon, &Button);
+ Icon.Margin(2.0f, &Icon);
+ DoButton_Icon(IMAGE_BROWSEICONS, SPRITE_BROWSE_HEART, &Icon);
+ }
+
if(g_Config.m_BrFilterSpectators)
str_format(aTemp, sizeof(aTemp), "%i/%i", pItem->m_NumPlayers, pItem->m_MaxPlayers);
else
@@ -463,7 +497,7 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
g_Config.m_BrFilterFull ^= 1;
ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter);
- if (DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends"), g_Config.m_BrFilterFriends, &Button))
+ if (DoButton_CheckBox(&g_Config.m_BrFilterFriends, Localize("Show friends only"), g_Config.m_BrFilterFriends, &Button))
g_Config.m_BrFilterFriends ^= 1;
ServerFilter.HSplitTop(20.0f, &Button, &ServerFilter);
@@ -622,14 +656,26 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
RenderTools()->DrawUIRect(&ServerScoreBoard, vec4(0,0,0,0.15f), CUI::CORNER_B, 4.0f);
UI()->DoLabelScaled(&ServerHeader, Localize("Scoreboard"), FontSize+2.0f, 0);
- if (pSelectedServer)
+ if(pSelectedServer)
{
ServerScoreBoard.Margin(3.0f, &ServerScoreBoard);
for (int i = 0; i < pSelectedServer->m_NumClients; i++)
{
CUIRect Name, Clan, Score, Flag;
ServerScoreBoard.HSplitTop(25.0f, &Name, &ServerScoreBoard);
- RenderTools()->DrawUIRect(&Name, vec4(1,1,1,(i%2+1)*0.05f), CUI::CORNER_ALL, 4.0f);
+ if(UI()->DoButtonLogic(&pSelectedServer->m_aClients[i], "", 0, &Name))
+ {
+ if(pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_PLAYER)
+ m_pClient->Friends()->RemoveFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan);
+ else
+ m_pClient->Friends()->AddFriend(pSelectedServer->m_aClients[i].m_aName, pSelectedServer->m_aClients[i].m_aClan);
+ FriendlistOnUpdate();
+ Client()->ServerBrowserUpdate();
+ }
+
+ vec4 Colour = pSelectedServer->m_aClients[i].m_FriendState == IFriends::FRIEND_NO ? vec4(1.0f, 1.0f, 1.0f, (i%2+1)*0.05f) :
+ vec4(0.5f, 1.0f, 0.5f, 0.15f+(i%2+1)*0.05f);
+ RenderTools()->DrawUIRect(&Name, Colour, CUI::CORNER_ALL, 4.0f);
Name.VSplitLeft(5.0f, 0, &Name);
Name.VSplitLeft(30.0f, &Score, &Name);
Name.VSplitRight(34.0f, &Name, &Flag);
@@ -701,45 +747,102 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
}
}
+void CMenus::FriendlistOnUpdate()
+{
+ m_lFriends.clear();
+ for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i)
+ {
+ CFriendItem Item;
+ Item.m_pFriendInfo = m_pClient->Friends()->GetFriend(i);
+ Item.m_NumFound = 0;
+ m_lFriends.add_unsorted(Item);
+ }
+ m_lFriends.sort_range();
+}
+
void CMenus::RenderServerbrowserFriends(CUIRect View)
{
+ static int s_Inited = 0;
+ if(!s_Inited)
+ {
+ FriendlistOnUpdate();
+ s_Inited = 1;
+ }
+
CUIRect ServerFriends = View, FilterHeader;
- const float FontSize = 12.0f;
+ const float FontSize = 10.0f;
// header
ServerFriends.HSplitTop(ms_ListheaderHeight, &FilterHeader, &ServerFriends);
RenderTools()->DrawUIRect(&FilterHeader, vec4(1,1,1,0.25f), CUI::CORNER_T, 4.0f);
RenderTools()->DrawUIRect(&ServerFriends, vec4(0,0,0,0.15f), 0, 4.0f);
- UI()->DoLabelScaled(&FilterHeader, Localize("Friends"), FontSize+2.0f, 0);
+ UI()->DoLabelScaled(&FilterHeader, Localize("Friends"), FontSize+4.0f, 0);
CUIRect Button, List;
- ServerFriends.VSplitLeft(5.0f, 0, &ServerFriends);
ServerFriends.Margin(3.0f, &ServerFriends);
- ServerFriends.VMargin(5.0f, &ServerFriends);
+ ServerFriends.VMargin(3.0f, &ServerFriends);
ServerFriends.HSplitBottom(100.0f, &List, &ServerFriends);
// friends list(remove friend)
- static int s_FriendList = 0;
static float s_ScrollValue = 0;
- UiDoListboxStart(&s_FriendList, &List, 40.0f, "", "", m_pClient->Friends()->NumFriends(), 1, m_FriendlistSelectedIndex, s_ScrollValue);
+ UiDoListboxStart(&m_lFriends, &List, 30.0f, "", "", m_lFriends.size(), 1, m_FriendlistSelectedIndex, s_ScrollValue);
- for(int i = 0; i < m_pClient->Friends()->NumFriends(); ++i)
+ m_lFriends.sort_range();
+ for(int i = 0; i < m_lFriends.size(); ++i)
{
- const CFriendInfo *pFriend = m_pClient->Friends()->GetFriend(i);
- CListboxItem Item = UiDoListboxNextItem(pFriend);
+ CListboxItem Item = UiDoListboxNextItem(&m_lFriends[i]);
if(Item.m_Visible)
{
- Item.m_Rect.Margin(2.5f, &Item.m_Rect);
- RenderTools()->DrawUIRect(&Item.m_Rect, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_ALL, 4.0f);
- Item.m_Rect.Margin(2.5f, &Item.m_Rect);
- Item.m_Rect.HSplitTop(14.0f, &Item.m_Rect, &Button);
- UI()->DoLabelScaled(&Item.m_Rect, pFriend->m_aName, FontSize, -1);
- UI()->DoLabelScaled(&Button, pFriend->m_aClan, FontSize, -1);
+ Item.m_Rect.Margin(1.5f, &Item.m_Rect);
+ CUIRect OnState;
+ Item.m_Rect.VSplitRight(30.0f, &Item.m_Rect, &OnState);
+ RenderTools()->DrawUIRect(&Item.m_Rect, vec4(1.0f, 1.0f, 1.0f, 0.1f), CUI::CORNER_L, 4.0f);
+
+ Item.m_Rect.VMargin(2.5f, &Item.m_Rect);
+ Item.m_Rect.HSplitTop(12.0f, &Item.m_Rect, &Button);
+ UI()->DoLabelScaled(&Item.m_Rect, m_lFriends[i].m_pFriendInfo->m_aName, FontSize, -1);
+ UI()->DoLabelScaled(&Button, m_lFriends[i].m_pFriendInfo->m_aClan, FontSize, -1);
+
+ RenderTools()->DrawUIRect(&OnState, m_lFriends[i].m_NumFound ? vec4(0.0f, 1.0f, 0.0f, 0.25f) : vec4(1.0f, 0.0f, 0.0f, 0.25f), CUI::CORNER_R, 4.0f);
+ OnState.HMargin((OnState.h-FontSize)/3, &OnState);
+ OnState.VMargin(5.0f, &OnState);
+ char aBuf[64];
+ str_format(aBuf, sizeof(aBuf), "%i", m_lFriends[i].m_NumFound);
+ UI()->DoLabelScaled(&OnState, aBuf, FontSize+2, 1);
}
}
- m_FriendlistSelectedIndex = UiDoListboxEnd(&s_ScrollValue, 0);
+ bool Activated = false;
+ m_FriendlistSelectedIndex = UiDoListboxEnd(&s_ScrollValue, &Activated);
+
+ // activate found server with friend
+ if(Activated && !m_EnterPressed && m_lFriends[m_FriendlistSelectedIndex].m_NumFound)
+ {
+ bool Found = false;
+ int NumServers = ServerBrowser()->NumSortedServers();
+ for (int i = 0; i < NumServers && !Found; i++)
+ {
+ int ItemIndex = m_SelectedIndex != -1 ? (m_SelectedIndex+i+1)%NumServers : i;
+ const CServerInfo *pItem = ServerBrowser()->SortedGet(ItemIndex);
+ if(pItem->m_FriendState != IFriends::FRIEND_NO)
+ {
+ for(int j = 0; j < pItem->m_NumClients && !Found; ++j)
+ {
+ if(pItem->m_aClients[j].m_FriendState != IFriends::FRIEND_NO &&
+ str_quickhash(pItem->m_aClients[j].m_aClan) == m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_ClanHash &&
+ (!m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_aName[0] ||
+ str_quickhash(pItem->m_aClients[j].m_aName) == m_lFriends[m_FriendlistSelectedIndex].m_pFriendInfo->m_NameHash))
+ {
+ str_copy(g_Config.m_UiServerAddress, pItem->m_aAddress, sizeof(g_Config.m_UiServerAddress));
+ m_ScrollOffset = ItemIndex;
+ m_SelectedIndex = ItemIndex;
+ Found = true;
+ }
+ }
+ }
+ }
+ }
ServerFriends.HSplitTop(2.5f, 0, &ServerFriends);
ServerFriends.HSplitTop(20.0f, &Button, &ServerFriends);
@@ -774,10 +877,11 @@ void CMenus::RenderServerbrowserFriends(CUIRect View)
ServerFriends.HSplitTop(3.0f, 0, &ServerFriends);
ServerFriends.HSplitTop(20.0f, &Button, &ServerFriends);
- static int s_RemoveButton = 0;
- if(DoButton_Menu(&s_RemoveButton, Localize("Add Friend"), 0, &Button))
+ static int s_AddButton = 0;
+ if(DoButton_Menu(&s_AddButton, Localize("Add Friend"), 0, &Button))
{
m_pClient->Friends()->AddFriend(s_aName, s_aClan);
+ FriendlistOnUpdate();
Client()->ServerBrowserUpdate();
}
}
@@ -916,6 +1020,16 @@ void CMenus::RenderServerbrowser(CUIRect MainView)
}
}
+void CMenus::ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
+{
+ pfnCallback(pResult, pCallbackUserData);
+ if(pResult->NumArguments() == 2 && ((CMenus *)pUserData)->Client()->State() == IClient::STATE_OFFLINE)
+ {
+ ((CMenus *)pUserData)->FriendlistOnUpdate();
+ ((CMenus *)pUserData)->Client()->ServerBrowserUpdate();
+ }
+}
+
void CMenus::ConchainServerbrowserUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{
pfnCallback(pResult, pCallbackUserData);