summaryrefslogblamecommitdiff
path: root/src/net/ea/beingrecv.cpp
blob: 0a8168c6c1bb20455fced9bf798e61c4b4f78eb9 (plain) (tree)
1
2
3
4
5



                                                            
                                                    
















                                                                         
                             

                         
                          
                 
                          





                                  

                                        







                                            
                          

                               

                             




                  
                   


                                    
                         
 
                                                       
 
                                                

                                      
                                                  





                                                   
                                                      


                                                        
                                                  









                                                                    
                   






                                                              





                                                                 





                                                     





                                                                         
                                                 



























                                                                 


                                        
                                              

 
                                                       
 
                                                

                      
                                                  


















































































                                                                               
                                                     

                  
                                              

 
                                                        
 
                                                 

                                      
                                                   






                                                    

                                         
                                                   


               


                                                                        
     

                                                   


                                                 
                                               

 
                                                        
 
                                                 

                                      
                                                   



                                                        
                                                        

                                                             

                                              







                                            































                                                                    
                                                       


                   
                                               

 
                                                      
 
                                               

                                      
                                                 














                                                                  
                                                     




                       
                                             

 
                                                              
 
                                               








                                       
                                             

 
                                                         






                                                    
                          




                                                     
                                                      
 
                                               


                                              
                                                 





                                                   
                                             

 
                                                      
 
                                               

                                                      
                                                 










                                                                 
                                         
                               


                                          
                                                 


                                                 
                                  

                                   



                                               


                                

















                                                                   


















































                                                                       

                        
 
                            
                                             

 

                                               


















                                                   






                                                   






                                                                          
/*
 *  The ManaPlus Client
 *  Copyright (C) 2004-2009  The Mana World Development Team
 *  Copyright (C) 2009-2010  The Mana Developers
 *  Copyright (C) 2011-2016  The ManaPlus Developers
 *
 *  This file is part of The ManaPlus Client.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "net/ea/beingrecv.h"

#include "actormanager.h"
#include "configuration.h"
#include "game.h"
#include "notifymanager.h"
#include "party.h"

#include "being/localplayer.h"
#include "being/playerrelation.h"
#include "being/playerrelations.h"

#include "enums/resources/notifytypes.h"

#include "enums/resources/map/mapitemtype.h"

#include "gui/viewport.h"

#include "gui/windows/socialwindow.h"

#include "resources/map/map.h"

#include "net/messagein.h"
#include "net/serverfeatures.h"

#include "utils/checkutils.h"

#include "debug.h"

namespace Ea
{

namespace BeingRecv
{
    bool mSync = false;
    BeingId mSpawnId = BeingId_zero;
}  // namespace BeingRecv

void BeingRecv::processBeingRemove(Net::MessageIn &msg)
{
    BLOCK_START("BeingRecv::processBeingRemove")
    if (!actorManager || !localPlayer)
    {
        BLOCK_END("BeingRecv::processBeingRemove")
        return;
    }

    // A being should be removed or has died

    const BeingId id = msg.readBeingId("being id");
    const uint8_t type = msg.readUInt8("remove flag");
    Being *const dstBeing = actorManager->findBeing(id);
    if (!dstBeing)
    {
        BLOCK_END("BeingRecv::processBeingRemove")
        return;
    }

    localPlayer->followMoveTo(dstBeing, localPlayer->getNextDestX(),
        localPlayer->getNextDestY());

    // If this is player's current target, clear it.
    if (dstBeing == localPlayer->getTarget())
        localPlayer->stopAttack(true);

    if (type == 1U)
    {
        if (dstBeing->getCurrentAction() != BeingAction::DEAD)
        {
            dstBeing->setAction(BeingAction::DEAD, 0);
            dstBeing->recalcSpritesOrder();
        }
    }
    else if (type == 0U && dstBeing->getType() == ActorType::Npc)
    {
        const BeingInfo *const info = dstBeing->getInfo();
        if (!info || info->getAllowDelete())
            actorManager->destroy(dstBeing);
    }
    else
    {
        if (dstBeing->getType() == ActorType::Player)
        {
            if (socialWindow)
                socialWindow->updateActiveList();
            const std::string name = dstBeing->getName();
            if (!name.empty() && config.getBoolValue("logPlayerActions"))
            {
                switch (type)
                {
                    case 0:
                        dstBeing->serverRemove();
                        break;
                    case 1:
                        NotifyManager::notify(
                            NotifyTypes::BEING_REMOVE_DIED,
                            name);
                        break;
                    case 2:
                        NotifyManager::notify(
                            NotifyTypes::BEING_REMOVE_LOGGED_OUT,
                            name);
                        break;
                    case 3:
                        NotifyManager::notify(
                            NotifyTypes::BEING_REMOVE_WARPED,
                            name);
                        break;
                    case 4:
                        NotifyManager::notify(
                            NotifyTypes::BEING_REMOVE_TRICK_DEAD,
                            name);
                        break;
                    default:
                        NotifyManager::notify(
                            NotifyTypes::BEING_REMOVE_UNKNOWN,
                            name);
                        break;
                }
            }
        }
        actorManager->destroy(dstBeing);
    }
    BLOCK_END("BeingRecv::processBeingRemove")
}

void BeingRecv::processBeingAction(Net::MessageIn &msg)
{
    BLOCK_START("BeingRecv::processBeingAction")
    if (!actorManager)
    {
        BLOCK_END("BeingRecv::processBeingAction")
        return;
    }

    Being *const srcBeing = actorManager->findBeing(
        msg.readBeingId("src being id"));
    Being *const dstBeing = actorManager->findBeing(
        msg.readBeingId("dst being id"));

    msg.readInt32("tick");
    const int srcSpeed = msg.readInt32("src speed");
    msg.readInt32("dst speed");
    const int param1 = msg.readInt16("param1");
    msg.readInt16("param 2");
    const AttackTypeT type = static_cast<AttackTypeT>(
        msg.readUInt8("type"));
    msg.readInt16("param 3");

    switch (type)
    {
        case AttackType::HIT:  // Damage
        case AttackType::CRITICAL:  // Critical Damage
        case AttackType::MULTI:  // Critical Damage
        case AttackType::REFLECT:  // Reflected Damage
        case AttackType::FLEE:  // Lucky Dodge
            if (srcBeing)
            {
                if (srcSpeed && srcBeing->getType() == ActorType::Player)
                    srcBeing->setAttackDelay(srcSpeed);
                // attackid=1, type
                srcBeing->handleAttack(dstBeing, param1, 1);
                if (srcBeing->getType() == ActorType::Player)
                    srcBeing->setAttackTime();
            }
            if (dstBeing)
            {
                // level not present, using 1
                dstBeing->takeDamage(srcBeing, param1,
                    static_cast<AttackTypeT>(type), 1);
            }
            break;

        case AttackType::PICKUP:
            break;
            // tmw server can send here garbage?
//            if (srcBeing)
//                srcBeing->setAction(BeingAction::DEAD, 0);

        case AttackType::SIT:
            if (srcBeing)
            {
                srcBeing->setAction(BeingAction::SIT, 0);
                if (srcBeing->getType() == ActorType::Player)
                {
                    srcBeing->setMoveTime();
                    if (localPlayer)
                        localPlayer->imitateAction(srcBeing, BeingAction::SIT);
                }
            }
            break;

        case AttackType::STAND:
            if (srcBeing)
            {
                srcBeing->setAction(BeingAction::STAND, 0);
                if (srcBeing->getType() == ActorType::Player)
                {
                    srcBeing->setMoveTime();
                    if (localPlayer)
                    {
                        localPlayer->imitateAction(srcBeing,
                            BeingAction::STAND);
                    }
                }
            }
            break;
        default:
        case AttackType::SPLASH:
        case AttackType::SKILL:
        case AttackType::REPEATE:
        case AttackType::MULTI_REFLECT:
        case AttackType::TOUCH_SKILL:
        case AttackType::MISS:
        case AttackType::SKILLMISS:
            UNIMPLIMENTEDPACKETFIELD(CAST_S32(type));
            break;
    }
    BLOCK_END("BeingRecv::processBeingAction")
}

void BeingRecv::processBeingEmotion(Net::MessageIn &msg)
{
    BLOCK_START("BeingRecv::processBeingEmotion")
    if (!localPlayer || !actorManager)
    {
        BLOCK_END("BeingRecv::processBeingEmotion")
        return;
    }

    Being *const dstBeing = actorManager->findBeing(
        msg.readBeingId("being id"));
    if (!dstBeing)
    {
        DEBUGLOGSTR("invisible player?");
        msg.readUInt8("emote");
        BLOCK_END("BeingRecv::processBeingEmotion")
        return;
    }

    const uint8_t emote = msg.readUInt8("emote");
    if (emote &&
        player_relations.hasPermission(dstBeing, PlayerRelation::EMOTE))
    {
        dstBeing->setEmote(emote, 0);
        localPlayer->imitateEmote(dstBeing, emote);
    }
    if (dstBeing->getType() == ActorType::Player)
        dstBeing->setOtherTime();
    BLOCK_END("BeingRecv::processBeingEmotion")
}

void BeingRecv::processNameResponse(Net::MessageIn &msg)
{
    BLOCK_START("BeingRecv::processNameResponse")
    if (!localPlayer || !actorManager)
    {
        BLOCK_END("BeingRecv::processNameResponse")
        return;
    }

    const BeingId beingId = msg.readBeingId("being id");
    const std::string name = msg.readString(24, "name");
    Being *const dstBeing = actorManager->findBeing(beingId);

    actorManager->updateNameId(name, beingId);

    if (dstBeing)
    {
        if (beingId == localPlayer->getId())
        {
            localPlayer->pingResponse();
        }
        else
        {
            if (dstBeing->getType() != ActorType::Portal)
            {
                dstBeing->setName(name);
            }
            else if (viewport)
            {
                Map *const map = viewport->getMap();
                if (map)
                {
                    map->addPortalTile(name, MapItemType::PORTAL,
                        dstBeing->getTileX(), dstBeing->getTileY());
                }
            }
            dstBeing->updateGuild();
            dstBeing->addToCache();

            if (dstBeing->getType() == ActorType::Player)
                dstBeing->updateColors();

            if (localPlayer)
            {
                const Party *const party = localPlayer->getParty();
                if (party && party->isMember(dstBeing->getId()))
                {
                    PartyMember *const member = party->getMember(
                        dstBeing->getId());

                    if (member)
                        member->setName(dstBeing->getName());
                }
                localPlayer->checkNewName(dstBeing);
            }
            BLOCK_END("BeingRecv::processNameResponse")
            return;
        }
    }
    BLOCK_END("BeingRecv::processNameResponse")
}

void BeingRecv::processPlayerStop(Net::MessageIn &msg)
{
    BLOCK_START("BeingRecv::processPlayerStop")
    if (!actorManager || !localPlayer)
    {
        BLOCK_END("BeingRecv::processPlayerStop")
        return;
    }

    const BeingId id = msg.readBeingId("account id");

    if (mSync || id != localPlayer->getId())
    {
        Being *const dstBeing = actorManager->findBeing(id);
        if (dstBeing)
        {
            const uint16_t x = msg.readInt16("x");
            const uint16_t y = msg.readInt16("y");
            dstBeing->setTileCoords(x, y);
            if (dstBeing->getCurrentAction() == BeingAction::MOVE)
                dstBeing->setAction(BeingAction::STAND, 0);
            BLOCK_END("BeingRecv::processPlayerStop")
            return;
        }
    }
    msg.readInt16("x");
    msg.readInt16("y");
    BLOCK_END("BeingRecv::processPlayerStop")
}

void BeingRecv::processPlayerMoveToAttack(Net::MessageIn &msg)
{
    BLOCK_START("BeingRecv::processPlayerStop")
    msg.readInt32("target id");
    msg.readInt16("target x");
    msg.readInt16("target y");
    msg.readInt16("x");
    msg.readInt16("y");
    msg.readInt16("attack range");

    if (localPlayer)
        localPlayer->fixAttackTarget();
    BLOCK_END("BeingRecv::processPlayerStop")
}

void BeingRecv::processSkillNoDamage(Net::MessageIn &msg)
{
    const int id = msg.readInt16("skill id");
    const int heal = msg.readInt16("heal");
    Being *const dstBeing = actorManager->findBeing(
        msg.readBeingId("dst being id"));
    Being *const srcBeing = actorManager->findBeing(
        msg.readBeingId("src being id"));
    msg.readUInt8("fail");

    if (srcBeing)
        srcBeing->handleSkill(dstBeing, heal, id, 1);
}

void BeingRecv::processPvpMapMode(Net::MessageIn &msg)
{
    BLOCK_START("BeingRecv::processPvpMapMode")
    const Game *const game = Game::instance();
    if (!game)
    {
        BLOCK_END("BeingRecv::processPvpMapMode")
        return;
    }

    Map *const map = game->getCurrentMap();
    if (map)
        map->setPvpMode(msg.readInt16("pvp mode"));
    BLOCK_END("BeingRecv::processPvpMapMode")
}

void BeingRecv::processBeingMove3(Net::MessageIn &msg)
{
    BLOCK_START("BeingRecv::processBeingMove3")
    if (!actorManager || !serverFeatures->haveMove3())
    {
        BLOCK_END("BeingRecv::processBeingMove3")
        return;
    }

    static const int16_t dirx[8] = {0, -1, -1, -1,  0,  1, 1, 1};
    static const int16_t diry[8] = {1,  1,  0, -1, -1, -1, 0, 1};

    const int len = msg.readInt16("len") - 14;
    Being *const dstBeing = actorManager->findBeing(
        msg.readBeingId("being id"));
    if (!dstBeing)
    {
        DEBUGLOGSTR("invisible player?");
        msg.readInt16("speed");
        msg.readInt16("x");
        msg.readInt16("y");
        msg.readBytes(len, "moving path");
        BLOCK_END("BeingRecv::processBeingMove3")
        return;
    }
    const int16_t speed = msg.readInt16("speed");
    dstBeing->setWalkSpeed(speed);
    int16_t x = msg.readInt16("x");
    int16_t y = msg.readInt16("y");

    dstBeing->setAction(BeingAction::STAND, 0);
    dstBeing->setTileCoords(x, y);

    if (dstBeing == localPlayer)
        return;

    const unsigned char *moves = msg.readBytes(len, "moving path");
    Path path;
    if (moves)
    {
        for (int f = 0; f < len; f ++)
        {
            const unsigned char dir = moves[f];
            if (dir <= 7)
            {
                x += dirx[dir];
                y += diry[dir];
                path.push_back(Position(x, y));
            }
            else
            {
                logger->log("bad move packet: %d", dir);
            }
        }
        const int x1 = dstBeing->getCachedX();
        const int y1 = dstBeing->getCachedY();
        if (x1 != x || y1 != y)
        {
//            reportAlways("Found desync being move. "
//                "Calculated target (%d,%d). "
//                "Actual target (%d,%d).",
//                static_cast<int>(x),
//                static_cast<int>(y),
//                static_cast<int>(x1),
//                static_cast<int>(y1));
            if (len > 0)
            {
                const unsigned char dir = moves[0];
                uint16_t x0 = x;
                uint16_t y0 = y;

                if (dir <= 7)
                {
                    x0 -= dirx[0];
                    y0 -= diry[0];
                    if (x1 == x0 && y1 == y0)
                    {
                        path.erase(path.begin());
                        logger->log("Fixed being moving desync by "
                            "removing one tile");
                    }
                    else
                    {
                        x0 = x;
                        y0 = y;
                        if (abs(x0 - x1) < 2 && abs(y0 - y1) < 2)
                        {
                            path.push_back(Position(x1, y1));
                            logger->log("Fixed being moving desync by "
                                "adding one tile");
                        }
                        else
                        {
                            reportAlways("Error: being moving desync. "
                                "Calculated target (%d,%d). "
                                "Actual target (%d,%d).",
                                static_cast<int>(x),
                                static_cast<int>(y),
                                static_cast<int>(x1),
                                static_cast<int>(y1));
                        }
                    }
                }
            }
        }
        delete [] moves;
    }

    dstBeing->setPath(path);
    BLOCK_END("BeingRecv::processBeingMove3")
}

Being *BeingRecv::createBeing(const BeingId id,
                              const int job)
{
    if (!actorManager)
        return nullptr;

    ActorTypeT type = ActorType::Unknown;
    if (job <= 25 || (job >= 4001 && job <= 4049))
        type = ActorType::Player;
    else if (job >= 46 && job <= 1000)
        type = ActorType::Npc;
    else if (job > 1000 && job <= 2000)
        type = ActorType::Monster;
    else if (job == 45)
        type = ActorType::Portal;

    Being *const being = actorManager->createBeing(
        id, type, fromInt(job, BeingTypeId));
    return being;
}

void BeingRecv::setSprite(Being *const being,
                          const unsigned int slot,
                          const int id,
                          const std::string &color,
                          const ItemColor colorId,
                          const bool isWeapon,
                          const bool isTempSprite)
{
    if (!being)
        return;
    being->updateSprite(slot, id, color, colorId, isWeapon, isTempSprite);
}

}  // namespace Ea