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