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 Result = FRIEND_CLAN;
82 else if(m_aFriends[i].m_NameHash == NameHash && !str_comp(a: m_aFriends[i].m_aName, b: pName))
83 {
84 Result = FRIEND_PLAYER;
85 break;
86 }
87 }
88 }
89 return Result;
90}
91
92bool CFriends::IsFriend(const char *pName, const char *pClan, bool PlayersOnly) const
93{
94 unsigned NameHash = str_quickhash(str: pName);
95 unsigned ClanHash = str_quickhash(str: pClan);
96 for(int i = 0; i < m_NumFriends; ++i)
97 {
98 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))) &&
99 ((!PlayersOnly && m_aFriends[i].m_aName[0] == 0) || (m_aFriends[i].m_NameHash == NameHash && !str_comp(a: m_aFriends[i].m_aName, b: pName))))
100 return true;
101 }
102 return false;
103}
104
105void CFriends::AddFriend(const char *pName, const char *pClan)
106{
107 if(m_NumFriends == MAX_FRIENDS || (pName[0] == 0 && pClan[0] == 0))
108 return;
109
110 // make sure we don't have the friend already
111 unsigned NameHash = str_quickhash(str: pName);
112 unsigned ClanHash = str_quickhash(str: pClan);
113 for(int i = 0; i < m_NumFriends; ++i)
114 {
115 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))))
116 return;
117 }
118
119 str_copy(dst&: m_aFriends[m_NumFriends].m_aName, src: pName);
120 str_copy(dst&: m_aFriends[m_NumFriends].m_aClan, src: pClan);
121 m_aFriends[m_NumFriends].m_NameHash = NameHash;
122 m_aFriends[m_NumFriends].m_ClanHash = ClanHash;
123 ++m_NumFriends;
124}
125
126void CFriends::RemoveFriend(const char *pName, const char *pClan)
127{
128 unsigned NameHash = str_quickhash(str: pName);
129 unsigned ClanHash = str_quickhash(str: pClan);
130 for(int i = 0; i < m_NumFriends; ++i)
131 {
132 if((m_aFriends[i].m_NameHash == NameHash && !str_comp(a: m_aFriends[i].m_aName, b: pName)) &&
133 ((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))))
134 {
135 RemoveFriend(Index: i);
136 return;
137 }
138 }
139}
140
141void CFriends::RemoveFriend(int Index)
142{
143 dbg_assert(Index >= 0 && Index < m_NumFriends, "Invalid Index: %d", Index);
144 mem_move(dest: &m_aFriends[Index], source: &m_aFriends[Index + 1], size: sizeof(CFriendInfo) * (m_NumFriends - (Index + 1)));
145 --m_NumFriends;
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