1 |
|
|
/* |
2 |
|
|
* The ManaPlus Client |
3 |
|
|
* Copyright (C) 2009 The Mana World Development Team |
4 |
|
|
* Copyright (C) 2011-2019 The ManaPlus Developers |
5 |
|
|
* Copyright (C) 2009-2021 Andrei Karas |
6 |
|
|
* |
7 |
|
|
* This file is part of The ManaPlus Client. |
8 |
|
|
* |
9 |
|
|
* This program is free software; you can redistribute it and/or modify |
10 |
|
|
* it under the terms of the GNU General Public License as published by |
11 |
|
|
* the Free Software Foundation; either version 2 of the License, or |
12 |
|
|
* any later version. |
13 |
|
|
* |
14 |
|
|
* This program is distributed in the hope that it will be useful, |
15 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 |
|
|
* GNU General Public License for more details. |
18 |
|
|
* |
19 |
|
|
* You should have received a copy of the GNU General Public License |
20 |
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
21 |
|
|
*/ |
22 |
|
|
|
23 |
|
|
#include "gui/windows/whoisonline.h" |
24 |
|
|
|
25 |
|
|
#include "actormanager.h" |
26 |
|
|
#include "configuration.h" |
27 |
|
|
#include "guild.h" |
28 |
|
|
#include "party.h" |
29 |
|
|
#ifdef TMWA_SUPPORT |
30 |
|
|
#include "settings.h" |
31 |
|
|
#endif // TMWA_SUPPORT |
32 |
|
|
|
33 |
|
|
#include "gui/onlineplayer.h" |
34 |
|
|
#include "gui/popupmanager.h" |
35 |
|
|
#include "gui/viewport.h" |
36 |
|
|
|
37 |
|
|
#include "gui/popups/popupmenu.h" |
38 |
|
|
|
39 |
|
|
#include "gui/windows/chatwindow.h" |
40 |
|
|
#include "gui/windows/setupwindow.h" |
41 |
|
|
#include "gui/windows/socialwindow.h" |
42 |
|
|
|
43 |
|
|
#include "gui/widgets/button.h" |
44 |
|
|
#include "gui/widgets/scrollarea.h" |
45 |
|
|
#include "gui/widgets/staticbrowserbox.h" |
46 |
|
|
|
47 |
|
|
#include "being/beingflag.h" |
48 |
|
|
#include "being/localplayer.h" |
49 |
|
|
#include "being/playerrelations.h" |
50 |
|
|
|
51 |
|
|
#ifdef TMWA_SUPPORT |
52 |
|
|
#include "net/download.h" |
53 |
|
|
#endif // TMWA_SUPPORT |
54 |
|
|
#include "net/packetlimiter.h" |
55 |
|
|
#include "net/playerhandler.h" |
56 |
|
|
#include "net/serverfeatures.h" |
57 |
|
|
|
58 |
|
|
#ifdef TMWA_SUPPORT |
59 |
|
|
#include "resources/db/groupdb.h" |
60 |
|
|
#endif // TMWA_SUPPORT |
61 |
|
|
|
62 |
|
|
#include "utils/foreach.h" |
63 |
|
|
#include "utils/gettext.h" |
64 |
|
|
#include "utils/sdlhelper.h" |
65 |
|
|
|
66 |
|
|
#ifdef TMWA_SUPPORT |
67 |
|
|
#include "net/net.h" |
68 |
|
|
#else // TMWA_SUPPORT |
69 |
|
|
#include <curl/curl.h> |
70 |
|
|
#endif // TMWA_SUPPORT |
71 |
|
|
|
72 |
|
|
#include "debug.h" |
73 |
|
|
|
74 |
|
|
#ifdef free |
75 |
|
|
#undef free |
76 |
|
|
#endif // free |
77 |
|
|
|
78 |
|
|
#ifdef malloc |
79 |
|
|
#undef malloc |
80 |
|
|
#endif // malloc |
81 |
|
|
|
82 |
|
|
WhoIsOnline *whoIsOnline = nullptr; |
83 |
|
|
|
84 |
|
|
namespace |
85 |
|
|
{ |
86 |
|
|
class NameFunctuator final |
87 |
|
|
{ |
88 |
|
|
public: |
89 |
|
|
A_DEFAULT_COPY(NameFunctuator) |
90 |
|
|
|
91 |
|
|
bool operator()(const OnlinePlayer *left, |
92 |
|
|
const OnlinePlayer *right) const |
93 |
|
|
{ |
94 |
|
|
return (compareStrI(left->getNick(), right->getNick()) < 0); |
95 |
|
|
} |
96 |
|
|
} nameCompare; |
97 |
|
|
} // namespace |
98 |
|
|
|
99 |
|
1 |
WhoIsOnline::WhoIsOnline() : |
100 |
|
|
// TRANSLATORS: who is online window name |
101 |
|
1 |
Window(_("Who Is Online - Updating"), |
102 |
|
|
Modal_false, |
103 |
|
|
nullptr, |
104 |
|
|
"whoisonline.xml"), |
105 |
|
|
mUpdateTimer(0), |
106 |
|
|
mThread(nullptr), |
107 |
|
|
mMemoryBuffer(nullptr), |
108 |
✓✗ |
1 |
mCurlError(new char[CURL_ERROR_SIZE]), |
109 |
|
|
mBrowserBox(new StaticBrowserBox(this, Opaque_true, |
110 |
✓✗✓✗
|
1 |
"onlinebrowserbox.xml")), |
111 |
|
|
mScrollArea(new ScrollArea(this, |
112 |
✓✗✓✗
|
2 |
mBrowserBox, Opaque_false, std::string())), |
113 |
|
|
// TRANSLATORS: who is online. button. |
114 |
✓✗✓✗
|
2 |
mUpdateButton(new Button(this, _("Update"), "update", BUTTON_SKIN, this)), |
115 |
|
|
mOnlinePlayers(), |
116 |
|
|
mOnlineNicks(), |
117 |
|
|
mFriends(), |
118 |
|
|
mNeutral(), |
119 |
|
|
mDisregard(), |
120 |
|
|
mEnemy(), |
121 |
|
|
mDownloadedBytes(0), |
122 |
|
|
mDownloadStatus(UPDATE_LIST), |
123 |
|
|
mDownloadComplete(true), |
124 |
|
|
mAllowUpdate(true), |
125 |
|
|
mShowLevel(false), |
126 |
✓✗✓✗
|
4 |
mUpdateOnlineList(config.getBoolValue("updateOnlineList")), |
127 |
|
|
mGroupFriends(true), |
128 |
|
|
#ifdef TMWA_SUPPORT |
129 |
✓✗ |
1 |
mWebList(Net::getNetworkType() == ServerType::TMWATHENA), |
130 |
|
|
#endif |
131 |
✓✗✓✗ ✓✗✓✗ ✓✗ |
30 |
mServerSideList(serverFeatures->haveServerOnlineList()) |
132 |
|
|
{ |
133 |
|
1 |
mCurlError[0] = 0; |
134 |
✓✗ |
5 |
setWindowName("WhoIsOnline"); |
135 |
|
1 |
} |
136 |
|
|
|
137 |
|
1 |
void WhoIsOnline::postInit() |
138 |
|
|
{ |
139 |
|
1 |
Window::postInit(); |
140 |
|
1 |
const int h = 350; |
141 |
|
1 |
const int w = 200; |
142 |
|
1 |
setDefaultSize(w, h, ImagePosition::CENTER, 0, 0); |
143 |
|
|
|
144 |
|
1 |
setVisible(Visible_false); |
145 |
|
1 |
setCloseButton(true); |
146 |
|
1 |
setResizable(true); |
147 |
|
1 |
setStickyButtonLock(true); |
148 |
|
2 |
setSaveVisible(true); |
149 |
|
|
|
150 |
✗✓ |
1 |
if (setupWindow != nullptr) |
151 |
|
|
setupWindow->registerWindowForReset(this); |
152 |
|
|
|
153 |
|
2 |
mUpdateButton->setEnabled(false); |
154 |
|
1 |
mUpdateButton->setDimension(Rect(5, 5, w - 10, 20 + 5)); |
155 |
|
|
|
156 |
|
2 |
mBrowserBox->setOpaque(Opaque_false); |
157 |
|
1 |
mScrollArea->setDimension(Rect(5, 20 + 10, w - 10, h - 10 - 30)); |
158 |
|
1 |
mScrollArea->setSize(w - 10, h - 10 - 30); |
159 |
|
2 |
mScrollArea->setSelectable(false); |
160 |
|
1 |
mBrowserBox->setLinkHandler(this); |
161 |
|
|
|
162 |
|
1 |
add(mUpdateButton); |
163 |
|
1 |
add(mScrollArea); |
164 |
|
|
|
165 |
|
2 |
setLocationRelativeTo(getParent()); |
166 |
|
|
|
167 |
|
1 |
loadWindowState(); |
168 |
|
2 |
enableVisibleSound(true); |
169 |
|
|
|
170 |
|
1 |
download(); |
171 |
|
|
|
172 |
|
2 |
widgetResized(Event(nullptr)); |
173 |
✓✗ |
4 |
config.addListener("updateOnlineList", this); |
174 |
✓✗ |
4 |
config.addListener("groupFriends", this); |
175 |
✓✗ |
4 |
mGroupFriends = config.getBoolValue("groupFriends"); |
176 |
|
1 |
} |
177 |
|
|
|
178 |
|
12 |
WhoIsOnline::~WhoIsOnline() |
179 |
|
|
{ |
180 |
|
1 |
config.removeListeners(this); |
181 |
|
|
CHECKLISTENERS |
182 |
|
|
|
183 |
|
1 |
SDL::WaitThread(mThread); |
184 |
|
1 |
mThread = nullptr; |
185 |
|
1 |
free(mMemoryBuffer); |
186 |
|
1 |
mMemoryBuffer = nullptr; |
187 |
|
|
|
188 |
|
|
// Remove possibly leftover temporary download |
189 |
✓✗ |
1 |
delete []mCurlError; |
190 |
|
|
|
191 |
✗✓ |
3 |
FOR_EACH (std::set<OnlinePlayer*>::iterator, itd, mOnlinePlayers) |
192 |
|
|
delete *itd; |
193 |
|
2 |
mOnlinePlayers.clear(); |
194 |
|
2 |
mOnlineNicks.clear(); |
195 |
|
2 |
} |
196 |
|
|
|
197 |
|
|
void WhoIsOnline::handleLink(const std::string& link, MouseEvent *event) |
198 |
|
|
{ |
199 |
|
|
if ((event == nullptr) || event->getButton() == MouseButton::LEFT) |
200 |
|
|
{ |
201 |
|
|
if (chatWindow != nullptr) |
202 |
|
|
{ |
203 |
|
|
const std::string text = decodeLinkText(link); |
204 |
|
|
if (config.getBoolValue("whispertab")) |
205 |
|
|
{ |
206 |
|
|
chatWindow->localChatInput("/q " + text); |
207 |
|
|
} |
208 |
|
|
else |
209 |
|
|
{ |
210 |
|
|
chatWindow->addInputText( |
211 |
|
|
std::string("/w \"").append(text).append("\" "), |
212 |
|
|
true); |
213 |
|
|
} |
214 |
|
|
} |
215 |
|
|
} |
216 |
|
|
else if (event->getButton() == MouseButton::RIGHT) |
217 |
|
|
{ |
218 |
|
|
if ((localPlayer != nullptr) && link == localPlayer->getName()) |
219 |
|
|
return; |
220 |
|
|
|
221 |
|
|
if (popupMenu != nullptr) |
222 |
|
|
{ |
223 |
|
|
if (actorManager != nullptr) |
224 |
|
|
{ |
225 |
|
|
const std::string text = decodeLinkText(link); |
226 |
|
|
Being *const being = actorManager->findBeingByName( |
227 |
|
|
text, ActorType::Player); |
228 |
|
|
|
229 |
|
|
if ((being != nullptr) && (popupManager != nullptr)) |
230 |
|
|
{ |
231 |
|
|
popupMenu->showPopup(viewport->mMouseX, |
232 |
|
|
viewport->mMouseY, |
233 |
|
|
being); |
234 |
|
|
return; |
235 |
|
|
} |
236 |
|
|
} |
237 |
|
|
popupMenu->showPlayerPopup(link); |
238 |
|
|
} |
239 |
|
|
} |
240 |
|
|
} |
241 |
|
|
|
242 |
|
|
void WhoIsOnline::updateWindow(size_t numOnline) |
243 |
|
|
{ |
244 |
|
|
// Set window caption |
245 |
|
|
// TRANSLATORS: who is online window name |
246 |
|
|
setCaption(_("Who Is Online - ") + toString(CAST_U32(numOnline))); |
247 |
|
|
|
248 |
|
|
// List the online people |
249 |
|
|
std::sort(mFriends.begin(), mFriends.end(), nameCompare); |
250 |
|
|
std::sort(mNeutral.begin(), mNeutral.end(), nameCompare); |
251 |
|
|
std::sort(mDisregard.begin(), mDisregard.end(), nameCompare); |
252 |
|
|
bool addedFromSection(false); |
253 |
|
|
FOR_EACH (STD_VECTOR<OnlinePlayer*>::const_iterator, it, mFriends) |
254 |
|
|
{ |
255 |
|
|
mBrowserBox->addRow((*it)->getText(), false); |
256 |
|
|
addedFromSection = true; |
257 |
|
|
} |
258 |
|
|
if (addedFromSection == true) |
259 |
|
|
{ |
260 |
|
|
mBrowserBox->addRow("---", false); |
261 |
|
|
addedFromSection = false; |
262 |
|
|
} |
263 |
|
|
FOR_EACH (STD_VECTOR<OnlinePlayer*>::const_iterator, it, mEnemy) |
264 |
|
|
{ |
265 |
|
|
mBrowserBox->addRow((*it)->getText(), false); |
266 |
|
|
addedFromSection = true; |
267 |
|
|
} |
268 |
|
|
if (addedFromSection == true) |
269 |
|
|
{ |
270 |
|
|
mBrowserBox->addRow("---", false); |
271 |
|
|
addedFromSection = false; |
272 |
|
|
} |
273 |
|
|
FOR_EACH (STD_VECTOR<OnlinePlayer*>::const_iterator, it, mNeutral) |
274 |
|
|
{ |
275 |
|
|
mBrowserBox->addRow((*it)->getText(), false); |
276 |
|
|
addedFromSection = true; |
277 |
|
|
} |
278 |
|
|
if (addedFromSection == true && !mDisregard.empty()) |
279 |
|
|
mBrowserBox->addRow("---", false); |
280 |
|
|
|
281 |
|
|
FOR_EACH (STD_VECTOR<OnlinePlayer*>::const_iterator, it, mDisregard) |
282 |
|
|
mBrowserBox->addRow((*it)->getText(), false); |
283 |
|
|
|
284 |
|
|
if (mScrollArea->getVerticalMaxScroll() < |
285 |
|
|
mScrollArea->getVerticalScrollAmount()) |
286 |
|
|
{ |
287 |
|
|
mScrollArea->setVerticalScrollAmount( |
288 |
|
|
mScrollArea->getVerticalMaxScroll()); |
289 |
|
|
} |
290 |
|
|
mBrowserBox->updateHeight(); |
291 |
|
|
} |
292 |
|
|
|
293 |
|
|
void WhoIsOnline::handlerPlayerRelation(const std::string &nick, |
294 |
|
|
OnlinePlayer *const player) |
295 |
|
|
{ |
296 |
|
|
if (player == nullptr) |
297 |
|
|
return; |
298 |
|
|
switch (playerRelations.getRelation(nick)) |
299 |
|
|
{ |
300 |
|
|
case Relation::NEUTRAL: |
301 |
|
|
default: |
302 |
|
|
setNeutralColor(player); |
303 |
|
|
mNeutral.push_back(player); |
304 |
|
|
break; |
305 |
|
|
|
306 |
|
|
case Relation::FRIEND: |
307 |
|
|
player->setText("2"); |
308 |
|
|
if (mGroupFriends) |
309 |
|
|
mFriends.push_back(player); |
310 |
|
|
else |
311 |
|
|
mNeutral.push_back(player); |
312 |
|
|
break; |
313 |
|
|
|
314 |
|
|
case Relation::DISREGARDED: |
315 |
|
|
case Relation::BLACKLISTED: |
316 |
|
|
player->setText("8"); |
317 |
|
|
mDisregard.push_back(player); |
318 |
|
|
break; |
319 |
|
|
|
320 |
|
|
case Relation::ENEMY2: |
321 |
|
|
player->setText("1"); |
322 |
|
|
mEnemy.push_back(player); |
323 |
|
|
break; |
324 |
|
|
|
325 |
|
|
case Relation::IGNORED: |
326 |
|
|
case Relation::ERASED: |
327 |
|
|
// Ignore the ignored. |
328 |
|
|
break; |
329 |
|
|
} |
330 |
|
|
} |
331 |
|
|
|
332 |
|
|
void WhoIsOnline::loadList(const STD_VECTOR<OnlinePlayer*> &list) |
333 |
|
|
{ |
334 |
|
|
mBrowserBox->clearRows(); |
335 |
|
|
const size_t numOnline = list.size(); |
336 |
|
|
|
337 |
|
|
FOR_EACH (std::set<OnlinePlayer*>::iterator, itd, mOnlinePlayers) |
338 |
|
|
delete *itd; |
339 |
|
|
mOnlinePlayers.clear(); |
340 |
|
|
mOnlineNicks.clear(); |
341 |
|
|
|
342 |
|
|
mShowLevel = config.getBoolValue("showlevel"); |
343 |
|
|
|
344 |
|
|
FOR_EACH (STD_VECTOR<OnlinePlayer*>::const_iterator, it, list) |
345 |
|
|
{ |
346 |
|
|
OnlinePlayer *player = *it; |
347 |
|
|
const std::string nick = player->getNick(); |
348 |
|
|
mOnlinePlayers.insert(player); |
349 |
|
|
mOnlineNicks.insert(nick); |
350 |
|
|
|
351 |
|
|
if (!mShowLevel) |
352 |
|
|
player->setLevel(0); |
353 |
|
|
|
354 |
|
|
handlerPlayerRelation(nick, player); |
355 |
|
|
} |
356 |
|
|
|
357 |
|
|
updateWindow(numOnline); |
358 |
|
|
if (!mOnlineNicks.empty()) |
359 |
|
|
{ |
360 |
|
|
if (chatWindow != nullptr) |
361 |
|
|
chatWindow->updateOnline(mOnlineNicks); |
362 |
|
|
if (socialWindow != nullptr) |
363 |
|
|
socialWindow->updateActiveList(); |
364 |
|
|
if (actorManager != nullptr) |
365 |
|
|
actorManager->updateSeenPlayers(mOnlineNicks); |
366 |
|
|
} |
367 |
|
|
updateSize(); |
368 |
|
|
mFriends.clear(); |
369 |
|
|
mNeutral.clear(); |
370 |
|
|
mDisregard.clear(); |
371 |
|
|
mEnemy.clear(); |
372 |
|
|
} |
373 |
|
|
|
374 |
|
|
#ifdef TMWA_SUPPORT |
375 |
|
|
void WhoIsOnline::loadWebList() |
376 |
|
|
{ |
377 |
|
|
if (mMemoryBuffer == nullptr) |
378 |
|
|
return; |
379 |
|
|
|
380 |
|
|
// Reallocate and include terminating 0 character |
381 |
|
|
mMemoryBuffer = static_cast<char*>( |
382 |
|
|
realloc(mMemoryBuffer, mDownloadedBytes + 1)); |
383 |
|
|
if (mMemoryBuffer == nullptr) |
384 |
|
|
return; |
385 |
|
|
|
386 |
|
|
mMemoryBuffer[mDownloadedBytes] = '\0'; |
387 |
|
|
|
388 |
|
|
mBrowserBox->clearRows(); |
389 |
|
|
bool listStarted(false); |
390 |
|
|
std::string lineStr; |
391 |
|
|
size_t numOnline(0U); |
392 |
|
|
|
393 |
|
|
// Tokenize and add each line separately |
394 |
|
|
char *line = strtok(mMemoryBuffer, "\n"); |
395 |
|
|
const std::string gmText("(GM)"); |
396 |
|
|
const std::string gmText2("(gm)"); |
397 |
|
|
|
398 |
|
|
FOR_EACH (std::set<OnlinePlayer*>::iterator, itd, mOnlinePlayers) |
399 |
|
|
delete *itd; |
400 |
|
|
|
401 |
|
|
mOnlinePlayers.clear(); |
402 |
|
|
mOnlineNicks.clear(); |
403 |
|
|
|
404 |
|
|
mShowLevel = config.getBoolValue("showlevel"); |
405 |
|
|
|
406 |
|
|
while (line != nullptr) |
407 |
|
|
{ |
408 |
|
|
std::string nick; |
409 |
|
|
lineStr = line; |
410 |
|
|
trim(lineStr); |
411 |
|
|
if (listStarted == true) |
412 |
|
|
{ |
413 |
|
|
if (lineStr.find(" users are online.") == std::string::npos) |
414 |
|
|
{ |
415 |
|
|
if (lineStr.length() > 24) |
416 |
|
|
{ |
417 |
|
|
nick = lineStr.substr(0, 24); |
418 |
|
|
lineStr = lineStr.substr(25); |
419 |
|
|
} |
420 |
|
|
else |
421 |
|
|
{ |
422 |
|
|
nick = lineStr; |
423 |
|
|
lineStr.clear(); |
424 |
|
|
} |
425 |
|
|
trim(nick); |
426 |
|
|
|
427 |
|
|
bool isGM(false); |
428 |
|
|
size_t pos = lineStr.find(gmText, 0); |
429 |
|
|
if (pos != std::string::npos) |
430 |
|
|
{ |
431 |
|
|
lineStr = lineStr.substr(pos + gmText.length()); |
432 |
|
|
isGM = true; |
433 |
|
|
} |
434 |
|
|
else |
435 |
|
|
{ |
436 |
|
|
pos = lineStr.find(gmText2, 0); |
437 |
|
|
if (pos != std::string::npos) |
438 |
|
|
{ |
439 |
|
|
lineStr = lineStr.substr(pos + gmText.length()); |
440 |
|
|
isGM = true; |
441 |
|
|
} |
442 |
|
|
} |
443 |
|
|
|
444 |
|
|
trim(lineStr); |
445 |
|
|
pos = lineStr.find('/', 0); |
446 |
|
|
|
447 |
|
|
if (pos != std::string::npos) |
448 |
|
|
lineStr = lineStr.substr(0, pos); |
449 |
|
|
|
450 |
|
|
int level = 0; |
451 |
|
|
if (!lineStr.empty()) |
452 |
|
|
level = atoi(lineStr.c_str()); |
453 |
|
|
|
454 |
|
|
if (actorManager != nullptr) |
455 |
|
|
{ |
456 |
|
|
Being *const being = actorManager->findBeingByName( |
457 |
|
|
nick, ActorType::Player); |
458 |
|
|
if (being != nullptr) |
459 |
|
|
{ |
460 |
|
|
if (level > 0) |
461 |
|
|
{ |
462 |
|
|
being->setLevel(level); |
463 |
|
|
being->updateName(); |
464 |
|
|
} |
465 |
|
|
else |
466 |
|
|
{ |
467 |
|
|
if (being->getLevel() > 1) |
468 |
|
|
level = being->getLevel(); |
469 |
|
|
} |
470 |
|
|
} |
471 |
|
|
} |
472 |
|
|
|
473 |
|
|
if (!mShowLevel) |
474 |
|
|
level = 0; |
475 |
|
|
|
476 |
|
|
OnlinePlayer *const player = new OnlinePlayer(nick, |
477 |
|
|
CAST_U8(255), level, |
478 |
|
|
Gender::UNSPECIFIED, -1, -1); |
479 |
|
|
mOnlinePlayers.insert(player); |
480 |
|
|
mOnlineNicks.insert(nick); |
481 |
|
|
|
482 |
|
|
if (isGM) |
483 |
|
|
player->setIsGM(true); |
484 |
|
|
|
485 |
|
|
numOnline++; |
486 |
|
|
handlerPlayerRelation(nick, player); |
487 |
|
|
} |
488 |
|
|
} |
489 |
|
|
else if (lineStr.find("------------------------------") |
490 |
|
|
!= std::string::npos) |
491 |
|
|
{ |
492 |
|
|
listStarted = true; |
493 |
|
|
} |
494 |
|
|
line = strtok(nullptr, "\n"); |
495 |
|
|
} |
496 |
|
|
|
497 |
|
|
updateWindow(numOnline); |
498 |
|
|
mBrowserBox->updateHeight(); |
499 |
|
|
|
500 |
|
|
// Free the memory buffer now that we don't need it anymore |
501 |
|
|
free(mMemoryBuffer); |
502 |
|
|
mMemoryBuffer = nullptr; |
503 |
|
|
mFriends.clear(); |
504 |
|
|
mNeutral.clear(); |
505 |
|
|
mDisregard.clear(); |
506 |
|
|
mEnemy.clear(); |
507 |
|
|
} |
508 |
|
|
|
509 |
|
|
size_t WhoIsOnline::memoryWrite(void *restrict ptr, |
510 |
|
|
size_t size, |
511 |
|
|
size_t nmemb, |
512 |
|
|
FILE *restrict stream) |
513 |
|
|
{ |
514 |
|
|
if (stream == nullptr) |
515 |
|
|
return 0; |
516 |
|
|
|
517 |
|
|
WhoIsOnline *restrict const wio = |
518 |
|
|
reinterpret_cast<WhoIsOnline *restrict>(stream); |
519 |
|
|
const size_t totalMem = size * nmemb; |
520 |
|
|
wio->mMemoryBuffer = static_cast<char*>(realloc(wio->mMemoryBuffer, |
521 |
|
|
CAST_SIZE(wio->mDownloadedBytes) + totalMem)); |
522 |
|
|
if (wio->mMemoryBuffer != nullptr) |
523 |
|
|
{ |
524 |
|
|
memcpy(&(wio->mMemoryBuffer[wio->mDownloadedBytes]), ptr, totalMem); |
525 |
|
|
wio->mDownloadedBytes += CAST_S32(totalMem); |
526 |
|
|
} |
527 |
|
|
|
528 |
|
|
return totalMem; |
529 |
|
|
} |
530 |
|
|
|
531 |
|
|
int WhoIsOnline::downloadThread(void *ptr) |
532 |
|
|
{ |
533 |
|
|
int attempts = 0; |
534 |
|
|
WhoIsOnline *const wio = reinterpret_cast<WhoIsOnline *>(ptr); |
535 |
|
|
if (wio == nullptr) |
536 |
|
|
return 0; |
537 |
|
|
CURLcode res; |
538 |
|
|
const std::string url(settings.onlineListUrl + "/online.txt"); |
539 |
|
|
|
540 |
|
|
while (attempts < 1 && !wio->mDownloadComplete) |
541 |
|
|
{ |
542 |
|
|
CURL *curl = curl_easy_init(); |
543 |
|
|
if (curl != nullptr) |
544 |
|
|
{ |
545 |
|
|
if (!wio->mAllowUpdate) |
546 |
|
|
{ |
547 |
|
|
curl_easy_cleanup(curl); |
548 |
|
|
curl = nullptr; |
549 |
|
|
break; |
550 |
|
|
} |
551 |
|
|
wio->mDownloadedBytes = 0; |
552 |
|
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); |
553 |
|
|
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); |
554 |
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, |
555 |
|
|
&WhoIsOnline::memoryWrite); |
556 |
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr); |
557 |
|
|
|
558 |
|
|
curl_easy_setopt(curl, |
559 |
|
|
CURLOPT_USERAGENT, |
560 |
|
|
settings.userAgent.c_str()); |
561 |
|
|
|
562 |
|
|
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, wio->mCurlError); |
563 |
|
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); |
564 |
|
|
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1); |
565 |
|
|
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, ptr); |
566 |
|
|
|
567 |
|
|
#if LIBCURL_VERSION_NUM >= 0x070a00 |
568 |
|
|
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); |
569 |
|
|
#endif // LIBCURL_VERSION_NUM >= 0x070a00 |
570 |
|
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 7); |
571 |
|
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30); |
572 |
|
|
Net::Download::addHeaders(curl); |
573 |
|
|
Net::Download::addProxy(curl); |
574 |
|
|
Net::Download::secureCurl(curl); |
575 |
|
|
Net::Download::addCommonFlags(curl); |
576 |
|
|
|
577 |
|
|
// Make sure the resources2.txt and news.txt aren't cached, |
578 |
|
|
// in order to always get the latest version. |
579 |
|
|
curl_slist *pHeaders = nullptr; |
580 |
|
|
pHeaders = curl_slist_append( |
581 |
|
|
pHeaders, "pragma: no-cache"); |
582 |
|
|
pHeaders = curl_slist_append(pHeaders, |
583 |
|
|
"Cache-Control: no-cache"); |
584 |
|
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, pHeaders); |
585 |
|
|
|
586 |
|
|
if ((res = curl_easy_perform(curl)) != 0) |
587 |
|
|
{ |
588 |
|
|
wio->mDownloadStatus = UPDATE_ERROR; |
589 |
|
|
PRAGMA45(GCC diagnostic push) |
590 |
|
|
PRAGMA45(GCC diagnostic ignored "-Wswitch-enum") |
591 |
|
|
switch (res) |
592 |
|
|
{ |
593 |
|
|
case CURLE_COULDNT_CONNECT: |
594 |
|
|
default: |
595 |
|
|
std::cerr << "curl error " |
596 |
|
|
<< CAST_U32(res) << ": " |
597 |
|
|
<< wio->mCurlError << " host: " |
598 |
|
|
<< url.c_str() << std::endl; |
599 |
|
|
break; |
600 |
|
|
} |
601 |
|
|
PRAGMA45(GCC diagnostic pop) |
602 |
|
|
attempts++; |
603 |
|
|
curl_easy_cleanup(curl); |
604 |
|
|
curl_slist_free_all(pHeaders); |
605 |
|
|
curl = nullptr; |
606 |
|
|
continue; |
607 |
|
|
} |
608 |
|
|
|
609 |
|
|
curl_easy_cleanup(curl); |
610 |
|
|
curl_slist_free_all(pHeaders); |
611 |
|
|
|
612 |
|
|
// It's stored in memory, we're done |
613 |
|
|
wio->mDownloadComplete = true; |
614 |
|
|
} |
615 |
|
|
if (!wio->mAllowUpdate) |
616 |
|
|
break; |
617 |
|
|
attempts++; |
618 |
|
|
} |
619 |
|
|
|
620 |
|
|
if (!wio->mDownloadComplete) |
621 |
|
|
wio->mDownloadStatus = UPDATE_ERROR; |
622 |
|
|
return 0; |
623 |
|
|
} |
624 |
|
|
#endif // TMWA_SUPPORT |
625 |
|
|
|
626 |
|
1 |
void WhoIsOnline::download() |
627 |
|
|
{ |
628 |
✗✓ |
1 |
if (mServerSideList) |
629 |
|
|
{ |
630 |
|
|
if (PacketLimiter::limitPackets(PacketType::PACKET_ONLINELIST)) |
631 |
|
|
playerHandler->requestOnlineList(); |
632 |
|
|
} |
633 |
|
|
#ifdef TMWA_SUPPORT |
634 |
✗✓ |
1 |
else if (mWebList) |
635 |
|
|
{ |
636 |
|
|
mDownloadComplete = true; |
637 |
|
|
SDL::WaitThread(mThread); |
638 |
|
|
mThread = nullptr; |
639 |
|
|
mDownloadComplete = false; |
640 |
|
|
mThread = SDL::createThread(&WhoIsOnline::downloadThread, |
641 |
|
|
"whoisonline", this); |
642 |
|
|
if (mThread == nullptr) |
643 |
|
|
mDownloadStatus = UPDATE_ERROR; |
644 |
|
|
} |
645 |
|
|
#endif // TMWA_SUPPORT |
646 |
|
1 |
} |
647 |
|
|
|
648 |
|
|
void WhoIsOnline::logic() |
649 |
|
|
{ |
650 |
|
|
BLOCK_START("WhoIsOnline::logic") |
651 |
|
|
mScrollArea->logic(); |
652 |
|
|
BLOCK_END("WhoIsOnline::logic") |
653 |
|
|
} |
654 |
|
|
|
655 |
|
|
void WhoIsOnline::slowLogic() |
656 |
|
|
{ |
657 |
|
|
if (!mAllowUpdate) |
658 |
|
|
return; |
659 |
|
|
|
660 |
|
|
BLOCK_START("WhoIsOnline::slowLogic") |
661 |
|
|
if (mUpdateTimer == 0) |
662 |
|
|
mUpdateTimer = cur_time; |
663 |
|
|
|
664 |
|
|
const double timeDiff = difftime(cur_time, mUpdateTimer); |
665 |
|
|
const int timeLimit = isWindowVisible() ? 20 : 120; |
666 |
|
|
|
667 |
|
|
if (mUpdateOnlineList && timeDiff >= timeLimit |
668 |
|
|
&& mDownloadStatus != UPDATE_LIST) |
669 |
|
|
{ |
670 |
|
|
if (mDownloadComplete == true) |
671 |
|
|
{ |
672 |
|
|
// TRANSLATORS: who is online window name |
673 |
|
|
setCaption(_("Who Is Online - Updating")); |
674 |
|
|
mUpdateTimer = 0; |
675 |
|
|
mDownloadStatus = UPDATE_LIST; |
676 |
|
|
download(); |
677 |
|
|
} |
678 |
|
|
} |
679 |
|
|
|
680 |
|
|
#ifdef TMWA_SUPPORT |
681 |
|
|
switch (mDownloadStatus) |
682 |
|
|
{ |
683 |
|
|
case UPDATE_ERROR: |
684 |
|
|
logger->assertLog("Failed to fetch the online list:"); |
685 |
|
|
if (mCurlError != nullptr) |
686 |
|
|
logger->assertLog("%s", mCurlError); |
687 |
|
|
mDownloadStatus = UPDATE_COMPLETE; |
688 |
|
|
// TRANSLATORS: who is online window name |
689 |
|
|
setCaption(_("Who Is Online - error")); |
690 |
|
|
mUpdateButton->setEnabled(true); |
691 |
|
|
mUpdateTimer = cur_time + 240; |
692 |
|
|
mDownloadComplete = true; |
693 |
|
|
updateSize(); |
694 |
|
|
break; |
695 |
|
|
case UPDATE_LIST: |
696 |
|
|
if (mDownloadComplete == true) |
697 |
|
|
{ |
698 |
|
|
loadWebList(); |
699 |
|
|
mDownloadStatus = UPDATE_COMPLETE; |
700 |
|
|
mUpdateButton->setEnabled(true); |
701 |
|
|
mUpdateTimer = 0; |
702 |
|
|
updateSize(); |
703 |
|
|
if (!mOnlineNicks.empty()) |
704 |
|
|
{ |
705 |
|
|
if (chatWindow != nullptr) |
706 |
|
|
chatWindow->updateOnline(mOnlineNicks); |
707 |
|
|
if (socialWindow != nullptr) |
708 |
|
|
socialWindow->updateActiveList(); |
709 |
|
|
if (actorManager != nullptr) |
710 |
|
|
actorManager->updateSeenPlayers(mOnlineNicks); |
711 |
|
|
} |
712 |
|
|
} |
713 |
|
|
break; |
714 |
|
|
case UPDATE_COMPLETE: |
715 |
|
|
default: |
716 |
|
|
break; |
717 |
|
|
} |
718 |
|
|
#endif // TMWA_SUPPORT |
719 |
|
|
|
720 |
|
|
BLOCK_END("WhoIsOnline::slowLogic") |
721 |
|
|
} |
722 |
|
|
|
723 |
|
|
void WhoIsOnline::action(const ActionEvent &event) |
724 |
|
|
{ |
725 |
|
|
if (event.getId() == "update") |
726 |
|
|
{ |
727 |
|
|
#ifdef TMWA_SUPPORT |
728 |
|
|
if (!mServerSideList) |
729 |
|
|
{ |
730 |
|
|
if (mDownloadStatus == UPDATE_COMPLETE) |
731 |
|
|
{ |
732 |
|
|
mUpdateTimer = cur_time - 20; |
733 |
|
|
if (mUpdateButton != nullptr) |
734 |
|
|
mUpdateButton->setEnabled(false); |
735 |
|
|
// TRANSLATORS: who is online window name |
736 |
|
|
setCaption(_("Who Is Online - Update")); |
737 |
|
|
SDL::WaitThread(mThread); |
738 |
|
|
mThread = nullptr; |
739 |
|
|
mDownloadComplete = true; |
740 |
|
|
} |
741 |
|
|
} |
742 |
|
|
else |
743 |
|
|
#endif // TMWA_SUPPORT |
744 |
|
|
{ |
745 |
|
|
if (PacketLimiter::limitPackets(PacketType::PACKET_ONLINELIST)) |
746 |
|
|
{ |
747 |
|
|
mUpdateTimer = cur_time; |
748 |
|
|
playerHandler->requestOnlineList(); |
749 |
|
|
} |
750 |
|
|
} |
751 |
|
|
} |
752 |
|
|
} |
753 |
|
|
|
754 |
|
1 |
void WhoIsOnline::widgetResized(const Event &event) |
755 |
|
|
{ |
756 |
|
2 |
Window::widgetResized(event); |
757 |
|
2 |
updateSize(); |
758 |
|
1 |
} |
759 |
|
|
|
760 |
|
2 |
void WhoIsOnline::updateSize() |
761 |
|
|
{ |
762 |
|
4 |
const Rect area = getChildrenArea(); |
763 |
✓✗ |
2 |
if (mUpdateButton != nullptr) |
764 |
|
2 |
mUpdateButton->setWidth(area.width - 10); |
765 |
|
|
|
766 |
✓✗ |
2 |
if (mScrollArea != nullptr) |
767 |
|
2 |
mScrollArea->setSize(area.width - 10, area.height - 10 - 30); |
768 |
|
2 |
} |
769 |
|
|
|
770 |
|
|
const std::string WhoIsOnline::prepareNick(const std::string &restrict nick, |
771 |
|
|
const int level, |
772 |
|
|
const std::string &restrict |
773 |
|
|
color) const |
774 |
|
|
{ |
775 |
|
|
const std::string text = encodeLinkText(nick); |
776 |
|
|
if (mShowLevel && level > 1) |
777 |
|
|
{ |
778 |
|
|
return strprintf("@@%s|##%s%s (%d)@@", text.c_str(), |
779 |
|
|
color.c_str(), text.c_str(), level); |
780 |
|
|
} |
781 |
|
|
return strprintf("@@%s|##%s%s@@", text.c_str(), |
782 |
|
|
color.c_str(), text.c_str()); |
783 |
|
|
} |
784 |
|
|
|
785 |
|
|
void WhoIsOnline::optionChanged(const std::string &name) |
786 |
|
|
{ |
787 |
|
|
if (name == "updateOnlineList") |
788 |
|
|
mUpdateOnlineList = config.getBoolValue("updateOnlineList"); |
789 |
|
|
else if (name == "groupFriends") |
790 |
|
|
mGroupFriends = config.getBoolValue("groupFriends"); |
791 |
|
|
} |
792 |
|
|
|
793 |
|
|
void WhoIsOnline::setNeutralColor(OnlinePlayer *const player) |
794 |
|
|
{ |
795 |
|
|
if (player == nullptr) |
796 |
|
|
return; |
797 |
|
|
|
798 |
|
|
if ((actorManager != nullptr) && (localPlayer != nullptr)) |
799 |
|
|
{ |
800 |
|
|
const std::string &nick = player->getNick(); |
801 |
|
|
if (nick == localPlayer->getName()) |
802 |
|
|
{ |
803 |
|
|
player->setText("s"); |
804 |
|
|
return; |
805 |
|
|
} |
806 |
|
|
if (localPlayer->isInParty()) |
807 |
|
|
{ |
808 |
|
|
const Party *const party = localPlayer->getParty(); |
809 |
|
|
if (party != nullptr) |
810 |
|
|
{ |
811 |
|
|
if (party->getMember(nick) != nullptr) |
812 |
|
|
{ |
813 |
|
|
player->setText("P"); |
814 |
|
|
return; |
815 |
|
|
} |
816 |
|
|
} |
817 |
|
|
} |
818 |
|
|
|
819 |
|
|
const Being *const being = actorManager->findBeingByName(nick, |
820 |
|
|
ActorType::Player); |
821 |
|
|
if (being != nullptr) |
822 |
|
|
{ |
823 |
|
|
const Guild *const guild2 = localPlayer->getGuild(); |
824 |
|
|
if (guild2 != nullptr) |
825 |
|
|
{ |
826 |
|
|
const Guild *const guild1 = being->getGuild(); |
827 |
|
|
if (guild1 != nullptr) |
828 |
|
|
{ |
829 |
|
|
if (guild1->getId() == guild2->getId() |
830 |
|
|
|| (guild2->getMember(nick) != nullptr)) |
831 |
|
|
{ |
832 |
|
|
player->setText("U"); |
833 |
|
|
return; |
834 |
|
|
} |
835 |
|
|
} |
836 |
|
|
else if (guild2->isMember(nick)) |
837 |
|
|
{ |
838 |
|
|
player->setText("U"); |
839 |
|
|
return; |
840 |
|
|
} |
841 |
|
|
} |
842 |
|
|
} |
843 |
|
|
const Guild *const guild3 = Guild::getGuild(1); |
844 |
|
|
if ((guild3 != nullptr) && guild3->isMember(nick)) |
845 |
|
|
{ |
846 |
|
|
player->setText("U"); |
847 |
|
|
return; |
848 |
|
|
} |
849 |
|
|
} |
850 |
|
|
player->setText("0"); |
851 |
|
|
} |
852 |
|
|
|
853 |
|
|
void WhoIsOnline::getPlayerNames(StringVect &names) |
854 |
|
|
{ |
855 |
|
|
names.clear(); |
856 |
|
|
FOR_EACH (std::set<std::string>::const_iterator, it, mOnlineNicks) |
857 |
|
|
names.push_back(*it); |
858 |
|
|
} |
859 |
|
|
|
860 |
|
|
void OnlinePlayer::setText(std::string color) |
861 |
|
|
{ |
862 |
|
|
mText.clear(); |
863 |
|
|
|
864 |
|
|
if (mStatus != 255 && (actorManager != nullptr)) |
865 |
|
|
{ |
866 |
|
|
Being *const being = actorManager->findBeingByName( |
867 |
|
|
mNick, ActorType::Player); |
868 |
|
|
if (being != nullptr) |
869 |
|
|
{ |
870 |
|
|
being->setState(mStatus); |
871 |
|
|
// for now highlight versions > 3 |
872 |
|
|
if (mVersion > 3) |
873 |
|
|
being->setAdvanced(true); |
874 |
|
|
} |
875 |
|
|
} |
876 |
|
|
|
877 |
|
|
#ifdef TMWA_SUPPORT |
878 |
|
|
if (mGroup != -1 && GroupDb::getShowBadge(mGroup)) |
879 |
|
|
{ |
880 |
|
|
const std::string &name = GroupDb::getName(mGroup); |
881 |
|
|
mText.append(strprintf("(%s) ", name.c_str())); |
882 |
|
|
} |
883 |
|
|
else |
884 |
|
|
#endif |
885 |
|
|
{ |
886 |
|
|
if ((mStatus != 255 && ((mStatus & BeingFlag::GM) != 0)) || mIsGM) |
887 |
|
|
mText.append("(GM) "); |
888 |
|
|
} |
889 |
|
|
|
890 |
|
|
if (mLevel > 0) |
891 |
|
|
mText.append(strprintf("%d", mLevel)); |
892 |
|
|
|
893 |
|
|
if (mGender == Gender::FEMALE) |
894 |
|
|
mText.append("\u2640"); |
895 |
|
|
else if (mGender == Gender::MALE) |
896 |
|
|
mText.append("\u2642"); |
897 |
|
|
|
898 |
|
|
#ifdef TMWA_SUPPORT |
899 |
|
|
if (mGroup != -1 && GroupDb::getHighlightName(mGroup) && color == "0") |
900 |
|
|
color = "2"; |
901 |
|
|
#endif |
902 |
|
|
|
903 |
|
|
if (mStatus > 0 && mStatus != 255) |
904 |
|
|
{ |
905 |
|
|
if ((mStatus & BeingFlag::SHOP) != 0) |
906 |
|
|
mText.append("$"); |
907 |
|
|
if ((mStatus & BeingFlag::AWAY) != 0) |
908 |
|
|
{ |
909 |
|
|
// TRANSLATORS: this away status writed in player nick |
910 |
|
|
mText.append(_("A")); |
911 |
|
|
} |
912 |
|
|
if ((mStatus & BeingFlag::INACTIVE) != 0) |
913 |
|
|
{ |
914 |
|
|
// TRANSLATORS: this inactive status writed in player nick |
915 |
|
|
mText.append(_("I")); |
916 |
|
|
} |
917 |
|
|
|
918 |
|
|
if (((mStatus & BeingFlag::GM) != 0) && color == "0") |
919 |
|
|
color = "2"; |
920 |
|
|
} |
921 |
|
|
else if (mIsGM && color == "0") |
922 |
|
|
{ |
923 |
|
|
color = "2"; |
924 |
|
|
} |
925 |
|
|
|
926 |
|
|
if (mVersion > 0) |
927 |
|
|
mText.append(strprintf(" - %d", mVersion)); |
928 |
|
|
|
929 |
|
|
const std::string text = encodeLinkText(mNick); |
930 |
|
|
mText = strprintf("@@%s|##%s%s %s@@", text.c_str(), color.c_str(), |
931 |
|
|
text.c_str(), mText.c_str()); |
932 |
✓✗✓✗
|
3 |
} |