GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/net/ea/beingrecv.cpp Lines: 1 213 0.5 %
Date: 2019-06-27 Branches: 0 235 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-2019  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/ea/beingrecv.h"
24
25
#include "actormanager.h"
26
#include "configuration.h"
27
#include "game.h"
28
#include "notifymanager.h"
29
#include "party.h"
30
31
#include "being/localplayer.h"
32
#include "being/playerrelation.h"
33
#include "being/playerrelations.h"
34
35
#include "enums/resources/notifytypes.h"
36
37
#include "enums/resources/map/mapitemtype.h"
38
39
#include "gui/viewport.h"
40
41
#include "gui/windows/socialwindow.h"
42
43
#include "utils/foreach.h"
44
45
#include "resources/map/map.h"
46
47
#include "net/messagein.h"
48
#include "net/serverfeatures.h"
49
50
#include "debug.h"
51
52
namespace Ea
53
{
54
55
namespace BeingRecv
56
{
57
    BeingId mSpawnId = BeingId_zero;
58
}  // namespace BeingRecv
59
60
void BeingRecv::processBeingRemove(Net::MessageIn &msg)
61
{
62
    BLOCK_START("BeingRecv::processBeingRemove")
63
    if ((actorManager == nullptr) || (localPlayer == nullptr))
64
    {
65
        BLOCK_END("BeingRecv::processBeingRemove")
66
        return;
67
    }
68
69
    // A being should be removed or has died
70
71
    const BeingId id = msg.readBeingId("being id");
72
    const uint8_t type = msg.readUInt8("remove flag");
73
    Being *const dstBeing = actorManager->findBeing(id);
74
    if (dstBeing == nullptr)
75
    {
76
        BLOCK_END("BeingRecv::processBeingRemove")
77
        return;
78
    }
79
80
    localPlayer->followMoveTo(dstBeing, localPlayer->getNextDestX(),
81
        localPlayer->getNextDestY());
82
83
    // If this is player's current target, clear it.
84
    if (dstBeing == localPlayer->getTarget())
85
        localPlayer->stopAttack(true);
86
87
    if (type == 1U)
88
    {
89
        if (dstBeing->getCurrentAction() != BeingAction::DEAD)
90
        {
91
            dstBeing->setAction(BeingAction::DEAD, 0);
92
            dstBeing->recalcSpritesOrder();
93
        }
94
    }
95
    else if (type == 0U && dstBeing->getType() == ActorType::Npc)
96
    {
97
        const BeingInfo *const info = dstBeing->getInfo();
98
        if ((info == nullptr) || (info->getAllowDelete() != 0))
99
            actorManager->destroy(dstBeing);
100
    }
101
    else
102
    {
103
        if (dstBeing->getType() == ActorType::Player)
104
        {
105
            if (socialWindow != nullptr)
106
                socialWindow->updateActiveList();
107
            const std::string name = dstBeing->getName();
108
            if (!name.empty() && config.getBoolValue("logPlayerActions"))
109
            {
110
                switch (type)
111
                {
112
                    case 0:
113
                        dstBeing->serverRemove();
114
                        break;
115
                    case 1:
116
                        NotifyManager::notify(
117
                            NotifyTypes::BEING_REMOVE_DIED,
118
                            name);
119
                        break;
120
                    case 2:
121
                        NotifyManager::notify(
122
                            NotifyTypes::BEING_REMOVE_LOGGED_OUT,
123
                            name);
124
                        break;
125
                    case 3:
126
                        NotifyManager::notify(
127
                            NotifyTypes::BEING_REMOVE_WARPED,
128
                            name);
129
                        break;
130
                    case 4:
131
                        NotifyManager::notify(
132
                            NotifyTypes::BEING_REMOVE_TRICK_DEAD,
133
                            name);
134
                        break;
135
                    default:
136
                        NotifyManager::notify(
137
                            NotifyTypes::BEING_REMOVE_UNKNOWN,
138
                            name);
139
                        break;
140
                }
141
            }
142
        }
143
        actorManager->destroy(dstBeing);
144
    }
145
    BLOCK_END("BeingRecv::processBeingRemove")
146
}
147
148
void BeingRecv::processBeingAction(Net::MessageIn &msg)
149
{
150
    BLOCK_START("BeingRecv::processBeingAction")
151
    if (actorManager == nullptr)
152
    {
153
        BLOCK_END("BeingRecv::processBeingAction")
154
        return;
155
    }
156
157
    Being *const srcBeing = actorManager->findBeing(
158
        msg.readBeingId("src being id"));
159
    Being *const dstBeing = actorManager->findBeing(
160
        msg.readBeingId("dst being id"));
161
162
    msg.readInt32("tick");
163
    const int srcSpeed = msg.readInt32("src speed");
164
    msg.readInt32("dst speed");
165
    const int param1 = msg.readInt16("param1");
166
    msg.readInt16("param 2");
167
    const AttackTypeT type = static_cast<AttackTypeT>(
168
        msg.readUInt8("type"));
169
    msg.readInt16("param 3");
170
171
    switch (type)
172
    {
173
        case AttackType::HIT:  // Damage
174
        case AttackType::CRITICAL:  // Critical Damage
175
        case AttackType::MULTI:  // Critical Damage
176
        case AttackType::REFLECT:  // Reflected Damage
177
        case AttackType::FLEE:  // Lucky Dodge
178
            if (srcBeing != nullptr)
179
            {
180
                if (srcSpeed != 0 && srcBeing->getType() == ActorType::Player)
181
                    srcBeing->setAttackDelay(srcSpeed);
182
                // attackid=1, type
183
                srcBeing->handleAttack(dstBeing, param1, 1);
184
                if (srcBeing->getType() == ActorType::Player)
185
                    srcBeing->setAttackTime();
186
            }
187
            if (dstBeing != nullptr)
188
            {
189
                // level not present, using 1
190
                dstBeing->takeDamage(srcBeing, param1,
191
                    static_cast<AttackTypeT>(type), 1, 1);
192
            }
193
            break;
194
195
        case AttackType::PICKUP:
196
            break;
197
            // tmw server can send here garbage?
198
//            if (srcBeing)
199
//                srcBeing->setAction(BeingAction::DEAD, 0);
200
201
        case AttackType::SIT:
202
            if (srcBeing != nullptr)
203
            {
204
                srcBeing->setAction(BeingAction::SIT, 0);
205
                if (srcBeing->getType() == ActorType::Player)
206
                {
207
                    srcBeing->setMoveTime();
208
                    if (localPlayer != nullptr)
209
                        localPlayer->imitateAction(srcBeing, BeingAction::SIT);
210
                }
211
            }
212
            break;
213
214
        case AttackType::STAND:
215
            if (srcBeing != nullptr)
216
            {
217
                srcBeing->setAction(BeingAction::STAND, 0);
218
                if (srcBeing->getType() == ActorType::Player)
219
                {
220
                    srcBeing->setMoveTime();
221
                    if (localPlayer != nullptr)
222
                    {
223
                        localPlayer->imitateAction(srcBeing,
224
                            BeingAction::STAND);
225
                    }
226
                }
227
            }
228
            break;
229
        default:
230
        case AttackType::SPLASH:
231
        case AttackType::SKILL:
232
        case AttackType::REPEATE:
233
        case AttackType::MULTI_REFLECT:
234
        case AttackType::TOUCH_SKILL:
235
        case AttackType::MISS:
236
        case AttackType::SKILLMISS:
237
            UNIMPLEMENTEDPACKETFIELD(CAST_S32(type));
238
            break;
239
    }
240
    BLOCK_END("BeingRecv::processBeingAction")
241
}
242
243
void BeingRecv::processBeingEmotion(Net::MessageIn &msg)
244
{
245
    BLOCK_START("BeingRecv::processBeingEmotion")
246
    if ((localPlayer == nullptr) || (actorManager == nullptr))
247
    {
248
        BLOCK_END("BeingRecv::processBeingEmotion")
249
        return;
250
    }
251
252
    Being *const dstBeing = actorManager->findBeing(
253
        msg.readBeingId("being id"));
254
    if (dstBeing == nullptr)
255
    {
256
        DEBUGLOGSTR("invisible player?");
257
        msg.readUInt8("emote");
258
        BLOCK_END("BeingRecv::processBeingEmotion")
259
        return;
260
    }
261
262
    const uint8_t emote = msg.readUInt8("emote");
263
    if ((emote != 0U) &&
264
        playerRelations.hasPermission(dstBeing, PlayerRelation::EMOTE))
265
    {
266
        dstBeing->setEmote(emote, 0);
267
        localPlayer->imitateEmote(dstBeing, emote);
268
    }
269
    if (dstBeing->getType() == ActorType::Player)
270
        dstBeing->setOtherTime();
271
    BLOCK_END("BeingRecv::processBeingEmotion")
272
}
273
274
void BeingRecv::processNameResponse(Net::MessageIn &msg)
275
{
276
    BLOCK_START("BeingRecv::processNameResponse")
277
    if ((localPlayer == nullptr) || (actorManager == nullptr))
278
    {
279
        BLOCK_END("BeingRecv::processNameResponse")
280
        return;
281
    }
282
283
    const BeingId beingId = msg.readBeingId("being id");
284
    const std::string name = msg.readString(24, "name");
285
    Being *const dstBeing = actorManager->findBeing(beingId);
286
287
    actorManager->updateNameId(name, beingId);
288
289
    if (dstBeing != nullptr)
290
    {
291
        if (beingId == localPlayer->getId())
292
        {
293
            localPlayer->pingResponse();
294
        }
295
        else
296
        {
297
            if (dstBeing->getType() != ActorType::Portal)
298
            {
299
                dstBeing->setName(name);
300
            }
301
            else if (viewport != nullptr)
302
            {
303
                Map *const map = viewport->getMap();
304
                if (map != nullptr)
305
                {
306
                    map->addPortalTile(name, MapItemType::PORTAL,
307
                        dstBeing->getTileX(), dstBeing->getTileY());
308
                }
309
            }
310
            dstBeing->updateGuild();
311
            dstBeing->addToCache();
312
313
            if (dstBeing->getType() == ActorType::Player)
314
                dstBeing->updateColors();
315
316
            if (localPlayer != nullptr)
317
            {
318
                const Party *const party = localPlayer->getParty();
319
                if (party != nullptr && party->isMember(dstBeing->getId()))
320
                {
321
                    PartyMember *const member = party->getMember(
322
                        dstBeing->getId());
323
324
                    if (member != nullptr)
325
                        member->setName(dstBeing->getName());
326
                }
327
                localPlayer->checkNewName(dstBeing);
328
            }
329
            BLOCK_END("BeingRecv::processNameResponse")
330
            return;
331
        }
332
    }
333
    BLOCK_END("BeingRecv::processNameResponse")
334
}
335
336
void BeingRecv::processPlayerStop(Net::MessageIn &msg)
337
{
338
    BLOCK_START("BeingRecv::processPlayerStop")
339
    if ((actorManager == nullptr) || (localPlayer == nullptr))
340
    {
341
        BLOCK_END("BeingRecv::processPlayerStop")
342
        return;
343
    }
344
345
    const BeingId id = msg.readBeingId("account id");
346
347
//    if (mSync || id != localPlayer->getId())
348
    {
349
        Being *const dstBeing = actorManager->findBeing(id);
350
        if (dstBeing != nullptr)
351
        {
352
            const uint16_t x = msg.readInt16("x");
353
            const uint16_t y = msg.readInt16("y");
354
            dstBeing->setTileCoords(x, y);
355
            if (dstBeing->getCurrentAction() == BeingAction::MOVE)
356
                dstBeing->setAction(BeingAction::STAND, 0);
357
            BLOCK_END("BeingRecv::processPlayerStop")
358
            return;
359
        }
360
    }
361
    msg.readInt16("x");
362
    msg.readInt16("y");
363
    BLOCK_END("BeingRecv::processPlayerStop")
364
}
365
366
void BeingRecv::processPlayerMoveToAttack(Net::MessageIn &msg)
367
{
368
    BLOCK_START("BeingRecv::processPlayerStop")
369
    msg.readInt32("target id");
370
    msg.readInt16("target x");
371
    msg.readInt16("target y");
372
    msg.readInt16("x");
373
    msg.readInt16("y");
374
    msg.readInt16("attack range");
375
376
    if (localPlayer != nullptr)
377
        localPlayer->fixAttackTarget();
378
    BLOCK_END("BeingRecv::processPlayerStop")
379
}
380
381
void BeingRecv::processSkillNoDamage(Net::MessageIn &msg)
382
{
383
    if (actorManager == nullptr)
384
        return;
385
    const int id = msg.readInt16("skill id");
386
    int heal;
387
    if (msg.getVersion() >= 20131223)
388
        heal = msg.readInt32("heal");
389
    else
390
        heal = msg.readInt16("heal");
391
    Being *const dstBeing = actorManager->findBeing(
392
        msg.readBeingId("dst being id"));
393
    Being *const srcBeing = actorManager->findBeing(
394
        msg.readBeingId("src being id"));
395
    msg.readUInt8("fail");
396
397
    if (srcBeing != nullptr)
398
        srcBeing->handleSkill(dstBeing, heal, id, 1);
399
}
400
401
void BeingRecv::processPvpMapMode(Net::MessageIn &msg)
402
{
403
    BLOCK_START("BeingRecv::processPvpMapMode")
404
    const Game *const game = Game::instance();
405
    if (game == nullptr)
406
    {
407
        BLOCK_END("BeingRecv::processPvpMapMode")
408
        return;
409
    }
410
411
    Map *const map = game->getCurrentMap();
412
    if (map != nullptr)
413
        map->setPvpMode(msg.readInt16("pvp mode"));
414
    BLOCK_END("BeingRecv::processPvpMapMode")
415
}
416
417
void BeingRecv::processBeingMove3(Net::MessageIn &msg)
418
{
419
    BLOCK_START("BeingRecv::processBeingMove3")
420
    if ((actorManager == nullptr) || !serverFeatures->haveMove3())
421
    {
422
        BLOCK_END("BeingRecv::processBeingMove3")
423
        return;
424
    }
425
426
    static const int16_t dirx[8] = {0, -1, -1, -1,  0,  1, 1, 1};
427
    static const int16_t diry[8] = {1,  1,  0, -1, -1, -1, 0, 1};
428
429
    const int len = msg.readInt16("len") - 14;
430
    Being *const dstBeing = actorManager->findBeing(
431
        msg.readBeingId("being id"));
432
    if (dstBeing == nullptr ||
433
        dstBeing == localPlayer)
434
    {
435
        DEBUGLOGSTR("invisible player?");
436
        msg.readInt16("speed");
437
        msg.readInt16("x");
438
        msg.readInt16("y");
439
        unsigned char *bytes = msg.readBytes(len, "moving path");
440
        delete [] bytes;
441
        BLOCK_END("BeingRecv::processBeingMove3")
442
        return;
443
    }
444
    const int16_t speed = msg.readInt16("speed");
445
    dstBeing->setWalkSpeed(speed);
446
    const int16_t x = msg.readInt16("x");
447
    const int16_t y = msg.readInt16("y");
448
    const unsigned char *moves = msg.readBytes(len, "moving path");
449
450
    Path path;
451
    if (moves != nullptr)
452
    {
453
        int x2 = dstBeing->getCachedX();
454
        int y2 = dstBeing->getCachedY();
455
        Path path2;
456
        path2.push_back(Position(x2, y2));
457
        for (int f = len - 1; f >= 0; f --)
458
        {
459
            const unsigned char dir = moves[f];
460
            if (dir <= 7)
461
            {
462
                x2 -= dirx[dir];
463
                y2 -= diry[dir];
464
                // fix possible wrong move outside of map
465
                if (x2 < 0)
466
                    x2 = 0;
467
                if (y2 < 0)
468
                    y2 = 0;
469
                path2.push_back(Position(x2, y2));
470
                if (x2 == x && y2 == y)
471
                    break;
472
            }
473
            else
474
            {
475
                logger->log("bad move packet: %d", dir);
476
            }
477
        }
478
479
        if (!path2.empty())
480
        {
481
            const Position &pos = path2.back();
482
            if (x != pos.x ||
483
                y != pos.y)
484
            {
485
                dstBeing->setTileCoords(pos.x, pos.y);
486
            }
487
        }
488
489
        path2.pop_back();
490
        FOR_EACHR (PathRIterator, it, path2)
491
        {
492
            path.push_back(*it);
493
        }
494
        delete [] moves;
495
    }
496
497
    if (path.empty())
498
        return;
499
500
    dstBeing->setAction(BeingAction::STAND, 0);
501
    dstBeing->setTileCoords(x, y);
502
    dstBeing->setPath(path);
503
    BLOCK_END("BeingRecv::processBeingMove3")
504
}
505
506
Being *BeingRecv::createBeing(const BeingId id,
507
                              const int job)
508
{
509
    if (actorManager == nullptr)
510
        return nullptr;
511
512
    ActorTypeT type = ActorType::Unknown;
513
    if (job <= 25 || (job >= 4001 && job <= 4049))
514
        type = ActorType::Player;
515
    else if (job >= 46 && job <= 1000)
516
        type = ActorType::Npc;
517
    else if (job > 1000 && job <= 2000)
518
        type = ActorType::Monster;
519
    else if (job == 45)
520
        type = ActorType::Portal;
521
522
    return actorManager->createBeing(
523
        id, type, fromInt(job, BeingTypeId));
524
}
525
526
2
}  // namespace Ea