1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#include "friends.h"
4
5#include <base/dbg.h>
6#include <base/math.h>
7#include <base/mem.h>
8#include <base/str.h>
9
10#include <engine/config.h>
11#include <engine/console.h>
12#include <engine/shared/config.h>
13
14CFriends::CFriends()
15{
16 mem_zero(block: m_aFriends, size: sizeof(m_aFriends));
17 m_NumFriends = 0;
18 m_Foes = false;
19}
20
21void CFriends::ConAddFriend(IConsole::IResult *pResult, void *pUserData)
22{
23 CFriends *pSelf = (CFriends *)pUserData;
24 pSelf->AddFriend(pName: pResult->GetString(Index: 0), pClan: pResult->GetString(Index: 1));
25}
26
27void CFriends::ConRemoveFriend(IConsole::IResult *pResult, void *pUserData)
28{
29 CFriends *pSelf = (CFriends *)pUserData;
30 pSelf->RemoveFriend(pName: pResult->GetString(Index: 0), pClan: pResult->GetString(Index: 1));
31}
32
33void CFriends::ConFriends(IConsole::IResult *pResult, void *pUserData)
34{
35 CFriends *pSelf = (CFriends *)pUserData;
36 pSelf->Friends();
37}
38
39void CFriends::Init(bool Foes)
40{
41 m_Foes = Foes;
42
43 IConfigManager *pConfigManager = Kernel()->RequestInterface<IConfigManager>();
44 if(pConfigManager)
45 pConfigManager->RegisterCallback(pfnFunc: ConfigSaveCallback, pUserData: this);
46
47 IConsole *pConsole = Kernel()->RequestInterface<IConsole>();
48 if(pConsole)
49 {
50 if(Foes)
51 {
52 pConsole->Register(pName: "add_foe", pParams: "s[name] ?s[clan]", Flags: CFGFLAG_CLIENT, pfnFunc: ConAddFriend, pUser: this, pHelp: "Add a foe");
53 pConsole->Register(pName: "remove_foe", pParams: "s[name] ?s[clan]", Flags: CFGFLAG_CLIENT, pfnFunc: ConRemoveFriend, pUser: this, pHelp: "Remove a foe");
54 pConsole->Register(pName: "foes", pParams: "", Flags: CFGFLAG_CLIENT, pfnFunc: ConFriends, pUser: this, pHelp: "List foes");
55 }
56 else
57 {
58 pConsole->Register(pName: "add_friend", pParams: "s[name] ?s[clan]", Flags: CFGFLAG_CLIENT, pfnFunc: ConAddFriend, pUser: this, pHelp: "Add a friend");
59 pConsole->Register(pName: "remove_friend", pParams: "s[name] ?s[clan]", Flags: CFGFLAG_CLIENT, pfnFunc: ConRemoveFriend, pUser: this, pHelp: "Remove a friend");
60 pConsole->Register(pName: "friends", pParams: "", Flags: CFGFLAG_CLIENT, pfnFunc: ConFriends, pUser: this, pHelp: "List friends");
61 }
62 }
63}
64
65const CFriendInfo *CFriends::GetFriend(int Index) const
66{
67 dbg_assert(Index >= 0 && Index < m_NumFriends, "Invalid Index: %d", Index);
68 return &m_aFriends[Index];
69}
70
71int CFriends::GetFriendState(const char *pName, const char *pClan) const
72{
73 int Result = FRIEND_NO;
74 unsigned NameHash = str_quickhash(str: pName);
75 unsigned ClanHash = str_quickhash(str: pClan);
76 for(int i = 0; i < m_NumFriends; ++i)
77 {
78 if((g_Config.m_ClFriendsIgnoreClan && m_aFriends[i].m_aName[0]) || (m_aFriends[i].m_ClanHash == ClanHash && !str_comp(a: m_aFriends[i].m_aClan, b: pClan)))
79 {
80 if(m_aFriends[i].m_aName[0] == 0)
81 {
82 Result = FRIEND_CLAN;
83 }
84 else if(m_aFriends[i].m_NameHash == NameHash && !str_comp(a: m_aFriends[i].m_aName, b: pName))
85 {
86 Result = FRIEND_PLAYER;
87 break;
88 }
89 }
90 }
91 return Result;
92}
93
94bool CFriends::IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const
95{
96 unsigned NameHash = str_quickhash(str: pName);
97 unsigned ClanHash = str_quickhash(str: pClan);
98 for(int i = 0; i < m_NumFriends; ++i)
99 {
100 if(((g_Config.m_ClFriendsIgnoreClan && m_aFriends[i].m_aName[0]) || (m_aFriends[i].m_ClanHash == ClanHash && !str_comp(a: m_aFriends[i].m_aClan, b: pClan))) &&
101 ((!PlayersOnly && m_aFriends[i].m_aName[0] == 0) || (m_aFriends[i].m_NameHash == NameHash && !str_comp(a: m_aFriends[i].m_aName, b: pName))))
102 return true;
103 }
104 return false;
105}
106
107void CFriends::AddFriend(const char *pName, const char *pClan)
108{
109 if(m_NumFriends == MAX_FRIENDS || (pName[0] == 0 && pClan[0] == 0))
110 return;
111
112 // make sure we don't have the friend already
113 unsigned NameHash = str_quickhash(str: pName);
114 unsigned ClanHash = str_quickhash(str: pClan);
115 for(int i = 0; i < m_NumFriends; ++i)
116 {
117 if((m_aFriends[i].m_NameHash == NameHash && !str_comp(a: m_aFriends[i].m_aName, b: pName)) && ((g_Config.m_ClFriendsIgnoreClan && m_aFriends[i].m_aName[0]) || (m_aFriends[i].m_ClanHash == ClanHash && !str_comp(a: m_aFriends[i].m_aClan, b: pClan))))
118 return;
119 }
120
121 str_copy(dst&: m_aFriends[m_NumFriends].m_aName, src: pName);
122 str_copy(dst&: m_aFriends[m_NumFriends].m_aClan, src: pClan);
123 m_aFriends[m_NumFriends].m_NameHash = NameHash;
124 m_aFriends[m_NumFriends].m_ClanHash = ClanHash;
125 ++m_NumFriends;
126}
127
128void CFriends::RemoveFriend(const char *pName, const char *pClan)
129{
130 unsigned NameHash = str_quickhash(str: pName);
131 unsigned ClanHash = str_quickhash(str: pClan);
132 for(int i = 0; i < m_NumFriends; ++i)
133 {
134 if((m_aFriends[i].m_NameHash == NameHash && !str_comp(a: m_aFriends[i].m_aName, b: pName)) &&
135 ((g_Config.m_ClFriendsIgnoreClan && m_aFriends[i].m_aName[0]) || (m_aFriends[i].m_ClanHash == ClanHash && !str_comp(a: m_aFriends[i].m_aClan, b: pClan))))
136 {
137 RemoveFriend(Index: i);
138 return;
139 }
140 }
141}
142
143void CFriends::RemoveFriend(int Index)
144{
145 dbg_assert(Index >= 0 && Index < m_NumFriends, "Invalid Index: %d", Index);
146 mem_move(dest: &m_aFriends[Index], source: &m_aFriends[Index + 1], size: sizeof(CFriendInfo) * (m_NumFriends - (Index + 1)));
147 --m_NumFriends;
148}
149
150void CFriends::Friends()
151{
152 char aBuf[128];
153 IConsole *pConsole = Kernel()->RequestInterface<IConsole>();
154 if(pConsole)
155 {
156 for(int i = 0; i < m_NumFriends; ++i)
157 {
158 str_format(buffer: aBuf, buffer_size: sizeof(aBuf), format: "Name: %s, Clan: %s", m_aFriends[i].m_aName, m_aFriends[i].m_aClan);
159
160 pConsole->Print(Level: IConsole::OUTPUT_LEVEL_STANDARD, pFrom: m_Foes ? "foes" : "friends", pStr: aBuf, PrintColor: color_cast<ColorRGBA>(hsl: ColorHSLA(g_Config.m_ClMessageHighlightColor)));
161 }
162 }
163}
164
165void CFriends::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData)
166{
167 CFriends *pSelf = (CFriends *)pUserData;
168 char aBuf[128];
169 const char *pEnd = aBuf + sizeof(aBuf) - 4;
170 for(int i = 0; i < pSelf->m_NumFriends; ++i)
171 {
172 str_copy(dst&: aBuf, src: pSelf->m_Foes ? "add_foe " : "add_friend ");
173
174 str_append(dst&: aBuf, src: "\"");
175 char *pDst = aBuf + str_length(str: aBuf);
176 str_escape(dst: &pDst, src: pSelf->m_aFriends[i].m_aName, end: pEnd);
177 str_append(dst&: aBuf, src: "\" \"");
178 pDst = aBuf + str_length(str: aBuf);
179 str_escape(dst: &pDst, src: pSelf->m_aFriends[i].m_aClan, end: pEnd);
180 str_append(dst&: aBuf, src: "\"");
181
182 pConfigManager->WriteLine(pLine: aBuf);
183 }
184}
185