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