1 /*
2 * The ManaPlus Client
3 * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net>
4 * Copyright (C) 2011-2019 The ManaPlus Developers
5 * Copyright (C) 2019-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 "net/eathena/partyrecv.h"
24
25 #include "notifymanager.h"
26 #include "party.h"
27
28 #include "being/localplayer.h"
29
30 #include "enums/resources/notifytypes.h"
31
32 #include "gui/windows/chatwindow.h"
33 #include "gui/windows/socialwindow.h"
34
35 #include "gui/widgets/tabs/chat/partytab.h"
36
37 #include "net/messagein.h"
38
39 #include "net/ea/partyrecv.h"
40
41 #include "utils/checkutils.h"
42 #include "utils/foreach.h"
43
44 #include "debug.h"
45
46 extern bool packets_zero;
47
48 namespace EAthena
49 {
50
51 namespace PartyRecv
52 {
53 PartyShareT mShareAutoItems = PartyShare::UNKNOWN;
54 } // namespace PartyRecv
55
processPartyInvitationStats(Net::MessageIn & msg)56 void PartyRecv::processPartyInvitationStats(Net::MessageIn &msg)
57 {
58 // +++ for now server allow only switch this option but not using it.
59 msg.readUInt8("allow party");
60 }
61
processPartyMemberInfo(Net::MessageIn & msg)62 void PartyRecv::processPartyMemberInfo(Net::MessageIn &msg)
63 {
64 const BeingId id = msg.readBeingId("account id");
65 if (msg.getVersion() >= 20171207)
66 msg.readBeingId("char id");
67 const bool leader = msg.readInt32("leader") == 0U;
68 int level = 0;
69 if (msg.getVersionMain() >= 20170524 ||
70 msg.getVersionRe() >= 20170502 ||
71 packets_zero == true)
72 {
73 msg.readInt16("class");
74 level = msg.readInt16("level");
75 }
76 const int x = msg.readInt16("x");
77 const int y = msg.readInt16("y");
78 const bool online = msg.readInt8("online") == 0U;
79 msg.readString(24, "party name");
80 const std::string nick = msg.readString(24, "player name");
81 const std::string map = msg.readString(16, "map name");
82 msg.readInt8("pickup item share (&1)");
83 msg.readInt8("get item share (&2)");
84
85 if (Ea::taParty == nullptr)
86 return;
87
88 PartyMember *const member = Ea::taParty->addMember(id, nick);
89 if (member != nullptr)
90 {
91 if ((partyTab != nullptr) && member->getOnline() != online)
92 partyTab->showOnline(nick, fromBool(online, Online));
93 member->setLeader(leader);
94 member->setOnline(online);
95 member->setMap(map);
96 member->setX(x);
97 member->setY(y);
98 if (level != 0)
99 member->setLevel(level);
100 }
101 }
102
processPartyMemberJobLevel(Net::MessageIn & msg)103 void PartyRecv::processPartyMemberJobLevel(Net::MessageIn &msg)
104 {
105 const BeingId id = msg.readBeingId("account id");
106 msg.readInt16("class");
107 const int level = msg.readInt16("level");
108
109 if (Ea::taParty == nullptr)
110 return;
111
112 PartyMember *const member = Ea::taParty->getMember(id);
113 if (member != nullptr)
114 {
115 member->setOnline(true);
116 if (level != 0)
117 member->setLevel(level);
118 }
119 else
120 {
121 reportAlways("processPartyMemberJobLevel: party member not exists.")
122 }
123 }
124
processPartySettings(Net::MessageIn & msg)125 void PartyRecv::processPartySettings(Net::MessageIn &msg)
126 {
127 if (partyTab == nullptr)
128 {
129 if (chatWindow == nullptr)
130 return;
131
132 Ea::PartyRecv::createTab();
133 }
134
135 const PartyShareT exp = static_cast<PartyShareT>(
136 msg.readInt32("party exp"));
137 Ea::PartyRecv::processPartyExpSettingsContinue(msg, exp);
138 if (msg.getVersion() >= 20090603)
139 {
140 const PartyShareT item = static_cast<PartyShareT>(
141 msg.readInt8("pickup item share (&1)"));
142 Ea::PartyRecv::processPartyItemSettingsContinue(msg, item);
143 const PartyShareT autoItem = static_cast<PartyShareT>(
144 msg.readInt8("get auto item share (&2)"));
145 processPartyAutoItemSettingsContinue(msg, autoItem);
146 }
147 }
148
processPartyInfo(Net::MessageIn & msg)149 void PartyRecv::processPartyInfo(Net::MessageIn &msg)
150 {
151 bool isOldParty = false;
152 std::set<std::string> names;
153 std::set<std::string> onlineNames;
154 if (Ea::taParty == nullptr)
155 {
156 logger->log1("error: party empty in SMSG_PARTY_INFO");
157 Ea::taParty = Party::getParty(1);
158 }
159 if (partyTab == nullptr)
160 Ea::PartyRecv::createTab();
161
162 if (Ea::taParty != nullptr)
163 {
164 if (Ea::taParty->getNumberOfElements() > 1)
165 {
166 isOldParty = true;
167 Ea::taParty->getNamesSet(names);
168 const Party::MemberList *const members = Ea::taParty->getMembers();
169 FOR_EACHP (Party::MemberList::const_iterator, it, members)
170 {
171 if ((*it)->getOnline())
172 onlineNames.insert((*it)->getName());
173 }
174 if (localPlayer != nullptr)
175 onlineNames.insert(localPlayer->getName());
176 }
177 }
178
179 if (localPlayer == nullptr)
180 logger->log1("error: localPlayer==0 in SMSG_PARTY_INFO");
181
182 if (Ea::taParty != nullptr)
183 Ea::taParty->clearMembers();
184
185 const int length = msg.readInt16("len");
186 if (Ea::taParty != nullptr)
187 {
188 const std::string name = msg.readString(24, "party name");
189 Ea::taParty->setName(name);
190 }
191 else
192 {
193 msg.readString(24, "party name");
194 }
195
196 int partySize = 0;
197 const int offset = 28;
198 if (msg.getVersion() >= 20171207)
199 {
200 partySize = 4 + 4 + 24 + 16 + 1 + 1 + 2 + 2;
201 }
202 else if (msg.getVersionMain() >= 20170524 ||
203 msg.getVersionRe() >= 20170502 ||
204 packets_zero == true)
205 {
206 partySize = 4 + 24 + 16 + 1 + 1 + 2 + 2;
207 }
208 else
209 {
210 partySize = 4 + 24 + 16 + 1 + 1;
211 }
212
213 const int count = (length - offset) / partySize;
214 if (localPlayer != nullptr &&
215 Ea::taParty != nullptr)
216 {
217 localPlayer->setParty(Ea::taParty);
218 localPlayer->setPartyName(Ea::taParty->getName());
219 }
220
221 for (int i = 0; i < count; i++)
222 {
223 const BeingId id = msg.readBeingId("account id");
224 if (msg.getVersion() >= 20171207)
225 msg.readBeingId("char id");
226 std::string nick = msg.readString(24, "nick");
227 std::string map = msg.readString(16, "map name");
228 const bool leader = msg.readUInt8("leader") == 0U;
229 const bool online = msg.readUInt8("online") == 0U;
230 int level = 0;
231 if (msg.getVersionMain() >= 20170524 ||
232 msg.getVersionRe() >= 20170502 ||
233 packets_zero == true)
234 {
235 msg.readInt16("class");
236 level = msg.readInt16("level");
237 }
238
239 if (Ea::taParty != nullptr)
240 {
241 bool joined(false);
242
243 if (isOldParty)
244 {
245 if (names.find(nick) == names.end())
246 {
247 NotifyManager::notify(NotifyTypes::PARTY_USER_JOINED,
248 nick);
249 joined = true;
250 }
251 }
252 PartyMember *const member = Ea::taParty->addMember(id, nick);
253 if (member != nullptr)
254 {
255 if (!joined && (partyTab != nullptr))
256 {
257 if (!names.empty() && ((onlineNames.find(nick)
258 == onlineNames.end() && online)
259 || (onlineNames.find(nick) != onlineNames.end()
260 && !online)))
261 {
262 partyTab->showOnline(nick, fromBool(online, Online));
263 }
264 }
265 member->setLeader(leader);
266 member->setOnline(online);
267 member->setMap(map);
268 if (level != 0)
269 member->setLevel(level);
270 }
271 }
272 }
273
274 // fix for wrong data sent by old hercules. in future need delete it
275 if (msg.getVersion() >= 20170502 && msg.getUnreadLength() >= 6)
276 {
277 msg.readInt8("pickup item share (&1)");
278 msg.readInt8("get item share (&2)");
279 msg.readInt32("unknown");
280 }
281
282 if (Ea::taParty != nullptr)
283 Ea::taParty->sort();
284
285 if ((localPlayer != nullptr) && (Ea::taParty != nullptr))
286 {
287 localPlayer->setParty(Ea::taParty);
288 localPlayer->setPartyName(Ea::taParty->getName());
289 if (socialWindow != nullptr)
290 socialWindow->updateParty();
291 }
292 }
293
processPartyMessage(Net::MessageIn & msg)294 void PartyRecv::processPartyMessage(Net::MessageIn &msg)
295 {
296 const int msgLength = msg.readInt16("len") - 8;
297 if (msgLength <= 0)
298 return;
299
300 const BeingId id = msg.readBeingId("id");
301 std::string chatMsg = msg.readString(msgLength, "message");
302
303 const size_t pos = chatMsg.find(" : ", 0);
304 if (pos != std::string::npos)
305 chatMsg.erase(0, pos + 3);
306
307 if ((Ea::taParty != nullptr) && (partyTab != nullptr))
308 {
309 const PartyMember *const member = Ea::taParty->getMember(id);
310 if (member != nullptr)
311 {
312 partyTab->chatLog(member->getName(), chatMsg);
313 }
314 else
315 {
316 NotifyManager::notify(NotifyTypes::PARTY_UNKNOWN_USER_MSG,
317 chatMsg);
318 }
319 }
320 }
321
processPartyInviteResponse(Net::MessageIn & msg)322 void PartyRecv::processPartyInviteResponse(Net::MessageIn &msg)
323 {
324 if (partyTab == nullptr)
325 return;
326
327 const std::string nick = msg.readString(24, "nick");
328
329 switch (msg.readInt32("result"))
330 {
331 case 0:
332 NotifyManager::notify(NotifyTypes::PARTY_INVITE_ALREADY_MEMBER,
333 nick);
334 break;
335 case 1:
336 NotifyManager::notify(NotifyTypes::PARTY_INVITE_REFUSED, nick);
337 break;
338 case 2:
339 NotifyManager::notify(NotifyTypes::PARTY_INVITE_DONE, nick);
340 break;
341 case 3:
342 NotifyManager::notify(NotifyTypes::PARTY_INVITE_PARTY_FULL,
343 nick);
344 break;
345 case 4:
346 NotifyManager::notify(NotifyTypes::PARTY_INVITE_PARTY_SAME_ACCOUNT,
347 nick);
348 break;
349 case 5:
350 NotifyManager::notify(
351 NotifyTypes::PARTY_INVITE_PARTY_BLOCKED_INVITE,
352 nick);
353 break;
354 case 7:
355 NotifyManager::notify(NotifyTypes::PARTY_INVITE_PARTY_NOT_ONLINE,
356 nick);
357 break;
358 default:
359 NotifyManager::notify(NotifyTypes::PARTY_INVITE_ERROR, nick);
360 break;
361 }
362 }
363
processPartyItemPickup(Net::MessageIn & msg)364 void PartyRecv::processPartyItemPickup(Net::MessageIn &msg)
365 {
366 UNIMPLEMENTEDPACKET;
367 // +++ probably need add option to show pickup notifications
368 // in party tab
369 msg.readBeingId("account id");
370 msg.readItemId("item id");
371 msg.readUInt8("identify");
372 msg.readUInt8("attribute");
373 msg.readUInt8("refine");
374 for (int f = 0; f < maxCards; f++)
375 msg.readItemId("card");
376 msg.readInt16("equip location");
377 msg.readUInt8("item type");
378 // for color can be used ItemColorManager
379 }
380
processPartyLeader(Net::MessageIn & msg)381 void PartyRecv::processPartyLeader(Net::MessageIn &msg)
382 {
383 PartyMember *const oldMember = Ea::taParty->getMember(
384 msg.readBeingId("old leder id"));
385 PartyMember *const newMember = Ea::taParty->getMember(
386 msg.readBeingId("new leder id"));
387 if (oldMember != nullptr)
388 oldMember->setLeader(false);
389 if (newMember != nullptr)
390 newMember->setLeader(true);
391 }
392
processPartyInvited(Net::MessageIn & msg)393 void PartyRecv::processPartyInvited(Net::MessageIn &msg)
394 {
395 if (socialWindow == nullptr)
396 {
397 msg.readInt32("party id");
398 msg.readString(24, "party name");
399 return;
400 }
401 const int id = msg.readInt32("party id");
402 const std::string partyName = msg.readString(24, "party name");
403
404 if (socialWindow != nullptr)
405 socialWindow->showPartyInvite(partyName, std::string(), id);
406 }
407
processPartyAutoItemSettingsContinue(Net::MessageIn & msg,const PartyShareT item)408 void PartyRecv::processPartyAutoItemSettingsContinue(Net::MessageIn &msg,
409 const PartyShareT item)
410 {
411 switch (item)
412 {
413 case PartyShare::YES:
414 if (mShareAutoItems == PartyShare::YES)
415 break;
416 mShareAutoItems = PartyShare::YES;
417 NotifyManager::notify(NotifyTypes::PARTY_ITEM_SHARE_ON);
418 break;
419 case PartyShare::NO:
420 if (mShareAutoItems == PartyShare::NO)
421 break;
422 mShareAutoItems = PartyShare::NO;
423 NotifyManager::notify(NotifyTypes::PARTY_ITEM_SHARE_OFF);
424 break;
425 case PartyShare::NOT_POSSIBLE:
426 if (mShareAutoItems == PartyShare::NOT_POSSIBLE)
427 break;
428 mShareAutoItems = PartyShare::NOT_POSSIBLE;
429 NotifyManager::notify(NotifyTypes::PARTY_ITEM_SHARE_ERROR);
430 break;
431 default:
432 case PartyShare::UNKNOWN:
433 UNIMPLEMENTEDPACKETFIELD(CAST_S32(item));
434 break;
435 }
436 }
437
processPartyMemberDead(Net::MessageIn & msg)438 void PartyRecv::processPartyMemberDead(Net::MessageIn &msg)
439 {
440 const BeingId id = msg.readBeingId("account id");
441 const int isDead = msg.readUInt8("is dead");
442 PartyMember *const member = Ea::taParty->getMember(id);
443 if (member != nullptr && isDead != 0)
444 {
445 member->setHp(0);
446 }
447 }
448
449 } // namespace EAthena
450