summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/being.h4
-rw-r--r--src/game.cpp24
-rw-r--r--src/net/beinghandler.cpp145
-rw-r--r--src/net/beinghandler.h6
-rw-r--r--src/net/charserverhandler.cpp7
-rw-r--r--src/net/playerhandler.cpp7
-rw-r--r--src/net/protocol.h5
7 files changed, 169 insertions, 29 deletions
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