From b506fe0ff8a2039167aa7c349087af4dd03e1921 Mon Sep 17 00:00:00 2001 From: Bjørn Lindeijer Date: Thu, 28 Aug 2008 18:01:15 +0000 Subject: Applied patch put together by Jaxad0127 with changes from the Aethyra project. Improves compatibility with the newer version of eAthena and works around an initialization bug. --- src/being.h | 4 ++ src/game.cpp | 24 ++++++- src/net/beinghandler.cpp | 145 +++++++++++++++++++++++++++++++++++------- src/net/beinghandler.h | 6 +- src/net/charserverhandler.cpp | 7 +- src/net/playerhandler.cpp | 7 +- src/net/protocol.h | 5 ++ 7 files changed, 169 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/being.h b/src/being.h index 5ea8c0be..046f7b9a 100644 --- a/src/being.h +++ b/src/being.h @@ -93,8 +93,12 @@ class Being : public Sprite SHOE_SPRITE, BOTTOMCLOTHES_SPRITE, TOPCLOTHES_SPRITE, + MISC1_SPRITE, + MISC2_SPRITE, HAIR_SPRITE, HAT_SPRITE, + CAPE_SPRITE, + GLOVES_SPRITE, WEAPON_SPRITE, SHIELD_SPRITE, VECTOREND_SPRITE diff --git a/src/game.cpp b/src/game.cpp index 37375ca2..aaf5242c 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -69,6 +69,7 @@ #include "gui/trade.h" #include "gui/viewport.h" +#include "net/protocol.h" #include "net/beinghandler.h" #include "net/buysellhandler.h" #include "net/chathandler.h" @@ -80,6 +81,7 @@ #include "net/playerhandler.h" #include "net/skillhandler.h" #include "net/tradehandler.h" +#include "net/messageout.h" #include "resources/imagewriter.h" @@ -260,7 +262,7 @@ void destroyGuiWindows() Game::Game(Network *network): mNetwork(network), - mBeingHandler(new BeingHandler()), + mBeingHandler(new BeingHandler(config.getValue("EnableSync", 0) == 1)), mBuySellHandler(new BuySellHandler()), mChatHandler(new ChatHandler()), mEquipmentHandler(new EquipmentHandler()), @@ -291,7 +293,6 @@ Game::Game(Network *network): // Initialize beings beingManager->setPlayer(player_node); player_node->setNetwork(network); - engine->changeMap(map_path); Joystick::init(); // TODO: The user should be able to choose which one to use @@ -311,6 +312,25 @@ Game::Game(Network *network): network->registerHandler(mPlayerHandler.get()); network->registerHandler(mSkillHandler.get()); network->registerHandler(mTradeHandler.get()); + + /* + * THIS IS A TEMPORARY WORKAROUND! + * + * To prevent the server from sending data before the client has + * initialized, it's been modified to wait for a "ping" from the client to + * complete its initialization. + * + * The real fix is to make sure we are not throwing away messages in the + * network buffer due to not having registered the handlers above straight + * after receiving a login success from the map server. + * + * The response from eAthena on this packet is ignored by the client. + */ + MessageOut msg(mNetwork); + msg.writeInt16(CMSG_CLIENT_PING); + msg.writeInt32(tick_time); + + engine->changeMap(map_path); } Game::~Game() diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp index be8412d9..c43c674f 100644 --- a/src/net/beinghandler.cpp +++ b/src/net/beinghandler.cpp @@ -40,11 +40,13 @@ const int EMOTION_TIME = 150; /**< Duration of emotion icon */ -BeingHandler::BeingHandler() +BeingHandler::BeingHandler(bool enableSync): + mSync(enableSync) { static const Uint16 _messages[] = { SMSG_BEING_VISIBLE, SMSG_BEING_MOVE, + SMSG_BEING_MOVE2, SMSG_BEING_REMOVE, SMSG_BEING_ACTION, SMSG_BEING_LEVELUP, @@ -55,6 +57,8 @@ BeingHandler::BeingHandler() SMSG_PLAYER_UPDATE_1, SMSG_PLAYER_UPDATE_2, SMSG_PLAYER_MOVE, + SMSG_PLAYER_STOP, + SMSG_PLAYER_MOVE_TO_ATTACK, 0x0119, 0 }; @@ -66,6 +70,8 @@ void BeingHandler::handleMessage(MessageIn *msg) Uint32 id; Uint16 job, speed; Uint16 headTop, headMid, headBottom; + Uint16 shoes, gloves, cape, misc1, misc2; + Uint16 weapon, shield; Sint16 param1; Sint8 type; Being *srcBeing, *dstBeing; @@ -78,8 +84,8 @@ void BeingHandler::handleMessage(MessageIn *msg) // Information about a being in range id = msg->readInt32(); speed = msg->readInt16(); - msg->readInt16(); // unknown - msg->readInt16(); // unknown + msg->readInt16(); // opt1 + msg->readInt16(); // opt2 msg->readInt16(); // option job = msg->readInt16(); // class @@ -122,8 +128,10 @@ void BeingHandler::handleMessage(MessageIn *msg) headTop = msg->readInt16(); headMid = msg->readInt16(); hairColor = msg->readInt16(); - msg->readInt16(); // clothes color -not used + msg->readInt16(); // clothes color - not used msg->readInt16(); // head dir + shoes = 0; + gloves = 0; msg->readInt16(); // guild msg->readInt16(); // unknown msg->readInt16(); // unknown @@ -133,9 +141,11 @@ void BeingHandler::handleMessage(MessageIn *msg) dstBeing->setGender(1 - msg->readInt8()); // gender // Set these after the gender, as the sprites may be gender-specific - dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, headBottom); - dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid); - dstBeing->setSprite(Being::HAT_SPRITE, headTop); + dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, headBottom); + dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid); + dstBeing->setSprite(Being::HAT_SPRITE, headTop); + dstBeing->setSprite(Being::SHOE_SPRITE, shoes); + dstBeing->setSprite(Being::GLOVES_SPRITE, gloves); dstBeing->setHairStyle(hairStyle, hairColor); if (msg->getId() == SMSG_BEING_MOVE) @@ -159,6 +169,33 @@ void BeingHandler::handleMessage(MessageIn *msg) msg->readInt8(); // unknown / sit break; + case SMSG_BEING_MOVE2: + /* + * A simplified movement packet, used by the + * later versions of eAthena for both mobs and + * players + */ + dstBeing = beingManager->findBeing(msg->readInt32()); + + Uint16 srcX, srcY, dstX, dstY; + msg->readCoordinatePair(srcX, srcY, dstX, dstY); + msg->readInt32(); // Server tick + + /* + * This packet doesn't have enough info to actually + * create a new being, so if the being isn't found, + * we'll just pretend the packet didn't happen + */ + + if (dstBeing) { + dstBeing->setAction(Being::STAND); + dstBeing->mX = srcX; + dstBeing->mY = srcY; + dstBeing->setDestination(dstX, dstY); + } + + break; + case SMSG_BEING_REMOVE: // A being should be removed or has died dstBeing = beingManager->findBeing(msg->readInt32()); @@ -166,6 +203,11 @@ void BeingHandler::handleMessage(MessageIn *msg) if (!dstBeing) break; + if (dstBeing == player_node->getTarget()) + { + player_node->stopAttack(); + } + if (msg->readInt8() == 1) { dstBeing->setAction(Being::DEAD); @@ -175,10 +217,6 @@ void BeingHandler::handleMessage(MessageIn *msg) beingManager->destroyBeing(dstBeing); } - if (dstBeing == player_node->getTarget()) - { - player_node->stopAttack(); - } break; case SMSG_BEING_ACTION: @@ -257,7 +295,7 @@ void BeingHandler::handleMessage(MessageIn *msg) case SMSG_BEING_CHANGE_LOOKS2: { /* - * SMSG_BEING_CHANGE_LOOKS (0x00c3) and + * SMSG_BEING_CHANGE_LOOKS (0x00c3) and * SMSG_BEING_CHANGE_LOOKS2 (0x01d7) do basically the same * thing. The difference is that ...LOOKS carries a single * 8 bit value, where ...LOOKS2 carries two 16 bit values. @@ -288,7 +326,7 @@ void BeingHandler::handleMessage(MessageIn *msg) case 1: // eAthena LOOK_HAIR dstBeing->setHairStyle(id, -1); break; - case 2: // Weapon ID in id, Shield ID in id2 + case 2: // Weapon ID in id, Shield ID in id2 dstBeing->setSprite(Being::WEAPON_SPRITE, id); dstBeing->setSprite(Being::SHIELD_SPRITE, id2); break; @@ -304,9 +342,24 @@ void BeingHandler::handleMessage(MessageIn *msg) case 6: // eAthena LOOK_HAIR_COLOR dstBeing->setHairStyle(-1, id); break; + case 8: // eAthena LOOK_SHIELD + dstBeing->setSprite(Being::SHIELD_SPRITE, id); + break; case 9: // eAthena LOOK_SHOES dstBeing->setSprite(Being::SHOE_SPRITE, id); break; + case 10: // LOOK_GLOVES + dstBeing->setSprite(Being::GLOVES_SPRITE, id); + break; + case 11: // LOOK_CAPE + dstBeing->setSprite(Being::CAPE_SPRITE, id); + break; + case 12: + dstBeing->setSprite(Being::MISC1_SPRITE, id); + break; + case 13: + dstBeing->setSprite(Being::MISC2_SPRITE, id); + break; default: logger->log("SMSG_BEING_CHANGE_LOOKS: unsupported type: " "%d, id: %d", type, id); @@ -328,9 +381,9 @@ void BeingHandler::handleMessage(MessageIn *msg) // An update about a player, potentially including movement. id = msg->readInt32(); speed = msg->readInt16(); - msg->readInt16(); // option 1 - msg->readInt16(); // option 2 - msg->readInt16(); // option + cape = msg->readInt16(); + misc1 = msg->readInt16(); + misc2 = msg->readInt16(); job = msg->readInt16(); dstBeing = beingManager->findBeing(id); @@ -343,9 +396,9 @@ void BeingHandler::handleMessage(MessageIn *msg) dstBeing->setWalkSpeed(speed); dstBeing->mJob = job; hairStyle = msg->readInt16(); - dstBeing->setSprite(Being::WEAPON_SPRITE, msg->readInt16()); - dstBeing->setSprite(Being::SHIELD_SPRITE, msg->readInt16()); - headBottom = msg->readInt16(); + weapon = msg->readInt16(); + shield = msg->readInt16(); + headBottom = msg->readInt16(); if (msg->getId() == SMSG_PLAYER_MOVE) { @@ -355,7 +408,9 @@ void BeingHandler::handleMessage(MessageIn *msg) headTop = msg->readInt16(); headMid = msg->readInt16(); hairColor = msg->readInt16(); - msg->readInt16(); // clothes color - not used + shoes = 0; + gloves = 0; + msg->readInt16(); // clothes color - not used msg->readInt16(); // head dir msg->readInt32(); // guild msg->readInt32(); // emblem @@ -364,9 +419,18 @@ void BeingHandler::handleMessage(MessageIn *msg) dstBeing->setGender(1 - msg->readInt8()); // gender // Set these after the gender, as the sprites may be gender-specific - dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, headBottom); + dstBeing->setSprite(Being::WEAPON_SPRITE, weapon); + dstBeing->setSprite(Being::SHIELD_SPRITE, shield); + dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, headBottom); dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, headMid); dstBeing->setSprite(Being::HAT_SPRITE, headTop); + dstBeing->setSprite(Being::SHOE_SPRITE, shoes); + // Compensation for the unpatched TMW server + if (gloves > 10) + dstBeing->setSprite(Being::GLOVES_SPRITE, gloves); + dstBeing->setSprite(Being::CAPE_SPRITE, cape); + dstBeing->setSprite(Being::MISC1_SPRITE, misc1); + dstBeing->setSprite(Being::MISC2_SPRITE, misc2); dstBeing->setHairStyle(hairStyle, hairColor); if (msg->getId() == SMSG_PLAYER_MOVE) @@ -384,8 +448,7 @@ void BeingHandler::handleMessage(MessageIn *msg) dstBeing->setDirection(dir); } - msg->readInt8(); // unknown - msg->readInt8(); // unknown + msg->readInt16(); // GM status if (msg->getId() == SMSG_PLAYER_UPDATE_1) { @@ -406,6 +469,42 @@ void BeingHandler::handleMessage(MessageIn *msg) dstBeing->mFrame = 0; break; + case SMSG_PLAYER_STOP: + /* + * Instruction from server to stop walking at x, y. + * + * Some people like having this enabled. Others absolutely + * despise it. So I'm setting to so that it only affects the + * local player if the person has set a key "EnableSync" to "1" + * in their config.xml file. + * + * This packet will be honored for all other beings, regardless + * of the config setting. + */ + + id = msg->readInt32(); + if (mSync || id != player_node->getId()) { + dstBeing = beingManager->findBeing(id); + if (dstBeing) { + dstBeing->mX = msg->readInt16(); + dstBeing->mY = msg->readInt16(); + if (dstBeing->mAction == Being::WALK) { + dstBeing->mFrame = 0; + dstBeing->setAction(Being::STAND); + } + } + } + break; + + case SMSG_PLAYER_MOVE_TO_ATTACK: + /* + * This is an *advisory* message, telling the client that + * it needs to move the character before attacking + * a target (out of range, obstruction in line of fire). + * We can safely ignore this... + */ + break; + case 0x0119: // Change in players look logger->log("0x0119 %i %i %i %x %i", msg->readInt32(), diff --git a/src/net/beinghandler.h b/src/net/beinghandler.h index 03012f39..5d6d91a4 100644 --- a/src/net/beinghandler.h +++ b/src/net/beinghandler.h @@ -29,9 +29,13 @@ class BeingHandler : public MessageHandler { public: - BeingHandler(); + BeingHandler(bool); void handleMessage(MessageIn *msg); + + private: + // Should we honor server "Stop Walking" packets + bool mSync; }; #endif diff --git a/src/net/charserverhandler.cpp b/src/net/charserverhandler.cpp index c49c7640..baa841f4 100644 --- a/src/net/charserverhandler.cpp +++ b/src/net/charserverhandler.cpp @@ -191,7 +191,10 @@ LocalPlayer* CharServerHandler::readPlayerData(MessageIn &msg, int &slot) tempPlayer->mGp = msg.readInt32(); tempPlayer->mJobXp = msg.readInt32(); tempPlayer->mJobLevel = msg.readInt32(); - msg.skip(8); // unknown + tempPlayer->setSprite(Being::SHOE_SPRITE, msg.readInt16()); + tempPlayer->setSprite(Being::GLOVES_SPRITE, msg.readInt16()); + tempPlayer->setSprite(Being::CAPE_SPRITE, msg.readInt16()); + tempPlayer->setSprite(Being::MISC1_SPRITE, msg.readInt16()); msg.readInt32(); // option msg.readInt32(); // karma msg.readInt32(); // manner @@ -213,7 +216,7 @@ LocalPlayer* CharServerHandler::readPlayerData(MessageIn &msg, int &slot) tempPlayer->setSprite(Being::TOPCLOTHES_SPRITE, msg.readInt16()); // head option mid int hairColor = msg.readInt16(); tempPlayer->setHairStyle(hairStyle, hairColor); - msg.readInt16(); // unknown + tempPlayer->setSprite(Being::MISC2_SPRITE, msg.readInt16()); tempPlayer->setName(msg.readString(24)); for (int i = 0; i < 6; i++) { tempPlayer->mAttr[i] = msg.readInt8(); diff --git a/src/net/playerhandler.cpp b/src/net/playerhandler.cpp index 2502c144..463868db 100644 --- a/src/net/playerhandler.cpp +++ b/src/net/playerhandler.cpp @@ -123,6 +123,12 @@ void PlayerHandler::handleMessage(MessageIn *msg) logger->log("Warping to %s (%d, %d)", mapPath.c_str(), x, y); + /* + * We must clear the local player's target *before* the call + * to changeMap, as it deletes all beings. + */ + player_node->stopAttack(); + // Switch the actual map, deleting the previous one engine->changeMap(mapPath); @@ -132,7 +138,6 @@ void PlayerHandler::handleMessage(MessageIn *msg) float scrollOffsetY = (y - player_node->mY) * 32; player_node->setAction(Being::STAND); - player_node->stopAttack(); player_node->mFrame = 0; player_node->mX = x; player_node->mY = y; diff --git a/src/net/protocol.h b/src/net/protocol.h index eb41a9ac..a2aa50c3 100644 --- a/src/net/protocol.h +++ b/src/net/protocol.h @@ -30,6 +30,8 @@ #define SMSG_PLAYER_UPDATE_1 0x01d8 #define SMSG_PLAYER_UPDATE_2 0x01d9 #define SMSG_PLAYER_MOVE 0x01da /**< A nearby player moves */ +#define SMSG_PLAYER_STOP 0x0088 /**< Stop walking, set position */ +#define SMSG_PLAYER_MOVE_TO_ATTACK 0x0139 /**< Move to within attack range */ #define SMSG_PLAYER_STAT_UPDATE_1 0x00b0 #define SMSG_PLAYER_STAT_UPDATE_2 0x00b1 #define SMSG_PLAYER_STAT_UPDATE_3 0x0141 @@ -56,6 +58,8 @@ #define SMSG_ITEM_REMOVE 0x00a1 /**< An item disappers */ #define SMSG_BEING_VISIBLE 0x0078 #define SMSG_BEING_MOVE 0x007b /**< A nearby monster moves */ +#define SMSG_BEING_SPAWN 0x007c /**< A being spawns nearby */ +#define SMSG_BEING_MOVE2 0x0086 /**< New eAthena being moves */ #define SMSG_BEING_REMOVE 0x0080 #define SMSG_BEING_CHANGE_LOOKS 0x00c3 #define SMSG_BEING_CHANGE_LOOKS2 0x01d7 /**< Same as 0x00c3, but 16 bit ID */ @@ -87,6 +91,7 @@ #define SMSG_TRADE_COMPLETE 0x00f0 // Packets from client to server +#define CMSG_CLIENT_PING 0x007e /**< Send to server with tick */ #define CMSG_TRADE_RESPONSE 0x00e6 #define CMSG_ITEM_PICKUP 0x009f #define CMSG_MAP_LOADED 0x007d -- cgit v1.2.3-70-g09d2