GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/eathena/playerrecv.cpp Lines: 1 279 0.4 %
Date: 2018-11-12 Branches: 0 130 0.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2004-2009  The Mana World Development Team
4
 *  Copyright (C) 2009-2010  The Mana Developers
5
 *  Copyright (C) 2011-2018  The ManaPlus Developers
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/playerrecv.h"
24
25
#include "actormanager.h"
26
#include "configuration.h"
27
#include "notifymanager.h"
28
#include "party.h"
29
30
#include "being/beingflag.h"
31
#include "being/localplayer.h"
32
#include "being/playerinfo.h"
33
34
#include "const/net/nostat.h"
35
36
#include "enums/resources/notifytypes.h"
37
38
#include "gui/onlineplayer.h"
39
40
#include "gui/windows/statuswindow.h"
41
#include "gui/windows/whoisonline.h"
42
43
#include "gui/widgets/tabs/chat/chattab.h"
44
45
#include "net/eathena/sp.h"
46
#include "net/playerhandler.h"
47
48
#include "utils/gettext.h"
49
50
#include "debug.h"
51
52
namespace EAthena
53
{
54
55
void PlayerRecv::processPlayerShortcuts(Net::MessageIn &msg)
56
{
57
    // +++ player shortcuts ignored. It also disabled on server side.
58
    // may be in future better use it?
59
    if (msg.getVersion() >= 20141022)
60
        msg.readUInt8("rotate");
61
    for (int f = 0; f < 27; f ++)
62
    {
63
        msg.readUInt8("type 0: item, 1: skill");
64
        msg.readInt32("item or skill id");
65
        msg.readInt16("skill level");
66
    }
67
    msg.skip(77, "unused");
68
}
69
70
void PlayerRecv::processPlayerShowEquip(Net::MessageIn &msg)
71
{
72
    // +++ for now server allow only switch this option but not using it.
73
    msg.readUInt8("show equip");  // 1 mean need open "equipment" window
74
}
75
76
void PlayerRecv::processPlayerStatUpdate5(Net::MessageIn &msg)
77
{
78
    BLOCK_START("PlayerRecv::processPlayerStatUpdate5")
79
    PlayerInfo::setAttribute(Attributes::PLAYER_CHAR_POINTS,
80
        msg.readInt16("char points"),
81
        Notify_true);
82
83
    unsigned int val = msg.readUInt8("str");
84
    PlayerInfo::setStatBase(Attributes::PLAYER_STR,
85
        val,
86
        Notify_true);
87
    if (statusWindow != nullptr)
88
    {
89
        statusWindow->setPointsNeeded(Attributes::PLAYER_STR,
90
            msg.readUInt8("str cost"));
91
    }
92
    else
93
    {
94
        msg.readUInt8("str need");
95
    }
96
97
    val = msg.readUInt8("agi");
98
    PlayerInfo::setStatBase(Attributes::PLAYER_AGI, val, Notify_true);
99
    if (statusWindow != nullptr)
100
    {
101
        statusWindow->setPointsNeeded(Attributes::PLAYER_AGI,
102
            msg.readUInt8("agi cost"));
103
    }
104
    else
105
    {
106
        msg.readUInt8("agi cost");
107
    }
108
109
    val = msg.readUInt8("vit");
110
    PlayerInfo::setStatBase(Attributes::PLAYER_VIT, val, Notify_true);
111
    if (statusWindow != nullptr)
112
    {
113
        statusWindow->setPointsNeeded(Attributes::PLAYER_VIT,
114
            msg.readUInt8("vit cost"));
115
    }
116
    else
117
    {
118
        msg.readUInt8("vit cost");
119
    }
120
121
    val = msg.readUInt8("int");
122
    PlayerInfo::setStatBase(Attributes::PLAYER_INT, val, Notify_true);
123
    if (statusWindow != nullptr)
124
    {
125
        statusWindow->setPointsNeeded(Attributes::PLAYER_INT,
126
            msg.readUInt8("int cost"));
127
    }
128
    else
129
    {
130
        msg.readUInt8("int cost");
131
    }
132
133
    val = msg.readUInt8("dex");
134
    PlayerInfo::setStatBase(Attributes::PLAYER_DEX, val, Notify_true);
135
    if (statusWindow != nullptr)
136
    {
137
        statusWindow->setPointsNeeded(Attributes::PLAYER_DEX,
138
            msg.readUInt8("dex cost"));
139
    }
140
    else
141
    {
142
        msg.readUInt8("dex cost");
143
    }
144
145
    val = msg.readUInt8("luk");
146
    PlayerInfo::setStatBase(Attributes::PLAYER_LUK, val, Notify_true);
147
    if (statusWindow != nullptr)
148
    {
149
        statusWindow->setPointsNeeded(Attributes::PLAYER_LUK,
150
            msg.readUInt8("luk cost"));
151
    }
152
    else
153
    {
154
        msg.readUInt8("luk cost");
155
    }
156
157
    PlayerInfo::setStatBase(Attributes::PLAYER_ATK,
158
        msg.readInt16("left atk"),
159
        Notify_false);
160
    PlayerInfo::setStatMod(Attributes::PLAYER_ATK,
161
        msg.readInt16("right atk"),
162
        Notify_true);
163
    PlayerInfo::updateAttrs();
164
165
    val = msg.readInt16("right matk");
166
    PlayerInfo::setStatBase(Attributes::PLAYER_MATK, val, Notify_false);
167
168
    val = msg.readInt16("left matk");
169
    PlayerInfo::setStatMod(Attributes::PLAYER_MATK,
170
        val,
171
        Notify_true);
172
173
    PlayerInfo::setStatBase(Attributes::PLAYER_DEF,
174
        msg.readInt16("left def"),
175
        Notify_false);
176
    PlayerInfo::setStatMod(Attributes::PLAYER_DEF,
177
        msg.readInt16("right def"),
178
        Notify_true);
179
180
    PlayerInfo::setStatBase(Attributes::PLAYER_MDEF,
181
        msg.readInt16("left mdef"),
182
        Notify_false);
183
    PlayerInfo::setStatMod(Attributes::PLAYER_MDEF,
184
        msg.readInt16("right mdef"),
185
        Notify_true);
186
187
    PlayerInfo::setStatBase(Attributes::PLAYER_HIT,
188
        msg.readInt16("hit"),
189
        Notify_true);
190
191
    PlayerInfo::setStatBase(Attributes::PLAYER_FLEE,
192
        msg.readInt16("flee"),
193
        Notify_false);
194
    PlayerInfo::setStatMod(Attributes::PLAYER_FLEE,
195
        msg.readInt16("flee2/10"),
196
        Notify_true);
197
198
    PlayerInfo::setStatBase(Attributes::PLAYER_CRIT,
199
        msg.readInt16("crit/10"),
200
        Notify_true);
201
202
    PlayerInfo::setAttribute(Attributes::PLAYER_ATTACK_DELAY,
203
        msg.readInt16("attack speed"),
204
        Notify_true);
205
    msg.readInt16("plus speed = 0");
206
207
    BLOCK_END("PlayerRecv::processPlayerStatUpdate5")
208
}
209
210
void PlayerRecv::processPlayerGetExp(Net::MessageIn &msg)
211
{
212
    if (localPlayer == nullptr)
213
        return;
214
    const BeingId id = msg.readBeingId("player id");
215
    const int exp = msg.readInt32("exp amount");
216
    const int stat = msg.readInt16("exp type");
217
    const bool fromQuest = msg.readInt16("is from quest") != 0;
218
    if (!fromQuest && id == localPlayer->getId())
219
    {
220
        if (stat == 1)
221
            localPlayer->addXpMessage(exp);
222
        else if (stat == 2)
223
            localPlayer->addJobMessage(exp);
224
        else
225
            UNIMPLEMENTEDPACKETFIELD(stat);
226
    }
227
    // need show particle depend on isQuest flag, for now ignored
228
}
229
230
void PlayerRecv::processPlayerGetExp2(Net::MessageIn &msg)
231
{
232
    if (localPlayer == nullptr)
233
        return;
234
    const BeingId id = msg.readBeingId("player id");
235
    const int64_t exp = msg.readInt64("exp amount");
236
    const int stat = msg.readInt16("exp type");
237
    const bool fromQuest = msg.readInt16("is from quest") != 0;
238
    if (!fromQuest && id == localPlayer->getId())
239
    {
240
        if (stat == 1)
241
            localPlayer->addXpMessage(exp);
242
        else if (stat == 2)
243
            localPlayer->addJobMessage(exp);
244
        else
245
            UNIMPLEMENTEDPACKETFIELD(stat);
246
    }
247
    // need show particle depend on isQuest flag, for now ignored
248
}
249
250
void PlayerRecv::processWalkResponse(Net::MessageIn &msg)
251
{
252
    BLOCK_START("PlayerRecv::processWalkResponse")
253
    /*
254
      * This client assumes that all walk messages succeed,
255
      * and that the server will send a correction notice
256
      * otherwise.
257
      */
258
    uint16_t srcX;
259
    uint16_t srcY;
260
    uint16_t dstX;
261
    uint16_t dstY;
262
    msg.readInt32("tick");
263
    msg.readCoordinatePair(srcX, srcY, dstX, dstY, "move path");
264
    msg.readUInt8("(sx<<4) | (sy&0x0f)");
265
    if (localPlayer != nullptr)
266
        localPlayer->setRealPos(dstX, dstY);
267
    BLOCK_END("PlayerRecv::processWalkResponse")
268
}
269
270
void PlayerRecv::processWalkError(Net::MessageIn &msg)
271
{
272
    msg.readInt32("tick");
273
    const int x = msg.readInt16("x");
274
    const int y = msg.readInt16("y");
275
    if (localPlayer != nullptr)
276
        localPlayer->failMove(x, y);
277
}
278
279
void PlayerRecv::processPvpInfo(Net::MessageIn &msg)
280
{
281
    UNIMPLEMENTEDPACKET;
282
    msg.readInt32("char id");
283
    msg.readBeingId("account id");
284
    msg.readInt32("pvp won");
285
    msg.readInt32("pvp lost");
286
    msg.readInt32("pvp point");
287
}
288
289
void PlayerRecv::processPlayerHeal(Net::MessageIn &msg)
290
{
291
    if (localPlayer == nullptr)
292
        return;
293
294
    const int type = msg.readInt16("var id");
295
    int amount;
296
    if (msg.getVersion() >= 20150513)
297
        amount = msg.readInt32("value");
298
    else
299
        amount = msg.readInt16("value");
300
    if (type == Sp::HP)
301
    {
302
        const int base = PlayerInfo::getAttribute(Attributes::PLAYER_HP) +
303
            amount;
304
        PlayerInfo::setAttribute(Attributes::PLAYER_HP,
305
            base,
306
            Notify_true);
307
        if (localPlayer->isInParty() && (Party::getParty(1) != nullptr))
308
        {
309
            PartyMember *const m = Party::getParty(1)
310
                ->getMember(localPlayer->getId());
311
            if (m != nullptr)
312
            {
313
                m->setHp(base);
314
                m->setMaxHp(PlayerInfo::getAttribute(
315
                    Attributes::PLAYER_MAX_HP));
316
            }
317
        }
318
        localPlayer->addHpMessage(amount);
319
    }
320
    else if (type == Sp::SP)
321
    {
322
        localPlayer->addSpMessage(amount);
323
    }
324
}
325
326
void PlayerRecv::processPlayerSkillMessage(Net::MessageIn &msg)
327
{
328
    const int message = msg.readInt32("type");
329
    switch (message)
330
    {
331
        case 0x15:
332
            NotifyManager::notify(NotifyTypes::SKILL_END_ALL_NEGATIVE_STATUS);
333
            break;
334
        case 0x16:
335
            NotifyManager::notify(NotifyTypes::SKILL_IMMUNITY_TO_ALL_STATUSES);
336
            break;
337
        case 0x17:
338
            NotifyManager::notify(NotifyTypes::SKILL_MAX_HP);
339
            break;
340
        case 0x18:
341
            NotifyManager::notify(NotifyTypes::SKILL_MAX_SP);
342
            break;
343
        case 0x19:
344
            NotifyManager::notify(NotifyTypes::SKILL_ALL_STATUS_PLUS_20);
345
            break;
346
        case 0x1c:
347
            NotifyManager::notify(NotifyTypes::SKILL_ENCHANT_WEAPON_HOLY);
348
            break;
349
        case 0x1d:
350
            NotifyManager::notify(NotifyTypes::SKILL_ENCHANT_ARMOR_HOLY);
351
            break;
352
        case 0x1e:
353
            NotifyManager::notify(NotifyTypes::SKILL_DEF_PLUS_25);
354
            break;
355
        case 0x1f:
356
            NotifyManager::notify(NotifyTypes::SKILL_ATTACK_PLUS_100);
357
            break;
358
        case 0x20:
359
            NotifyManager::notify(NotifyTypes::SKILL_FLEE_PLUS_50);
360
            break;
361
        case 0x28:
362
            NotifyManager::notify(NotifyTypes::SKILL_FULL_STRIP_FAILED);
363
            break;
364
        default:
365
            NotifyManager::notify(NotifyTypes::SKILL_MESSAGE_UNKNOWN);
366
            break;
367
    }
368
}
369
370
void PlayerRecv::processNotifyMapInfo(Net::MessageIn &msg)
371
{
372
    UNIMPLEMENTEDPACKET;
373
    msg.readInt16("type");
374
}
375
376
void PlayerRecv::processPlayerFameBlacksmith(Net::MessageIn &msg)
377
{
378
    UNIMPLEMENTEDPACKET;
379
    msg.readInt32("points");
380
    msg.readInt32("total points");
381
}
382
383
void PlayerRecv::processPlayerFameAlchemist(Net::MessageIn &msg)
384
{
385
    UNIMPLEMENTEDPACKET;
386
    msg.readInt32("points");
387
    msg.readInt32("total points");
388
}
389
390
void PlayerRecv::processPlayerUpgradeMessage(Net::MessageIn &msg)
391
{
392
    UNIMPLEMENTEDPACKET;
393
    msg.readInt32("result");
394
    msg.readItemId("item id");
395
}
396
397
void PlayerRecv::processPlayerFameTaekwon(Net::MessageIn &msg)
398
{
399
    UNIMPLEMENTEDPACKET;
400
    msg.readInt32("points");
401
    msg.readInt32("total points");
402
}
403
404
void PlayerRecv::processPlayerReadBook(Net::MessageIn &msg)
405
{
406
    UNIMPLEMENTEDPACKET;
407
    msg.readInt32("book id");
408
    msg.readInt32("page");
409
}
410
411
void PlayerRecv::processPlayerEquipTickAck(Net::MessageIn &msg)
412
{
413
    // +++ actually ZC_CONFIG
414
    UNIMPLEMENTEDPACKET;
415
    msg.readInt32("unused");
416
    msg.readInt32("flag");
417
}
418
419
void PlayerRecv::processPlayerAutoShadowSpellList(Net::MessageIn &msg)
420
{
421
    UNIMPLEMENTEDPACKET;
422
    const int count = (msg.readInt16("len") - 8) / 2;
423
    for (int f = 0; f < count; f ++)
424
        msg.readInt16("skill id");
425
}
426
427
void PlayerRecv::processPlayerRankPoints(Net::MessageIn &msg)
428
{
429
    UNIMPLEMENTEDPACKET;
430
    msg.readInt16("type");
431
    msg.readInt32("points");
432
    msg.readInt32("fame");
433
}
434
435
void PlayerRecv::processOnlineList(Net::MessageIn &msg)
436
{
437
    if (whoIsOnline == nullptr)
438
        return;
439
440
    BLOCK_START("PlayerRecv::processOnlineList")
441
    const int size = msg.readInt16("len") - 4;
442
    STD_VECTOR<OnlinePlayer*> arr;
443
444
    if (size == 0)
445
    {
446
        if (whoIsOnline != nullptr)
447
            whoIsOnline->loadList(arr);
448
        BLOCK_END("PlayerRecv::processOnlineList")
449
        return;
450
    }
451
452
    char *const start = reinterpret_cast<char*>(msg.readBytes(size, "nicks"));
453
    if (start == nullptr)
454
    {
455
        BLOCK_END("PlayerRecv::processOnlineList")
456
        return;
457
    }
458
459
    const char *buf = start;
460
461
    int addVal = 3;
462
463
    while (buf - start + 1 < size
464
           && (*(buf + CAST_SIZE(addVal)) != 0))
465
    {
466
        const unsigned char status = *buf;
467
        buf ++;
468
        const unsigned char level = *buf;
469
        buf ++;
470
        const unsigned char ver = *buf;
471
        buf ++;
472
473
        GenderT gender = Gender::UNSPECIFIED;
474
        if (config.getBoolValue("showgender"))
475
        {
476
            if ((status & BeingFlag::GENDER_MALE) != 0)
477
                gender = Gender::MALE;
478
            else if ((status & BeingFlag::GENDER_HIDDEN) != 0)
479
                gender = Gender::HIDDEN;
480
            else
481
                gender = Gender::FEMALE;
482
        }
483
        arr.push_back(new OnlinePlayer(static_cast<const char*>(buf),
484
            status, level, gender, ver));
485
        buf += strlen(buf) + 1;
486
    }
487
488
    if (whoIsOnline != nullptr)
489
        whoIsOnline->loadList(arr);
490
    delete [] start;
491
    BLOCK_END("PlayerRecv::processOnlineList")
492
}
493
494
void PlayerRecv::processDressRoomOpen(Net::MessageIn &msg)
495
{
496
    UNIMPLEMENTEDPACKET;
497
    msg.readInt16("view");
498
}
499
500
void PlayerRecv::processKilledBy(Net::MessageIn &msg)
501
{
502
    if (actorManager == nullptr)
503
        return;
504
    const BeingId id = msg.readBeingId("killer id");
505
    const Being *const dstBeing = actorManager->findBeing(id);
506
    if (id == BeingId_zero)
507
    {
508
        debugMsg(
509
            // TRANSLATORS: player killed message
510
            _("You were killed by unknown source."));
511
    }
512
    else
513
    {
514
        std::string name;
515
        if (dstBeing != nullptr)
516
            name = dstBeing->getName();
517
        else
518
            name = strprintf("?%u", CAST_U32(id));
519
        debugMsg(strprintf(
520
            // TRANSLATORS: player killed message
521
            _("You were killed by %s."),
522
            name.c_str()));
523
    }
524
}
525
526
void PlayerRecv::processPlayerAttrs(Net::MessageIn &msg)
527
{
528
    const int len = msg.readInt16("len");
529
    if (len < 8)
530
        return;
531
532
    const int groupId = msg.readInt32("group id");
533
534
    if (localPlayer == nullptr)
535
        return;
536
537
    localPlayer->setGroupId(groupId);
538
}
539
540
void PlayerRecv::processPlayerStatUpdate7(Net::MessageIn &msg)
541
{
542
    BLOCK_START("PlayerRecv::processPlayerStatUpdate7")
543
    const int type = msg.readInt16("type");
544
    const int64_t value = msg.readInt64("value");
545
    playerHandler->setStat(msg, type, value, NoStat, Notify_true);
546
    BLOCK_END("PlayerRecv::processPlayerStatUpdate7")
547
}
548
549
void PlayerRecv::processSelectStyleAck(Net::MessageIn &msg)
550
{
551
    UNIMPLEMENTEDPACKET;
552
    msg.readUInt8("flag");
553
}
554
555
void PlayerRecv::processSetTitleAck(Net::MessageIn &msg)
556
{
557
    UNIMPLEMENTEDPACKET;
558
    msg.readUInt8("fail flag");
559
    msg.readInt32("title");
560
}
561
562
2
}  // namespace EAthena