diff options
Diffstat (limited to 'src/net')
91 files changed, 7819 insertions, 2507 deletions
diff --git a/src/net/accountserver/account.cpp b/src/net/accountserver/account.cpp new file mode 100644 index 00000000..f734c4eb --- /dev/null +++ b/src/net/accountserver/account.cpp @@ -0,0 +1,107 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "account.h" + +#include <string> + +#include "internal.h" + +#include "../connection.h" +#include "../messageout.h" +#include "../protocol.h" + +#include "../../utils/sha256.h" + +void Net::AccountServer::Account::createCharacter( + const std::string &name, char hairStyle, char hairColor, char gender, + short strength, short agility, short vitality, + short intelligence, short dexterity, short willpower) +{ + MessageOut msg(PAMSG_CHAR_CREATE); + + msg.writeString(name); + msg.writeInt8(hairStyle); + msg.writeInt8(hairColor); + msg.writeInt8(gender); + msg.writeInt16(strength); + msg.writeInt16(agility); + msg.writeInt16(vitality); + msg.writeInt16(intelligence); + msg.writeInt16(dexterity); + msg.writeInt16(willpower); + + Net::AccountServer::connection->send(msg); +} + +void Net::AccountServer::Account::deleteCharacter(char slot) +{ + MessageOut msg(PAMSG_CHAR_DELETE); + + msg.writeInt8(slot); + + Net::AccountServer::connection->send(msg); +} + +void Net::AccountServer::Account::selectCharacter(char slot) +{ + MessageOut msg(PAMSG_CHAR_SELECT); + + msg.writeInt8(slot); + + Net::AccountServer::connection->send(msg); +} + +void Net::AccountServer::Account::unregister(const std::string &username, + const std::string &password) +{ + MessageOut msg(PAMSG_UNREGISTER); + + msg.writeString(username); + msg.writeString(sha256(username + password)); + + Net::AccountServer::connection->send(msg); +} + +void Net::AccountServer::Account::changeEmail(const std::string &email) +{ + MessageOut msg(PAMSG_EMAIL_CHANGE); + + // Email is sent clearly so the server can validate the data. + // Encryption is assumed server-side. + msg.writeString(email); + + Net::AccountServer::connection->send(msg); +} + +void Net::AccountServer::Account::changePassword( + const std::string &username, + const std::string &oldPassword, + const std::string &newPassword) +{ + MessageOut msg(PAMSG_PASSWORD_CHANGE); + + // Change password using SHA2 encryption + msg.writeString(sha256(username + oldPassword)); + msg.writeString(sha256(username + newPassword)); + + Net::AccountServer::connection->send(msg); +} diff --git a/src/net/accountserver/account.h b/src/net/accountserver/account.h new file mode 100644 index 00000000..581bcb42 --- /dev/null +++ b/src/net/accountserver/account.h @@ -0,0 +1,54 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_ACCOUNTSERVER_CHARACTER_H +#define _TMW_NET_ACCOUNTSERVER_CHARACTER_H + +#include <iosfwd> + +namespace Net +{ + namespace AccountServer + { + namespace Account + { + void createCharacter(const std::string &name, + char hairStyle, char hairColor, char gender, + short strength, short agility, short vitality, + short intelligence, short dexterity, short willpower); + + void deleteCharacter(char slot); + + void selectCharacter(char slot); + + void unregister(const std::string &username, + const std::string &password); + + void changeEmail(const std::string &email); + + void changePassword(const std::string &username, + const std::string &oldPassword, + const std::string &newPassword); + } + } +} + +#endif diff --git a/src/net/accountserver/accountserver.cpp b/src/net/accountserver/accountserver.cpp new file mode 100644 index 00000000..b1ce590c --- /dev/null +++ b/src/net/accountserver/accountserver.cpp @@ -0,0 +1,81 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "accountserver.h" + +#include <string> + +#include "internal.h" + +#include "../connection.h" +#include "../messageout.h" +#include "../protocol.h" + +#include "../../utils/sha256.h" + +void Net::AccountServer::login(Net::Connection *connection, int version, + const std::string &username, const std::string &password) +{ + Net::AccountServer::connection = connection; + + MessageOut msg(PAMSG_LOGIN); + + msg.writeInt32(version); + msg.writeString(username); + msg.writeString(sha256(username + password)); + + Net::AccountServer::connection->send(msg); +} + +void Net::AccountServer::registerAccount(Net::Connection *connection, + int version, const std::string &username, const std::string &password, + const std::string &email) +{ + Net::AccountServer::connection = connection; + + MessageOut msg(PAMSG_REGISTER); + + msg.writeInt32(version); // client version + msg.writeString(username); + // When registering, the password and email hash is assumed by server. + // Hence, data can be validated safely server-side. + // This is the only time we send a clear password. + msg.writeString(password); + msg.writeString(email); + + Net::AccountServer::connection->send(msg); +} + +void Net::AccountServer::logout() +{ + MessageOut msg(PAMSG_LOGOUT); + Net::AccountServer::connection->send(msg); +} + +void Net::AccountServer::reconnectAccount(Net::Connection *connection, + const std::string &passToken) +{ + Net::AccountServer::connection = connection; + + MessageOut msg(PAMSG_RECONNECT); + msg.writeString(passToken, 32); + Net::AccountServer::connection->send(msg); +} diff --git a/src/net/accountserver/accountserver.h b/src/net/accountserver/accountserver.h new file mode 100644 index 00000000..8e0573fc --- /dev/null +++ b/src/net/accountserver/accountserver.h @@ -0,0 +1,47 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_ACCOUNTSERVER_ACCOUNTSERVER_H +#define _TMW_NET_ACCOUNTSERVER_ACCOUNTSERVER_H + +#include <iosfwd> + +namespace Net +{ + class Connection; + + namespace AccountServer + { + void login(Net::Connection *connection, int version, + const std::string &username, const std::string &password); + + void registerAccount(Net::Connection *connection, int version, + const std::string &username, const std::string &password, + const std::string &email); + + void logout(); + + void reconnectAccount(Net::Connection *connection, + const std::string &passToken); + } +} + +#endif diff --git a/src/net/accountserver/internal.cpp b/src/net/accountserver/internal.cpp new file mode 100644 index 00000000..a3be76a1 --- /dev/null +++ b/src/net/accountserver/internal.cpp @@ -0,0 +1,32 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal.h" + +namespace Net +{ + class Connection; + + namespace AccountServer + { + Connection *connection = 0; + } +} diff --git a/src/net/accountserver/internal.h b/src/net/accountserver/internal.h new file mode 100644 index 00000000..b3d64582 --- /dev/null +++ b/src/net/accountserver/internal.h @@ -0,0 +1,35 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_ACCOUNTSERVER_INTERNAL_H +#define _TMW_NET_ACCOUNTSERVER_INTERNAL_H + +namespace Net +{ + class Connection; + + namespace AccountServer + { + extern Connection *connection; + } +} + +#endif diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp index 3e746eb5..8f1fb8fd 100644 --- a/src/net/beinghandler.cpp +++ b/src/net/beinghandler.cpp @@ -1,542 +1,359 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <iostream> +#include "beinghandler.h" + #include <SDL_types.h> -#include "beinghandler.h" #include "messagein.h" #include "protocol.h" #include "../being.h" #include "../beingmanager.h" -#include "../effectmanager.h" #include "../game.h" #include "../localplayer.h" #include "../log.h" +#include "../main.h" #include "../npc.h" -#include "../player_relations.h" +#include "../particle.h" +#include "../sound.h" + +#include "../gui/ok_dialog.h" + +#include "../utils/gettext.h" + +#include "gameserver/player.h" const int EMOTION_TIME = 150; /**< Duration of emotion icon */ -BeingHandler::BeingHandler(bool enableSync): - mSync(enableSync) +BeingHandler::BeingHandler() { static const Uint16 _messages[] = { - SMSG_BEING_VISIBLE, - SMSG_BEING_MOVE, - SMSG_BEING_MOVE2, - SMSG_BEING_REMOVE, - SMSG_BEING_ACTION, - SMSG_BEING_SELFEFFECT, - SMSG_BEING_EMOTION, - SMSG_BEING_CHANGE_LOOKS, - SMSG_BEING_CHANGE_LOOKS2, - SMSG_BEING_NAME_RESPONSE, - SMSG_PLAYER_UPDATE_1, - SMSG_PLAYER_UPDATE_2, - SMSG_PLAYER_MOVE, - SMSG_PLAYER_STOP, - SMSG_PLAYER_MOVE_TO_ATTACK, - 0x0119, - 0x0196, + GPMSG_BEING_ATTACK, + GPMSG_BEING_ENTER, + GPMSG_BEING_LEAVE, + GPMSG_BEINGS_MOVE, + GPMSG_BEINGS_DAMAGE, + GPMSG_BEING_ACTION_CHANGE, + GPMSG_BEING_LOOKS_CHANGE, + GPMSG_BEING_DIR_CHANGE, 0 }; handledMessages = _messages; } -void BeingHandler::handleMessage(MessageIn *msg) +void BeingHandler::handleMessage(MessageIn &msg) { - Uint32 id; - Uint16 job, speed; - Uint16 headTop, headMid, headBottom; - Uint16 shoes, gloves; - Uint16 weapon, shield; - Uint16 gmstatus; - Sint16 param1; - int stunMode; - Uint32 statusEffects; - Sint8 type; - Uint16 status; - Being *srcBeing, *dstBeing; - int hairStyle, hairColor, flag; - - switch (msg->getId()) + switch (msg.getId()) { - case SMSG_BEING_VISIBLE: - case SMSG_BEING_MOVE: - // Information about a being in range - id = msg->readInt32(); - speed = msg->readInt16(); - stunMode = msg->readInt16(); // opt1 - statusEffects = msg->readInt16(); // opt2 - statusEffects |= ((Uint32)msg->readInt16()) << 16; // option - job = msg->readInt16(); // class - - dstBeing = beingManager->findBeing(id); - - if (!dstBeing) - { - // Being with id >= 110000000 and job 0 are better - // known as ghosts, so don't create those. - if (job == 0 && id >= 110000000) - { - break; - } - - dstBeing = beingManager->createBeing(id, job); - } - else if (msg->getId() == 0x0078) - { - dstBeing->clearPath(); - dstBeing->mFrame = 0; - dstBeing->mWalkTime = tick_time; - dstBeing->setAction(Being::STAND); - } - - - // Prevent division by 0 when calculating frame - if (speed == 0) { speed = 150; } - - dstBeing->setWalkSpeed(speed); - dstBeing->mJob = job; - hairStyle = msg->readInt16(); - dstBeing->setSprite(Being::WEAPON_SPRITE, msg->readInt16()); - headBottom = msg->readInt16(); - - if (msg->getId() == SMSG_BEING_MOVE) - { - msg->readInt32(); // server tick - } - - dstBeing->setSprite(Being::SHIELD_SPRITE, msg->readInt16()); - headTop = msg->readInt16(); - headMid = msg->readInt16(); - hairColor = msg->readInt16(); - shoes = msg->readInt16(); // clothes color - "abused" as shoes - gloves = msg->readInt16(); // head dir - "abused" as gloves - msg->readInt16(); // guild - msg->readInt16(); // unknown - msg->readInt16(); // unknown - msg->readInt16(); // manner - dstBeing->setStatusEffectBlock(32, msg->readInt16()); // opt3 - msg->readInt8(); // karma - dstBeing->setGender( - (msg->readInt8() == 0) ? GENDER_FEMALE : GENDER_MALE); - - // 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::SHOE_SPRITE, shoes); - dstBeing->setSprite(Being::GLOVES_SPRITE, gloves); - dstBeing->setHairStyle(hairStyle, hairColor); - - if (msg->getId() == SMSG_BEING_MOVE) - { - Uint16 srcX, srcY, dstX, dstY; - msg->readCoordinatePair(srcX, srcY, dstX, dstY); - dstBeing->setAction(Being::STAND); - dstBeing->mX = srcX; - dstBeing->mY = srcY; - dstBeing->setDestination(dstX, dstY); - } - else - { - Uint8 dir; - msg->readCoordinates(dstBeing->mX, dstBeing->mY, dir); - dstBeing->setDirection(dir); - } - - msg->readInt8(); // unknown - msg->readInt8(); // unknown - msg->readInt8(); // unknown / sit - - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); - dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); + case GPMSG_BEING_ENTER: + handleBeingEnterMessage(msg); 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); - } - + case GPMSG_BEING_LEAVE: + handleBeingLeaveMessage(msg); break; - - case SMSG_BEING_REMOVE: - // A being should be removed or has died - dstBeing = beingManager->findBeing(msg->readInt32()); - - if (!dstBeing) - break; - - // If this is player's current target, clear it. - if (dstBeing == player_node->getTarget()) - player_node->stopAttack(); - - if (dstBeing == current_npc) - current_npc->handleDeath(); - - if (msg->readInt8() == 1) - dstBeing->setAction(Being::DEAD); - else - beingManager->destroyBeing(dstBeing); - + case GPMSG_BEINGS_MOVE: + handleBeingsMoveMessage(msg); break; - - case SMSG_BEING_ACTION: - srcBeing = beingManager->findBeing(msg->readInt32()); - dstBeing = beingManager->findBeing(msg->readInt32()); - msg->readInt32(); // server tick - msg->readInt32(); // src speed - msg->readInt32(); // dst speed - param1 = msg->readInt16(); - msg->readInt16(); // param 2 - type = msg->readInt8(); - msg->readInt16(); // param 3 - - switch (type) - { - case 0x0a: // Critical Damage - if (dstBeing) - dstBeing->showCrit(); - case 0x00: // Damage - if (dstBeing) - dstBeing->takeDamage(param1); - if (srcBeing) - srcBeing->handleAttack(dstBeing, param1); - break; - - case 0x02: // Sit - if (srcBeing) - { - srcBeing->mFrame = 0; - srcBeing->setAction(Being::SIT); - } - break; - - case 0x03: // Stand up - if (srcBeing) - { - srcBeing->mFrame = 0; - srcBeing->setAction(Being::STAND); - } - break; - } + case GPMSG_BEING_ATTACK: + handleBeingAttackMessage(msg); break; + case GPMSG_BEINGS_DAMAGE: + handleBeingsDamageMessage(msg); + break; + case GPMSG_BEING_ACTION_CHANGE: + handleBeingActionChangeMessage(msg); + break; + case GPMSG_BEING_LOOKS_CHANGE: + handleBeingLooksChangeMessage(msg); + break; + case GPMSG_BEING_DIR_CHANGE: + handleBeingDirChangeMessage(msg); + break; + } +} - case SMSG_BEING_SELFEFFECT: { - id = (Uint32)msg->readInt32(); - if (!beingManager->findBeing(id)) - break; - - int effectType = msg->readInt32(); - Being* being = beingManager->findBeing(id); +static void handleLooks(Player *being, MessageIn &msg) +{ + // Order of sent slots. Has to be in sync with the server code. + static int const nb_slots = 4; + static int const slots[nb_slots] = + { Being::WEAPON_SPRITE, Being::HAT_SPRITE, Being::TOPCLOTHES_SPRITE, + Being::BOTTOMCLOTHES_SPRITE }; - effectManager->trigger(effectType, being); + int mask = msg.readInt8(); - break; + if (mask & (1 << 7)) + { + // The equipment has to be cleared first. + for (int i = 0; i < nb_slots; ++i) + { + being->setSprite(slots[i], 0); } + } - case SMSG_BEING_EMOTION: - if (!(dstBeing = beingManager->findBeing(msg->readInt32()))) - { - break; - } - - if (player_relations.hasPermission(dstBeing, PlayerRelation::EMOTE)) - dstBeing->setEmote(msg->readInt8(), EMOTION_TIME); - - break; + // Fill slots enumerated by the bitmask. + for (int i = 0; i < nb_slots; ++i) + { + if (!(mask & (1 << i))) continue; + int id = msg.readInt16(); + being->setSprite(slots[i], id); + } +} - case SMSG_BEING_CHANGE_LOOKS: - case SMSG_BEING_CHANGE_LOOKS2: +void BeingHandler::handleBeingEnterMessage(MessageIn &msg) +{ + int type = msg.readInt8(); + int id = msg.readInt16(); + Being::Action action = (Being::Action)msg.readInt8(); + int px = msg.readInt16(); + int py = msg.readInt16(); + Being *being; + + switch (type) + { + case OBJECT_PLAYER: { - /* - * 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. - * - * If type = 2, then the first 16 bit value is the weapon ID, - * and the second 16 bit value is the shield ID. If no - * shield is equipped, or type is not 2, then the second - * 16 bit value will be 0. - */ - - if (!(dstBeing = beingManager->findBeing(msg->readInt32()))) + std::string name = msg.readString(); + if (player_node->getName() == name) { - break; + being = player_node; + being->setId(id); } - - int type = msg->readInt8(); - int id = 0; - int id2 = 0; - - if (msg->getId() == SMSG_BEING_CHANGE_LOOKS) { - id = msg->readInt8(); - } else { // SMSG_BEING_CHANGE_LOOKS2 - id = msg->readInt16(); - id2 = msg->readInt16(); - } - - switch (type) { - case 1: // eAthena LOOK_HAIR - dstBeing->setHairStyle(id, -1); - break; - case 2: // Weapon ID in id, Shield ID in id2 - dstBeing->setSprite(Being::WEAPON_SPRITE, id); - dstBeing->setSprite(Being::SHIELD_SPRITE, id2); - break; - case 3: // Change lower headgear for eAthena, pants for us - dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, id); - break; - case 4: // Change upper headgear for eAthena, hat for us - dstBeing->setSprite(Being::HAT_SPRITE, id); - break; - case 5: // Change middle headgear for eathena, armor for us - dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, id); - break; - 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); - break; - } - } - break; - - case SMSG_BEING_NAME_RESPONSE: - if ((dstBeing = beingManager->findBeing(msg->readInt32()))) + else { - dstBeing->setName(msg->readString(24)); + being = beingManager->createBeing(id, type, 0); + being->setName(name); } - break; + Player *p = static_cast< Player * >(being); + int hs = msg.readInt8(), hc = msg.readInt8(); + p->setHairStyle(hs, hc); + p->setGender(msg.readInt8() == GENDER_MALE ? + GENDER_MALE : GENDER_FEMALE); + handleLooks(p, msg); + } break; + + case OBJECT_MONSTER: + case OBJECT_NPC: + { + int subtype = msg.readInt16(); + being = beingManager->createBeing(id, type, subtype); + std::string name = msg.readString(); + if (name.length() > 0) being->setName(name); + } break; + + default: + return; + } - case SMSG_PLAYER_UPDATE_1: - case SMSG_PLAYER_UPDATE_2: - case SMSG_PLAYER_MOVE: - // An update about a player, potentially including movement. - id = msg->readInt32(); - speed = msg->readInt16(); - stunMode = msg->readInt16(); // opt1; Aethyra use this as cape - statusEffects = msg->readInt16(); // opt2; Aethyra use this as misc1 - statusEffects |= ((Uint32) msg->readInt16()) - << 16; // status.options; Aethyra uses this as misc2 - job = msg->readInt16(); - - dstBeing = beingManager->findBeing(id); - - if (!dstBeing) - { - dstBeing = beingManager->createBeing(id, job); - } + being->setPosition(px, py); + being->setDestination(px, py); + being->setAction(action); +} - dstBeing->setWalkSpeed(speed); - dstBeing->mJob = job; - hairStyle = msg->readInt16(); - weapon = msg->readInt16(); - shield = msg->readInt16(); - headBottom = msg->readInt16(); +void BeingHandler::handleBeingLeaveMessage(MessageIn &msg) +{ + Being *being = beingManager->findBeing(msg.readInt16()); + if (!being) return; - if (msg->getId() == SMSG_PLAYER_MOVE) - { - msg->readInt32(); // server tick - } + beingManager->destroyBeing(being); +} - headTop = msg->readInt16(); - headMid = msg->readInt16(); - hairColor = msg->readInt16(); - msg->readInt16(); // clothes color - Aethyra-"abused" as shoes, we ignore it - msg->readInt16(); // head dir - Aethyra-"abused" as gloves, we ignore it - msg->readInt32(); // guild - msg->readInt16(); // emblem - msg->readInt16(); // manner - dstBeing->setStatusEffectBlock(32, msg->readInt16()); // opt3 - msg->readInt8(); // karma - dstBeing->setGender( - (msg->readInt8() == 0) ? GENDER_FEMALE : GENDER_MALE); - - // Set these after the gender, as the sprites may be gender-specific - 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::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) - { - Uint16 srcX, srcY, dstX, dstY; - msg->readCoordinatePair(srcX, srcY, dstX, dstY); - dstBeing->mX = srcX; - dstBeing->mY = srcY; - dstBeing->setDestination(dstX, dstY); - } - else +void BeingHandler::handleBeingsMoveMessage(MessageIn &msg) +{ + while (msg.getUnreadLength()) + { + int id = msg.readInt16(); + int flags = msg.readInt8(); + Being *being = beingManager->findBeing(id); + int sx = 0; + int sy = 0; + int dx = 0; + int dy = 0; + int speed = 0; + + printf("handleBeingsMoveMessage for %p (%s | %s)\n", + (void*) being, + (flags & MOVING_POSITION) ? "pos" : "", + (flags & MOVING_DESTINATION) ? "dest" : ""); + + if (flags & MOVING_POSITION) + { + Uint16 sx2, sy2; + msg.readCoordinates(sx2, sy2); + sx = sx2 * 32 + 16; + sy = sy2 * 32 + 16; + speed = msg.readInt8(); + } + if (flags & MOVING_DESTINATION) + { + dx = msg.readInt16(); + dy = msg.readInt16(); + if (!(flags & MOVING_POSITION)) { - Uint8 dir; - msg->readCoordinates(dstBeing->mX, dstBeing->mY, dir); - dstBeing->setDirection(dir); + sx = dx; + sy = dy; } + } + if (!being || !(flags & (MOVING_POSITION | MOVING_DESTINATION))) + { + continue; + } + if (speed) + { + /* The speed on the server is the cost of moving from one tile to + * the next. Beings get 1000 cost units per second. The speed is + * transferred as devided by 10, so that slower speeds fit in a + * byte. Here we convert the speed to pixels per second. + */ + const float tilesPerSecond = 100.0f / speed; + being->setWalkSpeed((int) (tilesPerSecond * 32)); + } - gmstatus = msg->readInt16(); - if (gmstatus & 0x80) - dstBeing->setGM(); + // Ignore messages from the server for the local player + if (being == player_node) + continue; - if (msg->getId() == SMSG_PLAYER_UPDATE_1) - { - switch (msg->readInt8()) - { - case 1: - if (dstBeing->getType() != Being::NPC) - dstBeing->setAction(Being::DEAD); - break; - - case 2: - dstBeing->setAction(Being::SIT); - break; - } - } - else if (msg->getId() == SMSG_PLAYER_MOVE) - { - msg->readInt8(); // unknown - } + // If being is a player, and he only moves a little, its ok to be a little out of sync + if (being->getType() == Being::PLAYER && abs(being->getPixelX() - dx) + + abs(being->getPixelY() - dy) < 2 * 32 && + (dx != being->getDestination().x && dy != being->getDestination().y)) + { + being->setDestination(being->getPixelX(),being->getPixelY()); + continue; + } + if (abs(being->getPixelX() - sx) + + abs(being->getPixelY() - sy) > 10 * 32) + { + // Too large a desynchronization. + being->setPosition(sx, sy); + being->setDestination(dx, dy); + } + else if (!(flags & MOVING_POSITION)) + { + being->setDestination(dx, dy); + } + else if (!(flags & MOVING_DESTINATION)) + { + being->adjustCourse(sx, sy); + } + else + { + being->adjustCourse(sx, sy, dx, dy); + } + } +} - msg->readInt8(); // Lv - msg->readInt8(); // unknown +void BeingHandler::handleBeingAttackMessage(MessageIn &msg) +{ + Being *being = beingManager->findBeing(msg.readInt16()); + int direction = msg.readInt8(); + int attackType = msg.readInt8(); - dstBeing->mWalkTime = tick_time; - dstBeing->mFrame = 0; + if (!being) return; - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); - dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); - break; + switch (direction) + { + case DIRECTION_UP: being->setDirection(Being::UP); break; + case DIRECTION_DOWN: being->setDirection(Being::DOWN); break; + case DIRECTION_LEFT: being->setDirection(Being::LEFT); break; + case DIRECTION_RIGHT: being->setDirection(Being::RIGHT); 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. - */ + being->setAction(Being::ATTACK, attackType); +} - 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; +void BeingHandler::handleBeingsDamageMessage(MessageIn &msg) +{ + while (msg.getUnreadLength()) + { + Being *being = beingManager->findBeing(msg.readInt16()); + int damage = msg.readInt16(); + if (being) + { + being->takeDamage(damage); + } + } +} - 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; +void BeingHandler::handleBeingActionChangeMessage(MessageIn &msg) +{ + Being* being = beingManager->findBeing(msg.readInt16()); + Being::Action action = (Being::Action) msg.readInt8(); + if (!being) return; - case 0x0119: - // Change in players' flags - id = msg->readInt32(); - dstBeing = beingManager->findBeing(id); - stunMode = msg->readInt16(); - statusEffects = msg->readInt16(); - statusEffects |= ((Uint32) msg->readInt16()) << 16; - msg->readInt8(); - - if (dstBeing) { - dstBeing->setStunMode(stunMode); - dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); - dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); - } - break; + being->setAction(action); + + if (action == Being::DEAD && being==player_node) + { + static char const *const deadMsg[] = + { + _("You are dead."), + _("We regret to inform you that your character was killed in battle."), + _("You are not that alive anymore."), + _("The cold hands of the grim reaper are grabbing for your soul."), + _("Game Over!"), + _("No, kids. Your character did not really die. It... err... went to a better place."), + _("Your plan of breaking your enemies weapon by bashing it with your throat failed."), + _("I guess this did not run too well."), + _("Do you want your possessions identified?"), // Nethack reference + _("Sadly, no trace of you was ever found..."), // Secret of Mana reference + _("Annihilated."), // Final Fantasy VI reference + _("Looks like you got your head handed to you."), //Earthbound reference + _("You screwed up again, dump your body down the tubes and get you another one.") // Leisure Suit Larry 1 Reference + + }; + std::string message(deadMsg[rand()%13]); + message.append(_(" Press OK to respawn")); + OkDialog *dlg = new OkDialog(_("You died"), message); + dlg->addActionListener(&(Net::GameServer::Player::respawnListener)); + } +} - case 0x0196: - // Status change - status = msg->readInt16(); - id = msg->readInt32(); - flag = msg->readInt8(); // 0: stop, 1: start +void BeingHandler::handleBeingLooksChangeMessage(MessageIn &msg) +{ + Being *being = beingManager->findBeing(msg.readInt16()); + if (!being || being->getType() != Being::PLAYER) return; + Player * player = static_cast< Player * >(being); + handleLooks(player, msg); + if (msg.getUnreadLength()) + { + int style = msg.readInt16(); + int color = msg.readInt16(); + player->setHairStyle(style, color); + player->setGender((Gender)msg.readInt16()); + } +} - dstBeing = beingManager->findBeing(id); - if (dstBeing) - dstBeing->setStatusEffect(status, flag); - break; +void BeingHandler::handleBeingDirChangeMessage(MessageIn &msg) +{ + Being *being = beingManager->findBeing(msg.readInt16()); + if (!being) return; + int data = msg.readInt8(); + switch (data) + { + case DIRECTION_UP: being->setDirection(Being::UP); break; + case DIRECTION_DOWN: being->setDirection(Being::DOWN); break; + case DIRECTION_LEFT: being->setDirection(Being::LEFT); break; + case DIRECTION_RIGHT: being->setDirection(Being::RIGHT); break; } } + diff --git a/src/net/beinghandler.h b/src/net/beinghandler.h index 54b82075..b02728b4 100644 --- a/src/net/beinghandler.h +++ b/src/net/beinghandler.h @@ -1,39 +1,45 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef NET_BEINGHANDLER_H -#define NET_BEINGHANDLER_H +#ifndef _TMW_NET_BEINGHANDLER_H +#define _TMW_NET_BEINGHANDLER_H #include "messagehandler.h" class BeingHandler : public MessageHandler { public: - BeingHandler(bool enableSync); + BeingHandler(); - void handleMessage(MessageIn *msg); + void handleMessage(MessageIn &msg); private: - // Should we honor server "Stop Walking" packets - bool mSync; + void handleBeingAttackMessage(MessageIn &msg); + void handleBeingEnterMessage(MessageIn &msg); + void handleBeingLeaveMessage(MessageIn &msg); + void handleBeingsMoveMessage(MessageIn &msg); + void handleBeingsDamageMessage(MessageIn &msg); + void handleBeingActionChangeMessage(MessageIn &msg); + void handleBeingLooksChangeMessage(MessageIn &msg); + void handleBeingDirChangeMessage(MessageIn &msg); }; #endif diff --git a/src/net/buysellhandler.cpp b/src/net/buysellhandler.cpp index a2442d70..a551f213 100644 --- a/src/net/buysellhandler.cpp +++ b/src/net/buysellhandler.cpp @@ -1,32 +1,32 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "buysellhandler.h" + #include <SDL_types.h> -#include "buysellhandler.h" #include "messagein.h" #include "protocol.h" #include "../beingmanager.h" -#include "../inventory.h" #include "../item.h" #include "../localplayer.h" #include "../npc.h" @@ -35,98 +35,57 @@ #include "../gui/chat.h" #include "../gui/sell.h" -#include "../utils/gettext.h" - extern BuyDialog *buyDialog; -extern Window *buySellDialog; extern SellDialog *sellDialog; +extern Window *buySellDialog; BuySellHandler::BuySellHandler() { static const Uint16 _messages[] = { - SMSG_NPC_BUY_SELL_CHOICE, - SMSG_NPC_BUY, - SMSG_NPC_SELL, - SMSG_NPC_BUY_RESPONSE, - SMSG_NPC_SELL_RESPONSE, + GPMSG_NPC_BUY, + GPMSG_NPC_SELL, 0 }; handledMessages = _messages; } -void BuySellHandler::handleMessage(MessageIn *msg) +void BuySellHandler::handleMessage(MessageIn &msg) { - int n_items; - switch (msg->getId()) + Being *being = beingManager->findBeing(msg.readInt16()); + if (!being || being->getType() != Being::NPC) { - case SMSG_NPC_BUY_SELL_CHOICE: - buyDialog->setVisible(false); - buyDialog->reset(); - sellDialog->setVisible(false); - sellDialog->reset(); - buySellDialog->setVisible(true); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(msg->readInt32())); - break; + return; + } + + current_npc = static_cast< NPC * >(being); - case SMSG_NPC_BUY: - msg->readInt16(); // length - n_items = (msg->getLength() - 4) / 11; + switch (msg.getId()) + { + case GPMSG_NPC_BUY: buyDialog->reset(); - buyDialog->setMoney(player_node->mGp); + buyDialog->setMoney(player_node->getMoney()); buyDialog->setVisible(true); - for (int k = 0; k < n_items; k++) + while (msg.getUnreadLength()) { - Sint32 value = msg->readInt32(); - msg->readInt32(); // DCvalue - msg->readInt8(); // type - Sint16 itemId = msg->readInt16(); - buyDialog->addItem(itemId, value); + int itemId = msg.readInt16(); + int amount = msg.readInt16(); + int value = msg.readInt16(); + buyDialog->addItem(itemId, amount, value); } break; - case SMSG_NPC_SELL: - msg->readInt16(); // length - n_items = (msg->getLength() - 4) / 10; - if (n_items > 0) { - sellDialog->setMoney(player_node->mGp); - sellDialog->reset(); - sellDialog->setVisible(true); - - for (int k = 0; k < n_items; k++) - { - Sint16 index = msg->readInt16(); - Sint32 value = msg->readInt32(); - msg->readInt32(); // OCvalue - - Item *item = player_node->getInventory()->getItem(index); - if (item && !(item->isEquipped())) { - sellDialog->addItem(item, value); - } - } - } - else { - chatWindow->chatLog(_("Nothing to sell"), BY_SERVER); - if (current_npc) current_npc->handleDeath(); - } - break; - - case SMSG_NPC_BUY_RESPONSE: - if (msg->readInt8() == 0) { - chatWindow->chatLog(_("Thanks for buying"), BY_SERVER); - } else { - // Reset player money since buy dialog already assumed purchase - // would go fine - buyDialog->setMoney(player_node->mGp); - chatWindow->chatLog(_("Unable to buy"), BY_SERVER); - } - break; + case GPMSG_NPC_SELL: + sellDialog->setMoney(player_node->getMoney()); + sellDialog->reset(); + sellDialog->setVisible(true); - case SMSG_NPC_SELL_RESPONSE: - if (msg->readInt8() == 0) { - chatWindow->chatLog(_("Thanks for selling"), BY_SERVER); - } else { - chatWindow->chatLog(_("Unable to sell"), BY_SERVER); + while (msg.getUnreadLength()) + { + int itemId = msg.readInt16(); + int amount = msg.readInt16(); + int value = msg.readInt16(); + sellDialog->addItem(itemId, amount, value); } break; } diff --git a/src/net/buysellhandler.h b/src/net/buysellhandler.h index 0ede7b48..719b76d9 100644 --- a/src/net/buysellhandler.h +++ b/src/net/buysellhandler.h @@ -29,7 +29,7 @@ class BuySellHandler : public MessageHandler public: BuySellHandler(); - void handleMessage(MessageIn *msg); + void handleMessage(MessageIn &msg); }; #endif diff --git a/src/net/charserverhandler.cpp b/src/net/charserverhandler.cpp index 8e743bf0..5905dba0 100644 --- a/src/net/charserverhandler.cpp +++ b/src/net/charserverhandler.cpp @@ -1,25 +1,27 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "charserverhandler.h" + +#include "connection.h" #include "messagein.h" #include "protocol.h" @@ -29,107 +31,73 @@ #include "../logindata.h" #include "../main.h" -#include "../gui/char_select.h" #include "../gui/ok_dialog.h" +#include "../gui/char_select.h" -#include "../utils/gettext.h" -#include "../utils/stringutils.h" +extern Net::Connection *gameServerConnection; +extern Net::Connection *chatServerConnection; CharServerHandler::CharServerHandler(): mCharCreateDialog(0) { static const Uint16 _messages[] = { - SMSG_CONNECTION_PROBLEM, - 0x006b, - 0x006c, - 0x006d, - 0x006e, - 0x006f, - 0x0070, - 0x0071, + APMSG_CHAR_CREATE_RESPONSE, + APMSG_CHAR_DELETE_RESPONSE, + APMSG_CHAR_INFO, + APMSG_CHAR_SELECT_RESPONSE, 0 }; handledMessages = _messages; } -void CharServerHandler::handleMessage(MessageIn *msg) +void CharServerHandler::handleMessage(MessageIn &msg) { - int slot, flags, code; + int slot; LocalPlayer *tempPlayer; - logger->log("CharServerHandler: Packet ID: %x, Length: %d", - msg->getId(), msg->getLength()); - switch (msg->getId()) + switch (msg.getId()) { - case SMSG_CONNECTION_PROBLEM: - code = msg->readInt8(); - logger->log("Connection problem: %i", code); - - switch (code) { - case 0: - errorMessage = _("Authentication failed"); - break; - case 1: - errorMessage = _("Map server(s) offline"); - break; - case 2: - errorMessage = _("This account is already logged in"); - break; - case 3: - errorMessage = _("Speed hack detected"); - break; - case 8: - errorMessage = _("Duplicated login"); - break; - default: - errorMessage = _("Unknown connection error"); - break; - } - state = ERROR_STATE; + case APMSG_CHAR_CREATE_RESPONSE: + handleCharCreateResponse(msg); break; - case 0x006b: - msg->skip(2); // Length word - flags = msg->readInt32(); // Aethyra extensions flags - logger->log("Server flags are: %x", flags); - msg->skip(16); // Unused - - // Derive number of characters from message length - n_character = (msg->getLength() - 24) / 106; - - for (int i = 0; i < n_character; i++) + case APMSG_CHAR_DELETE_RESPONSE: + { + int errMsg = msg.readInt8(); + // Character deletion successful + if (errMsg == ERRMSG_OK) { - tempPlayer = readPlayerData(*msg, slot); - mCharInfo->select(slot); - mCharInfo->setEntry(tempPlayer); - logger->log("CharServer: Player: %s (%d)", - tempPlayer->getName().c_str(), slot); + delete mCharInfo->getEntry(); + mCharInfo->setEntry(0); + mCharInfo->unlock(); + new OkDialog("Info", "Player deleted"); } - - state = CHAR_SELECT_STATE; - break; - - case 0x006c: - switch (msg->readInt8()) { - case 0: - errorMessage = _("Access denied"); - break; - case 1: - errorMessage = _("Cannot use this ID"); - break; - default: - errorMessage = _("Unknown failure to select character"); - break; + // Character deletion failed + else + { + std::string message = ""; + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + message = "Not logged in"; + break; + case ERRMSG_INVALID_ARGUMENT: + message = "Selection out of range"; + break; + default: + message = "Unknown error"; + } + mCharInfo->unlock(); + new OkDialog("Error", message); } - mCharInfo->unlock(); + } break; - case 0x006d: - tempPlayer = readPlayerData(*msg, slot); + case APMSG_CHAR_INFO: + tempPlayer = readPlayerData(msg, slot); mCharInfo->unlock(); mCharInfo->select(slot); mCharInfo->setEntry(tempPlayer); - n_character++; // Close the character create dialog if (mCharCreateDialog) @@ -139,97 +107,121 @@ void CharServerHandler::handleMessage(MessageIn *msg) } break; - case 0x006e: - new OkDialog(_("Error"), _("Failed to create character. Most likely" - " the name is already taken.")); - - if (mCharCreateDialog) - mCharCreateDialog->unlock(); + case APMSG_CHAR_SELECT_RESPONSE: + handleCharSelectResponse(msg); break; + } +} - case 0x006f: - delete mCharInfo->getEntry(); - mCharInfo->setEntry(0); - mCharInfo->unlock(); - n_character--; - new OkDialog(_("Info"), _("Player deleted")); - break; +void CharServerHandler::handleCharCreateResponse(MessageIn &msg) +{ + int errMsg = msg.readInt8(); - case 0x0070: - mCharInfo->unlock(); - new OkDialog(_("Error"), _("Failed to delete character.")); - break; + // Character creation failed + if (errMsg != ERRMSG_OK) + { + std::string message = ""; + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + message = "Not logged in"; + break; + case CREATE_TOO_MUCH_CHARACTERS: + message = "No empty slot"; + break; + case ERRMSG_INVALID_ARGUMENT: + message = "Invalid name"; + break; + case CREATE_EXISTS_NAME: + message = "Character's name already exists"; + break; + case CREATE_INVALID_HAIRSTYLE: + message = "Invalid hairstyle"; + break; + case CREATE_INVALID_HAIRCOLOR: + message = "Invalid hair color"; + break; + case CREATE_INVALID_GENDER: + message = "Invalid gender"; + break; + case CREATE_RAW_STATS_TOO_HIGH: + message = "Character's stats are too high"; + break; + case CREATE_RAW_STATS_TOO_LOW: + message = "Character's stats are too low"; + break; + case CREATE_RAW_STATS_EQUAL_TO_ZERO: + message = "One stat is zero"; + break; + default: + message = "Unknown error"; + break; + } + new OkDialog("Error", message); + } - case 0x0071: - player_node = mCharInfo->getEntry(); - slot = mCharInfo->getPos(); - msg->skip(4); // CharID, must be the same as player_node->charID - map_path = msg->readString(16); - mLoginData->hostname = ipToString(msg->readInt32()); - mLoginData->port = msg->readInt16(); - mCharInfo->unlock(); - mCharInfo->select(0); - // Clear unselected players infos - do + if (mCharCreateDialog) + mCharCreateDialog->unlock(); +} + +void CharServerHandler::handleCharSelectResponse(MessageIn &msg) +{ + int errMsg = msg.readInt8(); + + if (errMsg == ERRMSG_OK) + { + token = msg.readString(32); + std::string gameServer = msg.readString(); + unsigned short gameServerPort = msg.readInt16(); + std::string chatServer = msg.readString(); + unsigned short chatServerPort = msg.readInt16(); + + logger->log("Game server: %s:%d", gameServer.c_str(), gameServerPort); + logger->log("Chat server: %s:%d", chatServer.c_str(), chatServerPort); + + gameServerConnection->connect(gameServer, gameServerPort); + chatServerConnection->connect(chatServer, chatServerPort); + + // Keep the selected character and delete the others + player_node = mCharInfo->getEntry(); + int slot = mCharInfo->getPos(); + mCharInfo->unlock(); + mCharInfo->select(0); + + do { + LocalPlayer *tmp = mCharInfo->getEntry(); + if (tmp != player_node) { - LocalPlayer *tmp = mCharInfo->getEntry(); - if (tmp != player_node) - { - delete tmp; - mCharInfo->setEntry(0); - } - mCharInfo->next(); - } while (mCharInfo->getPos()); + delete tmp; + mCharInfo->setEntry(0); + } + mCharInfo->next(); + } while (mCharInfo->getPos()); + mCharInfo->select(slot); - mCharInfo->select(slot); - state = CONNECTING_STATE; - break; + mCharInfo->clear(); //player_node will be deleted by ~Game + + state = STATE_CONNECT_GAME; } } -LocalPlayer *CharServerHandler::readPlayerData(MessageIn &msg, int &slot) +LocalPlayer* CharServerHandler::readPlayerData(MessageIn &msg, int &slot) { - LocalPlayer *tempPlayer = new LocalPlayer(mLoginData->account_ID, 0, NULL); - tempPlayer->setGender( - (mLoginData->sex == 0) ? GENDER_FEMALE : GENDER_MALE); - - tempPlayer->mCharId = msg.readInt32(); - tempPlayer->setXp(msg.readInt32()); - tempPlayer->mGp = msg.readInt32(); - tempPlayer->mJobXp = msg.readInt32(); - tempPlayer->mJobLevel = msg.readInt32(); - 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 - msg.skip(2); // unknown - tempPlayer->mHp = msg.readInt16(); - tempPlayer->mMaxHp = msg.readInt16(); - tempPlayer->mMp = msg.readInt16(); - tempPlayer->mMaxMp = msg.readInt16(); - msg.readInt16(); // speed - msg.readInt16(); // class - int hairStyle = msg.readInt16(); - Uint16 weapon = msg.readInt16(); - tempPlayer->setSprite(Being::WEAPON_SPRITE, weapon); - tempPlayer->mLevel = msg.readInt16(); - msg.readInt16(); // skill point - tempPlayer->setSprite(Being::BOTTOMCLOTHES_SPRITE, msg.readInt16()); // head bottom - tempPlayer->setSprite(Being::SHIELD_SPRITE, msg.readInt16()); - tempPlayer->setSprite(Being::HAT_SPRITE, msg.readInt16()); // head option top - tempPlayer->setSprite(Being::TOPCLOTHES_SPRITE, msg.readInt16()); // head option mid - int hairColor = msg.readInt16(); - tempPlayer->setHairStyle(hairStyle, hairColor); - tempPlayer->setSprite(Being::MISC2_SPRITE, msg.readInt16()); - tempPlayer->setName(msg.readString(24)); - for (int i = 0; i < 6; i++) { - tempPlayer->mAttr[i] = msg.readInt8(); - } + LocalPlayer *tempPlayer = new LocalPlayer; slot = msg.readInt8(); // character slot - msg.readInt8(); // unknown + tempPlayer->setName(msg.readString()); + tempPlayer->setGender(msg.readInt8() == GENDER_MALE ? GENDER_MALE : GENDER_FEMALE); + int hs = msg.readInt8(), hc = msg.readInt8(); + tempPlayer->setHairStyle(hs, hc); + tempPlayer->setLevel(msg.readInt16()); + tempPlayer->setCharacterPoints(msg.readInt16()); + tempPlayer->setCorrectionPoints(msg.readInt16()); + tempPlayer->setMoney(msg.readInt32()); + + for (int i = 0; i < 7; i++) + { + tempPlayer->setAttributeBase(i, msg.readInt8()); + } return tempPlayer; } diff --git a/src/net/charserverhandler.h b/src/net/charserverhandler.h index 37b378f2..08ba5102 100644 --- a/src/net/charserverhandler.h +++ b/src/net/charserverhandler.h @@ -1,26 +1,26 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef NET_CHARSERVERHANDLER_H -#define NET_CHARSERVERHANDLER_H +#ifndef _TMW_NET_CHARSERVERHANDLER_H +#define _TMW_NET_CHARSERVERHANDLER_H #include "messagehandler.h" @@ -31,20 +31,21 @@ class LocalPlayer; class LoginData; /** - * Deals with incoming messages from the character server. + * Deals with incoming messages related to character selection. */ class CharServerHandler : public MessageHandler { public: CharServerHandler(); - void handleMessage(MessageIn *msg); + void + handleMessage(MessageIn &msg); - void setCharInfo(LockedArray<LocalPlayer*> *charInfo) - { mCharInfo = charInfo; } - - void setLoginData(LoginData *loginData) - { mLoginData = loginData; } + void + setCharInfo(LockedArray<LocalPlayer*> *charInfo) + { + mCharInfo = charInfo; + } /** * Sets the character create dialog. The handler will clean up this @@ -55,11 +56,17 @@ class CharServerHandler : public MessageHandler { mCharCreateDialog = window; } protected: - LoginData *mLoginData; + void + handleCharCreateResponse(MessageIn &msg); + + void + handleCharSelectResponse(MessageIn &msg); + LockedArray<LocalPlayer*> *mCharInfo; CharCreateDialog *mCharCreateDialog; - LocalPlayer* readPlayerData(MessageIn &msg, int &slot); + LocalPlayer* + readPlayerData(MessageIn &msg, int &slot); }; #endif diff --git a/src/net/chathandler.cpp b/src/net/chathandler.cpp index a3ccc4fb..90af899a 100644 --- a/src/net/chathandler.cpp +++ b/src/net/chathandler.cpp @@ -1,175 +1,285 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "chathandler.h" + #include <SDL_types.h> #include <string> +#include <iostream> -#include "chathandler.h" #include "messagein.h" #include "protocol.h" #include "../being.h" #include "../beingmanager.h" #include "../game.h" -#include "../player_relations.h" +#include "../channel.h" +#include "../channelmanager.h" #include "../gui/chat.h" - -#include "../utils/gettext.h" -#include "../utils/stringutils.h" +#include "../gui/guildwindow.h" extern Being *player_node; -#define SERVER_NAME "Server" - ChatHandler::ChatHandler() { static const Uint16 _messages[] = { - SMSG_BEING_CHAT, - SMSG_PLAYER_CHAT, - SMSG_WHISPER, - SMSG_WHISPER_RESPONSE, - SMSG_GM_CHAT, - SMSG_WHO_ANSWER, - 0x10c, // MVP + GPMSG_SAY, + CPMSG_ENTER_CHANNEL_RESPONSE, + CPMSG_LIST_CHANNELS_RESPONSE, + CPMSG_PUBMSG, + CPMSG_ANNOUNCEMENT, + CPMSG_PRIVMSG, + CPMSG_QUIT_CHANNEL_RESPONSE, + CPMSG_LIST_CHANNELUSERS_RESPONSE, + CPMSG_CHANNEL_EVENT, 0 }; handledMessages = _messages; } -void ChatHandler::handleMessage(MessageIn *msg) +void ChatHandler::handleMessage(MessageIn &msg) { - Being *being; - std::string chatMsg; - std::string nick; - Sint16 chatMsgLength; - - switch (msg->getId()) + switch (msg.getId()) { - case SMSG_WHISPER_RESPONSE: - switch (msg->readInt8()) - { - case 0x00: - // comment out since we'll local echo in chat.cpp instead, then only report failures - //chatWindow->chatLog("Whisper sent", BY_SERVER); - break; - case 0x01: - chatWindow->chatLog(_("Whisper could not be sent, user is offline"), BY_SERVER); - break; - case 0x02: - chatWindow->chatLog(_("Whisper could not be sent, ignored by user"), BY_SERVER); - break; - } + case GPMSG_SAY: + handleGameChatMessage(msg); break; - // Received whisper - case SMSG_WHISPER: - chatMsgLength = msg->readInt16() - 28; - nick = msg->readString(24); + case CPMSG_ENTER_CHANNEL_RESPONSE: + handleEnterChannelResponse(msg); + break; - if (chatMsgLength <= 0) - break; + case CPMSG_LIST_CHANNELS_RESPONSE: + handleListChannelsResponse(msg); + break; - chatMsg = msg->readString(chatMsgLength); - if (nick != SERVER_NAME) - chatMsg = nick + " : " + chatMsg; + case CPMSG_PRIVMSG: + handlePrivateMessage(msg); + break; - if (nick == SERVER_NAME) - chatWindow->chatLog(chatMsg, BY_SERVER); - else { - if (player_relations.hasPermission(nick, PlayerRelation::WHISPER)) - chatWindow->chatLog(chatMsg, ACT_WHISPER); - } + case CPMSG_ANNOUNCEMENT: + handleAnnouncement(msg); + break; + + case CPMSG_PUBMSG: + handleChatMessage(msg); + break; + case CPMSG_QUIT_CHANNEL_RESPONSE: + handleQuitChannelResponse(msg); break; - // Received speech from being - case SMSG_BEING_CHAT: { - chatMsgLength = msg->readInt16() - 8; - being = beingManager->findBeing(msg->readInt32()); + case CPMSG_LIST_CHANNELUSERS_RESPONSE: + handleListChannelUsersResponse(msg); + break; + + case CPMSG_CHANNEL_EVENT: + handleChannelEvent(msg); + } +} + +void ChatHandler::handleGameChatMessage(MessageIn &msg) +{ + short id = msg.readInt16(); + std::string chatMsg = msg.readString(); + + if (id == 0) + { + chatWindow->chatLog(chatMsg, BY_SERVER); + return; + } + + Being *being = beingManager->findBeing(id); + + if (being) + { + chatWindow->chatLog(being->getName() + " : " + chatMsg, + being == player_node ? BY_PLAYER : BY_OTHER, "General"); + being->setSpeech(chatMsg, SPEECH_TIME); + } + else + { + chatWindow->chatLog("Unknown : " + chatMsg, BY_OTHER, "General"); + } +} - if (!being || chatMsgLength <= 0) +void ChatHandler::handleEnterChannelResponse(MessageIn &msg) +{ + if(msg.readInt8() == ERRMSG_OK) + { + short channelId = msg.readInt16(); + std::string channelName = msg.readString(); + std::string announcement = msg.readString(); + Channel *channel = new Channel(channelId, channelName, announcement); + channelManager->addChannel(channel); + chatWindow->createNewChannelTab(channelName); + chatWindow->chatLog("Topic: " + announcement, BY_CHANNEL, channelName); + + std::string user; + std::string userModes; + chatWindow->chatLog("Players in this channel:", BY_CHANNEL, channelName); + while(msg.getUnreadLength()) + { + user = msg.readString(); + if (user == "") + return; + userModes = msg.readString(); + if (userModes.find('o') != std::string::npos) { - break; + user = "@" + user; } + chatWindow->chatLog(user, BY_CHANNEL, channelName); + } + + } + else + { + chatWindow->chatLog("Error joining channel", BY_SERVER); + } +} - chatMsg = msg->readString(chatMsgLength); +void ChatHandler::handleListChannelsResponse(MessageIn &msg) +{ + chatWindow->chatLog("Listing Channels", BY_SERVER); + while(msg.getUnreadLength()) + { + std::string channelName = msg.readString(); + if (channelName == "") + return; + std::ostringstream numUsers; + numUsers << msg.readInt16(); + channelName += " - "; + channelName += numUsers.str(); + chatWindow->chatLog(channelName, BY_SERVER); + } + chatWindow->chatLog("End of channel list", BY_SERVER); +} - std::string::size_type pos = chatMsg.find(" : ", 0); - std::string sender_name = ((pos == std::string::npos) - ? "" - : chatMsg.substr(0, pos)); +void ChatHandler::handlePrivateMessage(MessageIn &msg) +{ + std::string userNick = msg.readString(); + std::string chatMsg = msg.readString(); - // We use getIgnorePlayer instead of ignoringPlayer here because ignorePlayer' side - // effects are triggered right below for Being::IGNORE_SPEECH_FLOAT. - if (player_relations.checkPermissionSilently(sender_name, PlayerRelation::SPEECH_LOG)) - chatWindow->chatLog(chatMsg, BY_OTHER); + if (!chatWindow->tabExists(userNick)) + { + chatWindow->createNewChannelTab(userNick); - chatMsg.erase(0, pos + 3); - trim(chatMsg); + } + chatWindow->chatLog(userNick + ": " + chatMsg, BY_OTHER, userNick); +} + +void ChatHandler::handleAnnouncement(MessageIn &msg) +{ + std::string chatMsg = msg.readString(); + chatWindow->chatLog(chatMsg, BY_GM); +} - if (player_relations.hasPermission(sender_name, PlayerRelation::SPEECH_FLOAT)) - being->setSpeech(chatMsg, SPEECH_TIME); +void ChatHandler::handleChatMessage(MessageIn &msg) +{ + short channelId = msg.readInt16(); + std::string userNick = msg.readString(); + std::string chatMsg = msg.readString(); + + chatWindow->sendToChannel(channelId, userNick, chatMsg); +} + +void ChatHandler::handleQuitChannelResponse(MessageIn &msg) +{ + if(msg.readInt8() == ERRMSG_OK) + { + short channelId = msg.readInt16(); + // remove the chat tab + chatWindow->removeChannel(channelId); + } +} + +void ChatHandler::handleListChannelUsersResponse(MessageIn &msg) +{ + std::string channel = msg.readString(); + std::string userNick; + std::string userModes; + chatWindow->chatLog("Players in this channel:", BY_CHANNEL, channel); + while(msg.getUnreadLength()) + { + userNick = msg.readString(); + if (userNick == "") + { break; } + userModes = msg.readString(); + if (userModes.find('o') != std::string::npos) + { + userNick = "@" + userNick; + } + chatWindow->chatLog(userNick, BY_CHANNEL, channel); + } +} - case SMSG_PLAYER_CHAT: - case SMSG_GM_CHAT: { - chatMsgLength = msg->readInt16() - 4; +void ChatHandler::handleChannelEvent(MessageIn &msg) +{ + short channelId = msg.readInt16(); + char eventId = msg.readInt8(); + std::string line = msg.readString(); + Channel *channel = channelManager->findById(channelId); - if (chatMsgLength <= 0) - { + if(channel) + { + switch(eventId) + { + case CHAT_EVENT_NEW_PLAYER: + line += " entered the channel."; break; - } - - chatMsg = msg->readString(chatMsgLength); - std::string::size_type pos = chatMsg.find(" : ", 0); - if (msg->getId() == SMSG_PLAYER_CHAT) - { - chatWindow->chatLog(chatMsg, BY_PLAYER); + case CHAT_EVENT_LEAVING_PLAYER: + line += " left the channel."; + break; - if (pos != std::string::npos) - chatMsg.erase(0, pos + 3); + case CHAT_EVENT_TOPIC_CHANGE: + line = "Topic: " + line; + break; - trim(chatMsg); + case CHAT_EVENT_MODE_CHANGE: + { + int first = line.find(":"); + int second = line.find(":", first+1); + std::string user1 = line.substr(0, first); + std::string user2 = line.substr(first+1, second); + std::string mode = line.substr(second+1, line.length()); + line = user1 + " has set mode " + mode + " on user " + user2; + } break; - player_node->setSpeech(chatMsg, SPEECH_TIME); - } - else + case CHAT_EVENT_KICKED_PLAYER: { - chatWindow->chatLog(chatMsg, BY_GM); - } - break; - } + int first = line.find(":"); + std::string user1 = line.substr(0, first); + std::string user2 = line.substr(first+1, line.length()); + line = user1 + " has kicked " + user2; + } break; - case SMSG_WHO_ANSWER: - chatWindow->chatLog("Online users: " + toString(msg->readInt32()), - BY_SERVER); - break; + default: + line = "Unknown channel event."; + } - case 0x010c: - // Display MVP player - msg->readInt32(); // id - chatWindow->chatLog("MVP player", BY_SERVER); - break; + chatWindow->chatLog(line, BY_CHANNEL, channel->getName()); } } + diff --git a/src/net/chathandler.h b/src/net/chathandler.h index ff649205..a9e9bd99 100644 --- a/src/net/chathandler.h +++ b/src/net/chathandler.h @@ -28,8 +28,57 @@ class ChatHandler : public MessageHandler { public: ChatHandler(); - - void handleMessage(MessageIn *msg); + + /** + * Handle the given message appropriately. + */ + void handleMessage(MessageIn &msg); + + private: + /** + * Handle chat messages sent from the game server. + */ + void handleGameChatMessage(MessageIn &msg); + + /** + * Handle channel entry responses. + */ + void handleEnterChannelResponse(MessageIn &msg); + + /** + * Handle list channels responses. + */ + void handleListChannelsResponse(MessageIn &msg); + + /** + * Handle private messages. + */ + void handlePrivateMessage(MessageIn &msg); + + /** + * Handle announcements. + */ + void handleAnnouncement(MessageIn &msg); + + /** + * Handle chat messages. + */ + void handleChatMessage(MessageIn &msg); + + /** + * Handle quit channel responses. + */ + void handleQuitChannelResponse(MessageIn &msg); + + /** + * Handle list channel users responses. + */ + void handleListChannelUsersResponse(MessageIn &msg); + + /** + * Handle channel events. + */ + void handleChannelEvent(MessageIn &msg); }; #endif diff --git a/src/net/chatserver/chatserver.cpp b/src/net/chatserver/chatserver.cpp new file mode 100644 index 00000000..94e36b94 --- /dev/null +++ b/src/net/chatserver/chatserver.cpp @@ -0,0 +1,146 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "chatserver.h" + +#include "internal.h" + +#include "../connection.h" +#include "../messageout.h" +#include "../protocol.h" + +using Net::ChatServer::connection; + +void Net::ChatServer::connect(Net::Connection *connection, + const std::string &token) +{ + Net::ChatServer::connection = connection; + + MessageOut msg(PCMSG_CONNECT); + + msg.writeString(token, 32); + + connection->send(msg); +} + +void Net::ChatServer::logout() +{ + MessageOut msg(PCMSG_DISCONNECT); + + connection->send(msg); +} + +void Net::ChatServer::chat(short channel, const std::string &text) +{ + MessageOut msg(PCMSG_CHAT); + + msg.writeString(text); + msg.writeInt16(channel); + + connection->send(msg); +} + +void Net::ChatServer::announce(const std::string &text) +{ + MessageOut msg(PCMSG_ANNOUNCE); + + msg.writeString(text); + + connection->send(msg); +} + +void Net::ChatServer::privMsg(const std::string &recipient, + const std::string &text) +{ + MessageOut msg(PCMSG_PRIVMSG); + + msg.writeString(recipient); + msg.writeString(text); + + connection->send(msg); +} + +void Net::ChatServer::enterChannel(const std::string &channel, const std::string &password) +{ + MessageOut msg(PCMSG_ENTER_CHANNEL); + + msg.writeString(channel); + msg.writeString(password); + + connection->send(msg); +} + +void Net::ChatServer::quitChannel(short channel) +{ + MessageOut msg(PCMSG_QUIT_CHANNEL); + + msg.writeInt16(channel); + + connection->send(msg); +} + +void Net::ChatServer::getChannelList() +{ + MessageOut msg(PCMSG_LIST_CHANNELS); + + connection->send(msg); +} + +void Net::ChatServer::getUserList(const std::string &channel) +{ + MessageOut msg(PCMSG_LIST_CHANNELUSERS); + + msg.writeString(channel); + + connection->send(msg); +} + +void Net::ChatServer::setChannelTopic(short channel, const std::string &topic) +{ + MessageOut msg(PCMSG_TOPIC_CHANGE); + + msg.writeInt16(channel); + msg.writeString(topic); + + connection->send(msg); +} + +void Net::ChatServer::setUserMode(short channel, const std::string &user, + unsigned char mode) +{ + MessageOut msg(PCMSG_USER_MODE); + + msg.writeInt16(channel); + msg.writeString(user); + msg.writeInt8(mode); + + connection->send(msg); +} + +void Net::ChatServer::kickUser(short channel, const std::string &user) +{ + MessageOut msg(PCMSG_KICK_USER); + + msg.writeInt16(channel); + msg.writeString(user); + + connection->send(msg); +} diff --git a/src/net/chatserver/chatserver.h b/src/net/chatserver/chatserver.h new file mode 100644 index 00000000..1129239a --- /dev/null +++ b/src/net/chatserver/chatserver.h @@ -0,0 +1,60 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_CHATSERVER_CHATSERVER_H +#define _TMW_NET_CHATSERVER_CHATSERVER_H + +#include <iosfwd> + +namespace Net +{ + class Connection; + + namespace ChatServer + { + void connect(Net::Connection *connection, const std::string &token); + + void logout(); + + void chat(short channel, const std::string &text); + + void announce(const std::string &text); + + void privMsg(const std::string &recipient, const std::string &text); + + void enterChannel(const std::string &channel, const std::string &password); + + void quitChannel(short channel); + + void getChannelList(); + + void getUserList(const std::string &channel); + + void setChannelTopic(short channel, const std::string &topic); + + void setUserMode(short channel, const std::string &user, unsigned char mode); + + void kickUser(short channel, const std::string &user); + + } +} + +#endif diff --git a/src/net/chatserver/guild.cpp b/src/net/chatserver/guild.cpp new file mode 100644 index 00000000..042ff013 --- /dev/null +++ b/src/net/chatserver/guild.cpp @@ -0,0 +1,95 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "guild.h" + +#include "internal.h" + +#include "../connection.h" +#include "../messageout.h" +#include "../protocol.h" + +#include "../../log.h" + +void Net::ChatServer::Guild::createGuild(const std::string &name) +{ + logger->log("Sending PCMSG_GUILD_CREATE"); + MessageOut msg(PCMSG_GUILD_CREATE); + + msg.writeString(name); + + Net::ChatServer::connection->send(msg); +} + +void Net::ChatServer::Guild::invitePlayer(const std::string &name, short guildId) +{ + logger->log("Sending PCMSG_GUILD_INVITE"); + MessageOut msg(PCMSG_GUILD_INVITE); + + msg.writeInt16(guildId); + msg.writeString(name); + + Net::ChatServer::connection->send(msg); +} + +void Net::ChatServer::Guild::acceptInvite(const std::string &name) +{ + logger->log("Sending PCMSG_GUILD_ACCEPT"); + MessageOut msg(PCMSG_GUILD_ACCEPT); + + msg.writeString(name); + + Net::ChatServer::connection->send(msg); +} + +void Net::ChatServer::Guild::getGuildMembers(short guildId) +{ + logger->log("Sending PCMSG_GUILD_GET_MEMBERS"); + MessageOut msg(PCMSG_GUILD_GET_MEMBERS); + + msg.writeInt16(guildId); + + Net::ChatServer::connection->send(msg); +} + +void Net::ChatServer::Guild::promoteMember(const std::string &name, + short guildId, short level) +{ + logger->log("Sending PCMSG_GUILD_PROMOTE_MEMBER"); + MessageOut msg(PCMSG_GUILD_PROMOTE_MEMBER); + + msg.writeInt16(guildId); + msg.writeString(name); + msg.writeInt8(level); + + Net::ChatServer::connection->send(msg); +} + +void Net::ChatServer::Guild::quitGuild(short guildId) +{ + logger->log("Sending PCMSG_GUILD_QUIT"); + MessageOut msg(PCMSG_GUILD_QUIT); + + msg.writeInt16(guildId); + + Net::ChatServer::connection->send(msg); +} diff --git a/src/net/chatserver/guild.h b/src/net/chatserver/guild.h new file mode 100644 index 00000000..6c35be9f --- /dev/null +++ b/src/net/chatserver/guild.h @@ -0,0 +1,69 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _TMW_NET_CHATSERVER_GUILD_H +#define _TMW_NET_CHATSERVER_GUILD_H + +#include <iosfwd> + +namespace Net +{ + namespace ChatServer + { + namespace Guild + { + /** + * Create guild. + */ + void createGuild(const std::string &name); + + /** + * Invite a player to your guild. + */ + void invitePlayer(const std::string &name, short guildId); + + /** + * Accept an invite another player has sent to join their guild. + */ + void acceptInvite(const std::string &name); + + /** + * Get a list of members in a guild. + */ + void getGuildMembers(short guildId); + + /** + * Promote guild member + */ + void promoteMember(const std::string &name, short guildId, + short level); + + /** + * Quit guild. + */ + void quitGuild(short guildId); + } + } +} + +#endif + diff --git a/src/net/chatserver/internal.cpp b/src/net/chatserver/internal.cpp new file mode 100644 index 00000000..0822d93d --- /dev/null +++ b/src/net/chatserver/internal.cpp @@ -0,0 +1,32 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal.h" + +namespace Net +{ + class Connection; + + namespace ChatServer + { + Connection *connection = 0; + } +} diff --git a/src/net/chatserver/internal.h b/src/net/chatserver/internal.h new file mode 100644 index 00000000..b0f137c5 --- /dev/null +++ b/src/net/chatserver/internal.h @@ -0,0 +1,35 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_CHATSERVER_INTERNAL_H +#define _TMW_NET_CHATSERVER_INTERNAL_H + +namespace Net +{ + class Connection; + + namespace ChatServer + { + extern Connection *connection; + } +} + +#endif diff --git a/src/net/chatserver/party.cpp b/src/net/chatserver/party.cpp new file mode 100644 index 00000000..56eb57d2 --- /dev/null +++ b/src/net/chatserver/party.cpp @@ -0,0 +1,70 @@ +/* + * The Mana World + * Copyright 2008 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "party.h" + +#include "internal.h" + +#include "../connection.h" +#include "../messageout.h" +#include "../protocol.h" + +#include "../../log.h" + +void Net::ChatServer::Party::invitePlayer(const std::string &name) +{ + logger->log("Sending PCMSG_PARTY_INVITE"); + MessageOut msg(PCMSG_PARTY_INVITE); + + msg.writeString(name); + + Net::ChatServer::connection->send(msg); +} + +void Net::ChatServer::Party::acceptInvite(const std::string &name) +{ + logger->log("Sending PCMSG_PARTY_ACCEPT_INVITE"); + MessageOut msg(PCMSG_PARTY_ACCEPT_INVITE); + + msg.writeString(name); + + Net::ChatServer::connection->send(msg); +} + +void Net::ChatServer::Party::getPartyMembers() +{ + logger->log("Sending PCMSG_PARTY_GET_MEMBERS"); +// MessageOut msg(PCMSG_GUILD_GET_MEMBERS); + +// msg.writeInt16(guildId); + +// Net::ChatServer::connection->send(msg); +} + +void Net::ChatServer::Party::quitParty() +{ + logger->log("Sending PCMSG_PARTY_QUIT"); + MessageOut msg(PCMSG_PARTY_QUIT); + + Net::ChatServer::connection->send(msg); +} + diff --git a/src/net/chatserver/party.h b/src/net/chatserver/party.h new file mode 100644 index 00000000..c1febd66 --- /dev/null +++ b/src/net/chatserver/party.h @@ -0,0 +1,57 @@ +/* + * The Mana World + * Copyright 2008 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef _TMW_NET_CHATSERVER_PARTY_H +#define _TMW_NET_CHATSERVER_PARTY_H + +#include <iosfwd> + +namespace Net +{ + namespace ChatServer + { + namespace Party + { + /** + * Invite a player to the party. + */ + void invitePlayer(const std::string &name); + + /** + * Accept an invite another player has sent to join their party + */ + void acceptInvite(const std::string &name); + + /** + * Get a list of party members + */ + void getPartyMembers(); + + /** + * Leave party + */ + void quitParty(); + } + } +} + +#endif diff --git a/src/net/connection.cpp b/src/net/connection.cpp new file mode 100644 index 00000000..7d3c2328 --- /dev/null +++ b/src/net/connection.cpp @@ -0,0 +1,103 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "connection.h" + +#include <string> + +#include "internal.h" +#include "messageout.h" + +#include "../log.h" + +Net::Connection::Connection(ENetHost *client): + mConnection(0), mClient(client) +{ + Net::connections++; +} + +Net::Connection::~Connection() +{ + Net::connections--; +} + +bool Net::Connection::connect(const std::string &address, short port) +{ + logger->log("Net::Connection::connect(%s, %i)", address.c_str(), port); + + if (address.empty()) + { + logger->log("Net::Connection::connect() got empty address!"); + mState = NET_ERROR; + return false; + } + + ENetAddress enetAddress; + + enet_address_set_host(&enetAddress, address.c_str()); + enetAddress.port = port; + + // Initiate the connection, allocating channel 0. + mConnection = enet_host_connect(mClient, &enetAddress, 1); + + if (!mConnection) + { + logger->log("Unable to initiate connection to the server."); + mState = NET_ERROR; + return false; + } + + return true; +} + +void Net::Connection::disconnect() +{ + if (!mConnection) + return; + + enet_peer_disconnect(mConnection, 0); + enet_host_flush(mClient); + enet_peer_reset(mConnection); + + mConnection = 0; +} + +bool Net::Connection::isConnected() +{ + return bool (mConnection) ? + (mConnection->state == ENET_PEER_STATE_CONNECTED) : false; +} + +void Net::Connection::send(const MessageOut &msg) +{ + if (!isConnected()) + { + logger->log("Warning: cannot send message to not connected server!"); + return; + } + + //logger->log("Sending message of size %d...", msg.getDataSize()); + + ENetPacket *packet = enet_packet_create(msg.getData(), + msg.getDataSize(), + ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(mConnection, 0, packet); +} diff --git a/src/net/connection.h b/src/net/connection.h new file mode 100644 index 00000000..4ab3d24d --- /dev/null +++ b/src/net/connection.h @@ -0,0 +1,80 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_CONNECTION_H +#define _TMW_NET_CONNECTION_H + +#include <iosfwd> + +#include <enet/enet.h> + +class MessageOut; + +namespace Net +{ + /** + * \ingroup Network + */ + class Connection + { + public: + enum State { + OK, + NET_ERROR + }; + + ~Connection(); + + /** + * Connects to the given server with the specified address and port. + * This method is non-blocking, use isConnected to check whether the + * server is connected. + */ + bool connect(const std::string &address, short port); + + /** + * Disconnects from the given server. + */ + void disconnect(); + + State getState() { return mState; } + + /** + * Returns whether the server is connected. + */ + bool isConnected(); + + /** + * Sends a message. + */ + void send(const MessageOut &msg); + + private: + friend Connection *Net::getConnection(); + Connection(ENetHost *client); + + ENetPeer *mConnection; + ENetHost *mClient; + State mState; + }; +} + +#endif diff --git a/src/net/ea/beinghandler.cpp b/src/net/ea/beinghandler.cpp new file mode 100644 index 00000000..1edc6079 --- /dev/null +++ b/src/net/ea/beinghandler.cpp @@ -0,0 +1,542 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <iostream> +#include <SDL_types.h> + +#include "beinghandler.h" +#include "../messagein.h" +#include "protocol.h" + +#include "../../being.h" +#include "../../beingmanager.h" +#include "../../effectmanager.h" +#include "../../game.h" +#include "../../localplayer.h" +#include "../../log.h" +#include "../../npc.h" +#include "../../player_relations.h" + +const int EMOTION_TIME = 150; /**< Duration of emotion icon */ + +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_SELFEFFECT, + SMSG_BEING_EMOTION, + SMSG_BEING_CHANGE_LOOKS, + SMSG_BEING_CHANGE_LOOKS2, + SMSG_BEING_NAME_RESPONSE, + SMSG_PLAYER_UPDATE_1, + SMSG_PLAYER_UPDATE_2, + SMSG_PLAYER_MOVE, + SMSG_PLAYER_STOP, + SMSG_PLAYER_MOVE_TO_ATTACK, + 0x0119, + 0x0196, + 0 + }; + handledMessages = _messages; +} + +void BeingHandler::handleMessage(MessageIn &msg) +{ + Uint32 id; + Uint16 job, speed; + Uint16 headTop, headMid, headBottom; + Uint16 shoes, gloves; + Uint16 weapon, shield; + Uint16 gmstatus; + Sint16 param1; + int stunMode; + Uint32 statusEffects; + Sint8 type; + Uint16 status; + Being *srcBeing, *dstBeing; + int hairStyle, hairColor, flag; + + switch (msg.getId()) + { + case SMSG_BEING_VISIBLE: + case SMSG_BEING_MOVE: + // Information about a being in range + id = msg.readInt32(); + speed = msg.readInt16(); + stunMode = msg.readInt16(); // opt1 + statusEffects = msg.readInt16(); // opt2 + statusEffects |= ((Uint32)msg.readInt16()) << 16; // option + job = msg.readInt16(); // class + + dstBeing = beingManager->findBeing(id); + + if (!dstBeing) + { + // Being with id >= 110000000 and job 0 are better + // known as ghosts, so don't create those. + if (job == 0 && id >= 110000000) + { + break; + } + + dstBeing = beingManager->createBeing(id, job); + } + else if (msg.getId() == 0x0078) + { + dstBeing->clearPath(); + dstBeing->mFrame = 0; + dstBeing->mWalkTime = tick_time; + dstBeing->setAction(Being::STAND); + } + + + // Prevent division by 0 when calculating frame + if (speed == 0) { speed = 150; } + + dstBeing->setWalkSpeed(speed); + dstBeing->mJob = job; + hairStyle = msg.readInt16(); + dstBeing->setSprite(Being::WEAPON_SPRITE, msg.readInt16()); + headBottom = msg.readInt16(); + + if (msg.getId() == SMSG_BEING_MOVE) + { + msg.readInt32(); // server tick + } + + dstBeing->setSprite(Being::SHIELD_SPRITE, msg.readInt16()); + headTop = msg.readInt16(); + headMid = msg.readInt16(); + hairColor = msg.readInt16(); + shoes = msg.readInt16(); // clothes color - "abused" as shoes + gloves = msg.readInt16(); // head dir - "abused" as gloves + msg.readInt16(); // guild + msg.readInt16(); // unknown + msg.readInt16(); // unknown + msg.readInt16(); // manner + dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3 + msg.readInt8(); // karma + dstBeing->setGender( + (msg.readInt8() == 0) ? GENDER_FEMALE : GENDER_MALE); + + // 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::SHOE_SPRITE, shoes); + dstBeing->setSprite(Being::GLOVES_SPRITE, gloves); + dstBeing->setHairStyle(hairStyle, hairColor); + + if (msg.getId() == SMSG_BEING_MOVE) + { + Uint16 srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY); + dstBeing->setAction(Being::STAND); + dstBeing->mX = srcX; + dstBeing->mY = srcY; + dstBeing->setDestination(dstX, dstY); + } + else + { + Uint8 dir; + msg.readCoordinates(dstBeing->mX, dstBeing->mY, dir); + dstBeing->setDirection(dir); + } + + msg.readInt8(); // unknown + msg.readInt8(); // unknown + msg.readInt8(); // unknown / sit + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); + dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); + 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()); + + if (!dstBeing) + break; + + // If this is player's current target, clear it. + if (dstBeing == player_node->getTarget()) + player_node->stopAttack(); + + if (dstBeing == current_npc) + current_npc->handleDeath(); + + if (msg.readInt8() == 1) + dstBeing->setAction(Being::DEAD); + else + beingManager->destroyBeing(dstBeing); + + break; + + case SMSG_BEING_ACTION: + srcBeing = beingManager->findBeing(msg.readInt32()); + dstBeing = beingManager->findBeing(msg.readInt32()); + msg.readInt32(); // server tick + msg.readInt32(); // src speed + msg.readInt32(); // dst speed + param1 = msg.readInt16(); + msg.readInt16(); // param 2 + type = msg.readInt8(); + msg.readInt16(); // param 3 + + switch (type) + { + case 0x0a: // Critical Damage + if (dstBeing) + dstBeing->showCrit(); + case 0x00: // Damage + if (dstBeing) + dstBeing->takeDamage(param1); + if (srcBeing) + srcBeing->handleAttack(dstBeing, param1); + break; + + case 0x02: // Sit + if (srcBeing) + { + srcBeing->mFrame = 0; + srcBeing->setAction(Being::SIT); + } + break; + + case 0x03: // Stand up + if (srcBeing) + { + srcBeing->mFrame = 0; + srcBeing->setAction(Being::STAND); + } + break; + } + break; + + case SMSG_BEING_SELFEFFECT: { + id = (Uint32)msg.readInt32(); + if (!beingManager->findBeing(id)) + break; + + int effectType = msg.readInt32(); + Being* being = beingManager->findBeing(id); + + effectManager->trigger(effectType, being); + + break; + } + + case SMSG_BEING_EMOTION: + if (!(dstBeing = beingManager->findBeing(msg.readInt32()))) + { + break; + } + + if (player_relations.hasPermission(dstBeing, PlayerRelation::EMOTE)) + dstBeing->setEmote(msg.readInt8(), EMOTION_TIME); + + break; + + case SMSG_BEING_CHANGE_LOOKS: + case SMSG_BEING_CHANGE_LOOKS2: + { + /* + * 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. + * + * If type = 2, then the first 16 bit value is the weapon ID, + * and the second 16 bit value is the shield ID. If no + * shield is equipped, or type is not 2, then the second + * 16 bit value will be 0. + */ + + if (!(dstBeing = beingManager->findBeing(msg.readInt32()))) + { + break; + } + + int type = msg.readInt8(); + int id = 0; + int id2 = 0; + + if (msg.getId() == SMSG_BEING_CHANGE_LOOKS) { + id = msg.readInt8(); + } else { // SMSG_BEING_CHANGE_LOOKS2 + id = msg.readInt16(); + id2 = msg.readInt16(); + } + + switch (type) { + case 1: // eAthena LOOK_HAIR + dstBeing->setHairStyle(id, -1); + break; + case 2: // Weapon ID in id, Shield ID in id2 + dstBeing->setSprite(Being::WEAPON_SPRITE, id); + dstBeing->setSprite(Being::SHIELD_SPRITE, id2); + break; + case 3: // Change lower headgear for eAthena, pants for us + dstBeing->setSprite(Being::BOTTOMCLOTHES_SPRITE, id); + break; + case 4: // Change upper headgear for eAthena, hat for us + dstBeing->setSprite(Being::HAT_SPRITE, id); + break; + case 5: // Change middle headgear for eathena, armor for us + dstBeing->setSprite(Being::TOPCLOTHES_SPRITE, id); + break; + 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); + break; + } + } + break; + + case SMSG_BEING_NAME_RESPONSE: + if ((dstBeing = beingManager->findBeing(msg.readInt32()))) + { + dstBeing->setName(msg.readString(24)); + } + break; + + case SMSG_PLAYER_UPDATE_1: + case SMSG_PLAYER_UPDATE_2: + case SMSG_PLAYER_MOVE: + // An update about a player, potentially including movement. + id = msg.readInt32(); + speed = msg.readInt16(); + stunMode = msg.readInt16(); // opt1; Aethyra use this as cape + statusEffects = msg.readInt16(); // opt2; Aethyra use this as misc1 + statusEffects |= ((Uint32) msg.readInt16()) + << 16; // status.options; Aethyra uses this as misc2 + job = msg.readInt16(); + + dstBeing = beingManager->findBeing(id); + + if (!dstBeing) + { + dstBeing = beingManager->createBeing(id, job); + } + + dstBeing->setWalkSpeed(speed); + dstBeing->mJob = job; + hairStyle = msg.readInt16(); + weapon = msg.readInt16(); + shield = msg.readInt16(); + headBottom = msg.readInt16(); + + if (msg.getId() == SMSG_PLAYER_MOVE) + { + msg.readInt32(); // server tick + } + + headTop = msg.readInt16(); + headMid = msg.readInt16(); + hairColor = msg.readInt16(); + msg.readInt16(); // clothes color - Aethyra-"abused" as shoes, we ignore it + msg.readInt16(); // head dir - Aethyra-"abused" as gloves, we ignore it + msg.readInt32(); // guild + msg.readInt16(); // emblem + msg.readInt16(); // manner + dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3 + msg.readInt8(); // karma + dstBeing->setGender( + (msg.readInt8() == 0) ? GENDER_FEMALE : GENDER_MALE); + + // Set these after the gender, as the sprites may be gender-specific + 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::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) + { + Uint16 srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY); + dstBeing->mX = srcX; + dstBeing->mY = srcY; + dstBeing->setDestination(dstX, dstY); + } + else + { + Uint8 dir; + msg.readCoordinates(dstBeing->mX, dstBeing->mY, dir); + dstBeing->setDirection(dir); + } + + gmstatus = msg.readInt16(); + if (gmstatus & 0x80) + dstBeing->setGM(); + + if (msg.getId() == SMSG_PLAYER_UPDATE_1) + { + switch (msg.readInt8()) + { + case 1: + if (dstBeing->getType() != Being::NPC) + dstBeing->setAction(Being::DEAD); + break; + + case 2: + dstBeing->setAction(Being::SIT); + break; + } + } + else if (msg.getId() == SMSG_PLAYER_MOVE) + { + msg.readInt8(); // unknown + } + + msg.readInt8(); // Lv + msg.readInt8(); // unknown + + dstBeing->mWalkTime = tick_time; + dstBeing->mFrame = 0; + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); + dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); + 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' flags + id = msg.readInt32(); + dstBeing = beingManager->findBeing(id); + stunMode = msg.readInt16(); + statusEffects = msg.readInt16(); + statusEffects |= ((Uint32) msg.readInt16()) << 16; + msg.readInt8(); + + if (dstBeing) { + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); + dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); + } + break; + + case 0x0196: + // Status change + status = msg.readInt16(); + id = msg.readInt32(); + flag = msg.readInt8(); // 0: stop, 1: start + + dstBeing = beingManager->findBeing(id); + if (dstBeing) + dstBeing->setStatusEffect(status, flag); + break; + } +} diff --git a/src/net/ea/beinghandler.h b/src/net/ea/beinghandler.h new file mode 100644 index 00000000..16a7c8d6 --- /dev/null +++ b/src/net/ea/beinghandler.h @@ -0,0 +1,39 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NET_BEINGHANDLER_H +#define NET_BEINGHANDLER_H + +#include "../messagehandler.h" + +class BeingHandler : public MessageHandler +{ + public: + BeingHandler(bool enableSync); + + void handleMessage(MessageIn &msg); + + private: + // Should we honor server "Stop Walking" packets + bool mSync; +}; + +#endif diff --git a/src/net/ea/buysellhandler.cpp b/src/net/ea/buysellhandler.cpp new file mode 100644 index 00000000..480c71b8 --- /dev/null +++ b/src/net/ea/buysellhandler.cpp @@ -0,0 +1,133 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <SDL_types.h> + +#include "buysellhandler.h" +#include "../messagein.h" +#include "protocol.h" + +#include "../../beingmanager.h" +#include "../../inventory.h" +#include "../../item.h" +#include "../../localplayer.h" +#include "../../npc.h" + +#include "../../gui/buy.h" +#include "../../gui/chat.h" +#include "../../gui/sell.h" + +#include "../../utils/gettext.h" + +extern BuyDialog *buyDialog; +extern Window *buySellDialog; +extern SellDialog *sellDialog; + +BuySellHandler::BuySellHandler() +{ + static const Uint16 _messages[] = { + SMSG_NPC_BUY_SELL_CHOICE, + SMSG_NPC_BUY, + SMSG_NPC_SELL, + SMSG_NPC_BUY_RESPONSE, + SMSG_NPC_SELL_RESPONSE, + 0 + }; + handledMessages = _messages; +} + +void BuySellHandler::handleMessage(MessageIn &msg) +{ + int n_items; + switch (msg.getId()) + { + case SMSG_NPC_BUY_SELL_CHOICE: + buyDialog->setVisible(false); + buyDialog->reset(); + sellDialog->setVisible(false); + sellDialog->reset(); + buySellDialog->setVisible(true); + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(msg.readInt32())); + break; + + case SMSG_NPC_BUY: + msg.readInt16(); // length + n_items = (msg.getLength() - 4) / 11; + buyDialog->reset(); + buyDialog->setMoney(player_node->getMoney()); + buyDialog->setVisible(true); + + for (int k = 0; k < n_items; k++) + { + Sint32 value = msg.readInt32(); + msg.readInt32(); // DCvalue + msg.readInt8(); // type + Sint16 itemId = msg.readInt16(); + buyDialog->addItem(itemId, 0, value); + } + break; + + case SMSG_NPC_SELL: + msg.readInt16(); // length + n_items = (msg.getLength() - 4) / 10; + if (n_items > 0) { + sellDialog->setMoney(player_node->getMoney()); + sellDialog->reset(); + sellDialog->setVisible(true); + + for (int k = 0; k < n_items; k++) + { + Sint16 index = msg.readInt16(); + Sint32 value = msg.readInt32(); + msg.readInt32(); // OCvalue + + Item *item = player_node->getInventory()->getItem(index); + if (item && !(item->isEquipped())) { + sellDialog->addItem(item, value); + } + } + } + else { + chatWindow->chatLog(_("Nothing to sell"), BY_SERVER); + if (current_npc) current_npc->handleDeath(); + } + break; + + case SMSG_NPC_BUY_RESPONSE: + if (msg.readInt8() == 0) { + chatWindow->chatLog(_("Thanks for buying"), BY_SERVER); + } else { + // Reset player money since buy dialog already assumed purchase + // would go fine + buyDialog->setMoney(player_node->getMoney()); + chatWindow->chatLog(_("Unable to buy"), BY_SERVER); + } + break; + + case SMSG_NPC_SELL_RESPONSE: + if (msg.readInt8() == 0) { + chatWindow->chatLog(_("Thanks for selling"), BY_SERVER); + } else { + chatWindow->chatLog(_("Unable to sell"), BY_SERVER); + } + break; + } +} diff --git a/src/net/ea/buysellhandler.h b/src/net/ea/buysellhandler.h new file mode 100644 index 00000000..5bf58d8e --- /dev/null +++ b/src/net/ea/buysellhandler.h @@ -0,0 +1,35 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NET_BUYSELLHANDLER_H +#define NET_BUYSELLHANDLER_H + +#include "../messagehandler.h" + +class BuySellHandler : public MessageHandler +{ + public: + BuySellHandler(); + + void handleMessage(MessageIn &msg); +}; + +#endif diff --git a/src/net/ea/charserverhandler.cpp b/src/net/ea/charserverhandler.cpp new file mode 100644 index 00000000..0fef3de7 --- /dev/null +++ b/src/net/ea/charserverhandler.cpp @@ -0,0 +1,235 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "charserverhandler.h" +#include "../messagein.h" +#include "protocol.h" + +#include "../../game.h" +#include "../../localplayer.h" +#include "../../log.h" +#include "../../logindata.h" +#include "../../main.h" + +#include "../../gui/char_select.h" +#include "../../gui/ok_dialog.h" + +#include "../../utils/gettext.h" +#include "../../utils/stringutils.h" + +CharServerHandler::CharServerHandler(): + mCharCreateDialog(0) +{ + static const Uint16 _messages[] = { + SMSG_CONNECTION_PROBLEM, + 0x006b, + 0x006c, + 0x006d, + 0x006e, + 0x006f, + 0x0070, + 0x0071, + 0 + }; + handledMessages = _messages; +} + +void CharServerHandler::handleMessage(MessageIn &msg) +{ + int slot, flags, code; + LocalPlayer *tempPlayer; + + logger->log("CharServerHandler: Packet ID: %x, Length: %d", + msg.getId(), msg.getLength()); + switch (msg.getId()) + { + case SMSG_CONNECTION_PROBLEM: + code = msg.readInt8(); + logger->log("Connection problem: %i", code); + + switch (code) { + case 0: + errorMessage = _("Authentication failed"); + break; + case 1: + errorMessage = _("Map server(s) offline"); + break; + case 2: + errorMessage = _("This account is already logged in"); + break; + case 3: + errorMessage = _("Speed hack detected"); + break; + case 8: + errorMessage = _("Duplicated login"); + break; + default: + errorMessage = _("Unknown connection error"); + break; + } + state = STATE_ERROR; + break; + + case 0x006b: + msg.skip(2); // Length word + flags = msg.readInt32(); // Aethyra extensions flags + logger->log("Server flags are: %x", flags); + msg.skip(16); // Unused + + // Derive number of characters from message length + n_character = (msg.getLength() - 24) / 106; + + for (int i = 0; i < n_character; i++) + { + tempPlayer = readPlayerData(msg, slot); + mCharInfo->select(slot); + mCharInfo->setEntry(tempPlayer); + logger->log("CharServer: Player: %s (%d)", + tempPlayer->getName().c_str(), slot); + } + + state = STATE_CHAR_SELECT; + break; + + case 0x006c: + switch (msg.readInt8()) { + case 0: + errorMessage = _("Access denied"); + break; + case 1: + errorMessage = _("Cannot use this ID"); + break; + default: + errorMessage = _("Unknown failure to select character"); + break; + } + mCharInfo->unlock(); + break; + + case 0x006d: + tempPlayer = readPlayerData(msg, slot); + mCharInfo->unlock(); + mCharInfo->select(slot); + mCharInfo->setEntry(tempPlayer); + n_character++; + + // Close the character create dialog + if (mCharCreateDialog) + { + mCharCreateDialog->scheduleDelete(); + mCharCreateDialog = 0; + } + break; + + case 0x006e: + new OkDialog(_("Error"), _("Failed to create character. Most likely" + " the name is already taken.")); + + if (mCharCreateDialog) + mCharCreateDialog->unlock(); + break; + + case 0x006f: + delete mCharInfo->getEntry(); + mCharInfo->setEntry(0); + mCharInfo->unlock(); + n_character--; + new OkDialog(_("Info"), _("Player deleted")); + break; + + case 0x0070: + mCharInfo->unlock(); + new OkDialog(_("Error"), _("Failed to delete character.")); + break; + + case 0x0071: + player_node = mCharInfo->getEntry(); + slot = mCharInfo->getPos(); + msg.skip(4); // CharID, must be the same as player_node->charID + map_path = msg.readString(16); + mLoginData->hostname = ipToString(msg.readInt32()); + mLoginData->port = msg.readInt16(); + mCharInfo->unlock(); + mCharInfo->select(0); + // Clear unselected players infos + do + { + LocalPlayer *tmp = mCharInfo->getEntry(); + if (tmp != player_node) + { + delete tmp; + mCharInfo->setEntry(0); + } + mCharInfo->next(); + } while (mCharInfo->getPos()); + + mCharInfo->select(slot); + state = STATE_CONNECTING; + break; + } +} + +LocalPlayer *CharServerHandler::readPlayerData(MessageIn &msg, int &slot) +{ + LocalPlayer *tempPlayer = new LocalPlayer(mLoginData->account_ID, 0, NULL); + tempPlayer->setGender( + (mLoginData->sex == 0) ? GENDER_FEMALE : GENDER_MALE); + + tempPlayer->mCharId = msg.readInt32(); + tempPlayer->setXp(msg.readInt32()); + tempPlayer->setMoney(msg.readInt32()); + tempPlayer->mJobXp = msg.readInt32(); + tempPlayer->mJobLevel = msg.readInt32(); + 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 + msg.skip(2); // unknown + tempPlayer->setHp(msg.readInt16()); + tempPlayer->setMaxHp(msg.readInt16()); + tempPlayer->mMp = msg.readInt16(); + tempPlayer->mMaxMp = msg.readInt16(); + msg.readInt16(); // speed + msg.readInt16(); // class + int hairStyle = msg.readInt16(); + Uint16 weapon = msg.readInt16(); + tempPlayer->setSprite(Being::WEAPON_SPRITE, weapon); + tempPlayer->setLevel(msg.readInt16()); + msg.readInt16(); // skill point + tempPlayer->setSprite(Being::BOTTOMCLOTHES_SPRITE, msg.readInt16()); // head bottom + tempPlayer->setSprite(Being::SHIELD_SPRITE, msg.readInt16()); + tempPlayer->setSprite(Being::HAT_SPRITE, msg.readInt16()); // head option top + tempPlayer->setSprite(Being::TOPCLOTHES_SPRITE, msg.readInt16()); // head option mid + int hairColor = msg.readInt16(); + tempPlayer->setHairStyle(hairStyle, hairColor); + tempPlayer->setSprite(Being::MISC2_SPRITE, msg.readInt16()); + tempPlayer->setName(msg.readString(24)); + for (int i = 0; i < 6; i++) { + tempPlayer->mAttr[i] = msg.readInt8(); + } + slot = msg.readInt8(); // character slot + msg.readInt8(); // unknown + + return tempPlayer; +} diff --git a/src/net/ea/charserverhandler.h b/src/net/ea/charserverhandler.h new file mode 100644 index 00000000..237f5e49 --- /dev/null +++ b/src/net/ea/charserverhandler.h @@ -0,0 +1,65 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NET_CHARSERVERHANDLER_H +#define NET_CHARSERVERHANDLER_H + +#include "../messagehandler.h" + +#include "../../lockedarray.h" + +class CharCreateDialog; +class LocalPlayer; +class LoginData; + +/** + * Deals with incoming messages from the character server. + */ +class CharServerHandler : public MessageHandler +{ + public: + CharServerHandler(); + + void handleMessage(MessageIn &msg); + + void setCharInfo(LockedArray<LocalPlayer*> *charInfo) + { mCharInfo = charInfo; } + + void setLoginData(LoginData *loginData) + { mLoginData = loginData; } + + /** + * Sets the character create dialog. The handler will clean up this + * dialog when a new character is succesfully created, and will unlock + * the dialog when a new character failed to be created. + */ + void setCharCreateDialog(CharCreateDialog *window) + { mCharCreateDialog = window; } + + protected: + LoginData *mLoginData; + LockedArray<LocalPlayer*> *mCharInfo; + CharCreateDialog *mCharCreateDialog; + + LocalPlayer* readPlayerData(MessageIn &msg, int &slot); +}; + +#endif diff --git a/src/net/ea/chathandler.cpp b/src/net/ea/chathandler.cpp new file mode 100644 index 00000000..0293f987 --- /dev/null +++ b/src/net/ea/chathandler.cpp @@ -0,0 +1,175 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <SDL_types.h> +#include <string> + +#include "chathandler.h" +#include "../messagein.h" +#include "protocol.h" + +#include "../../being.h" +#include "../../beingmanager.h" +#include "../../game.h" +#include "../../player_relations.h" + +#include "../../gui/chat.h" + +#include "../../utils/gettext.h" +#include "../../utils/stringutils.h" + +extern Being *player_node; + +#define SERVER_NAME "Server" + +ChatHandler::ChatHandler() +{ + static const Uint16 _messages[] = { + SMSG_BEING_CHAT, + SMSG_PLAYER_CHAT, + SMSG_WHISPER, + SMSG_WHISPER_RESPONSE, + SMSG_GM_CHAT, + SMSG_WHO_ANSWER, + 0x10c, // MVP + 0 + }; + handledMessages = _messages; +} + +void ChatHandler::handleMessage(MessageIn &msg) +{ + Being *being; + std::string chatMsg; + std::string nick; + Sint16 chatMsgLength; + + switch (msg.getId()) + { + case SMSG_WHISPER_RESPONSE: + switch (msg.readInt8()) + { + case 0x00: + // comment out since we'll local echo in chat.cpp instead, then only report failures + //chatWindow->chatLog("Whisper sent", BY_SERVER); + break; + case 0x01: + chatWindow->chatLog(_("Whisper could not be sent, user is offline"), BY_SERVER); + break; + case 0x02: + chatWindow->chatLog(_("Whisper could not be sent, ignored by user"), BY_SERVER); + break; + } + break; + + // Received whisper + case SMSG_WHISPER: + chatMsgLength = msg.readInt16() - 28; + nick = msg.readString(24); + + if (chatMsgLength <= 0) + break; + + chatMsg = msg.readString(chatMsgLength); + if (nick != SERVER_NAME) + chatMsg = nick + " : " + chatMsg; + + if (nick == SERVER_NAME) + chatWindow->chatLog(chatMsg, BY_SERVER); + else { + if (player_relations.hasPermission(nick, PlayerRelation::WHISPER)) + chatWindow->chatLog(chatMsg, ACT_WHISPER); + } + + break; + + // Received speech from being + case SMSG_BEING_CHAT: { + chatMsgLength = msg.readInt16() - 8; + being = beingManager->findBeing(msg.readInt32()); + + if (!being || chatMsgLength <= 0) + { + break; + } + + chatMsg = msg.readString(chatMsgLength); + + std::string::size_type pos = chatMsg.find(" : ", 0); + std::string sender_name = ((pos == std::string::npos) + ? "" + : chatMsg.substr(0, pos)); + + // We use getIgnorePlayer instead of ignoringPlayer here because ignorePlayer' side + // effects are triggered right below for Being::IGNORE_SPEECH_FLOAT. + if (player_relations.checkPermissionSilently(sender_name, PlayerRelation::SPEECH_LOG)) + chatWindow->chatLog(chatMsg, BY_OTHER); + + chatMsg.erase(0, pos + 3); + trim(chatMsg); + + if (player_relations.hasPermission(sender_name, PlayerRelation::SPEECH_FLOAT)) + being->setSpeech(chatMsg, SPEECH_TIME); + break; + } + + case SMSG_PLAYER_CHAT: + case SMSG_GM_CHAT: { + chatMsgLength = msg.readInt16() - 4; + + if (chatMsgLength <= 0) + { + break; + } + + chatMsg = msg.readString(chatMsgLength); + std::string::size_type pos = chatMsg.find(" : ", 0); + + if (msg.getId() == SMSG_PLAYER_CHAT) + { + chatWindow->chatLog(chatMsg, BY_PLAYER); + + if (pos != std::string::npos) + chatMsg.erase(0, pos + 3); + + trim(chatMsg); + + player_node->setSpeech(chatMsg, SPEECH_TIME); + } + else + { + chatWindow->chatLog(chatMsg, BY_GM); + } + break; + } + + case SMSG_WHO_ANSWER: + chatWindow->chatLog("Online users: " + toString(msg.readInt32()), + BY_SERVER); + break; + + case 0x010c: + // Display MVP player + msg.readInt32(); // id + chatWindow->chatLog("MVP player", BY_SERVER); + break; + } +} diff --git a/src/net/ea/chathandler.h b/src/net/ea/chathandler.h new file mode 100644 index 00000000..8207b1d5 --- /dev/null +++ b/src/net/ea/chathandler.h @@ -0,0 +1,35 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NET_CHATHANDLER_H +#define NET_CHATHANDLER_H + +#include "../messagehandler.h" + +class ChatHandler : public MessageHandler +{ + public: + ChatHandler(); + + void handleMessage(MessageIn &msg); +}; + +#endif diff --git a/src/net/equipmenthandler.cpp b/src/net/ea/equipmenthandler.cpp index 9a3c396a..19063daf 100644 --- a/src/net/equipmenthandler.cpp +++ b/src/net/ea/equipmenthandler.cpp @@ -20,18 +20,18 @@ */ #include "equipmenthandler.h" -#include "messagein.h" +#include "../messagein.h" #include "protocol.h" -#include "../equipment.h" -#include "../inventory.h" -#include "../item.h" -#include "../localplayer.h" -#include "../log.h" +#include "../../equipment.h" +#include "../../inventory.h" +#include "../../item.h" +#include "../../localplayer.h" +#include "../../log.h" -#include "../gui/chat.h" +#include "../../gui/chat.h" -#include "../utils/gettext.h" +#include "../../utils/gettext.h" EquipmentHandler::EquipmentHandler() { @@ -46,7 +46,7 @@ EquipmentHandler::EquipmentHandler() handledMessages = _messages; } -void EquipmentHandler::handleMessage(MessageIn *msg) +void EquipmentHandler::handleMessage(MessageIn &msg) { Sint32 itemCount; Sint16 index, equipPoint, itemId; @@ -55,23 +55,23 @@ void EquipmentHandler::handleMessage(MessageIn *msg) Item *item; Inventory *inventory = player_node->getInventory(); - switch (msg->getId()) + switch (msg.getId()) { case SMSG_PLAYER_EQUIPMENT: - msg->readInt16(); // length - itemCount = (msg->getLength() - 4) / 20; + msg.readInt16(); // length + itemCount = (msg.getLength() - 4) / 20; for (int loop = 0; loop < itemCount; loop++) { - index = msg->readInt16(); - itemId = msg->readInt16(); - msg->readInt8(); // type - msg->readInt8(); // identify flag - msg->readInt16(); // equip type - equipPoint = msg->readInt16(); - msg->readInt8(); // attribute - msg->readInt8(); // refine - msg->skip(8); // card + index = msg.readInt16(); + itemId = msg.readInt16(); + msg.readInt8(); // type + msg.readInt8(); // identify flag + msg.readInt16(); // equip type + equipPoint = msg.readInt16(); + msg.readInt8(); // attribute + msg.readInt8(); // refine + msg.skip(8); // card inventory->setItem(index, itemId, 1, true); @@ -91,9 +91,9 @@ void EquipmentHandler::handleMessage(MessageIn *msg) break; case SMSG_PLAYER_EQUIP: - index = msg->readInt16(); - equipPoint = msg->readInt16(); - type = msg->readInt8(); + index = msg.readInt16(); + equipPoint = msg.readInt16(); + type = msg.readInt8(); logger->log("Equipping: %i %i %i", index, equipPoint, type); @@ -131,9 +131,9 @@ void EquipmentHandler::handleMessage(MessageIn *msg) break; case SMSG_PLAYER_UNEQUIP: - index = msg->readInt16(); - equipPoint = msg->readInt16(); - type = msg->readInt8(); + index = msg.readInt16(); + equipPoint = msg.readInt16(); + type = msg.readInt8(); if (!type) { chatWindow->chatLog(_("Unable to unequip."), BY_SERVER); @@ -169,11 +169,11 @@ void EquipmentHandler::handleMessage(MessageIn *msg) break; case SMSG_PLAYER_ATTACK_RANGE: - player_node->setAttackRange(msg->readInt16()); + player_node->setAttackRange(msg.readInt16()); break; case SMSG_PLAYER_ARROW_EQUIP: - index = msg->readInt16(); + index = msg.readInt16(); if (index <= 1) break; diff --git a/src/net/equipmenthandler.h b/src/net/ea/equipmenthandler.h index c66d7932..fe4a7ecc 100644 --- a/src/net/equipmenthandler.h +++ b/src/net/ea/equipmenthandler.h @@ -22,14 +22,14 @@ #ifndef NET_EQUIPMENTHANDLER_H #define NET_EQUIPMENTHANDLER_H -#include "messagehandler.h" +#include "../messagehandler.h" class EquipmentHandler : public MessageHandler { public: EquipmentHandler(); - void handleMessage(MessageIn *msg); + void handleMessage(MessageIn &msg); }; #endif diff --git a/src/net/ea/inventoryhandler.cpp b/src/net/ea/inventoryhandler.cpp new file mode 100644 index 00000000..71eee291 --- /dev/null +++ b/src/net/ea/inventoryhandler.cpp @@ -0,0 +1,227 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <SDL_types.h> + +#include "inventoryhandler.h" +#include "../messagein.h" +#include "protocol.h" + +#include "../../inventory.h" +#include "../../item.h" +#include "../../itemshortcut.h" +#include "../../localplayer.h" +#include "../../log.h" + +#include "../../gui/chat.h" + +#include "../../resources/iteminfo.h" + +#include "../../utils/gettext.h" +#include "../../utils/strprintf.h" +#include "../../utils/stringutils.h" + +InventoryHandler::InventoryHandler() +{ + static const Uint16 _messages[] = { + SMSG_PLAYER_INVENTORY, + SMSG_PLAYER_INVENTORY_ADD, + SMSG_PLAYER_INVENTORY_REMOVE, + SMSG_PLAYER_INVENTORY_USE, + SMSG_ITEM_USE_RESPONSE, + SMSG_PLAYER_STORAGE_ITEMS, + SMSG_PLAYER_STORAGE_EQUIP, + SMSG_PLAYER_STORAGE_STATUS, + SMSG_PLAYER_STORAGE_ADD, + SMSG_PLAYER_STORAGE_REMOVE, + SMSG_PLAYER_STORAGE_CLOSE, + 0 + }; + handledMessages = _messages; +} + +void InventoryHandler::handleMessage(MessageIn &msg) +{ + Sint32 number; + Sint16 index, amount, itemId, equipType, arrow; + Sint16 identified, cards[4], itemType; + Inventory *inventory = player_node->getInventory(); + Inventory *storage = player_node->getStorage(); + + switch (msg.getId()) + { + case SMSG_PLAYER_INVENTORY: + case SMSG_PLAYER_STORAGE_ITEMS: + case SMSG_PLAYER_STORAGE_EQUIP: + switch (msg.getId()) { + case SMSG_PLAYER_INVENTORY: + // Clear inventory - this will be a complete refresh + inventory->clear(); + break; + case SMSG_PLAYER_STORAGE_ITEMS: + /* + * This packet will always be followed by a + * SMSG_PLAYER_STORAGE_EQUIP packet. The two packets + * together comprise a complete refresh of storage, so + * clear storage here + */ + storage->clear(); + logger->log("Received SMSG_PLAYER_STORAGE_ITEMS"); + break; + default: + logger->log("Received SMSG_PLAYER_STORAGE_EQUIP"); + break; + } + msg.readInt16(); // length + number = (msg.getLength() - 4) / 18; + + for (int loop = 0; loop < number; loop++) { + index = msg.readInt16(); + itemId = msg.readInt16(); + itemType = msg.readInt8(); + identified = msg.readInt8(); + if (msg.getId() == SMSG_PLAYER_STORAGE_EQUIP) { + amount = 1; + msg.readInt16(); // Equip Point? + } else { + amount = msg.readInt16(); + } + arrow = msg.readInt16(); + if (msg.getId() == SMSG_PLAYER_STORAGE_EQUIP) { + msg.readInt8(); // Attribute (broken) + msg.readInt8(); // Refine level + } + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + + if (msg.getId() == SMSG_PLAYER_INVENTORY) { + inventory->setItem(index, itemId, amount, false); + + // Trick because arrows are not considered equipment + if (arrow & 0x8000) { + if (Item *item = inventory->getItem(index)) + item->setEquipment(true); + } + } else { + logger->log("Index:%d, ID:%d, Type:%d, Identified:%d, Qty:%d, Cards:%d, %d, %d, %d", + index, itemId, itemType, identified, amount, cards[0], cards[1], cards[2], cards[3]); + storage->setItem(index, itemId, amount, false); + } + } + break; + + case SMSG_PLAYER_INVENTORY_ADD: + index = msg.readInt16(); + amount = msg.readInt16(); + itemId = msg.readInt16(); + identified = msg.readInt8(); + msg.readInt8(); // attribute + msg.readInt8(); // refine + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + equipType = msg.readInt16(); + itemType = msg.readInt8(); + + if (msg.readInt8() > 0) { + chatWindow->chatLog(_("Unable to pick up item"), BY_SERVER); + } else { + const ItemInfo &itemInfo = ItemDB::get(itemId); + const std::string amountStr = + (amount > 1) ? toString(amount) : "a"; + chatWindow->chatLog(strprintf(_("You picked up %s %s"), + amountStr.c_str(), itemInfo.getName().c_str()), BY_SERVER); + + if (Item *item = inventory->getItem(index)) { + item->setId(itemId); + item->increaseQuantity(amount); + } else { + inventory->setItem(index, itemId, amount, equipType != 0); + } + } + break; + + case SMSG_PLAYER_INVENTORY_REMOVE: + index = msg.readInt16(); + amount = msg.readInt16(); + if (Item *item = inventory->getItem(index)) { + item->increaseQuantity(-amount); + if (item->getQuantity() == 0) + inventory->removeItemAt(index); + } + break; + + case SMSG_PLAYER_INVENTORY_USE: + index = msg.readInt16(); + msg.readInt16(); // item id + msg.readInt32(); // id + amount = msg.readInt16(); + msg.readInt8(); // type + + if (Item *item = inventory->getItem(index)) + item->setQuantity(amount); + break; + + case SMSG_ITEM_USE_RESPONSE: + index = msg.readInt16(); + amount = msg.readInt16(); + + if (msg.readInt8() == 0) { + chatWindow->chatLog(_("Failed to use item"), BY_SERVER); + } else { + if (Item *item = inventory->getItem(index)) + item->setQuantity(amount); + } + break; + + case SMSG_PLAYER_STORAGE_STATUS: + /* + * Basic slots used vs total slots info + * We don't really need this information, but this is + * the closest we get to an "Open Storage" packet + * from the server. It always comes after the two + * SMSG_PLAYER_STORAGE_... packets that update + * storage contents. + */ + logger->log("Received SMSG_PLAYER_STORAGE_STATUS"); + player_node->setInStorage(true); + break; + + case SMSG_PLAYER_STORAGE_ADD: + /* + * Move an item into storage + */ + break; + + case SMSG_PLAYER_STORAGE_REMOVE: + /* + * Move an item out of storage + */ + break; + + case SMSG_PLAYER_STORAGE_CLOSE: + /* + * Storage access has been closed + */ + player_node->setInStorage(false); + logger->log("Received SMSG_PLAYER_STORAGE_CLOSE"); + break; + } +} diff --git a/src/net/ea/inventoryhandler.h b/src/net/ea/inventoryhandler.h new file mode 100644 index 00000000..b2e469fa --- /dev/null +++ b/src/net/ea/inventoryhandler.h @@ -0,0 +1,35 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NET_INVENTORYHANDLER_H +#define NET_INVENTORYHANDLER_H + +#include "../messagehandler.h" + +class InventoryHandler : public MessageHandler +{ + public: + InventoryHandler(); + + void handleMessage(MessageIn &msg); +}; + +#endif diff --git a/src/net/ea/loginhandler.cpp b/src/net/ea/loginhandler.cpp new file mode 100644 index 00000000..3f58f2c0 --- /dev/null +++ b/src/net/ea/loginhandler.cpp @@ -0,0 +1,158 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "loginhandler.h" +#include "../messagein.h" +#include "protocol.h" + +#include "../../log.h" +#include "../../logindata.h" +#include "../../main.h" +#include "../../serverinfo.h" + +#include "../../utils/gettext.h" +#include "../../utils/strprintf.h" +#include "../../utils/stringutils.h" + +extern SERVER_INFO **server_info; + +LoginHandler::LoginHandler() +{ + static const Uint16 _messages[] = { + SMSG_CONNECTION_PROBLEM, + SMSG_UPDATE_HOST, + 0x0069, + 0x006a, + 0 + }; + handledMessages = _messages; +} + +void LoginHandler::handleMessage(MessageIn &msg) +{ + int code; + + switch (msg.getId()) + { + case SMSG_CONNECTION_PROBLEM: + code = msg.readInt8(); + logger->log("Connection problem: %i", code); + + switch (code) { + case 0: + errorMessage = _("Authentication failed"); + break; + case 1: + errorMessage = _("No servers available"); + break; + case 2: + errorMessage = _("This account is already logged in"); + break; + default: + errorMessage = _("Unknown connection error"); + break; + } + state = STATE_ERROR; + break; + + case SMSG_UPDATE_HOST: + int len; + + len = msg.readInt16() - 4; + mUpdateHost = msg.readString(len); + + logger->log("Received update host \"%s\" from login server", + mUpdateHost.c_str()); + break; + + case 0x0069: + // Skip the length word + msg.skip(2); + + n_server = (msg.getLength() - 47) / 32; + server_info = + (SERVER_INFO**) malloc(sizeof(SERVER_INFO*) * n_server); + + mLoginData->session_ID1 = msg.readInt32(); + mLoginData->account_ID = msg.readInt32(); + mLoginData->session_ID2 = msg.readInt32(); + msg.skip(30); // unknown + mLoginData->sex = msg.readInt8(); + + for (int i = 0; i < n_server; i++) + { + server_info[i] = new SERVER_INFO; + + server_info[i]->address = msg.readInt32(); + server_info[i]->port = msg.readInt16(); + server_info[i]->name = msg.readString(20); + server_info[i]->online_users = msg.readInt32(); + server_info[i]->updateHost = mUpdateHost; + msg.skip(2); // unknown + + logger->log("Network: Server: %s (%s:%d)", + server_info[i]->name.c_str(), + ipToString(server_info[i]->address), + server_info[i]->port); + } + state = STATE_CHAR_SERVER; + break; + + case 0x006a: + code = msg.readInt8(); + logger->log("Login::error code: %i", code); + + switch (code) { + case 0: + errorMessage = _("Unregistered ID"); + break; + case 1: + errorMessage = _("Wrong password"); + break; + case 2: + errorMessage = _("Account expired"); + break; + case 3: + errorMessage = _("Rejected from server"); + break; + case 4: + + errorMessage = _("You have been permanently banned from " + "the game. Please contact the GM Team."); + break; + case 6: + errorMessage = strprintf(_("You have been temporarily " + "banned from the game until " + "%s.\n Please contact the GM " + "team via the forums."), + msg.readString(20).c_str()); + break; + case 9: + errorMessage = _("This user name is already taken"); + break; + default: + errorMessage = _("Unknown error"); + break; + } + state = STATE_ERROR; + break; + } +} diff --git a/src/net/ea/loginhandler.h b/src/net/ea/loginhandler.h new file mode 100644 index 00000000..c2ba5083 --- /dev/null +++ b/src/net/ea/loginhandler.h @@ -0,0 +1,45 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NET_LOGINHANDLER_H +#define NET_LOGINHANDLER_H + +#include <string> + +#include "../messagehandler.h" + +struct LoginData; + +class LoginHandler : public MessageHandler +{ + public: + LoginHandler(); + + void handleMessage(MessageIn &msg); + + void setLoginData(LoginData *loginData) { mLoginData = loginData; }; + + private: + LoginData *mLoginData; + std::string mUpdateHost; +}; + +#endif diff --git a/src/net/maploginhandler.cpp b/src/net/ea/maploginhandler.cpp index b5192bd7..6931024e 100644 --- a/src/net/maploginhandler.cpp +++ b/src/net/ea/maploginhandler.cpp @@ -20,14 +20,14 @@ */ #include "maploginhandler.h" -#include "messagein.h" +#include "../messagein.h" #include "protocol.h" -#include "../localplayer.h" -#include "../log.h" -#include "../main.h" +#include "../../localplayer.h" +#include "../../log.h" +#include "../../main.h" -#include "../utils/gettext.h" +#include "../../utils/gettext.h" MapLoginHandler::MapLoginHandler() { @@ -39,15 +39,15 @@ MapLoginHandler::MapLoginHandler() handledMessages = _messages; } -void MapLoginHandler::handleMessage(MessageIn *msg) +void MapLoginHandler::handleMessage(MessageIn &msg) { int code; unsigned char direction; - switch (msg->getId()) + switch (msg.getId()) { case SMSG_CONNECTION_PROBLEM: - code = msg->readInt8(); + code = msg.readInt8(); logger->log("Connection problem: %i", code); switch (code) { @@ -61,16 +61,16 @@ void MapLoginHandler::handleMessage(MessageIn *msg) errorMessage = _("Unknown connection error"); break; } - state = ERROR_STATE; + state = STATE_ERROR; break; case SMSG_LOGIN_SUCCESS: - msg->readInt32(); // server tick - msg->readCoordinates(player_node->mX, player_node->mY, direction); - msg->skip(2); // unknown + msg.readInt32(); // server tick + msg.readCoordinates(player_node->mX, player_node->mY, direction); + msg.skip(2); // unknown logger->log("Protocol: Player start position: (%d, %d), Direction: %d", player_node->mX, player_node->mY, direction); - state = GAME_STATE; + state = STATE_GAME; break; } } diff --git a/src/net/maploginhandler.h b/src/net/ea/maploginhandler.h index c7fee70c..1ce5ee79 100644 --- a/src/net/maploginhandler.h +++ b/src/net/ea/maploginhandler.h @@ -22,14 +22,14 @@ #ifndef NET_MAPLOGINHANDLER_H #define NET_MAPLOGINHANDLER_H -#include "messagehandler.h" +#include "../messagehandler.h" class MapLoginHandler : public MessageHandler { public: MapLoginHandler(); - void handleMessage(MessageIn *msg); + void handleMessage(MessageIn &msg); }; #endif diff --git a/src/net/ea/network.cpp b/src/net/ea/network.cpp new file mode 100644 index 00000000..199e94da --- /dev/null +++ b/src/net/ea/network.cpp @@ -0,0 +1,436 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <sstream> + +#include "../messagehandler.h" +#include "../messagein.h" +#include "network.h" + +#include "../../log.h" +#include "../../utils/stringutils.h" + +/** Warning: buffers and other variables are shared, + so there can be only one connection active at a time */ + +short packet_lengths[] = { + 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +// #0x0040 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, -1, 55, 17, 3, 37, 46, -1, 23, -1, 3,108, 3, 2, + 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6, +// #0x0080 + 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 23, -1, -1, -1, 0, + 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6, + 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6, + 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3, +// #0x00C0 + 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 6, 2, 27, + 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1, + 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2, + 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10, +// #0x0100 + 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1, + 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16, + 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1, + 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26, +// #0x0140 + 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6, + 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42, + -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182, + 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1, +// #0x0180 + 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6, + 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, 4, 6, 2, 6, + 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4, + 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3, +// #0x01C0 + 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 0, 9, 9, 29, 6, 28, + 8, 14, 10, 35, 6, 8, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6, + 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1, + -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10, +// #0x200 + 26, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +const unsigned int BUFFER_SIZE = 65536; + +int networkThread(void *data) +{ + Network *network = static_cast<Network*>(data); + + if (!network->realConnect()) + return -1; + + network->receive(); + + return 0; +} + +Network::Network(): + mSocket(0), + mAddress(), mPort(0), + mInBuffer(new char[BUFFER_SIZE]), + mOutBuffer(new char[BUFFER_SIZE]), + mInSize(0), mOutSize(0), + mToSkip(0), + mState(IDLE), + mWorkerThread(0) +{ + mMutex = SDL_CreateMutex(); +} + +Network::~Network() +{ + clearHandlers(); + + if (mState != IDLE && mState != NET_ERROR) + disconnect(); + + SDL_DestroyMutex(mMutex); + + delete[] mInBuffer; + delete[] mOutBuffer; +} + +bool Network::connect(const std::string &address, short port) +{ + if (mState != IDLE && mState != NET_ERROR) + { + logger->log("Tried to connect an already connected socket!"); + return false; + } + + if (address.empty()) + { + setError("Empty address given to Network::connect()!"); + return false; + } + + logger->log("Network::Connecting to %s:%i", address.c_str(), port); + + mAddress = address; + mPort = port; + + // Reset to sane values + mOutSize = 0; + mInSize = 0; + mToSkip = 0; + + mState = CONNECTING; + mWorkerThread = SDL_CreateThread(networkThread, this); + if (!mWorkerThread) + { + setError("Unable to create network worker thread"); + return false; + } + + return true; +} + +void Network::disconnect() +{ + mState = IDLE; + + if (mWorkerThread) + { + SDL_WaitThread(mWorkerThread, NULL); + mWorkerThread = NULL; + } + + if (mSocket) + { + SDLNet_TCP_Close(mSocket); + mSocket = 0; + } +} + +void Network::registerHandler(MessageHandler *handler) +{ + for (const Uint16 *i = handler->handledMessages; *i; i++) + { + mMessageHandlers[*i] = handler; + } + + handler->setNetwork(this); +} + +void Network::unregisterHandler(MessageHandler *handler) +{ + for (const Uint16 *i = handler->handledMessages; *i; i++) + { + mMessageHandlers.erase(*i); + } + + handler->setNetwork(0); +} + +void Network::clearHandlers() +{ + MessageHandlerIterator i; + for (i = mMessageHandlers.begin(); i != mMessageHandlers.end(); i++) + { + i->second->setNetwork(0); + } + mMessageHandlers.clear(); +} + +void Network::dispatchMessages() +{ + while (messageReady()) + { + MessageIn msg = getNextMessage(); + + MessageHandlerIterator iter = mMessageHandlers.find(msg.getId()); + + if (iter != mMessageHandlers.end()) + iter->second->handleMessage(msg); + else + logger->log("Unhandled packet: %x", msg.getId()); + + skip(msg.getLength()); + } +} + +void Network::flush() +{ + if (!mOutSize || mState != CONNECTED) + return; + + int ret; + + + SDL_mutexP(mMutex); + ret = SDLNet_TCP_Send(mSocket, mOutBuffer, mOutSize); + if (ret < (int)mOutSize) + { + setError("Error in SDLNet_TCP_Send(): " + + std::string(SDLNet_GetError())); + } + mOutSize = 0; + SDL_mutexV(mMutex); +} + +void Network::skip(int len) +{ + SDL_mutexP(mMutex); + mToSkip += len; + if (!mInSize) + { + SDL_mutexV(mMutex); + return; + } + + if (mInSize >= mToSkip) + { + mInSize -= mToSkip; + memmove(mInBuffer, mInBuffer + mToSkip, mInSize); + mToSkip = 0; + } + else + { + mToSkip -= mInSize; + mInSize = 0; + } + SDL_mutexV(mMutex); +} + +bool Network::messageReady() +{ + int len = -1; + + SDL_mutexP(mMutex); + if (mInSize >= 2) + { + len = packet_lengths[readWord(0)]; + + if (len == -1 && mInSize > 4) + len = readWord(2); + + } + + bool ret = (mInSize >= static_cast<unsigned int>(len)); + SDL_mutexV(mMutex); + + return ret; +} + +MessageIn Network::getNextMessage() +{ + while (!messageReady()) + { + if (mState == NET_ERROR) + break; + } + + SDL_mutexP(mMutex); + int msgId = readWord(0); + int len = packet_lengths[msgId]; + + if (len == -1) + len = readWord(2); + +#ifdef DEBUG + logger->log("Received packet 0x%x of length %d", msgId, len); +#endif + + MessageIn msg(mInBuffer, len); + SDL_mutexV(mMutex); + + return msg; +} + +bool Network::realConnect() +{ + IPaddress ipAddress; + + if (SDLNet_ResolveHost(&ipAddress, mAddress.c_str(), mPort) == -1) + { + std::string error = "Unable to resolve host \"" + mAddress + "\""; + setError(error); + logger->log("SDLNet_ResolveHost: %s", error.c_str()); + return false; + } + + mState = CONNECTING; + + mSocket = SDLNet_TCP_Open(&ipAddress); + if (!mSocket) + { + logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError()); + setError(SDLNet_GetError()); + return false; + } + + logger->log("Network::Started session with %s:%i", + ipToString(ipAddress.host), ipAddress.port); + + mState = CONNECTED; + + return true; +} + +void Network::receive() +{ + SDLNet_SocketSet set; + + if (!(set = SDLNet_AllocSocketSet(1))) + { + setError("Error in SDLNet_AllocSocketSet(): " + + std::string(SDLNet_GetError())); + return; + } + + if (SDLNet_TCP_AddSocket(set, mSocket) == -1) + { + setError("Error in SDLNet_AddSocket(): " + + std::string(SDLNet_GetError())); + } + + while (mState == CONNECTED) + { + // TODO Try to get this to block all the time while still being able + // to escape the loop + int numReady = SDLNet_CheckSockets(set, ((Uint32)500)); + int ret; + switch (numReady) + { + case -1: + logger->log("Error: SDLNet_CheckSockets"); + // FALLTHROUGH + case 0: + break; + + case 1: + // Receive data from the socket + SDL_mutexP(mMutex); + ret = SDLNet_TCP_Recv(mSocket, mInBuffer + mInSize, BUFFER_SIZE - mInSize); + + if (!ret) + { + // We got disconnected + mState = IDLE; + logger->log("Disconnected."); + } + else if (ret < 0) + { + setError("Error in SDLNet_TCP_Recv(): " + + std::string(SDLNet_GetError())); + } + else { + mInSize += ret; + if (mToSkip) + { + if (mInSize >= mToSkip) + { + mInSize -= mToSkip; + memmove(mInBuffer, mInBuffer + mToSkip, mInSize); + mToSkip = 0; + } + else + { + mToSkip -= mInSize; + mInSize = 0; + } + } + } + SDL_mutexV(mMutex); + break; + + default: + // more than one socket is ready.. + // this should not happen since we only listen once socket. + std::stringstream errorStream; + errorStream << "Error in SDLNet_TCP_Recv(), " << numReady + << " sockets are ready: " << SDLNet_GetError(); + setError(errorStream.str()); + break; + } + } + + if (SDLNet_TCP_DelSocket(set, mSocket) == -1) + { + logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError()); + } + + SDLNet_FreeSocketSet(set); +} + +void Network::setError(const std::string& error) +{ + logger->log("Network error: %s", error.c_str()); + mError = error; + mState = NET_ERROR; +} + +Uint16 Network::readWord(int pos) +{ +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + return SDL_Swap16((*(Uint16*)(mInBuffer+(pos)))); +#else + return (*(Uint16*)(mInBuffer+(pos))); +#endif +} diff --git a/src/net/ea/network.h b/src/net/ea/network.h new file mode 100644 index 00000000..02fe7538 --- /dev/null +++ b/src/net/ea/network.h @@ -0,0 +1,118 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NETWORK_ +#define NETWORK_ + +#include <map> +#include <SDL_net.h> +#include <SDL_thread.h> +#include <string> + +/** + * Protocol version, reported to the eAthena char and mapserver who can adjust + * the protocol accordingly. + */ +#define CLIENT_PROTOCOL_VERSION 1 + +class MessageHandler; +class MessageIn; + +class Network; + +class Network +{ + public: + friend int networkThread(void *data); + friend class MessageOut; + + Network(); + + ~Network(); + + bool connect(const std::string &address, short port); + + void disconnect(); + + void registerHandler(MessageHandler *handler); + + void unregisterHandler(MessageHandler *handler); + + void clearHandlers(); + + int getState() const { return mState; } + + const std::string& getError() const { return mError; } + + bool isConnected() const { return mState == CONNECTED; } + + int getInSize() const { return mInSize; } + + void skip(int len); + + bool messageReady(); + + MessageIn getNextMessage(); + + void dispatchMessages(); + + void flush(); + + // ERROR replaced by NET_ERROR because already defined in Windows + enum { + IDLE, + CONNECTED, + CONNECTING, + DATA, + NET_ERROR + }; + + protected: + void setError(const std::string& error); + + Uint16 readWord(int pos); + + bool realConnect(); + + void receive(); + + TCPsocket mSocket; + + std::string mAddress; + short mPort; + + char *mInBuffer, *mOutBuffer; + unsigned int mInSize, mOutSize; + + unsigned int mToSkip; + + int mState; + std::string mError; + + SDL_Thread *mWorkerThread; + SDL_mutex *mMutex; + + typedef std::map<Uint16, MessageHandler*> MessageHandlers; + typedef MessageHandlers::iterator MessageHandlerIterator; + MessageHandlers mMessageHandlers; +}; + +#endif diff --git a/src/net/ea/npchandler.cpp b/src/net/ea/npchandler.cpp new file mode 100644 index 00000000..068a3be6 --- /dev/null +++ b/src/net/ea/npchandler.cpp @@ -0,0 +1,111 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../messagein.h" +#include "npchandler.h" +#include "protocol.h" + +#include "../../beingmanager.h" +#include "../../localplayer.h" +#include "../../npc.h" + +#include "../../gui/npc_text.h" +#include "../../gui/npcintegerdialog.h" +#include "../../gui/npclistdialog.h" +#include "../../gui/npcstringdialog.h" + +extern NpcIntegerDialog *npcIntegerDialog; +extern NpcListDialog *npcListDialog; +extern NpcTextDialog *npcTextDialog; +extern NpcStringDialog *npcStringDialog; + +NPCHandler::NPCHandler() +{ + static const Uint16 _messages[] = { + SMSG_NPC_CHOICE, + SMSG_NPC_MESSAGE, + SMSG_NPC_NEXT, + SMSG_NPC_CLOSE, + SMSG_NPC_INT_INPUT, + SMSG_NPC_STR_INPUT, + 0 + }; + handledMessages = _messages; +} + +void NPCHandler::handleMessage(MessageIn &msg) +{ + int id; + + switch (msg.getId()) + { + case SMSG_NPC_CHOICE: + msg.readInt16(); // length + id = msg.readInt32(); + player_node->setAction(LocalPlayer::STAND); + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); + npcListDialog->parseItems(msg.readString(msg.getLength() - 8)); + npcListDialog->setVisible(true); + break; + + case SMSG_NPC_MESSAGE: + msg.readInt16(); // length + id = msg.readInt32(); + player_node->setAction(LocalPlayer::STAND); + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); + npcTextDialog->addText(msg.readString(msg.getLength() - 8)); + npcListDialog->setVisible(false); + npcTextDialog->setVisible(true); + break; + + case SMSG_NPC_CLOSE: + id = msg.readInt32(); + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); + npcTextDialog->showCloseButton(); + break; + + case SMSG_NPC_NEXT: + // Next button in NPC dialog, currently unused + id = msg.readInt32(); + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); + npcTextDialog->showNextButton(); + break; + + case SMSG_NPC_INT_INPUT: + // Request for an integer + id = msg.readInt32(); + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); + npcIntegerDialog->setRange(0, 2147483647); + npcIntegerDialog->setDefaultValue(0); + npcIntegerDialog->setVisible(true); + npcIntegerDialog->requestFocus(); + break; + + case SMSG_NPC_STR_INPUT: + // Request for a string + id = msg.readInt32(); + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); + npcStringDialog->setValue(""); + npcStringDialog->setVisible(true); + npcStringDialog->requestFocus(); + break; + } +} diff --git a/src/net/ea/npchandler.h b/src/net/ea/npchandler.h new file mode 100644 index 00000000..49df20c3 --- /dev/null +++ b/src/net/ea/npchandler.h @@ -0,0 +1,35 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NET_NPCHANDLER_H +#define NET_NPCHANDLER_H + +#include "../messagehandler.h" + +class NPCHandler : public MessageHandler +{ + public: + NPCHandler(); + + void handleMessage(MessageIn &msg); +}; + +#endif diff --git a/src/net/ea/partyhandler.cpp b/src/net/ea/partyhandler.cpp new file mode 100644 index 00000000..d1d3b55e --- /dev/null +++ b/src/net/ea/partyhandler.cpp @@ -0,0 +1,122 @@ +/* + * The Mana World + * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net> + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <guichan/actionlistener.hpp> + +#include "partyhandler.h" +#include "protocol.h" +#include "../messagein.h" + +#include "../../gui/chat.h" +#include "../../gui/confirm_dialog.h" + +#include "../../beingmanager.h" +#include "../../party.h" + +PartyHandler::PartyHandler(Party *party) : mParty(party) +{ + static const Uint16 _messages[] = { + SMSG_PARTY_CREATE, + SMSG_PARTY_INFO, + SMSG_PARTY_INVITE, + SMSG_PARTY_INVITED, + SMSG_PARTY_SETTINGS, + SMSG_PARTY_MEMBER_INFO, + SMSG_PARTY_LEAVE, + SMSG_PARTY_UPDATE_HP, + SMSG_PARTY_UPDATE_COORDS, + SMSG_PARTY_MESSAGE, + 0 + }; + handledMessages = _messages; +} + +void PartyHandler::handleMessage(MessageIn &msg) +{ + switch (msg.getId()) + { + case SMSG_PARTY_CREATE: + mParty->createResponse(msg.readInt8()); + break; + case SMSG_PARTY_INFO: + break; + case SMSG_PARTY_INVITE: + { + std::string nick = msg.readString(24); + int status = msg.readInt8(); + mParty->inviteResponse(nick, status); + break; + } + case SMSG_PARTY_INVITED: + { + int id = msg.readInt32(); + Being *being = beingManager->findBeing(id); + if (!being) + { + break; + } + std::string nick; + int gender = 0; + std::string partyName = ""; + if (being->getType() != Being::PLAYER) + { + nick = ""; + } + else + { + nick = being->getName(); + gender = being->getGender(); + partyName = msg.readString(24); + } + mParty->invitedAsk(nick, gender, partyName); + break; + } + case SMSG_PARTY_SETTINGS: + break; + case SMSG_PARTY_MEMBER_INFO: + break; + case SMSG_PARTY_LEAVE: + { + /*int id = */msg.readInt32(); + std::string nick = msg.readString(24); + /*int fail = */msg.readInt8(); + mParty->leftResponse(nick); + break; + } + case SMSG_PARTY_UPDATE_HP: + break; + case SMSG_PARTY_UPDATE_COORDS: + break; + case SMSG_PARTY_MESSAGE: + { // new block to enable local variables + int msgLength = msg.readInt16() - 8; + if (msgLength <= 0) + { + return; + } + int id = msg.readInt32(); + Being *being = beingManager->findBeing(id); + std::string chatMsg = msg.readString(msgLength); + mParty->receiveChat(being, chatMsg); + } + break; + } +} diff --git a/src/net/ea/partyhandler.h b/src/net/ea/partyhandler.h new file mode 100644 index 00000000..5c10eb21 --- /dev/null +++ b/src/net/ea/partyhandler.h @@ -0,0 +1,39 @@ +/* + * The Mana World + * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net> + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PARTYHANDLER_H +#define PARTYHANDLER_H + +#include "../messagehandler.h" + +class Party; + +class PartyHandler : public MessageHandler +{ + public: + PartyHandler(Party *party); + + void handleMessage(MessageIn &msg); + private: + Party *mParty; +}; + +#endif diff --git a/src/net/ea/playerhandler.cpp b/src/net/ea/playerhandler.cpp new file mode 100644 index 00000000..9f0acbb3 --- /dev/null +++ b/src/net/ea/playerhandler.cpp @@ -0,0 +1,417 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../messagein.h" +#include "playerhandler.h" +#include "protocol.h" + +#include "../../engine.h" +#include "../../localplayer.h" +#include "../../log.h" +#include "../../npc.h" +#include "../../units.h" + +#include "../../gui/buy.h" +#include "../../gui/chat.h" +#include "../../gui/gui.h" +#include "../../gui/npclistdialog.h" +#include "../../gui/npc_text.h" +#include "../../gui/ok_dialog.h" +#include "../../gui/sell.h" +#include "../../gui/skill.h" +#include "../../gui/viewport.h" + +#include "../../utils/stringutils.h" +#include "../../utils/gettext.h" + +// TODO Move somewhere else +OkDialog *weightNotice = NULL; +OkDialog *deathNotice = NULL; + +extern NpcListDialog *npcListDialog; +extern NpcTextDialog *npcTextDialog; +extern BuyDialog *buyDialog; +extern SellDialog *sellDialog; +extern Window *buySellDialog; + +// Max. distance we are willing to scroll after a teleport; +// everything beyond will reset the port hard. +static const int MAP_TELEPORT_SCROLL_DISTANCE = 8; + +/** + * Listener used for handling the overweigth message. + */ +// TODO Move somewhere else +namespace { + struct WeightListener : public gcn::ActionListener + { + void action(const gcn::ActionEvent &event) + { + weightNotice = NULL; + } + } weightListener; +} + +/** + * Listener used for handling death message. + */ +// TODO Move somewhere else +namespace { + struct DeathListener : public gcn::ActionListener + { + void action(const gcn::ActionEvent &event) + { + player_node->revive(); + deathNotice = NULL; + npcListDialog->setVisible(false); + npcTextDialog->setVisible(false); + buyDialog->setVisible(false); + sellDialog->setVisible(false); + buySellDialog->setVisible(false); + if (current_npc) current_npc->handleDeath(); + } + } deathListener; +} + +PlayerHandler::PlayerHandler() +{ + static const Uint16 _messages[] = { + SMSG_WALK_RESPONSE, + SMSG_PLAYER_WARP, + SMSG_PLAYER_STAT_UPDATE_1, + SMSG_PLAYER_STAT_UPDATE_2, + SMSG_PLAYER_STAT_UPDATE_3, + SMSG_PLAYER_STAT_UPDATE_4, + SMSG_PLAYER_STAT_UPDATE_5, + SMSG_PLAYER_STAT_UPDATE_6, + SMSG_PLAYER_ARROW_MESSAGE, + 0 + }; + handledMessages = _messages; +} + +void PlayerHandler::handleMessage(MessageIn &msg) +{ + switch (msg.getId()) + { + case SMSG_WALK_RESPONSE: + /* + * This client assumes that all walk messages succeed, + * and that the server will send a correction notice + * otherwise. + */ + break; + + case SMSG_PLAYER_WARP: + { + std::string mapPath = msg.readString(16); + bool nearby; + Uint16 x = msg.readInt16(); + Uint16 y = msg.readInt16(); + + 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(); + + nearby = (engine->getCurrentMapName() == mapPath); + + // Switch the actual map, deleting the previous one if necessary + engine->changeMap(mapPath); + + if (current_npc) current_npc->handleDeath(); + + float scrollOffsetX = 0.0f; + float scrollOffsetY = 0.0f; + + /* Scroll if neccessary */ + if (!nearby + || (abs(x - player_node->mX) > MAP_TELEPORT_SCROLL_DISTANCE) + || (abs(y - player_node->mY) > MAP_TELEPORT_SCROLL_DISTANCE)) + { + scrollOffsetX = (x - player_node->mX) * 32; + scrollOffsetY = (y - player_node->mY) * 32; + } + + player_node->setAction(Being::STAND); + player_node->mFrame = 0; + player_node->mX = x; + player_node->mY = y; + + logger->log("Adjust scrolling by %d:%d", + (int)scrollOffsetX, + (int)scrollOffsetY); + + viewport->scrollBy(scrollOffsetX, scrollOffsetY); + } + break; + + case SMSG_PLAYER_STAT_UPDATE_1: + { + Sint16 type = msg.readInt16(); + Uint32 value = msg.readInt32(); + + switch (type) + { + //case 0x0000: + // player_node->setWalkSpeed(msg.readInt32()); + // break; + case 0x0005: player_node->setHp(value); break; + case 0x0006: player_node->setMaxHp(value); break; + case 0x0007: player_node->mMp = value; break; + case 0x0008: player_node->mMaxMp = value; break; + case 0x0009: + player_node->mStatsPointsToAttribute = value; + break; + case 0x000b: player_node->setLevel(value); break; + case 0x000c: + player_node->mSkillPoint = value; + skillDialog->update(); + break; + case 0x0018: + if ((int) value >= player_node->getMaxWeight() / 2 && + player_node->getTotalWeight() < + player_node->getMaxWeight() / 2) + { + weightNotice = new OkDialog(_("Message"), + _("You are carrying more than " + "half your weight. You are " + "unable to regain health.")); + weightNotice->addActionListener( + &weightListener); + } + player_node->setTotalWeight(value); + break; + case 0x0019: player_node->setMaxWeight(value); break; + case 0x0029: player_node->ATK = value; break; + case 0x002b: player_node->MATK = value; break; + case 0x002d: player_node->DEF = value; break; + case 0x002e: player_node->DEF_BONUS = value; break; + case 0x002f: player_node->MDEF = value; break; + case 0x0031: player_node->HIT = value; break; + case 0x0032: player_node->FLEE = value; break; + case 0x0035: player_node->mAttackSpeed = value; break; + case 0x0037: player_node->mJobLevel = value; break; + } + + if (player_node->getHp() == 0 && !deathNotice) + { + static char const *const deadMsg[] = + { + _("You are dead."), + _("We regret to inform you that your character was " + "killed in battle."), + _("You are not that alive anymore."), + _("The cold hands of the grim reaper are grabbing for " + "your soul."), + _("Game Over!"), + _("Insert coin to continue"), + _("No, kids. Your character did not really die. It... " + "err... went to a better place."), + _("Your plan of breaking your enemies weapon by " + "bashing it with your throat failed."), + _("I guess this did not run too well."), + // NetHack reference: + _("Do you want your possessions identified?"), + // Secret of Mana reference: + _("Sadly, no trace of you was ever found..."), + // Final Fantasy VI reference: + _("Annihilated."), + // Earthbound reference: + _("Looks like you got your head handed to you."), + // Leisure Suit Larry 1 reference: + _("You screwed up again, dump your body down the tubes " + "and get you another one."), + // Monty Python references (Dead Parrot sketch mostly): + _("You're not dead yet. You're just resting."), + _("You are no more."), + _("You have ceased to be."), + _("You've expired and gone to meet your maker."), + _("You're a stiff."), + _("Bereft of life, you rest in peace."), + _("If you weren't so animated, you'd be pushing up the " + "daisies."), + _("Your metabolic processes are now history."), + _("You're off the twig."), + _("You've kicked the bucket."), + _("You've shuffled off your mortal coil, run down the " + "curtain and joined the bleedin' choir invisibile."), + _("You are an ex-player."), + _("You're pining for the fjords.") + }; + std::string message(deadMsg[rand()%27]); + + deathNotice = new OkDialog(_("Message"), message); + deathNotice->addActionListener(&deathListener); + player_node->setAction(Being::DEAD); + } + } + break; + + case SMSG_PLAYER_STAT_UPDATE_2: + switch (msg.readInt16()) { + case 0x0001: + player_node->setXp(msg.readInt32()); + break; + case 0x0002: + player_node->mJobXp = msg.readInt32(); + break; + case 0x0014: { + int curGp = player_node->getMoney(); + player_node->setMoney(msg.readInt32()); + if (player_node->getMoney() > curGp) + chatWindow->chatLog(_("You picked up ") + + Units::formatCurrency(player_node->getMoney() + - curGp), BY_SERVER); + } + break; + case 0x0016: + player_node->mXpForNextLevel = msg.readInt32(); + break; + case 0x0017: + player_node->mJobXpForNextLevel = msg.readInt32(); + break; + } + break; + + case SMSG_PLAYER_STAT_UPDATE_3: + { + Sint32 type = msg.readInt32(); + Sint32 base = msg.readInt32(); + Sint32 bonus = msg.readInt32(); + Sint32 total = base + bonus; + + switch (type) { + case 0x000d: player_node->mAttr[LocalPlayer::STR] = total; + break; + case 0x000e: player_node->mAttr[LocalPlayer::AGI] = total; + break; + case 0x000f: player_node->mAttr[LocalPlayer::VIT] = total; + break; + case 0x0010: player_node->mAttr[LocalPlayer::INT] = total; + break; + case 0x0011: player_node->mAttr[LocalPlayer::DEX] = total; + break; + case 0x0012: player_node->mAttr[LocalPlayer::LUK] = total; + break; + } + } + break; + + case SMSG_PLAYER_STAT_UPDATE_4: + { + Sint16 type = msg.readInt16(); + Sint8 fail = msg.readInt8(); + Sint8 value = msg.readInt8(); + + if (fail != 1) + break; + + switch (type) { + case 0x000d: player_node->mAttr[LocalPlayer::STR] = value; + break; + case 0x000e: player_node->mAttr[LocalPlayer::AGI] = value; + break; + case 0x000f: player_node->mAttr[LocalPlayer::VIT] = value; + break; + case 0x0010: player_node->mAttr[LocalPlayer::INT] = value; + break; + case 0x0011: player_node->mAttr[LocalPlayer::DEX] = value; + break; + case 0x0012: player_node->mAttr[LocalPlayer::LUK] = value; + break; + } + } + break; + + // Updates stats and status points + case SMSG_PLAYER_STAT_UPDATE_5: + player_node->mStatsPointsToAttribute = msg.readInt16(); + player_node->mAttr[LocalPlayer::STR] = msg.readInt8(); + player_node->mAttrUp[LocalPlayer::STR] = msg.readInt8(); + player_node->mAttr[LocalPlayer::AGI] = msg.readInt8(); + player_node->mAttrUp[LocalPlayer::AGI] = msg.readInt8(); + player_node->mAttr[LocalPlayer::VIT] = msg.readInt8(); + player_node->mAttrUp[LocalPlayer::VIT] = msg.readInt8(); + player_node->mAttr[LocalPlayer::INT] = msg.readInt8(); + player_node->mAttrUp[LocalPlayer::INT] = msg.readInt8(); + player_node->mAttr[LocalPlayer::DEX] = msg.readInt8(); + player_node->mAttrUp[LocalPlayer::DEX] = msg.readInt8(); + player_node->mAttr[LocalPlayer::LUK] = msg.readInt8(); + player_node->mAttrUp[LocalPlayer::LUK] = msg.readInt8(); + player_node->ATK = msg.readInt16(); // ATK + player_node->ATK_BONUS = msg.readInt16(); // ATK bonus + player_node->MATK = msg.readInt16(); // MATK max + player_node->MATK_BONUS = msg.readInt16(); // MATK min + player_node->DEF = msg.readInt16(); // DEF + player_node->DEF_BONUS = msg.readInt16(); // DEF bonus + player_node->MDEF = msg.readInt16(); // MDEF + player_node->MDEF_BONUS = msg.readInt16(); // MDEF bonus + player_node->HIT = msg.readInt16(); // HIT + player_node->FLEE = msg.readInt16(); // FLEE + player_node->FLEE_BONUS = msg.readInt16(); // FLEE bonus + msg.readInt16(); // critical + msg.readInt16(); // unknown + break; + + case SMSG_PLAYER_STAT_UPDATE_6: + switch (msg.readInt16()) { + case 0x0020: + player_node->mAttrUp[LocalPlayer::STR] = msg.readInt8(); + break; + case 0x0021: + player_node->mAttrUp[LocalPlayer::AGI] = msg.readInt8(); + break; + case 0x0022: + player_node->mAttrUp[LocalPlayer::VIT] = msg.readInt8(); + break; + case 0x0023: + player_node->mAttrUp[LocalPlayer::INT] = msg.readInt8(); + break; + case 0x0024: + player_node->mAttrUp[LocalPlayer::DEX] = msg.readInt8(); + break; + case 0x0025: + player_node->mAttrUp[LocalPlayer::LUK] = msg.readInt8(); + break; + } + break; + + case SMSG_PLAYER_ARROW_MESSAGE: + { + Sint16 type = msg.readInt16(); + + switch (type) { + case 0: + chatWindow->chatLog(_("Equip arrows first"), + BY_SERVER); + break; + default: + logger->log("0x013b: Unhandled message %i", type); + break; + } + } + break; + } +} diff --git a/src/net/ea/playerhandler.h b/src/net/ea/playerhandler.h new file mode 100644 index 00000000..f3352289 --- /dev/null +++ b/src/net/ea/playerhandler.h @@ -0,0 +1,35 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NET_PLAYERHANDLER_H +#define NET_PLAYERHANDLER_H + +#include "../messagehandler.h" + +class PlayerHandler : public MessageHandler +{ + public: + PlayerHandler(); + + void handleMessage(MessageIn &msg); +}; + +#endif diff --git a/src/net/protocol.cpp b/src/net/ea/protocol.cpp index 69d69901..69d69901 100644 --- a/src/net/protocol.cpp +++ b/src/net/ea/protocol.cpp diff --git a/src/net/ea/protocol.h b/src/net/ea/protocol.h new file mode 100644 index 00000000..55c0d8b6 --- /dev/null +++ b/src/net/ea/protocol.h @@ -0,0 +1,162 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PROTOCOL_H +#define PROTOCOL_H + +/********************************* + * Packets from server to client * + *********************************/ +#define SMSG_LOGIN_SUCCESS 0x0073 /**< Contains starting location */ +#define SMSG_SERVER_PING 0x007f /**< Contains server tick */ +#define SMSG_CONNECTION_PROBLEM 0x0081 +#define SMSG_UPDATE_HOST 0x0063 /**< Custom update host packet */ +#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 +#define SMSG_PLAYER_STAT_UPDATE_4 0x00bc +#define SMSG_PLAYER_STAT_UPDATE_5 0x00bd +#define SMSG_PLAYER_STAT_UPDATE_6 0x00be +#define SMSG_WHO_ANSWER 0x00c2 +#define SMSG_PLAYER_WARP 0x0091 /**< Warp player to map/location */ +#define SMSG_PLAYER_INVENTORY 0x01ee +#define SMSG_PLAYER_INVENTORY_ADD 0x00a0 +#define SMSG_PLAYER_INVENTORY_REMOVE 0x00af +#define SMSG_PLAYER_INVENTORY_USE 0x01c8 +#define SMSG_PLAYER_EQUIPMENT 0x00a4 +#define SMSG_PLAYER_EQUIP 0x00aa +#define SMSG_PLAYER_UNEQUIP 0x00ac +#define SMSG_PLAYER_ATTACK_RANGE 0x013a +#define SMSG_PLAYER_ARROW_EQUIP 0x013c +#define SMSG_PLAYER_ARROW_MESSAGE 0x013b +#define SMSG_PLAYER_SKILLS 0x010f +#define SMSG_SKILL_FAILED 0x0110 +#define SMSG_ITEM_USE_RESPONSE 0x00a8 +#define SMSG_ITEM_VISIBLE 0x009d /**< An item is on the floor */ +#define SMSG_ITEM_DROPPED 0x009e /**< An item is dropped */ +#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 */ +#define SMSG_BEING_SELFEFFECT 0x019b +#define SMSG_BEING_EMOTION 0x00c0 +#define SMSG_BEING_ACTION 0x008a /**< Attack, sit, stand up, ... */ +#define SMSG_BEING_CHAT 0x008d /**< A being talks */ +#define SMSG_BEING_NAME_RESPONSE 0x0095 /**< Has to be requested */ + +#define SMSG_NPC_MESSAGE 0x00b4 +#define SMSG_NPC_NEXT 0x00b5 +#define SMSG_NPC_CLOSE 0x00b6 +#define SMSG_NPC_CHOICE 0x00b7 /**< Display a choice */ +#define SMSG_NPC_BUY_SELL_CHOICE 0x00c4 +#define SMSG_NPC_BUY 0x00c6 +#define SMSG_NPC_SELL 0x00c7 +#define SMSG_NPC_BUY_RESPONSE 0x00ca +#define SMSG_NPC_SELL_RESPONSE 0x00cb +#define SMSG_NPC_INT_INPUT 0x0142 /**< Integer input */ +#define SMSG_NPC_STR_INPUT 0x01d4 /**< String input */ +#define SMSG_PLAYER_CHAT 0x008e /**< Player talks */ +#define SMSG_WHISPER 0x0097 /**< Whisper Recieved */ +#define SMSG_WHISPER_RESPONSE 0x0098 +#define SMSG_GM_CHAT 0x009a /**< GM announce */ +#define SMSG_WALK_RESPONSE 0x0087 + +#define SMSG_TRADE_REQUEST 0x00e5 /**< Receiving a request to trade */ +#define SMSG_TRADE_RESPONSE 0x00e7 +#define SMSG_TRADE_ITEM_ADD 0x00e9 +#define SMSG_TRADE_ITEM_ADD_RESPONSE 0x01b1 /**< Not standard eAthena! */ +#define SMSG_TRADE_OK 0x00ec +#define SMSG_TRADE_CANCEL 0x00ee +#define SMSG_TRADE_COMPLETE 0x00f0 + +#define SMSG_PARTY_CREATE 0x00fa +#define SMSG_PARTY_INFO 0x00fb +#define SMSG_PARTY_INVITE 0x00fd +#define SMSG_PARTY_INVITED 0x00fe +#define SMSG_PARTY_SETTINGS 0x0102 +#define SMSG_PARTY_MEMBER_INFO 0x0104 +#define SMSG_PARTY_LEAVE 0x0105 +#define SMSG_PARTY_UPDATE_HP 0x0106 +#define SMSG_PARTY_UPDATE_COORDS 0x0107 +#define SMSG_PARTY_MESSAGE 0x0109 + +#define SMSG_PLAYER_STORAGE_ITEMS 0x01f0 /**< Item list for storage */ +#define SMSG_PLAYER_STORAGE_EQUIP 0x00a6 /**< Equipment list for storage */ +#define SMSG_PLAYER_STORAGE_STATUS 0x00f2 /**< Slots used and total slots */ +#define SMSG_PLAYER_STORAGE_ADD 0x00f4 /**< Add item/equip to storage */ +#define SMSG_PLAYER_STORAGE_REMOVE 0x00f6 /**< Remove item/equip from storage */ +#define SMSG_PLAYER_STORAGE_CLOSE 0x00f8 /**< Storage access closed */ + +/********************************** + * 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 +#define CMSG_NPC_BUY_REQUEST 0x00c8 +#define CMSG_NPC_BUY_SELL_REQUEST 0x00c5 +#define CMSG_CHAT_MESSAGE 0x008c +#define CMSG_CHAT_WHISPER 0x0096 +#define CMSG_CHAT_ANNOUNCE 0x0099 +#define CMSG_CHAT_WHO 0x00c1 +#define CMSG_NPC_LIST_CHOICE 0x00b8 +#define CMSG_NPC_NEXT_REQUEST 0x00b9 +#define CMSG_NPC_SELL_REQUEST 0x00c9 +#define CMSG_NPC_INT_RESPONSE 0x0143 +#define CMSG_NPC_STR_RESPONSE 0x01d5 +#define CMSG_SKILL_LEVELUP_REQUEST 0x0112 +#define CMSG_STAT_UPDATE_REQUEST 0x00bb +#define CMSG_TRADE_ITEM_ADD_REQUEST 0x00e8 +#define CMSG_TRADE_CANCEL_REQUEST 0x00ed +#define CMSG_TRADE_ADD_COMPLETE 0x00eb +#define CMSG_TRADE_OK 0x00ef +#define CMSG_NPC_TALK 0x0090 +#define CMSG_TRADE_REQUEST 0x00e4 +#define CMSG_PLAYER_INVENTORY_USE 0x00a7 +#define CMSG_PLAYER_INVENTORY_DROP 0x00a2 +#define CMSG_PLAYER_EQUIP 0x00a9 +#define CMSG_PLAYER_UNEQUIP 0x00ab + +#define CMSG_PARTY_CREATE 0x00f9 +#define CMSG_PARTY_INVITE 0x00fc +#define CMSG_PARTY_INVITED 0x00ff +#define CMSG_PARTY_LEAVE 0x0100 /** Undocumented */ +#define CMSG_PARTY_SETTINGS 0x0101 +#define CMSG_PARTY_MESSAGE 0x0108 + +#define CMSG_MOVE_TO_STORAGE 0x00f3 /** Move item to storage */ +#define CSMG_MOVE_FROM_STORAGE 0x00f5 /** Remove item from storage */ +#define CMSG_CLOSE_STORAGE 0x00f7 /** Request storage close */ + +/** Encodes coords and direction in 3 bytes data */ +void set_coordinates(char *data, unsigned short x, unsigned short y, unsigned char direction); + +#endif diff --git a/src/net/skillhandler.cpp b/src/net/ea/skillhandler.cpp index e2185524..6e766008 100644 --- a/src/net/skillhandler.cpp +++ b/src/net/ea/skillhandler.cpp @@ -19,14 +19,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "messagein.h" +#include "../messagein.h" #include "protocol.h" #include "skillhandler.h" -#include "../log.h" +#include "../../log.h" -#include "../gui/chat.h" -#include "../gui/skill.h" +#include "../../gui/chat.h" +#include "../../gui/skill.h" SkillHandler::SkillHandler() { @@ -38,27 +38,27 @@ SkillHandler::SkillHandler() handledMessages = _messages; } -void SkillHandler::handleMessage(MessageIn *msg) +void SkillHandler::handleMessage(MessageIn &msg) { int skillCount; - switch (msg->getId()) + switch (msg.getId()) { case SMSG_PLAYER_SKILLS: - msg->readInt16(); // length - skillCount = (msg->getLength() - 4) / 37; + msg.readInt16(); // length + skillCount = (msg.getLength() - 4) / 37; skillDialog->cleanList(); for (int k = 0; k < skillCount; k++) { - Sint16 skillId = msg->readInt16(); - msg->readInt16(); // target type - msg->readInt16(); // unknown - Sint16 level = msg->readInt16(); - Sint16 sp = msg->readInt16(); - msg->readInt16(); // range - std::string skillName = msg->readString(24); - Sint8 up = msg->readInt8(); + Sint16 skillId = msg.readInt16(); + msg.readInt16(); // target type + msg.readInt16(); // unknown + Sint16 level = msg.readInt16(); + Sint16 sp = msg.readInt16(); + msg.readInt16(); // range + std::string skillName = msg.readString(24); + Sint8 up = msg.readInt8(); if (level != 0 || up != 0) { @@ -77,11 +77,11 @@ void SkillHandler::handleMessage(MessageIn *msg) // Action failed (ex. sit because you have not reached the // right level) CHATSKILL action; - action.skill = msg->readInt16(); - action.bskill = msg->readInt16(); - action.unused = msg->readInt16(); // unknown - action.success = msg->readInt8(); - action.reason = msg->readInt8(); + action.skill = msg.readInt16(); + action.bskill = msg.readInt16(); + action.unused = msg.readInt16(); // unknown + action.success = msg.readInt8(); + action.reason = msg.readInt8(); if (action.success != SKILL_FAILED && action.bskill == BSKILL_EMOTE) { diff --git a/src/net/skillhandler.h b/src/net/ea/skillhandler.h index 2b55605d..57d68f47 100644 --- a/src/net/skillhandler.h +++ b/src/net/ea/skillhandler.h @@ -22,14 +22,14 @@ #ifndef NET_SKILLHANDLER_H #define NET_SKILLHANDLER_H -#include "messagehandler.h" +#include "../messagehandler.h" class SkillHandler : public MessageHandler { public: SkillHandler(); - void handleMessage(MessageIn *msg); + void handleMessage(MessageIn &msg); }; #endif diff --git a/src/net/ea/tradehandler.cpp b/src/net/ea/tradehandler.cpp new file mode 100644 index 00000000..6c953a11 --- /dev/null +++ b/src/net/ea/tradehandler.cpp @@ -0,0 +1,220 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "../messagein.h" +#include "protocol.h" +#include "tradehandler.h" + +#include "../../inventory.h" +#include "../../item.h" +#include "../../localplayer.h" +#include "../../player_relations.h" + +#include "../../gui/chat.h" +#include "../../gui/confirm_dialog.h" +#include "../../gui/trade.h" + +#include "../../utils/gettext.h" + +std::string tradePartnerName; + +/** + * Listener for request trade dialogs + */ +namespace { + struct RequestTradeListener : public gcn::ActionListener + { + void action(const gcn::ActionEvent &event) + { + player_node->tradeReply(event.getId() == "yes"); + }; + } listener; +} + +TradeHandler::TradeHandler() +{ + static const Uint16 _messages[] = { + SMSG_TRADE_REQUEST, + SMSG_TRADE_RESPONSE, + SMSG_TRADE_ITEM_ADD, + SMSG_TRADE_ITEM_ADD_RESPONSE, + SMSG_TRADE_OK, + SMSG_TRADE_CANCEL, + SMSG_TRADE_COMPLETE, + 0 + }; + handledMessages = _messages; +} + + +void TradeHandler::handleMessage(MessageIn &msg) +{ + switch (msg.getId()) + { + case SMSG_TRADE_REQUEST: + // If a trade window or request window is already open, send a + // trade cancel to any other trade request. + // + // Note that it would be nice if the server would prevent this + // situation, and that the requesting player would get a + // special message about the player being occupied. + tradePartnerName = msg.readString(24); + + if (player_relations.hasPermission(tradePartnerName, PlayerRelation::TRADE)) + { + if (!player_node->tradeRequestOk()) + { + player_node->tradeReply(false); + break; + } + + player_node->setTrading(true); + ConfirmDialog *dlg; + dlg = new ConfirmDialog(_("Request for trade"), + tradePartnerName + + _(" wants to trade with you, do you accept?")); + dlg->addActionListener(&listener); + } + else + { + player_node->tradeReply(false); + break; + } + break; + + case SMSG_TRADE_RESPONSE: + switch (msg.readInt8()) + { + case 0: // Too far away + chatWindow->chatLog(_("Trading isn't possible. Trade partner is too far away."), + BY_SERVER); + break; + case 1: // Character doesn't exist + chatWindow->chatLog(_("Trading isn't possible. Character doesn't exist."), + BY_SERVER); + break; + case 2: // Invite request check failed... + chatWindow->chatLog(_("Trade cancelled due to an unknown reason."), + BY_SERVER); + break; + case 3: // Trade accepted + tradeWindow->reset(); + tradeWindow->setCaption( + _("Trade: You and ") + tradePartnerName); + tradeWindow->setVisible(true); + break; + case 4: // Trade cancelled + if (player_relations.hasPermission(tradePartnerName, + PlayerRelation::SPEECH_LOG)) + chatWindow->chatLog(_("Trade with ") + tradePartnerName + + _(" cancelled"), BY_SERVER); + // otherwise ignore silently + + tradeWindow->setVisible(false); + player_node->setTrading(false); + break; + default: // Shouldn't happen as well, but to be sure + chatWindow->chatLog(_("Unhandled trade cancel packet"), + BY_SERVER); + break; + } + break; + + case SMSG_TRADE_ITEM_ADD: + { + Sint32 amount = msg.readInt32(); + Sint16 type = msg.readInt16(); + msg.readInt8(); // identified flag + msg.readInt8(); // attribute + msg.readInt8(); // refine + msg.skip(8); // card (4 shorts) + + // TODO: handle also identified, etc + if (type == 0) { + tradeWindow->setMoney(amount); + } else { + tradeWindow->addItem(type, false, amount, false); + } + } + break; + + case SMSG_TRADE_ITEM_ADD_RESPONSE: + // Trade: New Item add response (was 0x00ea, now 01b1) + { + const int index = msg.readInt16(); + Item *item = player_node->getInventory()->getItem(index); + if (!item) + { + tradeWindow->receivedOk(true); + return; + } + Sint16 quantity = msg.readInt16(); + + switch (msg.readInt8()) + { + case 0: + // Successfully added item + if (item->isEquipment() && item->isEquipped()) + { + player_node->unequipItem(item); + } + tradeWindow->addItem(item->getId(), true, quantity, + item->isEquipment()); + item->increaseQuantity(-quantity); + break; + case 1: + // Add item failed - player overweighted + chatWindow->chatLog(_("Failed adding item. Trade partner is over weighted."), + BY_SERVER); + break; + case 2: + // Add item failed - player has no free slot + chatWindow->chatLog(_("Failed adding item. Trade partner has no free slot."), + BY_SERVER); + break; + default: + chatWindow->chatLog(_("Failed adding item for unknown reason."), + BY_SERVER); + break; + } + } + break; + + case SMSG_TRADE_OK: + // 0 means ok from myself, 1 means ok from other; + tradeWindow->receivedOk(msg.readInt8() == 0); + break; + + case SMSG_TRADE_CANCEL: + chatWindow->chatLog(_("Trade canceled."), BY_SERVER); + tradeWindow->setVisible(false); + tradeWindow->reset(); + player_node->setTrading(false); + break; + + case SMSG_TRADE_COMPLETE: + chatWindow->chatLog(_("Trade completed."), BY_SERVER); + tradeWindow->setVisible(false); + tradeWindow->reset(); + player_node->setTrading(false); + break; + } +} diff --git a/src/net/ea/tradehandler.h b/src/net/ea/tradehandler.h new file mode 100644 index 00000000..04335069 --- /dev/null +++ b/src/net/ea/tradehandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NET_TRADEHANDLER_H +#define NET_TRADEHANDLER_H + +#include "../messagehandler.h" + +class Network; + +class TradeHandler : public MessageHandler +{ + public: + TradeHandler(); + + void handleMessage(MessageIn &msg); +}; + +#endif diff --git a/src/net/effecthandler.cpp b/src/net/effecthandler.cpp new file mode 100644 index 00000000..8411b9e7 --- /dev/null +++ b/src/net/effecthandler.cpp @@ -0,0 +1,58 @@ +/* + * The Mana World + * Copyright (C) 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "effecthandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../effectmanager.h" + + +EffectHandler::EffectHandler() +{ + static const Uint16 _messages[] = { + GPMSG_CREATE_EFFECT, + 0 + }; + handledMessages = _messages; +} + +void EffectHandler::handleMessage(MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_CREATE_EFFECT: + handleCreateEffects(msg); + break; + default: + break; + } +} + +void EffectHandler::handleCreateEffects(MessageIn &msg) +{ + int id = msg.readInt16(); + Uint16 x = msg.readInt16(); + Uint16 y = msg.readInt16(); + effectManager->trigger(id, x, y); +} + diff --git a/src/net/effecthandler.h b/src/net/effecthandler.h new file mode 100644 index 00000000..283c7c10 --- /dev/null +++ b/src/net/effecthandler.h @@ -0,0 +1,38 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_EFFECTSHANDLER_H +#define _TMW_NET_EFFECTSHANDLER_H + +#include "messagehandler.h" + +class EffectHandler : public MessageHandler +{ + public: + EffectHandler(); + + void handleMessage(MessageIn &msg); + + private: + void handleCreateEffects(MessageIn &msg); +}; + +#endif diff --git a/src/net/gameserver/gameserver.cpp b/src/net/gameserver/gameserver.cpp new file mode 100644 index 00000000..1bdaef26 --- /dev/null +++ b/src/net/gameserver/gameserver.cpp @@ -0,0 +1,49 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "gameserver.h" + +#include "internal.h" + +#include "../connection.h" +#include "../messageout.h" +#include "../protocol.h" + +void Net::GameServer::connect(Net::Connection *connection, + const std::string &token) +{ + Net::GameServer::connection = connection; + + MessageOut msg(PGMSG_CONNECT); + + msg.writeString(token, 32); + + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::logout(bool reconnectAccount) +{ + MessageOut msg(PGMSG_DISCONNECT); + + msg.writeInt8((unsigned char) reconnectAccount); + + Net::GameServer::connection->send(msg); +} diff --git a/src/net/gameserver/gameserver.h b/src/net/gameserver/gameserver.h new file mode 100644 index 00000000..5ea2c718 --- /dev/null +++ b/src/net/gameserver/gameserver.h @@ -0,0 +1,39 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_GAMESERVER_GAMESERVER_H +#define _TMW_NET_GAMESERVER_GAMESERVER_H + +#include <iosfwd> + +namespace Net +{ + class Connection; + + namespace GameServer + { + void connect(Net::Connection *connection, const std::string &token); + + void logout(bool reconnectAccount); + } +} + +#endif diff --git a/src/net/gameserver/internal.cpp b/src/net/gameserver/internal.cpp new file mode 100644 index 00000000..6b6ba081 --- /dev/null +++ b/src/net/gameserver/internal.cpp @@ -0,0 +1,32 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal.h" + +namespace Net +{ + class Connection; + + namespace GameServer + { + Connection *connection = 0; + } +} diff --git a/src/net/gameserver/internal.h b/src/net/gameserver/internal.h new file mode 100644 index 00000000..df9787fe --- /dev/null +++ b/src/net/gameserver/internal.h @@ -0,0 +1,35 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_GAMESERVER_INTERNAL_H +#define _TMW_NET_GAMESERVER_INTERNAL_H + +namespace Net +{ + class Connection; + + namespace GameServer + { + extern Connection *connection; + } +} + +#endif diff --git a/src/net/gameserver/player.cpp b/src/net/gameserver/player.cpp new file mode 100644 index 00000000..95c13ec2 --- /dev/null +++ b/src/net/gameserver/player.cpp @@ -0,0 +1,202 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "player.h" + +#include "internal.h" + +#include "../connection.h" +#include "../messageout.h" +#include "../protocol.h" + +void RespawnRequestListener::action(const gcn::ActionEvent &event) +{ + Net::GameServer::Player::respawn(); +} + +void Net::GameServer::Player::say(const std::string &text) +{ + MessageOut msg(PGMSG_SAY); + msg.writeString(text); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::walk(int x, int y) +{ + MessageOut msg(PGMSG_WALK); + msg.writeInt16(x); + msg.writeInt16(y); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::pickUp(int x, int y) +{ + MessageOut msg(PGMSG_PICKUP); + msg.writeInt16(x); + msg.writeInt16(y); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::moveItem(int oldSlot, int newSlot, int amount) +{ + MessageOut msg(PGMSG_MOVE_ITEM); + msg.writeInt8(oldSlot); + msg.writeInt8(newSlot); + msg.writeInt8(amount); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::drop(int slot, int amount) +{ + MessageOut msg(PGMSG_DROP); + msg.writeInt8(slot); + msg.writeInt8(amount); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::equip(int slot) +{ + MessageOut msg(PGMSG_EQUIP); + msg.writeInt8(slot); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::unequip(int slot) +{ + MessageOut msg(PGMSG_UNEQUIP); + msg.writeInt8(slot); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::useItem(int slot) +{ + MessageOut msg(PGMSG_USE_ITEM); + msg.writeInt8(slot); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::attack(int direction) +{ + MessageOut msg(PGMSG_ATTACK); + msg.writeInt8(direction); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::useSpecial(int special) +{ + MessageOut msg(PGMSG_USE_SPECIAL); + msg.writeInt8(special); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::changeAction(Being::Action action) +{ + MessageOut msg(PGMSG_ACTION_CHANGE); + msg.writeInt8(action); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::talkToNPC(int id, bool restart) +{ + MessageOut msg(restart ? PGMSG_NPC_TALK : PGMSG_NPC_TALK_NEXT); + msg.writeInt16(id); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::selectFromNPC(int id, int choice) +{ + MessageOut msg(PGMSG_NPC_SELECT); + msg.writeInt16(id); + msg.writeInt8(choice); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::requestTrade(int id) +{ + MessageOut msg(PGMSG_TRADE_REQUEST); + msg.writeInt16(id); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::acceptTrade(bool accept) +{ + MessageOut msg(accept ? PGMSG_TRADE_ACCEPT : PGMSG_TRADE_CANCEL); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::tradeItem(int slot, int amount) +{ + MessageOut msg(PGMSG_TRADE_ADD_ITEM); + msg.writeInt8(slot); + msg.writeInt8(amount); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::tradeMoney(int amount) +{ + MessageOut msg(PGMSG_TRADE_SET_MONEY); + msg.writeInt32(amount); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::tradeWithNPC(int item, int amount) +{ + MessageOut msg(PGMSG_NPC_BUYSELL); + msg.writeInt16(item); + msg.writeInt16(amount); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::sendLetter(const std::string &player, + const std::string &text) +{ + MessageOut msg(PGMSG_NPC_POST_SEND); + msg.writeString(player); + msg.writeString(text); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::raiseAttribute(int attribute) +{ + MessageOut msg(PGMSG_RAISE_ATTRIBUTE); + msg.writeInt8(attribute); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::lowerAttribute(int attribute) +{ + MessageOut msg(PGMSG_LOWER_ATTRIBUTE); + msg.writeInt8(attribute); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::respawn() +{ + MessageOut msg(PGMSG_RESPAWN); + Net::GameServer::connection->send(msg); +} + +void Net::GameServer::Player::changeDir(unsigned char dir) +{ + MessageOut msg(PGMSG_DIRECTION_CHANGE); + msg.writeInt8(dir); + Net::GameServer::connection->send(msg); +} diff --git a/src/net/gameserver/player.h b/src/net/gameserver/player.h new file mode 100644 index 00000000..9e68ced9 --- /dev/null +++ b/src/net/gameserver/player.h @@ -0,0 +1,71 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_GAMESERVER_PLAYER_H +#define _TMW_NET_GAMESERVER_PLAYER_H + +#include "../../being.h" + +#include <guichan/actionlistener.hpp> + +#include <iosfwd> + + +struct RespawnRequestListener : public gcn::ActionListener +{ + void action(const gcn::ActionEvent &event); +}; + +namespace Net +{ + namespace GameServer + { + namespace Player + { + void say(const std::string &text); + void walk(int x, int y); + void pickUp(int x, int y); + void moveItem(int oldSlot, int newSlot, int amount); + void drop(int slot, int amount); + void equip(int slot); + void unequip(int slot); + void useItem(int slot); + void attack(int direction); + void useSpecial(int special); + void changeAction(Being::Action action); + void talkToNPC(int id, bool restart); + void selectFromNPC(int id, int choice); + void requestTrade(int id); + void acceptTrade(bool accept); + void tradeItem(int slot, int amount); + void tradeMoney(int amount); + void tradeWithNPC(int item, int amount); + void sendLetter(const std::string &player, const std::string &text); + void raiseAttribute(int attribute); + void lowerAttribute(int attribute); + void respawn(); + static RespawnRequestListener respawnListener; + void changeDir(unsigned char dir); + } + } +} + +#endif diff --git a/src/net/guildhandler.cpp b/src/net/guildhandler.cpp new file mode 100644 index 00000000..cf886ab3 --- /dev/null +++ b/src/net/guildhandler.cpp @@ -0,0 +1,240 @@ +/* + * The Mana World + * Copyright 2008 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <iostream> +#include "guildhandler.h" + +#include "protocol.h" +#include "messagein.h" + +#include "chatserver/chatserver.h" +#include "chatserver/guild.h" + +#include "../gui/guildwindow.h" +#include "../gui/chat.h" +#include "../guild.h" +#include "../log.h" +#include "../localplayer.h" +#include "../channel.h" +#include "../channelmanager.h" + +GuildHandler::GuildHandler() +{ + static const Uint16 _messages[] = { + CPMSG_GUILD_CREATE_RESPONSE, + CPMSG_GUILD_INVITE_RESPONSE, + CPMSG_GUILD_ACCEPT_RESPONSE, + CPMSG_GUILD_GET_MEMBERS_RESPONSE, + CPMSG_GUILD_UPDATE_LIST, + CPMSG_GUILD_INVITED, + CPMSG_GUILD_REJOIN, + CPMSG_GUILD_QUIT_RESPONSE, + 0 + }; + handledMessages = _messages; + +} + +void GuildHandler::handleMessage(MessageIn &msg) +{ + switch (msg.getId()) + { + case CPMSG_GUILD_CREATE_RESPONSE: + { + logger->log("Received CPMSG_GUILD_CREATE_RESPONSE"); + if(msg.readInt8() == ERRMSG_OK) + { + // TODO - Acknowledge guild was created + chatWindow->chatLog("Guild created."); + joinedGuild(msg); + } + else + { + chatWindow->chatLog("Error creating guild."); + } + } break; + + case CPMSG_GUILD_INVITE_RESPONSE: + { + logger->log("Received CPMSG_GUILD_INVITE_RESPONSE"); + if(msg.readInt8() == ERRMSG_OK) + { + // TODO - Acknowledge invite was sent + chatWindow->chatLog("Invite sent."); + } + } break; + + case CPMSG_GUILD_ACCEPT_RESPONSE: + { + logger->log("Received CPMSG_GUILD_ACCEPT_RESPONSE"); + if(msg.readInt8() == ERRMSG_OK) + { + // TODO - Acknowledge accepted into guild + joinedGuild(msg); + } + } break; + + case CPMSG_GUILD_GET_MEMBERS_RESPONSE: + { + logger->log("Received CPMSG_GUILD_GET_MEMBERS_RESPONSE"); + if(msg.readInt8() == ERRMSG_OK) + { + std::string guildMember; + bool online; + std::string guildName; + Guild *guild; + + short guildId = msg.readInt16(); + guild = player_node->getGuild(guildId); + + if (!guild) + return; + + guildName = guild->getName(); + + while(msg.getUnreadLength()) + { + guildMember = msg.readString(); + online = msg.readInt8(); + if(guildMember != "") + { + guild->addMember(guildMember); + guildWindow->setOnline(guildName, guildMember, online); + } + } + + guildWindow->updateTab(); + } + } break; + + case CPMSG_GUILD_UPDATE_LIST: + { + logger->log("Received CPMSG_GUILD_UPDATE_LIST"); + short guildId = msg.readInt16(); + std::string guildMember = msg.readString(); + char eventId = msg.readInt8(); + + Guild *guild = player_node->getGuild(guildId); + if (guild) + { + switch(eventId) + { + case GUILD_EVENT_NEW_PLAYER: + guild->addMember(guildMember); + guildWindow->setOnline(guild->getName(), guildMember, true); + break; + + case GUILD_EVENT_LEAVING_PLAYER: + guild->removeMember(guildMember); + break; + + case GUILD_EVENT_ONLINE_PLAYER: + guildWindow->setOnline(guild->getName(), guildMember, true); + break; + + case GUILD_EVENT_OFFLINE_PLAYER: + guildWindow->setOnline(guild->getName(), guildMember, false); + break; + + default: + logger->log("Invalid guild event"); + } + } + guildWindow->updateTab(); + + + } break; + + case CPMSG_GUILD_INVITED: + { + logger->log("Received CPMSG_GUILD_INVITED"); + std::string inviterName = msg.readString(); + std::string guildName = msg.readString(); + + // Open a dialog asking if the player accepts joining the guild. + guildWindow->openAcceptDialog(inviterName, guildName); + } break; + + case CPMSG_GUILD_PROMOTE_MEMBER_RESPONSE: + { + logger->log("Received CPMSG_GUILD_PROMOTE_MEMBER_RESPONSE"); + + if (msg.readInt8() == ERRMSG_OK) + { + // promotion succeeded + chatWindow->chatLog("Member was promoted successfully"); + } + else + { + // promotion failed + chatWindow->chatLog("Failed to promote member"); + } + } + + case CPMSG_GUILD_REJOIN: + { + logger->log("Received CPMSG_GUILD_REJOIN"); + + joinedGuild(msg); + } break; + + case CPMSG_GUILD_QUIT_RESPONSE: + { + logger->log("Received CPMSG_GUILD_QUIT_RESPONSE"); + + if (msg.readInt8() == ERRMSG_OK) + { + // Must remove tab first, as it wont find the guild + // name after its removed from the player + int guildId = msg.readInt16(); + Guild *guild = player_node->getGuild(guildId); + if (guild) + { + chatWindow->removeChannel(guild->getName()); + guildWindow->removeTab(guildId); + player_node->removeGuild(guildId); + } + } + } break; + } +} + +void GuildHandler::joinedGuild(MessageIn &msg) +{ + std::string guildName = msg.readString(); + short guildId = msg.readInt16(); + short permissions = msg.readInt16(); + short channelId = msg.readInt16(); + std::string announcement = msg.readString(); + + // Add guild to player and create new guild tab + Guild *guild = player_node->addGuild(guildId, permissions); + guild->setName(guildName); + guildWindow->newGuildTab(guildName); + guildWindow->requestMemberList(guildId); + + // Automatically create the guild channel + // COMMENT: Should this go here?? + Channel *channel = new Channel(channelId, guildName, announcement); + channelManager->addChannel(channel); + chatWindow->createNewChannelTab(guildName); + chatWindow->chatLog("Topic: " + announcement, BY_CHANNEL, guildName); +} diff --git a/src/net/guildhandler.h b/src/net/guildhandler.h new file mode 100644 index 00000000..4eb2da0b --- /dev/null +++ b/src/net/guildhandler.h @@ -0,0 +1,40 @@ +/* + * The Mana World + * Copyright 2008 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_GUILDHANDLER_H +#define _TMW_NET_GUILDHANDLER_H + +#include "messagehandler.h" + +#include <string> + +class GuildHandler : public MessageHandler +{ +public: + GuildHandler(); + + void handleMessage(MessageIn &msg); + +protected: + void joinedGuild(MessageIn &msg); +}; + +#endif diff --git a/src/net/internal.cpp b/src/net/internal.cpp new file mode 100644 index 00000000..4cb88a4e --- /dev/null +++ b/src/net/internal.cpp @@ -0,0 +1,27 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "internal.h" + +namespace Net +{ + int connections = 0; +} diff --git a/src/net/internal.h b/src/net/internal.h new file mode 100644 index 00000000..1e411605 --- /dev/null +++ b/src/net/internal.h @@ -0,0 +1,30 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_INTERNAL_H +#define _TMW_NET_INTERNAL_H + +namespace Net +{ + extern int connections; +} + +#endif diff --git a/src/net/inventoryhandler.cpp b/src/net/inventoryhandler.cpp index d5ac0c29..41032f13 100644 --- a/src/net/inventoryhandler.cpp +++ b/src/net/inventoryhandler.cpp @@ -1,227 +1,79 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "inventoryhandler.h" + #include <SDL_types.h> -#include "inventoryhandler.h" #include "messagein.h" #include "protocol.h" +#include "../equipment.h" #include "../inventory.h" #include "../item.h" #include "../itemshortcut.h" #include "../localplayer.h" -#include "../log.h" #include "../gui/chat.h" - #include "../resources/iteminfo.h" -#include "../utils/gettext.h" -#include "../utils/strprintf.h" -#include "../utils/stringutils.h" - InventoryHandler::InventoryHandler() { static const Uint16 _messages[] = { - SMSG_PLAYER_INVENTORY, - SMSG_PLAYER_INVENTORY_ADD, - SMSG_PLAYER_INVENTORY_REMOVE, - SMSG_PLAYER_INVENTORY_USE, - SMSG_ITEM_USE_RESPONSE, - SMSG_PLAYER_STORAGE_ITEMS, - SMSG_PLAYER_STORAGE_EQUIP, - SMSG_PLAYER_STORAGE_STATUS, - SMSG_PLAYER_STORAGE_ADD, - SMSG_PLAYER_STORAGE_REMOVE, - SMSG_PLAYER_STORAGE_CLOSE, + GPMSG_INVENTORY_FULL, + GPMSG_INVENTORY, 0 }; handledMessages = _messages; } -void InventoryHandler::handleMessage(MessageIn *msg) +void InventoryHandler::handleMessage(MessageIn &msg) { - Sint32 number; - Sint16 index, amount, itemId, equipType, arrow; - Sint16 identified, cards[4], itemType; - Inventory *inventory = player_node->getInventory(); - Inventory *storage = player_node->getStorage(); - - switch (msg->getId()) + switch (msg.getId()) { - case SMSG_PLAYER_INVENTORY: - case SMSG_PLAYER_STORAGE_ITEMS: - case SMSG_PLAYER_STORAGE_EQUIP: - switch (msg->getId()) { - case SMSG_PLAYER_INVENTORY: - // Clear inventory - this will be a complete refresh - inventory->clear(); - break; - case SMSG_PLAYER_STORAGE_ITEMS: - /* - * This packet will always be followed by a - * SMSG_PLAYER_STORAGE_EQUIP packet. The two packets - * together comprise a complete refresh of storage, so - * clear storage here - */ - storage->clear(); - logger->log("Received SMSG_PLAYER_STORAGE_ITEMS"); - break; - default: - logger->log("Received SMSG_PLAYER_STORAGE_EQUIP"); - break; - } - msg->readInt16(); // length - number = (msg->getLength() - 4) / 18; - - for (int loop = 0; loop < number; loop++) { - index = msg->readInt16(); - itemId = msg->readInt16(); - itemType = msg->readInt8(); - identified = msg->readInt8(); - if (msg->getId() == SMSG_PLAYER_STORAGE_EQUIP) { - amount = 1; - msg->readInt16(); // Equip Point? - } else { - amount = msg->readInt16(); - } - arrow = msg->readInt16(); - if (msg->getId() == SMSG_PLAYER_STORAGE_EQUIP) { - msg->readInt8(); // Attribute (broken) - msg->readInt8(); // Refine level + case GPMSG_INVENTORY_FULL: + player_node->clearInventory(); + // no break! + + case GPMSG_INVENTORY: + while (msg.getUnreadLength()) + { + int slot = msg.readInt8(); + if (slot == 255) + { + player_node->setMoney(msg.readInt32()); + continue; } - for (int i = 0; i < 4; i++) - cards[i] = msg->readInt16(); - if (msg->getId() == SMSG_PLAYER_INVENTORY) { - inventory->setItem(index, itemId, amount, false); - - // Trick because arrows are not considered equipment - if (arrow & 0x8000) { - if (Item *item = inventory->getItem(index)) - item->setEquipment(true); - } - } else { - logger->log("Index:%d, ID:%d, Type:%d, Identified:%d, Qty:%d, Cards:%d, %d, %d, %d", - index, itemId, itemType, identified, amount, cards[0], cards[1], cards[2], cards[3]); - storage->setItem(index, itemId, amount, false); + int id = msg.readInt16(); + if (slot < EQUIPMENT_SIZE) + { + player_node->mEquipment->setEquipment(slot, id); } - } - break; - - case SMSG_PLAYER_INVENTORY_ADD: - index = msg->readInt16(); - amount = msg->readInt16(); - itemId = msg->readInt16(); - identified = msg->readInt8(); - msg->readInt8(); // attribute - msg->readInt8(); // refine - for (int i = 0; i < 4; i++) - cards[i] = msg->readInt16(); - equipType = msg->readInt16(); - itemType = msg->readInt8(); - - if (msg->readInt8() > 0) { - chatWindow->chatLog(_("Unable to pick up item"), BY_SERVER); - } else { - const ItemInfo &itemInfo = ItemDB::get(itemId); - const std::string amountStr = - (amount > 1) ? toString(amount) : "a"; - chatWindow->chatLog(strprintf(_("You picked up %s %s"), - amountStr.c_str(), itemInfo.getName().c_str()), BY_SERVER); - - if (Item *item = inventory->getItem(index)) { - item->setId(itemId); - item->increaseQuantity(amount); - } else { - inventory->setItem(index, itemId, amount, equipType != 0); + else if (slot >= 32 && slot < 32 + INVENTORY_SIZE) + { + int amount = id ? msg.readInt8() : 0; + player_node->setInvItem(slot - 32, id, amount); } - } - break; - - case SMSG_PLAYER_INVENTORY_REMOVE: - index = msg->readInt16(); - amount = msg->readInt16(); - if (Item *item = inventory->getItem(index)) { - item->increaseQuantity(-amount); - if (item->getQuantity() == 0) - inventory->removeItemAt(index); - } - break; - - case SMSG_PLAYER_INVENTORY_USE: - index = msg->readInt16(); - msg->readInt16(); // item id - msg->readInt32(); // id - amount = msg->readInt16(); - msg->readInt8(); // type - - if (Item *item = inventory->getItem(index)) - item->setQuantity(amount); - break; - - case SMSG_ITEM_USE_RESPONSE: - index = msg->readInt16(); - amount = msg->readInt16(); - - if (msg->readInt8() == 0) { - chatWindow->chatLog(_("Failed to use item"), BY_SERVER); - } else { - if (Item *item = inventory->getItem(index)) - item->setQuantity(amount); - } - break; - - case SMSG_PLAYER_STORAGE_STATUS: - /* - * Basic slots used vs total slots info - * We don't really need this information, but this is - * the closest we get to an "Open Storage" packet - * from the server. It always comes after the two - * SMSG_PLAYER_STORAGE_... packets that update - * storage contents. - */ - logger->log("Received SMSG_PLAYER_STORAGE_STATUS"); - player_node->setInStorage(true); - break; - - case SMSG_PLAYER_STORAGE_ADD: - /* - * Move an item into storage - */ - break; - - case SMSG_PLAYER_STORAGE_REMOVE: - /* - * Move an item out of storage - */ - break; - - case SMSG_PLAYER_STORAGE_CLOSE: - /* - * Storage access has been closed - */ - player_node->setInStorage(false); - logger->log("Received SMSG_PLAYER_STORAGE_CLOSE"); + }; break; } } diff --git a/src/net/inventoryhandler.h b/src/net/inventoryhandler.h index 336b2e98..9b457abe 100644 --- a/src/net/inventoryhandler.h +++ b/src/net/inventoryhandler.h @@ -29,7 +29,7 @@ class InventoryHandler : public MessageHandler public: InventoryHandler(); - void handleMessage(MessageIn *msg); + void handleMessage(MessageIn &msg); }; #endif diff --git a/src/net/itemhandler.cpp b/src/net/itemhandler.cpp index 8c4af4e4..189c6eb9 100644 --- a/src/net/itemhandler.cpp +++ b/src/net/itemhandler.cpp @@ -29,39 +29,36 @@ ItemHandler::ItemHandler() { static const Uint16 _messages[] = { - SMSG_ITEM_VISIBLE, - SMSG_ITEM_DROPPED, - SMSG_ITEM_REMOVE, + GPMSG_ITEMS, + GPMSG_ITEM_APPEAR, 0 }; handledMessages = _messages; } -void ItemHandler::handleMessage(MessageIn *msg) +void ItemHandler::handleMessage(MessageIn &msg) { - Uint32 id; - Uint16 x, y; - Sint16 itemId; - - switch (msg->getId()) + switch (msg.getId()) { - case SMSG_ITEM_VISIBLE: - case SMSG_ITEM_DROPPED: - id = msg->readInt32(); - itemId = msg->readInt16(); - msg->readInt8(); // identify flag - x = msg->readInt16(); - y = msg->readInt16(); - msg->skip(4); // amount,subX,subY / subX,subY,amount - - floorItemManager->create(id, itemId, x, y, engine->getCurrentMap()); - break; + case GPMSG_ITEM_APPEAR: + case GPMSG_ITEMS: + { + while (msg.getUnreadLength()) + { + int itemId = msg.readInt16(); + int x = msg.readInt16(); + int y = msg.readInt16(); + int id = (x << 16) | y; // dummy id - case SMSG_ITEM_REMOVE: - FloorItem *item; - item = floorItemManager->findById(msg->readInt32()); - if (item) - floorItemManager->destroy(item); - break; + if (itemId) + { + floorItemManager->create(id, itemId, x / 32, y / 32, engine->getCurrentMap()); + } + else if (FloorItem *item = floorItemManager->findById(id)) + { + floorItemManager->destroy(item); + } + } + } break; } } diff --git a/src/net/itemhandler.h b/src/net/itemhandler.h index 0cb3b42a..12057bb1 100644 --- a/src/net/itemhandler.h +++ b/src/net/itemhandler.h @@ -29,7 +29,7 @@ class ItemHandler : public MessageHandler public: ItemHandler(); - void handleMessage(MessageIn *msg); + void handleMessage(MessageIn &msg); }; #endif diff --git a/src/net/loginhandler.cpp b/src/net/loginhandler.cpp index 2695fc7b..37c3608a 100644 --- a/src/net/loginhandler.cpp +++ b/src/net/loginhandler.cpp @@ -1,158 +1,225 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "loginhandler.h" + #include "messagein.h" #include "protocol.h" -#include "../log.h" #include "../logindata.h" #include "../main.h" -#include "../serverinfo.h" - -#include "../utils/gettext.h" -#include "../utils/strprintf.h" -#include "../utils/stringutils.h" - -extern SERVER_INFO **server_info; LoginHandler::LoginHandler() { static const Uint16 _messages[] = { - SMSG_CONNECTION_PROBLEM, - SMSG_UPDATE_HOST, - 0x0069, - 0x006a, + APMSG_LOGIN_RESPONSE, + APMSG_REGISTER_RESPONSE, + APMSG_RECONNECT_RESPONSE, + APMSG_PASSWORD_CHANGE_RESPONSE, + APMSG_EMAIL_CHANGE_RESPONSE, 0 }; handledMessages = _messages; } -void LoginHandler::handleMessage(MessageIn *msg) +void LoginHandler::setLoginData(LoginData *loginData) { - int code; + mLoginData = loginData; +} - switch (msg->getId()) +void LoginHandler::handleMessage(MessageIn &msg) +{ + switch (msg.getId()) { - case SMSG_CONNECTION_PROBLEM: - code = msg->readInt8(); - logger->log("Connection problem: %i", code); - - switch (code) { - case 0: - errorMessage = _("Authentication failed"); - break; - case 1: - errorMessage = _("No servers available"); - break; - case 2: - errorMessage = _("This account is already logged in"); - break; - default: - errorMessage = _("Unknown connection error"); - break; + case APMSG_LOGIN_RESPONSE: + handleLoginResponse(msg); + break; + case APMSG_REGISTER_RESPONSE: + handleRegisterResponse(msg); + break; + case APMSG_RECONNECT_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful login + if (errMsg == ERRMSG_OK) + { + state = STATE_CHAR_SELECT; } - state = ERROR_STATE; + // Login failed + else + { + switch (errMsg) { + case ERRMSG_INVALID_ARGUMENT: + errorMessage = "Wrong magic_token"; + break; + case ERRMSG_FAILURE: + errorMessage = "Already logged in"; + break; + case LOGIN_SERVER_FULL: + errorMessage = "Server is full"; + break; + default: + errorMessage = "Unknown error"; + break; + } + state = STATE_ERROR; + } + } break; - case SMSG_UPDATE_HOST: - int len; - - len = msg->readInt16() - 4; - mUpdateHost = msg->readString(len); - - logger->log("Received update host \"%s\" from login server", - mUpdateHost.c_str()); - break; - - case 0x0069: - // Skip the length word - msg->skip(2); - - n_server = (msg->getLength() - 47) / 32; - server_info = - (SERVER_INFO**) malloc(sizeof(SERVER_INFO*) * n_server); - - mLoginData->session_ID1 = msg->readInt32(); - mLoginData->account_ID = msg->readInt32(); - mLoginData->session_ID2 = msg->readInt32(); - msg->skip(30); // unknown - mLoginData->sex = msg->readInt8(); - - for (int i = 0; i < n_server; i++) + case APMSG_PASSWORD_CHANGE_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful pass change + if (errMsg == ERRMSG_OK) { - server_info[i] = new SERVER_INFO; - - server_info[i]->address = msg->readInt32(); - server_info[i]->port = msg->readInt16(); - server_info[i]->name = msg->readString(20); - server_info[i]->online_users = msg->readInt32(); - server_info[i]->updateHost = mUpdateHost; - msg->skip(2); // unknown + state = STATE_CHANGEPASSWORD; + } + // pass change failed + else + { + switch (errMsg) { + case ERRMSG_INVALID_ARGUMENT: + errorMessage = "New password incorrect"; + break; + case ERRMSG_FAILURE: + errorMessage = "Old password incorrect"; + break; + case ERRMSG_NO_LOGIN: + errorMessage = "Account not connected. Please login first."; + break; + default: + errorMessage = "Unknown error"; + break; + } + state = STATE_ACCOUNTCHANGE_ERROR; + } + } + break; - logger->log("Network: Server: %s (%s:%d)", - server_info[i]->name.c_str(), - ipToString(server_info[i]->address), - server_info[i]->port); + case APMSG_EMAIL_CHANGE_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful pass change + if (errMsg == ERRMSG_OK) + { + state = STATE_CHANGEEMAIL; } - state = CHAR_SERVER_STATE; + // pass change failed + else + { + switch (errMsg) { + case ERRMSG_INVALID_ARGUMENT: + errorMessage = "New email address incorrect"; + break; + case ERRMSG_FAILURE: + errorMessage = "Old email address incorrect"; + break; + case ERRMSG_NO_LOGIN: + errorMessage = "Account not connected. Please login first."; + break; + case ERRMSG_EMAIL_ALREADY_EXISTS: + errorMessage = "The new Email Address already exists."; + break; + default: + errorMessage = "Unknown error"; + break; + } + state = STATE_ACCOUNTCHANGE_ERROR; + } + } break; - case 0x006a: - code = msg->readInt8(); - logger->log("Login::error code: %i", code); + } +} - switch (code) { - case 0: - errorMessage = _("Unregistered ID"); - break; - case 1: - errorMessage = _("Wrong password"); - break; - case 2: - errorMessage = _("Account expired"); - break; - case 3: - errorMessage = _("Rejected from server"); - break; - case 4: +void LoginHandler::handleLoginResponse(MessageIn &msg) +{ + const int errMsg = msg.readInt8(); - errorMessage = _("You have been permanently banned from " - "the game. Please contact the GM Team."); - break; - case 6: - errorMessage = strprintf(_("You have been temporarily " - "banned from the game until " - "%s.\n Please contact the GM " - "team via the forums."), - msg->readString(20).c_str()); - break; - case 9: - errorMessage = _("This user name is already taken"); - break; - default: - errorMessage = _("Unknown error"); - break; - } - state = ERROR_STATE; - break; + if (errMsg == ERRMSG_OK) + { + readUpdateHost(msg); + state = STATE_CHAR_SELECT; + } + else + { + switch (errMsg) { + case LOGIN_INVALID_VERSION: + errorMessage = "Client version is too old"; + break; + case ERRMSG_INVALID_ARGUMENT: + errorMessage = "Wrong username or password"; + break; + case ERRMSG_FAILURE: + errorMessage = "Already logged in"; + break; + case LOGIN_SERVER_FULL: + errorMessage = "Server is full"; + break; + default: + errorMessage = "Unknown error"; + break; + } + state = STATE_LOGIN_ERROR; + } +} + +void LoginHandler::handleRegisterResponse(MessageIn &msg) +{ + const int errMsg = msg.readInt8(); + + if (errMsg == ERRMSG_OK) + { + readUpdateHost(msg); + state = STATE_CHAR_SELECT; + } + else + { + switch (errMsg) { + case REGISTER_INVALID_VERSION: + errorMessage = "Client version is too old"; + break; + case ERRMSG_INVALID_ARGUMENT: + errorMessage = "Wrong username, password or email address"; + break; + case REGISTER_EXISTS_USERNAME: + errorMessage = "Username already exists"; + break; + case REGISTER_EXISTS_EMAIL: + errorMessage = "Email address already exists"; + break; + default: + errorMessage = "Unknown error"; + break; + } + state = STATE_LOGIN_ERROR; + } +} + +void LoginHandler::readUpdateHost(MessageIn &msg) +{ + // Set the update host when included in the message + if (msg.getUnreadLength() > 0) + { + mLoginData->updateHost = msg.readString(); } } diff --git a/src/net/loginhandler.h b/src/net/loginhandler.h index df86b634..015d6383 100644 --- a/src/net/loginhandler.h +++ b/src/net/loginhandler.h @@ -1,45 +1,47 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef NET_LOGINHANDLER_H -#define NET_LOGINHANDLER_H - -#include <string> +#ifndef _TMW_NET_LOGINHANDLER_H +#define _TMW_NET_LOGINHANDLER_H #include "messagehandler.h" -struct LoginData; +class LoginData; class LoginHandler : public MessageHandler { public: LoginHandler(); - void handleMessage(MessageIn *msg); + void setLoginData(LoginData *loginData); - void setLoginData(LoginData *loginData) { mLoginData = loginData; }; + void handleMessage(MessageIn &msg); private: + void handleLoginResponse(MessageIn &msg); + void handleRegisterResponse(MessageIn &msg); + + void readUpdateHost(MessageIn &msg); + LoginData *mLoginData; - std::string mUpdateHost; }; -#endif +#endif // _TMW_NET_LOGINHANDLER_H diff --git a/src/net/logouthandler.cpp b/src/net/logouthandler.cpp new file mode 100644 index 00000000..6dea4c83 --- /dev/null +++ b/src/net/logouthandler.cpp @@ -0,0 +1,215 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "logouthandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../main.h" + +LogoutHandler::LogoutHandler(): +mPassToken(NULL), mScenario(LOGOUT_EXIT), +mLoggedOutAccount(false), mLoggedOutGame(false), mLoggedOutChat(false) +{ + static const Uint16 _messages[] = { + APMSG_LOGOUT_RESPONSE, + APMSG_UNREGISTER_RESPONSE, + GPMSG_DISCONNECT_RESPONSE, + CPMSG_DISCONNECT_RESPONSE, + 0 + }; + handledMessages = _messages; +} + +void LogoutHandler::handleMessage(MessageIn &msg) +{ + switch (msg.getId()) + { + case APMSG_LOGOUT_RESPONSE: + { + int errMsg = msg.readInt8(); + + // Successful logout + if (errMsg == ERRMSG_OK) + { + mLoggedOutAccount = true; + + switch (mScenario) + { + case LOGOUT_SWITCH_ACCOUNTSERVER: + if (mLoggedOutGame && mLoggedOutChat) + state = STATE_SWITCH_ACCOUNTSERVER; + break; + + case LOGOUT_EXIT: + default: + if (mLoggedOutGame && mLoggedOutChat) + state = STATE_FORCE_QUIT; + break; + } + } + // Logout failed + else + { + switch (errMsg) { + case ERRMSG_NO_LOGIN: + errorMessage = "Accountserver: Not logged in"; + break; + default: + errorMessage = "Accountserver: Unknown error"; + break; + } + state = STATE_ERROR; + } + } + break; + case APMSG_UNREGISTER_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful unregistration + if (errMsg == ERRMSG_OK) + { + state = STATE_UNREGISTER; + } + // Unregistration failed + else + { + switch (errMsg) { + case ERRMSG_INVALID_ARGUMENT: + errorMessage = + "Accountserver: Wrong username or password"; + break; + default: + errorMessage = "Accountserver: Unknown error"; + break; + } + state = STATE_ACCOUNTCHANGE_ERROR; + } + } + break; + case GPMSG_DISCONNECT_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful logout + if (errMsg == ERRMSG_OK) + { + mLoggedOutGame = true; + + switch (mScenario) + { + case LOGOUT_SWITCH_CHARACTER: + if (mPassToken) + { + *mPassToken = msg.readString(32); + mPassToken = NULL; + } + if (mLoggedOutChat) state = STATE_RECONNECT_ACCOUNT; + break; + + case LOGOUT_SWITCH_ACCOUNTSERVER: + if (mLoggedOutAccount && mLoggedOutChat) + state = STATE_SWITCH_ACCOUNTSERVER; + break; + + case LOGOUT_EXIT: + default: + if (mLoggedOutAccount && mLoggedOutChat) + state = STATE_FORCE_QUIT; + break; + } + } + // Logout failed + else + { + switch (errMsg) { + case ERRMSG_NO_LOGIN: + errorMessage = "Gameserver: Not logged in"; + break; + default: + errorMessage = "Gameserver: Unknown error"; + break; + } + state = STATE_ERROR; + } + } + break; + case CPMSG_DISCONNECT_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful logout + if (errMsg == ERRMSG_OK) + { + mLoggedOutChat = true; + + switch (mScenario) + { + case LOGOUT_SWITCH_CHARACTER: + if (mLoggedOutGame) state = STATE_RECONNECT_ACCOUNT; + break; + + case LOGOUT_SWITCH_ACCOUNTSERVER: + if (mLoggedOutAccount && mLoggedOutGame) + state = STATE_SWITCH_ACCOUNTSERVER; + break; + + case LOGOUT_EXIT: + default: + if (mLoggedOutAccount && mLoggedOutGame) + { + state = STATE_FORCE_QUIT; + } + break; + } + } + else + { + switch (errMsg) { + case ERRMSG_NO_LOGIN: + errorMessage = "Chatserver: Not logged in"; + break; + default: + errorMessage = "Chatserver: Unknown error"; + break; + } + state = STATE_ERROR; + } + } + break; + } +} + +void +LogoutHandler::setScenario(unsigned short scenario, std::string* passToken) +{ + mScenario = scenario; + mPassToken = passToken; +} + +void +LogoutHandler::reset() +{ + mPassToken = NULL; + mScenario = LOGOUT_EXIT; + mLoggedOutAccount = false; + mLoggedOutGame = false; + mLoggedOutChat = false; +} diff --git a/src/net/logouthandler.h b/src/net/logouthandler.h new file mode 100644 index 00000000..369eaa80 --- /dev/null +++ b/src/net/logouthandler.h @@ -0,0 +1,62 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World 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. + * + * The Mana World 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 The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _TMW_NET_LOGOUTHANDLER_H +#define _TMW_NET_LOGOUTHANDLER_H + +#include <string> + +#include "messagehandler.h" + +/** + * The different scenarios for which LogoutHandler can be used + */ +enum { + LOGOUT_EXIT, + LOGOUT_SWITCH_ACCOUNTSERVER, + LOGOUT_SWITCH_CHARACTER +}; + +class LogoutHandler : public MessageHandler +{ + public: + LogoutHandler(); + + void handleMessage(MessageIn &msg); + + void setScenario(unsigned short scenario, + std::string* passToken = NULL); + + void reset(); + + void setAccountLoggedOut(){ mLoggedOutAccount = true; } + void setGameLoggedOut(){ mLoggedOutGame = true; } + void setChatLoggedOut(){ mLoggedOutChat = true; } + + private: + std::string* mPassToken; + unsigned short mScenario; + bool mLoggedOutAccount; + bool mLoggedOutGame; + bool mLoggedOutChat; +}; + +#endif diff --git a/src/net/messagehandler.cpp b/src/net/messagehandler.cpp index f1561a31..a765d0e8 100644 --- a/src/net/messagehandler.cpp +++ b/src/net/messagehandler.cpp @@ -22,21 +22,33 @@ #include <cassert> #include "messagehandler.h" +#ifdef TMWSERV_SUPPORT #include "network.h" +#else +#include "ea/network.h" +#endif -MessageHandler::MessageHandler(): - mNetwork(0) +MessageHandler::MessageHandler() +#ifdef EATHENA_SUPPORT + : mNetwork(0) +#endif { } MessageHandler::~MessageHandler() { +#ifdef TMWSERV_SUPPORT + Net::unregisterHandler(this); +#else if (mNetwork) mNetwork->unregisterHandler(this); +#endif } +#ifdef EATHENA_SUPPORT void MessageHandler::setNetwork(Network *network) { assert(!(network && mNetwork)); mNetwork = network; } +#endif diff --git a/src/net/messagehandler.h b/src/net/messagehandler.h index 45cdf8cd..261a8351 100644 --- a/src/net/messagehandler.h +++ b/src/net/messagehandler.h @@ -25,8 +25,13 @@ #include <SDL_types.h> class MessageIn; +#ifdef EATHENA_SUPPORT class Network; +#endif +/** + * \ingroup Network + */ class MessageHandler { public: @@ -35,12 +40,14 @@ class MessageHandler MessageHandler(); virtual ~MessageHandler(); - virtual void handleMessage(MessageIn *msg) =0; + virtual void handleMessage(MessageIn &msg) = 0; +#ifdef EATHENA_SUPPORT void setNetwork(Network *network); protected: Network *mNetwork; +#endif }; #endif diff --git a/src/net/messagein.cpp b/src/net/messagein.cpp index a288d936..813b440f 100644 --- a/src/net/messagein.cpp +++ b/src/net/messagein.cpp @@ -19,11 +19,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <cassert> +#include "messagein.h" + +#ifdef TMWSERV_SUPPORT +#include <enet/enet.h> +#else #include <SDL.h> #include <SDL_endian.h> +#endif -#include "messagein.h" +#include <cassert> #define MAKEWORD(low,high) \ ((unsigned short)(((unsigned char)(low)) | \ @@ -38,32 +43,68 @@ MessageIn::MessageIn(const char *data, unsigned int length): mId = readInt16(); } -Sint8 MessageIn::readInt8() +int MessageIn::readInt8() { - assert(mPos < mLength); - return mData[mPos++]; + int value = -1; + if (mPos < mLength) + { + value = (unsigned char) mData[mPos]; + } + mPos += 1; + return value; } -Sint16 MessageIn::readInt16() +int MessageIn::readInt16() { - assert(mPos + 2 <= mLength); - mPos += 2; + int value = -1; + if (mPos + 2 <= mLength) + { +#ifdef TMWSERV_SUPPORT + uint16_t t; + memcpy(&t, mData + mPos, 2); + value = (unsigned short) ENET_NET_TO_HOST_16(t); +#else #if SDL_BYTEORDER == SDL_BIG_ENDIAN - return SDL_Swap16(*(Sint16*)(mData + (mPos - 2))); + value = SDL_Swap16(*(Sint16*)(mData + mPos)); #else - return (*(Sint16*)(mData + (mPos - 2))); + value = (*(Sint16*)(mData + mPos)); #endif +#endif // TMWSERV_SUPPORT + } + mPos += 2; + return value; } -Sint32 MessageIn::readInt32() +int MessageIn::readInt32() { - assert(mPos + 4 <= mLength); - mPos += 4; + int value = -1; + if (mPos + 4 <= mLength) + { +#ifdef TMWSERV_SUPPORT + uint32_t t; + memcpy(&t, mData + mPos, 4); + value = ENET_NET_TO_HOST_32(t); +#else #if SDL_BYTEORDER == SDL_BIG_ENDIAN - return SDL_Swap32(*(Sint32*)(mData + (mPos - 4))); + value = SDL_Swap32(*(Sint32*)(mData + mPos)); #else - return (*(Sint32*)(mData + (mPos - 4))); + value = (*(Sint32*)(mData + mPos)); #endif +#endif // TMWSERV_SUPPORT + } + mPos += 4; + return value; +} + +void MessageIn::readCoordinates(Uint16 &x, Uint16 &y) +{ + if (mPos + 3 <= mLength) + { + unsigned char const *p = reinterpret_cast< unsigned char const * >(mData + mPos); + x = p[0] | ((p[1] & 0x07) << 8); + y = (p[1] >> 3) | ((p[2] & 0x3F) << 5); + } + mPos += 3; } void MessageIn::readCoordinates(Uint16 &x, Uint16 &y, Uint8 &direction) @@ -164,21 +205,3 @@ std::string MessageIn::readString(int length) mPos += length; return readString; } - -Sint8& operator<<(Sint8 &lhs, MessageIn &msg) -{ - lhs = msg.readInt8(); - return lhs; -} - -Sint16& operator<<(Sint16 &lhs, MessageIn &msg) -{ - lhs = msg.readInt16(); - return lhs; -} - -Sint32& operator<<(Sint32 &lhs, MessageIn &msg) -{ - lhs = msg.readInt32(); - return lhs; -} diff --git a/src/net/messagein.h b/src/net/messagein.h index 0ff6e78a..0d0e9981 100644 --- a/src/net/messagein.h +++ b/src/net/messagein.h @@ -27,13 +27,11 @@ /** * Used for parsing an incoming message. + * + * \ingroup Network */ class MessageIn { - friend Sint8& operator<<(Sint8 &lhs, MessageIn &msg); - friend Sint16& operator<<(Sint16 &lhs, MessageIn &msg); - friend Sint32& operator<<(Sint32 &lhs, MessageIn &msg); - public: /** * Constructor. @@ -43,16 +41,27 @@ class MessageIn /** * Returns the message ID. */ - short getId() { return mId; } + int getId() const { return mId; } /** * Returns the message length. */ - unsigned int getLength() { return mLength; } + unsigned int getLength() const { return mLength; } + + /** + * Returns the length of unread data. + */ + unsigned int getUnreadLength() const { return mLength - mPos; } - Sint8 readInt8(); /**< Reads a byte. */ - Sint16 readInt16(); /**< Reads a short. */ - Sint32 readInt32(); /**< Reads a long. */ + int readInt8(); /**< Reads a byte. */ + int readInt16(); /**< Reads a short. */ + int readInt32(); /**< Reads a long. */ + + /** + * Reads a 3-byte block containing tile-based coordinates. Used by + * tmwserv. + */ + void readCoordinates(Uint16 &x, Uint16 &y); /** * Reads a special 3 byte block used by eAthena, containing x and y @@ -82,8 +91,14 @@ class MessageIn private: const char* mData; /**< The message data. */ unsigned int mLength; /**< The length of the data. */ - unsigned int mPos; /**< The position in the data. */ - short mId; /**< The message ID. */ + unsigned short mId; /**< The message ID. */ + + /** + * Actual position in the packet. From 0 to packet->length. + * A value bigger than packet->length means EOP was reached when + * reading it. + */ + unsigned int mPos; }; #endif diff --git a/src/net/messageout.cpp b/src/net/messageout.cpp index bf4957be..f7ab6b41 100644 --- a/src/net/messageout.cpp +++ b/src/net/messageout.cpp @@ -19,95 +19,131 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <cstring> +#include "messageout.h" + +#ifdef TMWSERV_SUPPORT +#include <enet/enet.h> +#else +#include "ea/network.h" #include <SDL.h> #include <SDL_endian.h> -#include <string> +#endif -#include "messageout.h" -#include "network.h" +#include <cstring> +#include <string> +#ifdef TMWSERV_SUPPORT +MessageOut::MessageOut(short id): + mData(0), +#else MessageOut::MessageOut(Network *network): mNetwork(network), - mData(0), +#endif mDataSize(0), mPos(0) { +#ifdef TMWSERV_SUPPORT + writeInt16(id); +#else mData = mNetwork->mOutBuffer + mNetwork->mOutSize; +#endif +} + +#ifdef TMWSERV_SUPPORT +MessageOut::~MessageOut() +{ + free(mData); +} + +void MessageOut::expand(size_t bytes) +{ + mData = (char*)realloc(mData, bytes); + mDataSize = bytes; } +#endif void MessageOut::writeInt8(Sint8 value) { +#ifdef TMWSERV_SUPPORT + expand(mPos + 1); +#else + mNetwork->mOutSize += 1; +#endif mData[mPos] = value; - mPos += sizeof(Sint8); - mNetwork->mOutSize+= sizeof(Sint8); + mPos += 1; } void MessageOut::writeInt16(Sint16 value) { +#ifdef TMWSERV_SUPPORT + expand(mPos + 2); + uint16_t t = ENET_HOST_TO_NET_16(value); + memcpy(mData + mPos, &t, 2); +#else #if SDL_BYTEORDER == SDL_BIG_ENDIAN (*(Sint16 *)(mData + mPos)) = SDL_Swap16(value); #else (*(Sint16 *)(mData + mPos)) = value; #endif - mPos += sizeof(Sint16); - mNetwork->mOutSize += sizeof(Sint16); + mNetwork->mOutSize += 2; +#endif // TMWSERV_SUPPORT + mPos += 2; } void MessageOut::writeInt32(Sint32 value) { +#ifdef TMWSERV_SUPPORT + expand(mPos + 4); + uint32_t t = ENET_HOST_TO_NET_32(value); + memcpy(mData + mPos, &t, 4); +#else #if SDL_BYTEORDER == SDL_BIG_ENDIAN (*(Sint32 *)(mData + mPos)) = SDL_Swap32(value); #else (*(Sint32 *)(mData + mPos)) = value; #endif - mPos += sizeof(Sint32); - mNetwork->mOutSize += sizeof(Sint32); + mNetwork->mOutSize += 4; +#endif // TMWSERV_SUPPORT + mPos += 4; } void MessageOut::writeString(const std::string &string, int length) { - std::string toWrite = string; - + int stringLength = string.length(); if (length < 0) { // Write the length at the start if not fixed - writeInt16(string.length()); + writeInt16(stringLength); + length = stringLength; } - else + else if (length < stringLength) { // Make sure the length of the string is no longer than specified - toWrite = string.substr(0, length); + stringLength = length; } +#ifdef TMWSERV_SUPPORT + expand(mPos + length); +#else + mNetwork->mOutSize += length; +#endif // Write the actual string - memcpy(&mData[mPos], (void*)toWrite.c_str(), toWrite.length()); - mPos += toWrite.length(); - mNetwork->mOutSize += toWrite.length(); + memcpy(mData + mPos, string.c_str(), stringLength); // Pad remaining space with zeros - if (length > (int)toWrite.length()) + if (length > stringLength) { - memset(&mData[mPos], '\0', length - toWrite.length()); - mPos += length - toWrite.length(); - mNetwork->mOutSize += length - toWrite.length(); + memset(mData + mPos + stringLength, '\0', length - stringLength); } + mPos += length; } -MessageOut& operator<<(MessageOut &msg, const Sint8 &rhs) -{ - msg.writeInt8(rhs); - return msg; -} - -MessageOut& operator<<(MessageOut &msg, const Sint16 &rhs) +char *MessageOut::getData() const { - msg.writeInt16(rhs); - return msg; + return mData; } -MessageOut& operator<<(MessageOut &msg, const Sint32 &rhs) +unsigned int MessageOut::getDataSize() const { - msg.writeInt32(rhs); - return msg; + return mDataSize; } diff --git a/src/net/messageout.h b/src/net/messageout.h index b3a4ef68..bc701b92 100644 --- a/src/net/messageout.h +++ b/src/net/messageout.h @@ -25,26 +25,38 @@ #include <iosfwd> #include <SDL_types.h> +#ifdef EATHENA_SUPPORT class Network; +#endif /** * Used for building an outgoing message. + * + * With tmwserv, the message is sent using Net::Connection::send() when + * finished. + * + * \ingroup Network */ class MessageOut { - friend MessageOut& operator<<(MessageOut &msg, const Sint8 &rhs); - friend MessageOut& operator<<(MessageOut &msg, const Sint16 &rhs); - friend MessageOut& operator<<(MessageOut &msg, const Sint32 &rhs); - public: /** * Constructor. */ +#ifdef TMWSERV_SUPPORT + MessageOut(short id); + + /** + * Destructor. + */ + ~MessageOut(); +#else MessageOut(Network *network); +#endif void writeInt8(Sint8 value); /**< Writes a byte. */ void writeInt16(Sint16 value); /**< Writes a short. */ - void writeInt32(Sint32 value); /**< Writes a long. */ + void writeInt32(Sint32 value); /**< Writes a long. */ /** * Writes a string. If a fixed length is not given (-1), it is stored @@ -52,8 +64,29 @@ class MessageOut */ void writeString(const std::string &string, int length = -1); + /** + * Returns the content of the message. + */ + char *getData() const; + + /** + * Returns the length of the data. + */ + unsigned int getDataSize() const; + private: +#ifdef TMWSERV_SUPPORT + /** + * Expand the packet data to be able to hold more data. + * + * NOTE: For performance enhancements this method could allocate extra + * memory in advance instead of expanding size every time more data is + * added. + */ + void expand(size_t size); +#else Network *mNetwork; +#endif char *mData; /**< Data building up. */ unsigned int mDataSize; /**< Size of data. */ diff --git a/src/net/network.cpp b/src/net/network.cpp index 3fa046c4..57e368e2 100644 --- a/src/net/network.cpp +++ b/src/net/network.cpp @@ -1,436 +1,169 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <sstream> - -#include "messagehandler.h" -#include "messagein.h" #include "network.h" -#include "../log.h" -#include "../utils/stringutils.h" - -/** Warning: buffers and other variables are shared, - so there can be only one connection active at a time */ - -short packet_lengths[] = { - 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -// #0x0040 - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, -1, 55, 17, 3, 37, 46, -1, 23, -1, 3,108, 3, 2, - 3, 28, 19, 11, 3, -1, 9, 5, 54, 53, 58, 60, 41, 2, 6, 6, -// #0x0080 - 7, 3, 2, 2, 2, 5, 16, 12, 10, 7, 29, 23, -1, -1, -1, 0, - 7, 22, 28, 2, 6, 30, -1, -1, 3, -1, -1, 5, 9, 17, 17, 6, - 23, 6, 6, -1, -1, -1, -1, 8, 7, 6, 7, 4, 7, 0, -1, 6, - 8, 8, 3, 3, -1, 6, 6, -1, 7, 6, 2, 5, 6, 44, 5, 3, -// #0x00C0 - 7, 2, 6, 8, 6, 7, -1, -1, -1, -1, 3, 3, 6, 6, 2, 27, - 3, 4, 4, 2, -1, -1, 3, -1, 6, 14, 3, -1, 28, 29, -1, -1, - 30, 30, 26, 2, 6, 26, 3, 3, 8, 19, 5, 2, 3, 2, 2, 2, - 3, 2, 6, 8, 21, 8, 8, 2, 2, 26, 3, -1, 6, 27, 30, 10, -// #0x0100 - 2, 6, 6, 30, 79, 31, 10, 10, -1, -1, 4, 6, 6, 2, 11, -1, - 10, 39, 4, 10, 31, 35, 10, 18, 2, 13, 15, 20, 68, 2, 3, 16, - 6, 14, -1, -1, 21, 8, 8, 8, 8, 8, 2, 2, 3, 4, 2, -1, - 6, 86, 6, -1, -1, 7, -1, 6, 3, 16, 4, 4, 4, 6, 24, 26, -// #0x0140 - 22, 14, 6, 10, 23, 19, 6, 39, 8, 9, 6, 27, -1, 2, 6, 6, - 110, 6, -1, -1, -1, -1, -1, 6, -1, 54, 66, 54, 90, 42, 6, 42, - -1, -1, -1, -1, -1, 30, -1, 3, 14, 3, 30, 10, 43, 14,186,182, - 14, 30, 10, 3, -1, 6,106, -1, 4, 5, 4, -1, 6, 7, -1, -1, -// #0x0180 - 6, 3,106, 10, 10, 34, 0, 6, 8, 4, 4, 4, 29, -1, 10, 6, - 90, 86, 24, 6, 30,102, 9, 4, 8, 4, 14, 10, 4, 6, 2, 6, - 3, 3, 35, 5, 11, 26, -1, 4, 4, 6, 10, 12, 6, -1, 4, 4, - 11, 7, -1, 67, 12, 18,114, 6, 3, 6, 26, 26, 26, 26, 2, 3, -// #0x01C0 - 2, 14, 10, -1, 22, 22, 4, 2, 13, 97, 0, 9, 9, 29, 6, 28, - 8, 14, 10, 35, 6, 8, 4, 11, 54, 53, 60, 2, -1, 47, 33, 6, - 30, 8, 34, 14, 2, 6, 26, 2, 28, 81, 6, 10, 26, 2, -1, -1, - -1, -1, 20, 10, 32, 9, 34, 14, 2, 6, 48, 56, -1, 4, 5, 10, -// #0x200 - 26, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -}; - -const unsigned int BUFFER_SIZE = 65536; +#include <enet/enet.h> -int networkThread(void *data) -{ - Network *network = static_cast<Network*>(data); - - if (!network->realConnect()) - return -1; +#include <map> - network->receive(); +#include "connection.h" +#include "internal.h" +#include "messagehandler.h" +#include "messagein.h" - return 0; -} +#include "../log.h" -Network::Network(): - mSocket(0), - mAddress(), mPort(0), - mInBuffer(new char[BUFFER_SIZE]), - mOutBuffer(new char[BUFFER_SIZE]), - mInSize(0), mOutSize(0), - mToSkip(0), - mState(IDLE), - mWorkerThread(0) -{ - mMutex = SDL_CreateMutex(); +/** + * The local host which is shared for all outgoing connections. + */ +namespace { + ENetHost *client; } -Network::~Network() -{ - clearHandlers(); - - if (mState != IDLE && mState != NET_ERROR) - disconnect(); - - SDL_DestroyMutex(mMutex); - - delete[] mInBuffer; - delete[] mOutBuffer; -} +typedef std::map<unsigned short, MessageHandler*> MessageHandlers; +typedef MessageHandlers::iterator MessageHandlerIterator; +static MessageHandlers mMessageHandlers; -bool Network::connect(const std::string &address, short port) +void Net::initialize() { - if (mState != IDLE && mState != NET_ERROR) + if (enet_initialize()) { - logger->log("Tried to connect an already connected socket!"); - return false; + logger->error("Failed to initialize ENet."); } - if (address.empty()) + client = enet_host_create(NULL, 3, 0, 0); + + if (!client) { - setError("Empty address given to Network::connect()!"); - return false; + logger->error("Failed to create the local host."); } +} - logger->log("Network::Connecting to %s:%i", address.c_str(), port); - - mAddress = address; - mPort = port; - - // Reset to sane values - mOutSize = 0; - mInSize = 0; - mToSkip = 0; +void Net::finalize() +{ + if (!client) + return; // Wasn't initialized at all - mState = CONNECTING; - mWorkerThread = SDL_CreateThread(networkThread, this); - if (!mWorkerThread) - { - setError("Unable to create network worker thread"); - return false; + if (Net::connections) { + logger->error("Tried to shutdown the network subsystem while there " + "are network connections left!"); } - return true; + clearHandlers(); + enet_deinitialize(); } -void Network::disconnect() +Net::Connection *Net::getConnection() { - mState = IDLE; - - if (mWorkerThread) + if (!client) { - SDL_WaitThread(mWorkerThread, NULL); - mWorkerThread = NULL; + logger->error("Tried to instantiate a network object before " + "initializing the network subsystem!"); } - if (mSocket) - { - SDLNet_TCP_Close(mSocket); - mSocket = 0; - } + return new Net::Connection(client); } -void Network::registerHandler(MessageHandler *handler) +void Net::registerHandler(MessageHandler *handler) { for (const Uint16 *i = handler->handledMessages; *i; i++) { mMessageHandlers[*i] = handler; } - - handler->setNetwork(this); } -void Network::unregisterHandler(MessageHandler *handler) +void Net::unregisterHandler(MessageHandler *handler) { for (const Uint16 *i = handler->handledMessages; *i; i++) { mMessageHandlers.erase(*i); } - - handler->setNetwork(0); } -void Network::clearHandlers() +void Net::clearHandlers() { - MessageHandlerIterator i; - for (i = mMessageHandlers.begin(); i != mMessageHandlers.end(); i++) - { - i->second->setNetwork(0); - } mMessageHandlers.clear(); } -void Network::dispatchMessages() -{ - while (messageReady()) - { - MessageIn msg = getNextMessage(); - - MessageHandlerIterator iter = mMessageHandlers.find(msg.getId()); - - if (iter != mMessageHandlers.end()) - iter->second->handleMessage(&msg); - else - logger->log("Unhandled packet: %x", msg.getId()); - - skip(msg.getLength()); - } -} - -void Network::flush() -{ - if (!mOutSize || mState != CONNECTED) - return; - - int ret; - - - SDL_mutexP(mMutex); - ret = SDLNet_TCP_Send(mSocket, mOutBuffer, mOutSize); - if (ret < (int)mOutSize) - { - setError("Error in SDLNet_TCP_Send(): " + - std::string(SDLNet_GetError())); - } - mOutSize = 0; - SDL_mutexV(mMutex); -} - -void Network::skip(int len) -{ - SDL_mutexP(mMutex); - mToSkip += len; - if (!mInSize) - { - SDL_mutexV(mMutex); - return; - } - if (mInSize >= mToSkip) - { - mInSize -= mToSkip; - memmove(mInBuffer, mInBuffer + mToSkip, mInSize); - mToSkip = 0; - } - else - { - mToSkip -= mInSize; - mInSize = 0; - } - SDL_mutexV(mMutex); -} - -bool Network::messageReady() -{ - int len = -1; - - SDL_mutexP(mMutex); - if (mInSize >= 2) - { - len = packet_lengths[readWord(0)]; - - if (len == -1 && mInSize > 4) - len = readWord(2); - - } - - bool ret = (mInSize >= static_cast<unsigned int>(len)); - SDL_mutexV(mMutex); - - return ret; -} - -MessageIn Network::getNextMessage() +/** + * Dispatches a message to the appropriate message handler and + * destroys it afterwards. + */ +namespace { - while (!messageReady()) + void dispatchMessage(ENetPacket *packet) { - if (mState == NET_ERROR) - break; - } - - SDL_mutexP(mMutex); - int msgId = readWord(0); - int len = packet_lengths[msgId]; - - if (len == -1) - len = readWord(2); - -#ifdef DEBUG - logger->log("Received packet 0x%x of length %d", msgId, len); -#endif + MessageIn msg((const char *)packet->data, packet->dataLength); - MessageIn msg(mInBuffer, len); - SDL_mutexV(mMutex); - - return msg; -} - -bool Network::realConnect() -{ - IPaddress ipAddress; - - if (SDLNet_ResolveHost(&ipAddress, mAddress.c_str(), mPort) == -1) - { - std::string error = "Unable to resolve host \"" + mAddress + "\""; - setError(error); - logger->log("SDLNet_ResolveHost: %s", error.c_str()); - return false; - } + MessageHandlerIterator iter = mMessageHandlers.find(msg.getId()); - mState = CONNECTING; + if (iter != mMessageHandlers.end()) { + //logger->log("Received packet %x (%i B)", + // msg.getId(), msg.getLength()); + iter->second->handleMessage(msg); + } + else { + logger->log("Unhandled packet %x (%i B)", + msg.getId(), msg.getLength()); + } - mSocket = SDLNet_TCP_Open(&ipAddress); - if (!mSocket) - { - logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError()); - setError(SDLNet_GetError()); - return false; + // Clean up the packet now that we're done using it. + enet_packet_destroy(packet); } - - logger->log("Network::Started session with %s:%i", - ipToString(ipAddress.host), ipAddress.port); - - mState = CONNECTED; - - return true; } -void Network::receive() +void Net::flush() { - SDLNet_SocketSet set; - - if (!(set = SDLNet_AllocSocketSet(1))) - { - setError("Error in SDLNet_AllocSocketSet(): " + - std::string(SDLNet_GetError())); - return; - } - - if (SDLNet_TCP_AddSocket(set, mSocket) == -1) - { - setError("Error in SDLNet_AddSocket(): " + - std::string(SDLNet_GetError())); - } + ENetEvent event; - while (mState == CONNECTED) + // Wait up to 10 milliseconds for an event. + while (enet_host_service(client, &event, 10) > 0) { - // TODO Try to get this to block all the time while still being able - // to escape the loop - int numReady = SDLNet_CheckSockets(set, ((Uint32)500)); - int ret; - switch (numReady) + switch (event.type) { - case -1: - logger->log("Error: SDLNet_CheckSockets"); - // FALLTHROUGH - case 0: + case ENET_EVENT_TYPE_CONNECT: + logger->log("Connected to port %d.", event.peer->address.port); + // Store any relevant server information here. + event.peer->data = 0; break; - case 1: - // Receive data from the socket - SDL_mutexP(mMutex); - ret = SDLNet_TCP_Recv(mSocket, mInBuffer + mInSize, BUFFER_SIZE - mInSize); + case ENET_EVENT_TYPE_RECEIVE: + dispatchMessage(event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + logger->log("Disconnected."); + // Reset the server information. + event.peer->data = 0; + break; - if (!ret) - { - // We got disconnected - mState = IDLE; - logger->log("Disconnected."); - } - else if (ret < 0) - { - setError("Error in SDLNet_TCP_Recv(): " + - std::string(SDLNet_GetError())); - } - else { - mInSize += ret; - if (mToSkip) - { - if (mInSize >= mToSkip) - { - mInSize -= mToSkip; - memmove(mInBuffer, mInBuffer + mToSkip, mInSize); - mToSkip = 0; - } - else - { - mToSkip -= mInSize; - mInSize = 0; - } - } - } - SDL_mutexV(mMutex); + case ENET_EVENT_TYPE_NONE: + logger->log("No event during 10 milliseconds."); break; default: - // more than one socket is ready.. - // this should not happen since we only listen once socket. - std::stringstream errorStream; - errorStream << "Error in SDLNet_TCP_Recv(), " << numReady - << " sockets are ready: " << SDLNet_GetError(); - setError(errorStream.str()); + logger->log("Unhandled enet event."); break; } } - - if (SDLNet_TCP_DelSocket(set, mSocket) == -1) - { - logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError()); - } - - SDLNet_FreeSocketSet(set); -} - -void Network::setError(const std::string& error) -{ - logger->log("Network error: %s", error.c_str()); - mError = error; - mState = NET_ERROR; -} - -Uint16 Network::readWord(int pos) -{ -#if SDL_BYTEORDER == SDL_BIG_ENDIAN - return SDL_Swap16((*(Uint16*)(mInBuffer+(pos)))); -#else - return (*(Uint16*)(mInBuffer+(pos))); -#endif } diff --git a/src/net/network.h b/src/net/network.h index 02fe7538..13576e79 100644 --- a/src/net/network.h +++ b/src/net/network.h @@ -1,118 +1,79 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef NETWORK_ -#define NETWORK_ +#ifndef _TMW_NET_NETWORK_H +#define _TMW_NET_NETWORK_H -#include <map> -#include <SDL_net.h> -#include <SDL_thread.h> -#include <string> +#include <iosfwd> /** - * Protocol version, reported to the eAthena char and mapserver who can adjust - * the protocol accordingly. + * \defgroup Network Core network layer */ -#define CLIENT_PROTOCOL_VERSION 1 class MessageHandler; -class MessageIn; +class MessageOut; -class Network; - -class Network +/** + * \ingroup Network + */ +namespace Net { - public: - friend int networkThread(void *data); - friend class MessageOut; - - Network(); - - ~Network(); - - bool connect(const std::string &address, short port); - - void disconnect(); - - void registerHandler(MessageHandler *handler); - - void unregisterHandler(MessageHandler *handler); - - void clearHandlers(); - - int getState() const { return mState; } - - const std::string& getError() const { return mError; } - - bool isConnected() const { return mState == CONNECTED; } - - int getInSize() const { return mInSize; } - - void skip(int len); - - bool messageReady(); - - MessageIn getNextMessage(); - - void dispatchMessages(); - - void flush(); - - // ERROR replaced by NET_ERROR because already defined in Windows - enum { - IDLE, - CONNECTED, - CONNECTING, - DATA, - NET_ERROR - }; - - protected: - void setError(const std::string& error); - - Uint16 readWord(int pos); - - bool realConnect(); - - void receive(); - - TCPsocket mSocket; - - std::string mAddress; - short mPort; - - char *mInBuffer, *mOutBuffer; - unsigned int mInSize, mOutSize; - - unsigned int mToSkip; - - int mState; - std::string mError; - - SDL_Thread *mWorkerThread; - SDL_mutex *mMutex; - - typedef std::map<Uint16, MessageHandler*> MessageHandlers; - typedef MessageHandlers::iterator MessageHandlerIterator; - MessageHandlers mMessageHandlers; -}; + class Connection; + + /** + * Initializes the network subsystem. + */ + void initialize(); + + /** + * Finalizes the network subsystem. + */ + void finalize(); + + /** + * Returns a new Connection object. Should be deleted by the caller. + */ + Connection *getConnection(); + + /** + * Registers a message handler. A message handler handles a certain + * subset of incoming messages. + */ + void registerHandler(MessageHandler *handler); + + /** + * Unregisters a message handler. + */ + void unregisterHandler(MessageHandler *handler); + + /** + * Clears all registered message handlers. + */ + void clearHandlers(); + + /* + * Handles all events and dispatches incoming messages to the + * registered handlers + */ + void flush(); +} #endif diff --git a/src/net/npchandler.cpp b/src/net/npchandler.cpp index a6dc216b..30507537 100644 --- a/src/net/npchandler.cpp +++ b/src/net/npchandler.cpp @@ -1,111 +1,86 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "messagein.h" #include "npchandler.h" + +#include "messagein.h" #include "protocol.h" #include "../beingmanager.h" -#include "../localplayer.h" #include "../npc.h" -#include "../gui/npc_text.h" -#include "../gui/npcintegerdialog.h" #include "../gui/npclistdialog.h" -#include "../gui/npcstringdialog.h" +#include "../gui/npcpostdialog.h" +#include "../gui/npc_text.h" -extern NpcIntegerDialog *npcIntegerDialog; extern NpcListDialog *npcListDialog; extern NpcTextDialog *npcTextDialog; -extern NpcStringDialog *npcStringDialog; +extern NpcPostDialog *npcPostDialog; NPCHandler::NPCHandler() { static const Uint16 _messages[] = { - SMSG_NPC_CHOICE, - SMSG_NPC_MESSAGE, - SMSG_NPC_NEXT, - SMSG_NPC_CLOSE, - SMSG_NPC_INT_INPUT, - SMSG_NPC_STR_INPUT, + GPMSG_NPC_CHOICE, + GPMSG_NPC_POST, + GPMSG_NPC_MESSAGE, + GPMSG_NPC_ERROR, 0 }; handledMessages = _messages; } -void NPCHandler::handleMessage(MessageIn *msg) +void NPCHandler::handleMessage(MessageIn &msg) { - int id; - - switch (msg->getId()) + Being *being = beingManager->findBeing(msg.readInt16()); + if (!being || being->getType() != Being::NPC) { - case SMSG_NPC_CHOICE: - msg->readInt16(); // length - id = msg->readInt32(); - player_node->setAction(LocalPlayer::STAND); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); - npcListDialog->parseItems(msg->readString(msg->getLength() - 8)); - npcListDialog->setVisible(true); - break; - - case SMSG_NPC_MESSAGE: - msg->readInt16(); // length - id = msg->readInt32(); - player_node->setAction(LocalPlayer::STAND); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); - npcTextDialog->addText(msg->readString(msg->getLength() - 8)); - npcListDialog->setVisible(false); - npcTextDialog->setVisible(true); - break; + return; + } - case SMSG_NPC_CLOSE: - id = msg->readInt32(); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); - npcTextDialog->showCloseButton(); - break; + current_npc = static_cast< NPC * >(being); - case SMSG_NPC_NEXT: - // Next button in NPC dialog, currently unused - id = msg->readInt32(); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); - npcTextDialog->showNextButton(); + switch (msg.getId()) + { + case GPMSG_NPC_CHOICE: + npcListDialog->reset(); + while (msg.getUnreadLength()) + { + npcListDialog->addItem(msg.readString()); + } + npcListDialog->setVisible(true); break; - case SMSG_NPC_INT_INPUT: - // Request for an integer - id = msg->readInt32(); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); - npcIntegerDialog->setRange(0, 2147483647); - npcIntegerDialog->setDefaultValue(0); - npcIntegerDialog->setVisible(true); - npcIntegerDialog->requestFocus(); + case GPMSG_NPC_POST: + npcTextDialog->setVisible(false); + npcPostDialog->clear(); + npcPostDialog->setVisible(true); break; - case SMSG_NPC_STR_INPUT: - // Request for a string - id = msg->readInt32(); - current_npc = dynamic_cast<NPC*>(beingManager->findBeing(id)); - npcStringDialog->setValue(""); - npcStringDialog->setVisible(true); - npcStringDialog->requestFocus(); + case GPMSG_NPC_ERROR: + current_npc = NULL; + case GPMSG_NPC_MESSAGE: + npcTextDialog->addText(msg.readString(msg.getUnreadLength())); + npcListDialog->setVisible(false); + npcTextDialog->setVisible(true); + npcPostDialog->setVisible(false); break; } } diff --git a/src/net/npchandler.h b/src/net/npchandler.h index 7ac962eb..18ab0a05 100644 --- a/src/net/npchandler.h +++ b/src/net/npchandler.h @@ -29,7 +29,7 @@ class NPCHandler : public MessageHandler public: NPCHandler(); - void handleMessage(MessageIn *msg); + void handleMessage(MessageIn &msg); }; #endif diff --git a/src/net/partyhandler.cpp b/src/net/partyhandler.cpp index 96200add..60c51821 100644 --- a/src/net/partyhandler.cpp +++ b/src/net/partyhandler.cpp @@ -1,122 +1,103 @@ /* * The Mana World - * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net> + * Copyright 2008 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <guichan/actionlistener.hpp> - +#include <iostream> #include "partyhandler.h" + #include "protocol.h" #include "messagein.h" -#include "../gui/chat.h" -#include "../gui/confirm_dialog.h" +#include "chatserver/chatserver.h" -#include "../beingmanager.h" -#include "../party.h" +#include "../gui/chat.h" +#include "../gui/partywindow.h" +#include "../log.h" +#include "../localplayer.h" -PartyHandler::PartyHandler(Party *party) : mParty(party) +PartyHandler::PartyHandler() { static const Uint16 _messages[] = { - SMSG_PARTY_CREATE, - SMSG_PARTY_INFO, - SMSG_PARTY_INVITE, - SMSG_PARTY_INVITED, - SMSG_PARTY_SETTINGS, - SMSG_PARTY_MEMBER_INFO, - SMSG_PARTY_LEAVE, - SMSG_PARTY_UPDATE_HP, - SMSG_PARTY_UPDATE_COORDS, - SMSG_PARTY_MESSAGE, + CPMSG_PARTY_INVITE_RESPONSE, + CPMSG_PARTY_INVITED, + CPMSG_PARTY_ACCEPT_INVITE_RESPONSE, + CPMSG_PARTY_QUIT_RESPONSE, + CPMSG_PARTY_NEW_MEMBER, + CPMSG_PARTY_MEMBER_LEFT, 0 }; handledMessages = _messages; + } -void PartyHandler::handleMessage(MessageIn *msg) +void PartyHandler::handleMessage(MessageIn &msg) { - switch (msg->getId()) + switch (msg.getId()) { - case SMSG_PARTY_CREATE: - mParty->createResponse(msg->readInt8()); - break; - case SMSG_PARTY_INFO: - break; - case SMSG_PARTY_INVITE: + case CPMSG_PARTY_INVITE_RESPONSE: + { + if (msg.readInt8() == ERRMSG_OK) { - std::string nick = msg->readString(24); - int status = msg->readInt8(); - mParty->inviteResponse(nick, status); - break; + } - case SMSG_PARTY_INVITED: + } break; + + case CPMSG_PARTY_INVITED: + { + std::string inviter = msg.readString(); + partyWindow->showPartyInvite(inviter); + } break; + + case CPMSG_PARTY_ACCEPT_INVITE_RESPONSE: + { + if (msg.readInt8() == ERRMSG_OK) { - int id = msg->readInt32(); - Being *being = beingManager->findBeing(id); - if (!being) - { - break; - } - std::string nick; - int gender = 0; - std::string partyName = ""; - if (being->getType() != Being::PLAYER) - { - nick = ""; - } - else - { - nick = being->getName(); - gender = being->getGender(); - partyName = msg->readString(24); - } - mParty->invitedAsk(nick, gender, partyName); - break; + player_node->setInParty(true); + chatWindow->chatLog("Joined party"); } - case SMSG_PARTY_SETTINGS: - break; - case SMSG_PARTY_MEMBER_INFO: - break; - case SMSG_PARTY_LEAVE: + } + + case CPMSG_PARTY_QUIT_RESPONSE: + { + if (msg.readInt8() == ERRMSG_OK) { - /*int id = */msg->readInt32(); - std::string nick = msg->readString(24); - /*int fail = */msg->readInt8(); - mParty->leftResponse(nick); - break; - } - case SMSG_PARTY_UPDATE_HP: - break; - case SMSG_PARTY_UPDATE_COORDS: - break; - case SMSG_PARTY_MESSAGE: - { // new block to enable local variables - int msgLength = msg->readInt16() - 8; - if (msgLength <= 0) - { - return; - } - int id = msg->readInt32(); - Being *being = beingManager->findBeing(id); - std::string chatMsg = msg->readString(msgLength); - mParty->receiveChat(being, chatMsg); + player_node->setInParty(false); } - break; + } break; + + case CPMSG_PARTY_NEW_MEMBER: + { + msg.readInt16(); // being id + std::string name = msg.readString(); + + chatWindow->chatLog(name + " joined the party"); + + if (!player_node->getInParty()) + player_node->setInParty(true); + + partyWindow->addPartyMember(name); + } break; + + case CPMSG_PARTY_MEMBER_LEFT: + { + partyWindow->removePartyMember(msg.readString()); + } break; } } diff --git a/src/net/partyhandler.h b/src/net/partyhandler.h index fc02bf0a..b4257c34 100644 --- a/src/net/partyhandler.h +++ b/src/net/partyhandler.h @@ -1,39 +1,41 @@ /* * The Mana World - * Copyright (C) 2008 Lloyd Bryant <lloyd_bryant@netzero.net> + * Copyright 2008 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef PARTYHANDLER_H -#define PARTYHANDLER_H +#ifndef _TMW_NET_PARTYHANDLER_H +#define _TMW_NET_PARTYHANDLER_H #include "messagehandler.h" -class Party; +#include <string> class PartyHandler : public MessageHandler { - public: - PartyHandler(Party *party); +public: + PartyHandler(); + + void handleMessage(MessageIn &msg); + +protected: - void handleMessage(MessageIn *msg); - private: - Party *mParty; }; #endif + diff --git a/src/net/playerhandler.cpp b/src/net/playerhandler.cpp index fc3506fb..b4e2f328 100644 --- a/src/net/playerhandler.cpp +++ b/src/net/playerhandler.cpp @@ -1,33 +1,34 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "messagein.h" #include "playerhandler.h" + +#include "messagein.h" #include "protocol.h" #include "../engine.h" #include "../localplayer.h" #include "../log.h" +#include "../particle.h" #include "../npc.h" -#include "../units.h" #include "../gui/buy.h" #include "../gui/chat.h" @@ -39,9 +40,6 @@ #include "../gui/skill.h" #include "../gui/viewport.h" -#include "../utils/stringutils.h" -#include "../utils/gettext.h" - // TODO Move somewhere else OkDialog *weightNotice = NULL; OkDialog *deathNotice = NULL; @@ -52,9 +50,10 @@ extern BuyDialog *buyDialog; extern SellDialog *sellDialog; extern Window *buySellDialog; -// Max. distance we are willing to scroll after a teleport; -// everything beyond will reset the port hard. -static const int MAP_TELEPORT_SCROLL_DISTANCE = 8; +/* Max. distance we are willing to scroll after a teleport; + * everything beyond will reset the port hard. + */ +static const int MAP_TELEPORT_SCROLL_DISTANCE = 8 * 32; /** * Listener used for handling the overweigth message. @@ -86,7 +85,7 @@ namespace { buyDialog->setVisible(false); sellDialog->setVisible(false); buySellDialog->setVisible(false); - if (current_npc) current_npc->handleDeath(); + current_npc = 0; } } deathListener; } @@ -94,317 +93,192 @@ namespace { PlayerHandler::PlayerHandler() { static const Uint16 _messages[] = { - SMSG_WALK_RESPONSE, - SMSG_PLAYER_WARP, - SMSG_PLAYER_STAT_UPDATE_1, - SMSG_PLAYER_STAT_UPDATE_2, - SMSG_PLAYER_STAT_UPDATE_3, - SMSG_PLAYER_STAT_UPDATE_4, - SMSG_PLAYER_STAT_UPDATE_5, - SMSG_PLAYER_STAT_UPDATE_6, - SMSG_PLAYER_ARROW_MESSAGE, + GPMSG_PLAYER_MAP_CHANGE, + GPMSG_PLAYER_SERVER_CHANGE, + GPMSG_PLAYER_ATTRIBUTE_CHANGE, + GPMSG_PLAYER_EXP_CHANGE, + GPMSG_LEVELUP, + GPMSG_LEVEL_PROGRESS, + GPMSG_RAISE_ATTRIBUTE_RESPONSE, + GPMSG_LOWER_ATTRIBUTE_RESPONSE, 0 }; handledMessages = _messages; } -void PlayerHandler::handleMessage(MessageIn *msg) +void PlayerHandler::handleMessage(MessageIn &msg) { - switch (msg->getId()) + switch (msg.getId()) { - case SMSG_WALK_RESPONSE: - /* - * This client assumes that all walk messages succeed, - * and that the server will send a correction notice - * otherwise. - */ + case GPMSG_PLAYER_MAP_CHANGE: + handleMapChangeMessage(msg); break; - case SMSG_PLAYER_WARP: - { - std::string mapPath = msg->readString(16); - bool nearby; - Uint16 x = msg->readInt16(); - Uint16 y = msg->readInt16(); - - 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(); - - nearby = (engine->getCurrentMapName() == mapPath); - - // Switch the actual map, deleting the previous one if necessary - engine->changeMap(mapPath); + case GPMSG_PLAYER_SERVER_CHANGE: + { // TODO: Implement reconnecting to another game server + std::string token = msg.readString(32); + std::string address = msg.readString(); + int port = msg.readInt16(); + logger->log("Changing server to %s:%d", address.c_str(), port); + } break; - if (current_npc) current_npc->handleDeath(); - - float scrollOffsetX = 0.0f; - float scrollOffsetY = 0.0f; + case GPMSG_PLAYER_ATTRIBUTE_CHANGE: + { + logger->log("ATTRIBUTE UPDATE:"); + while (msg.getUnreadLength()) + { + int stat = msg.readInt8(); + int base = msg.readInt16(); + int value = msg.readInt16(); + logger->log("%d set to %d %d", stat, base, value); - /* Scroll if neccessary */ - if (!nearby - || (abs(x - player_node->mX) > MAP_TELEPORT_SCROLL_DISTANCE) - || (abs(y - player_node->mY) > MAP_TELEPORT_SCROLL_DISTANCE)) + if (stat == BASE_ATTR_HP) { - scrollOffsetX = (x - player_node->mX) * 32; - scrollOffsetY = (y - player_node->mY) * 32; + player_node->setMaxHp(base); + player_node->setHp(value); } + else if (stat < NB_CHARACTER_ATTRIBUTES) + { + if (stat >= CHAR_SKILL_BEGIN && stat < CHAR_SKILL_END + && player_node->getAttributeBase(stat) < base + && player_node->getAttributeBase(stat) > -1) + { + Particle* effect = particleEngine->addEffect("graphics/particles/skillup.particle.xml", 0, 0); + player_node->controlParticle(effect); + } - player_node->setAction(Being::STAND); - player_node->mFrame = 0; - player_node->mX = x; - player_node->mY = y; - - logger->log("Adjust scrolling by %d:%d", - (int)scrollOffsetX, - (int)scrollOffsetY); - - viewport->scrollBy(scrollOffsetX, scrollOffsetY); + player_node->setAttributeBase(stat, base); + player_node->setAttributeEffective(stat, value); + } + else + { + logger->log("Warning: server wants to update unknown " + "attribute %d to %d", stat, value); + } } - break; + } break; - case SMSG_PLAYER_STAT_UPDATE_1: + case GPMSG_PLAYER_EXP_CHANGE: + { + logger->log("EXP Update"); + while (msg.getUnreadLength()) { - Sint16 type = msg->readInt16(); - Uint32 value = msg->readInt32(); + int skill = msg.readInt8(); + int current = msg.readInt32(); + int next = msg.readInt32(); - switch (type) + if (skill < CHAR_SKILL_NB) { - //case 0x0000: - // player_node->setWalkSpeed(msg->readInt32()); - // break; - case 0x0005: player_node->mHp = value; break; - case 0x0006: player_node->mMaxHp = value; break; - case 0x0007: player_node->mMp = value; break; - case 0x0008: player_node->mMaxMp = value; break; - case 0x0009: - player_node->mStatsPointsToAttribute = value; - break; - case 0x000b: player_node->mLevel = value; break; - case 0x000c: - player_node->mSkillPoint = value; - skillDialog->update(); - break; - case 0x0018: - if (value >= player_node->mMaxWeight / 2 && - player_node->mTotalWeight < - player_node->mMaxWeight / 2) - { - weightNotice = new OkDialog(_("Message"), - _("You are carrying more than " - "half your weight. You are " - "unable to regain health.")); - weightNotice->addActionListener( - &weightListener); - } - player_node->mTotalWeight = value; - break; - case 0x0019: player_node->mMaxWeight = value; break; - case 0x0029: player_node->ATK = value; break; - case 0x002b: player_node->MATK = value; break; - case 0x002d: player_node->DEF = value; break; - case 0x002e: player_node->DEF_BONUS = value; break; - case 0x002f: player_node->MDEF = value; break; - case 0x0031: player_node->HIT = value; break; - case 0x0032: player_node->FLEE = value; break; - case 0x0035: player_node->mAttackSpeed = value; break; - case 0x0037: player_node->mJobLevel = value; break; + player_node->setExperience(skill, current, next); } - - if (player_node->mHp == 0 && !deathNotice) + else { - static char const *const deadMsg[] = - { - _("You are dead."), - _("We regret to inform you that your character was " - "killed in battle."), - _("You are not that alive anymore."), - _("The cold hands of the grim reaper are grabbing for " - "your soul."), - _("Game Over!"), - _("Insert coin to continue"), - _("No, kids. Your character did not really die. It... " - "err... went to a better place."), - _("Your plan of breaking your enemies weapon by " - "bashing it with your throat failed."), - _("I guess this did not run too well."), - // NetHack reference: - _("Do you want your possessions identified?"), - // Secret of Mana reference: - _("Sadly, no trace of you was ever found..."), - // Final Fantasy VI reference: - _("Annihilated."), - // Earthbound reference: - _("Looks like you got your head handed to you."), - // Leisure Suit Larry 1 reference: - _("You screwed up again, dump your body down the tubes " - "and get you another one."), - // Monty Python references (Dead Parrot sketch mostly): - _("You're not dead yet. You're just resting."), - _("You are no more."), - _("You have ceased to be."), - _("You've expired and gone to meet your maker."), - _("You're a stiff."), - _("Bereft of life, you rest in peace."), - _("If you weren't so animated, you'd be pushing up the " - "daisies."), - _("Your metabolic processes are now history."), - _("You're off the twig."), - _("You've kicked the bucket."), - _("You've shuffled off your mortal coil, run down the " - "curtain and joined the bleedin' choir invisibile."), - _("You are an ex-player."), - _("You're pining for the fjords.") - }; - std::string message(deadMsg[rand()%27]); - - deathNotice = new OkDialog(_("Message"), message); - deathNotice->addActionListener(&deathListener); - player_node->setAction(Being::DEAD); + logger->log("Warning: server wants to update experience of unknown " + "skill %d to %d / %d", skill, current, next); } } - break; + } break; - case SMSG_PLAYER_STAT_UPDATE_2: - switch (msg->readInt16()) { - case 0x0001: - player_node->setXp(msg->readInt32()); - break; - case 0x0002: - player_node->mJobXp = msg->readInt32(); - break; - case 0x0014: { - Uint32 curGp = player_node->mGp; - player_node->mGp = msg->readInt32(); - if (player_node->mGp > curGp) - chatWindow->chatLog(_("You picked up ") + - Units::formatCurrency(player_node->mGp - - curGp), BY_SERVER); - } - break; - case 0x0016: - player_node->mXpForNextLevel = msg->readInt32(); - break; - case 0x0017: - player_node->mJobXpForNextLevel = msg->readInt32(); - break; - } - break; - - case SMSG_PLAYER_STAT_UPDATE_3: - { - Sint32 type = msg->readInt32(); - Sint32 base = msg->readInt32(); - Sint32 bonus = msg->readInt32(); - Sint32 total = base + bonus; + case GPMSG_LEVELUP: + { + player_node->setLevel(msg.readInt16()); + player_node->setCharacterPoints(msg.readInt16()); + player_node->setCorrectionPoints(msg.readInt16()); + Particle* effect = particleEngine->addEffect("graphics/particles/levelup.particle.xml", 0, 0); + player_node->controlParticle(effect); + } break; - switch (type) { - case 0x000d: player_node->mAttr[LocalPlayer::STR] = total; - break; - case 0x000e: player_node->mAttr[LocalPlayer::AGI] = total; - break; - case 0x000f: player_node->mAttr[LocalPlayer::VIT] = total; - break; - case 0x0010: player_node->mAttr[LocalPlayer::INT] = total; - break; - case 0x0011: player_node->mAttr[LocalPlayer::DEX] = total; - break; - case 0x0012: player_node->mAttr[LocalPlayer::LUK] = total; - break; - } - } - break; - case SMSG_PLAYER_STAT_UPDATE_4: - { - Sint16 type = msg->readInt16(); - Sint8 fail = msg->readInt8(); - Sint8 value = msg->readInt8(); + case GPMSG_LEVEL_PROGRESS: + { + logger->log("Level Progress Update"); + player_node->setLevelProgress(msg.readInt8()); + } break; - if (fail != 1) - break; - switch (type) { - case 0x000d: player_node->mAttr[LocalPlayer::STR] = value; - break; - case 0x000e: player_node->mAttr[LocalPlayer::AGI] = value; - break; - case 0x000f: player_node->mAttr[LocalPlayer::VIT] = value; - break; - case 0x0010: player_node->mAttr[LocalPlayer::INT] = value; - break; - case 0x0011: player_node->mAttr[LocalPlayer::DEX] = value; - break; - case 0x0012: player_node->mAttr[LocalPlayer::LUK] = value; - break; - } + case GPMSG_RAISE_ATTRIBUTE_RESPONSE: + { + int errCode = msg.readInt8(); + int attrNum = msg.readInt8() - CHAR_ATTR_BEGIN; + switch (errCode) + { + case ATTRIBMOD_OK: + { + // feel(acknowledgment); + } break; + case ATTRIBMOD_INVALID_ATTRIBUTE: + { + logger->log("Warning: Server denied increase of attribute %d (unknown attribute) ", attrNum); + } break; + case ATTRIBMOD_NO_POINTS_LEFT: + { + // when the server says "you got no points" it + // has to be correct. The server is always right! + // undo attribute change and set points to 0 + logger->log("Warning: Server denied increase of attribute %d (no points left) ", attrNum); + int attrValue = player_node->getAttributeBase(attrNum) - 1; + player_node->setCharacterPoints(0); + player_node->setAttributeBase(attrNum, attrValue); + } break; + case ATTRIBMOD_DENIED: + { + // undo attribute change + logger->log("Warning: Server denied increase of attribute %d (reason unknown) ", attrNum); + int points = player_node->getCharacterPoints() - 1; + player_node->setCharacterPoints(points); + int attrValue = player_node->getAttributeBase(attrNum) - 1; + player_node->setAttributeBase(attrNum, attrValue); + } break; } - break; - - // Updates stats and status points - case SMSG_PLAYER_STAT_UPDATE_5: - player_node->mStatsPointsToAttribute = msg->readInt16(); - player_node->mAttr[LocalPlayer::STR] = msg->readInt8(); - player_node->mAttrUp[LocalPlayer::STR] = msg->readInt8(); - player_node->mAttr[LocalPlayer::AGI] = msg->readInt8(); - player_node->mAttrUp[LocalPlayer::AGI] = msg->readInt8(); - player_node->mAttr[LocalPlayer::VIT] = msg->readInt8(); - player_node->mAttrUp[LocalPlayer::VIT] = msg->readInt8(); - player_node->mAttr[LocalPlayer::INT] = msg->readInt8(); - player_node->mAttrUp[LocalPlayer::INT] = msg->readInt8(); - player_node->mAttr[LocalPlayer::DEX] = msg->readInt8(); - player_node->mAttrUp[LocalPlayer::DEX] = msg->readInt8(); - player_node->mAttr[LocalPlayer::LUK] = msg->readInt8(); - player_node->mAttrUp[LocalPlayer::LUK] = msg->readInt8(); - player_node->ATK = msg->readInt16(); // ATK - player_node->ATK_BONUS = msg->readInt16(); // ATK bonus - player_node->MATK = msg->readInt16(); // MATK max - player_node->MATK_BONUS = msg->readInt16(); // MATK min - player_node->DEF = msg->readInt16(); // DEF - player_node->DEF_BONUS = msg->readInt16(); // DEF bonus - player_node->MDEF = msg->readInt16(); // MDEF - player_node->MDEF_BONUS = msg->readInt16(); // MDEF bonus - player_node->HIT = msg->readInt16(); // HIT - player_node->FLEE = msg->readInt16(); // FLEE - player_node->FLEE_BONUS = msg->readInt16(); // FLEE bonus - msg->readInt16(); // critical - msg->readInt16(); // unknown - break; + } break; - case SMSG_PLAYER_STAT_UPDATE_6: - switch (msg->readInt16()) { - case 0x0020: - player_node->mAttrUp[LocalPlayer::STR] = msg->readInt8(); - break; - case 0x0021: - player_node->mAttrUp[LocalPlayer::AGI] = msg->readInt8(); - break; - case 0x0022: - player_node->mAttrUp[LocalPlayer::VIT] = msg->readInt8(); - break; - case 0x0023: - player_node->mAttrUp[LocalPlayer::INT] = msg->readInt8(); - break; - case 0x0024: - player_node->mAttrUp[LocalPlayer::DEX] = msg->readInt8(); - break; - case 0x0025: - player_node->mAttrUp[LocalPlayer::LUK] = msg->readInt8(); + case GPMSG_LOWER_ATTRIBUTE_RESPONSE: + { + int errCode = msg.readInt8(); + int attrNum = msg.readInt8() - CHAR_ATTR_BEGIN; + switch (errCode) + { + case ATTRIBMOD_OK: + { + // feel(acknowledgment); + } break; + case ATTRIBMOD_INVALID_ATTRIBUTE: + { + logger->log("Warning: Server denied reduction of attribute %d (unknown attribute) ", attrNum); + } break; + case ATTRIBMOD_NO_POINTS_LEFT: + { + // when the server says "you got no points" it + // has to be correct. The server is always right! + // undo attribute change and set points to 0 + logger->log("Warning: Server denied reduction of attribute %d (no points left) ", attrNum); + int attrValue = player_node->getAttributeBase(attrNum) + 1; + player_node->setCorrectionPoints(0); + player_node->setAttributeBase(attrNum, attrValue); break; + } break; + case ATTRIBMOD_DENIED: + { + // undo attribute change + logger->log("Warning: Server denied reduction of attribute %d (reason unknown) ", attrNum); + int charaPoints = player_node->getCharacterPoints() - 1; + player_node->setCharacterPoints(charaPoints); + int correctPoints = player_node->getCorrectionPoints() + 1; + player_node->setCorrectionPoints(correctPoints); + int attrValue = player_node->getAttributeBase(attrNum) + 1; + player_node->setAttributeBase(attrNum, attrValue); + } break; } - break; + } break; + /* case SMSG_PLAYER_ARROW_MESSAGE: { - Sint16 type = msg->readInt16(); + Sint16 type = msg.readInt16(); switch (type) { case 0: - chatWindow->chatLog(_("Equip arrows first"), + chatWindow->chatLog("Equip arrows first", BY_SERVER); break; default: @@ -413,5 +287,41 @@ void PlayerHandler::handleMessage(MessageIn *msg) } } break; + */ } } + +void +PlayerHandler::handleMapChangeMessage(MessageIn &msg) +{ + const std::string mapName = msg.readString(); + const unsigned short x = msg.readInt16(); + const unsigned short y = msg.readInt16(); + const bool nearby = (engine->getCurrentMapName() == mapName); + + logger->log("Changing map to %s (%d, %d)", mapName.c_str(), x, y); + + // Switch the actual map, deleting the previous one + engine->changeMap(mapName); + + current_npc = 0; + + const Vector &playerPos = player_node->getPosition(); + float scrollOffsetX = 0.0f; + float scrollOffsetY = 0.0f; + + /* Scroll if neccessary */ + if (!nearby + || (abs(x - (int) playerPos.x) > MAP_TELEPORT_SCROLL_DISTANCE) + || (abs(y - (int) playerPos.y) > MAP_TELEPORT_SCROLL_DISTANCE)) { + scrollOffsetX = x - (int) playerPos.x; + scrollOffsetY = y - (int) playerPos.y; + } + + player_node->setAction(Being::STAND); + player_node->setPosition(x, y); + + logger->log("Adjust scrolling by %d,%d", (int) scrollOffsetX, + (int) scrollOffsetY); + viewport->scrollBy(scrollOffsetX, scrollOffsetY); +} diff --git a/src/net/playerhandler.h b/src/net/playerhandler.h index 1a7c8da3..fef767da 100644 --- a/src/net/playerhandler.h +++ b/src/net/playerhandler.h @@ -29,7 +29,10 @@ class PlayerHandler : public MessageHandler public: PlayerHandler(); - void handleMessage(MessageIn *msg); + void handleMessage(MessageIn &msg); + + private: + void handleMapChangeMessage(MessageIn &msg); }; #endif diff --git a/src/net/protocol.h b/src/net/protocol.h index e9053451..73dd65e0 100644 --- a/src/net/protocol.h +++ b/src/net/protocol.h @@ -1,162 +1,295 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef PROTOCOL_ -#define PROTOCOL_ - -/********************************* - * Packets from server to client * - *********************************/ -#define SMSG_LOGIN_SUCCESS 0x0073 /**< Contains starting location */ -#define SMSG_SERVER_PING 0x007f /**< Contains server tick */ -#define SMSG_CONNECTION_PROBLEM 0x0081 -#define SMSG_UPDATE_HOST 0x0063 /**< Custom update host packet */ -#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 -#define SMSG_PLAYER_STAT_UPDATE_4 0x00bc -#define SMSG_PLAYER_STAT_UPDATE_5 0x00bd -#define SMSG_PLAYER_STAT_UPDATE_6 0x00be -#define SMSG_WHO_ANSWER 0x00c2 -#define SMSG_PLAYER_WARP 0x0091 /**< Warp player to map/location */ -#define SMSG_PLAYER_INVENTORY 0x01ee -#define SMSG_PLAYER_INVENTORY_ADD 0x00a0 -#define SMSG_PLAYER_INVENTORY_REMOVE 0x00af -#define SMSG_PLAYER_INVENTORY_USE 0x01c8 -#define SMSG_PLAYER_EQUIPMENT 0x00a4 -#define SMSG_PLAYER_EQUIP 0x00aa -#define SMSG_PLAYER_UNEQUIP 0x00ac -#define SMSG_PLAYER_ATTACK_RANGE 0x013a -#define SMSG_PLAYER_ARROW_EQUIP 0x013c -#define SMSG_PLAYER_ARROW_MESSAGE 0x013b -#define SMSG_PLAYER_SKILLS 0x010f -#define SMSG_SKILL_FAILED 0x0110 -#define SMSG_ITEM_USE_RESPONSE 0x00a8 -#define SMSG_ITEM_VISIBLE 0x009d /**< An item is on the floor */ -#define SMSG_ITEM_DROPPED 0x009e /**< An item is dropped */ -#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 */ -#define SMSG_BEING_SELFEFFECT 0x019b -#define SMSG_BEING_EMOTION 0x00c0 -#define SMSG_BEING_ACTION 0x008a /**< Attack, sit, stand up, ... */ -#define SMSG_BEING_CHAT 0x008d /**< A being talks */ -#define SMSG_BEING_NAME_RESPONSE 0x0095 /**< Has to be requested */ - -#define SMSG_NPC_MESSAGE 0x00b4 -#define SMSG_NPC_NEXT 0x00b5 -#define SMSG_NPC_CLOSE 0x00b6 -#define SMSG_NPC_CHOICE 0x00b7 /**< Display a choice */ -#define SMSG_NPC_BUY_SELL_CHOICE 0x00c4 -#define SMSG_NPC_BUY 0x00c6 -#define SMSG_NPC_SELL 0x00c7 -#define SMSG_NPC_BUY_RESPONSE 0x00ca -#define SMSG_NPC_SELL_RESPONSE 0x00cb -#define SMSG_NPC_INT_INPUT 0x0142 /**< Integer input */ -#define SMSG_NPC_STR_INPUT 0x01d4 /**< String input */ -#define SMSG_PLAYER_CHAT 0x008e /**< Player talks */ -#define SMSG_WHISPER 0x0097 /**< Whisper Recieved */ -#define SMSG_WHISPER_RESPONSE 0x0098 -#define SMSG_GM_CHAT 0x009a /**< GM announce */ -#define SMSG_WALK_RESPONSE 0x0087 - -#define SMSG_TRADE_REQUEST 0x00e5 /**< Receiving a request to trade */ -#define SMSG_TRADE_RESPONSE 0x00e7 -#define SMSG_TRADE_ITEM_ADD 0x00e9 -#define SMSG_TRADE_ITEM_ADD_RESPONSE 0x01b1 /**< Not standard eAthena! */ -#define SMSG_TRADE_OK 0x00ec -#define SMSG_TRADE_CANCEL 0x00ee -#define SMSG_TRADE_COMPLETE 0x00f0 - -#define SMSG_PARTY_CREATE 0x00fa -#define SMSG_PARTY_INFO 0x00fb -#define SMSG_PARTY_INVITE 0x00fd -#define SMSG_PARTY_INVITED 0x00fe -#define SMSG_PARTY_SETTINGS 0x0102 -#define SMSG_PARTY_MEMBER_INFO 0x0104 -#define SMSG_PARTY_LEAVE 0x0105 -#define SMSG_PARTY_UPDATE_HP 0x0106 -#define SMSG_PARTY_UPDATE_COORDS 0x0107 -#define SMSG_PARTY_MESSAGE 0x0109 - -#define SMSG_PLAYER_STORAGE_ITEMS 0x01f0 /**< Item list for storage */ -#define SMSG_PLAYER_STORAGE_EQUIP 0x00a6 /**< Equipment list for storage */ -#define SMSG_PLAYER_STORAGE_STATUS 0x00f2 /**< Slots used and total slots */ -#define SMSG_PLAYER_STORAGE_ADD 0x00f4 /**< Add item/equip to storage */ -#define SMSG_PLAYER_STORAGE_REMOVE 0x00f6 /**< Remove item/equip from storage */ -#define SMSG_PLAYER_STORAGE_CLOSE 0x00f8 /**< Storage access closed */ - -/********************************** - * 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 -#define CMSG_NPC_BUY_REQUEST 0x00c8 -#define CMSG_NPC_BUY_SELL_REQUEST 0x00c5 -#define CMSG_CHAT_MESSAGE 0x008c -#define CMSG_CHAT_WHISPER 0x0096 -#define CMSG_CHAT_ANNOUNCE 0x0099 -#define CMSG_CHAT_WHO 0x00c1 -#define CMSG_NPC_LIST_CHOICE 0x00b8 -#define CMSG_NPC_NEXT_REQUEST 0x00b9 -#define CMSG_NPC_SELL_REQUEST 0x00c9 -#define CMSG_NPC_INT_RESPONSE 0x0143 -#define CMSG_NPC_STR_RESPONSE 0x01d5 -#define CMSG_SKILL_LEVELUP_REQUEST 0x0112 -#define CMSG_STAT_UPDATE_REQUEST 0x00bb -#define CMSG_TRADE_ITEM_ADD_REQUEST 0x00e8 -#define CMSG_TRADE_CANCEL_REQUEST 0x00ed -#define CMSG_TRADE_ADD_COMPLETE 0x00eb -#define CMSG_TRADE_OK 0x00ef -#define CMSG_NPC_TALK 0x0090 -#define CMSG_TRADE_REQUEST 0x00e4 -#define CMSG_PLAYER_INVENTORY_USE 0x00a7 -#define CMSG_PLAYER_INVENTORY_DROP 0x00a2 -#define CMSG_PLAYER_EQUIP 0x00a9 -#define CMSG_PLAYER_UNEQUIP 0x00ab - -#define CMSG_PARTY_CREATE 0x00f9 -#define CMSG_PARTY_INVITE 0x00fc -#define CMSG_PARTY_INVITED 0x00ff -#define CMSG_PARTY_LEAVE 0x0100 /** Undocumented */ -#define CMSG_PARTY_SETTINGS 0x0101 -#define CMSG_PARTY_MESSAGE 0x0108 - -#define CMSG_MOVE_TO_STORAGE 0x00f3 /** Move item to storage */ -#define CSMG_MOVE_FROM_STORAGE 0x00f5 /** Remove item from storage */ -#define CMSG_CLOSE_STORAGE 0x00f7 /** Request storage close */ - -/** Encodes coords and direction in 3 bytes data */ -void set_coordinates(char *data, unsigned short x, unsigned short y, unsigned char direction); +#ifndef _TMW_PROTOCOL_ +#define _TMW_PROTOCOL_ + +/** + * Enumerated type for communicated messages: + * + * - PAMSG_*: from client to account server + * - APMSG_*: from account server to client + * - PCMSG_*: from client to chat server + * - CPMSG_*: from chat server to client + * - PGMSG_*: from client to game server + * - GPMSG_*: from game server to client + * + * Components: B byte, W word, D double word, S variable-size string + * C tile-based coordinates (B*3) + * + * Hosts: P (player's client), A (account server), C (char server), + * G (game server) + */ +enum { + // Login/Register + PAMSG_REGISTER = 0x0000, // L version, S username, S password, S email + APMSG_REGISTER_RESPONSE = 0x0002, // B error [, S updatehost] + PAMSG_UNREGISTER = 0x0003, // - + APMSG_UNREGISTER_RESPONSE = 0x0004, // B error + PAMSG_LOGIN = 0x0010, // L version, S username, S password + APMSG_LOGIN_RESPONSE = 0x0012, // B error [, S updatehost] + PAMSG_LOGOUT = 0x0013, // - + APMSG_LOGOUT_RESPONSE = 0x0014, // B error + PAMSG_CHAR_CREATE = 0x0020, // S name, B hair style, B hair color, B gender, W*6 stats + APMSG_CHAR_CREATE_RESPONSE = 0x0021, // B error + PAMSG_CHAR_DELETE = 0x0022, // B index + APMSG_CHAR_DELETE_RESPONSE = 0x0023, // B error + APMSG_CHAR_INFO = 0x0024, // B index, S name, B gender, B hair style, B hair color, W level, W character points, W correction points, D money, W*6 stats + PAMSG_CHAR_SELECT = 0x0026, // B index + APMSG_CHAR_SELECT_RESPONSE = 0x0027, // B error, B*32 token, S game address, W game port, S chat address, W chat port + PAMSG_EMAIL_CHANGE = 0x0030, // S email + APMSG_EMAIL_CHANGE_RESPONSE = 0x0031, // B error + PAMSG_PASSWORD_CHANGE = 0x0034, // S old password, S new password + APMSG_PASSWORD_CHANGE_RESPONSE = 0x0035, // B error + + PGMSG_CONNECT = 0x0050, // B*32 token + GPMSG_CONNECT_RESPONSE = 0x0051, // B error + PCMSG_CONNECT = 0x0053, // B*32 token + CPMSG_CONNECT_RESPONSE = 0x0054, // B error + + PGMSG_DISCONNECT = 0x0060, // B reconnect account + GPMSG_DISCONNECT_RESPONSE = 0x0061, // B error, B*32 token + PCMSG_DISCONNECT = 0x0063, // - + CPMSG_DISCONNECT_RESPONSE = 0x0064, // B error + + PAMSG_RECONNECT = 0x0065, // B*32 token + APMSG_RECONNECT_RESPONSE = 0x0066, // B error + + // Game + GPMSG_PLAYER_MAP_CHANGE = 0x0100, // S filename, W x, W y + GPMSG_PLAYER_SERVER_CHANGE = 0x0101, // B*32 token, S game address, W game port + PGMSG_PICKUP = 0x0110, // W*2 position + PGMSG_DROP = 0x0111, // B slot, B amount + PGMSG_EQUIP = 0x0112, // B slot + PGMSG_UNEQUIP = 0x0113, // B slot + PGMSG_MOVE_ITEM = 0x0114, // B slot1, B slot2, B amount + GPMSG_INVENTORY = 0x0120, // { B slot, W item id [, B amount] }* + GPMSG_INVENTORY_FULL = 0x0121, // { B slot, W item id [, B amount] }* + GPMSG_PLAYER_ATTRIBUTE_CHANGE = 0x0130, // { B attribute, W base value, W modified value }* + GPMSG_PLAYER_EXP_CHANGE = 0x0140, // { B skill, D exp got, D exp needed }* + GPMSG_LEVELUP = 0x0150, // W new level + GPMSG_LEVEL_PROGRESS = 0x0151, // B percent completed to next levelup + PGMSG_RAISE_ATTRIBUTE = 0x0160, // B attribute + GPMSG_RAISE_ATTRIBUTE_RESPONSE = 0x0161, // B error, B attribute + PGMSG_LOWER_ATTRIBUTE = 0x0170, // B attribute + GPMSG_LOWER_ATTRIBUTE_RESPONSE = 0x0171, // B error, B attribute + PGMSG_RESPAWN = 0x0180, // - + GPMSG_BEING_ENTER = 0x0200, // B type, W being id, B action, W*2 position + // player: S name, B hair style, B hair color, B gender, B item bitmask, { W item id }* + // monster: W type id + // npc: W type id + GPMSG_BEING_LEAVE = 0x0201, // W being id + GPMSG_ITEM_APPEAR = 0x0202, // W item id, W*2 position + GPMSG_BEING_LOOKS_CHANGE = 0x0210, // W weapon, W hat, W top clothes, W bottom clothes + PGMSG_WALK = 0x0260, // W*2 destination + PGMSG_ACTION_CHANGE = 0x0270, // B Action + GPMSG_BEING_ACTION_CHANGE = 0x0271, // W being id, B action + PGMSG_DIRECTION_CHANGE = 0x0272, // B Direction + GPMSG_BEING_DIR_CHANGE = 0x0273, // W being id, B direction + GPMSG_BEINGS_MOVE = 0x0280, // { W being id, B flags [, C position, B speed] [, W*2 destination] }* + GPMSG_ITEMS = 0x0281, // { W item id, W*2 position }* + PGMSG_ATTACK = 0x0290, // B direction + PGMSG_USE_SPECIAL = 0x0292, // B specialID + GPMSG_BEING_ATTACK = 0x0291, // W being id + PGMSG_SAY = 0x02A0, // S text + GPMSG_SAY = 0x02A1, // W being id, S text + GPMSG_NPC_CHOICE = 0x02B0, // W being id, { S text }* + GPMSG_NPC_MESSAGE = 0x02B1, // W being id, B* text + PGMSG_NPC_TALK = 0x02B2, // W being id + PGMSG_NPC_TALK_NEXT = 0x02B3, // W being id + PGMSG_NPC_SELECT = 0x02B4, // W being id, B choice + GPMSG_NPC_BUY = 0x02B5, // W being id, { W item id, W amount, W cost }* + GPMSG_NPC_SELL = 0x02B6, // W being id, { W item id, W amount, W cost }* + PGMSG_NPC_BUYSELL = 0x02B7, // W item id, W amount + GPMSG_NPC_ERROR = 0x02B8, // B error + GPMSG_NPC_POST = 0x02D0, // W being id + PGMSG_NPC_POST_SEND = 0x02D1, // S name, S text, W item id + GPMSG_NPC_POST_GET = 0x02D2, // W being id, { S name, S text, W item id } + PGMSG_TRADE_REQUEST = 0x02C0, // W being id + GPMSG_TRADE_REQUEST = 0x02C1, // W being id + GPMSG_TRADE_START = 0x02C2, // - + GPMSG_TRADE_COMPLETE = 0x02C3, // - + PGMSG_TRADE_CANCEL = 0x02C4, // - + GPMSG_TRADE_CANCEL = 0x02C5, // - + PGMSG_TRADE_ACCEPT = 0x02C6, // - + GPMSG_TRADE_ACCEPT = 0x02C7, // - + PGMSG_TRADE_ADD_ITEM = 0x02C8, // B slot, B amount + GPMSG_TRADE_ADD_ITEM = 0x02C9, // W item id, B amount + PGMSG_TRADE_SET_MONEY = 0x02CA, // L amount + GPMSG_TRADE_SET_MONEY = 0x02CB, // L amount + PGMSG_USE_ITEM = 0x0300, // B slot + GPMSG_USE_RESPONSE = 0x0301, // B error + GPMSG_BEINGS_DAMAGE = 0x0310, // { W being id, W amount }* + GPMSG_CREATE_EFFECT = 0x0320, // W effect id, W*2 position + + // Guild + PCMSG_GUILD_CREATE = 0x0350, // S name + CPMSG_GUILD_CREATE_RESPONSE = 0x0351, // B error, W guild, B rights, W channel + PCMSG_GUILD_INVITE = 0x0352, // W id, S name + CPMSG_GUILD_INVITE_RESPONSE = 0x0353, // B error + PCMSG_GUILD_ACCEPT = 0x0354, // W id + CPMSG_GUILD_ACCEPT_RESPONSE = 0x0355, // B error, W guild, B rights, W channel + PCMSG_GUILD_GET_MEMBERS = 0x0356, // W id + CPMSG_GUILD_GET_MEMBERS_RESPONSE = 0x0357, // S names, B online + CPMSG_GUILD_UPDATE_LIST = 0x0358, // W id, S name, B event + PCMSG_GUILD_QUIT = 0x0360, // W id + CPMSG_GUILD_QUIT_RESPONSE = 0x0361, // B error + PCMSG_GUILD_PROMOTE_MEMBER = 0x0365, // W guild, S name, B rights + CPMSG_GUILD_PROMOTE_MEMBER_RESPONSE = 0x0366, // B error + + CPMSG_GUILD_INVITED = 0x0370, // S char name, S guild name, W id + CPMSG_GUILD_REJOIN = 0x0371, // S name, W guild, W rights, W channel, S announce + + // Party + PCMSG_PARTY_INVITE = 0x03A0, // S name + CPMSG_PARTY_INVITE_RESPONSE = 0x03A1, // B error + CPMSG_PARTY_INVITED = 0x03A2, // S name + PCMSG_PARTY_ACCEPT_INVITE = 0x03A5, // S name + CPMSG_PARTY_ACCEPT_INVITE_RESPONSE = 0x03A6, // B error, { S name } + PCMSG_PARTY_QUIT = 0x03AA, // - + CPMSG_PARTY_QUIT_RESPONSE = 0x03AB, // B error + CPMSG_PARTY_NEW_MEMBER = 0x03B0, // W being id, S name + CPMSG_PARTY_MEMBER_LEFT = 0x03B1, // W being id + + // Chat + CPMSG_ERROR = 0x0401, // B error + CPMSG_ANNOUNCEMENT = 0x0402, // S text + CPMSG_PRIVMSG = 0x0403, // S user, S text + CPMSG_PUBMSG = 0x0404, // W channel, S user, S text + PCMSG_CHAT = 0x0410, // S text, W channel + PCMSG_ANNOUNCE = 0x0411, // S text + PCMSG_PRIVMSG = 0x0412, // S user, S text + // -- Channeling + CPMSG_CHANNEL_EVENT = 0x0430, // W channel, B event, S info + PCMSG_ENTER_CHANNEL = 0x0440, // S channel, S password + CPMSG_ENTER_CHANNEL_RESPONSE = 0x0441, // B error, W id, S name, S topic, S userlist + PCMSG_QUIT_CHANNEL = 0x0443, // W channel id + CPMSG_QUIT_CHANNEL_RESPONSE = 0x0444, // B error, W channel id + PCMSG_LIST_CHANNELS = 0x0445, // - + CPMSG_LIST_CHANNELS_RESPONSE = 0x0446, // S names, W number of users + PCMSG_LIST_CHANNELUSERS = 0x0460, // S channel + CPMSG_LIST_CHANNELUSERS_RESPONSE = 0x0461, // S channel, { S user, B mode } + PCMSG_TOPIC_CHANGE = 0x0462, // W channel id, S topic + // -- User mode + PCMSG_USER_MODE = 0x0465, // W channel id, S name, B mode + PCMSG_KICK_USER = 0x0466, // W channel id, S name + + XXMSG_INVALID = 0x7FFF +}; + +// Generic return values + +enum { + ERRMSG_OK = 0, // everything is fine + ERRMSG_FAILURE, // the action failed + ERRMSG_NO_LOGIN, // the user is not yet logged + ERRMSG_NO_CHARACTER_SELECTED, // the user needs a character + ERRMSG_INSUFFICIENT_RIGHTS, // the user is not privileged + ERRMSG_INVALID_ARGUMENT, // part of the received message was invalid + ERRMSG_EMAIL_ALREADY_EXISTS, // The Email Address already exists + ERRMSG_ALREADY_TAKEN, // name used was already taken + ERRMSG_SERVER_FULL, // the server is overloaded + ERRMSG_TIME_OUT // data failed to arrive in due time +}; + +// Login specific return values +enum { + LOGIN_INVALID_VERSION = 0x40, // the user is using an incompatible protocol + LOGIN_SERVER_FULL // the server is overloaded +}; + +// Account register specific return values +enum { + REGISTER_INVALID_VERSION = 0x40, // the user is using an incompatible protocol + REGISTER_EXISTS_USERNAME, // there already is an account with this username + REGISTER_EXISTS_EMAIL // there already is an account with this email address +}; + +// Character creation specific return values +enum { + CREATE_INVALID_HAIRSTYLE = 0x40, + CREATE_INVALID_HAIRCOLOR, + CREATE_INVALID_GENDER, + CREATE_RAW_STATS_TOO_HIGH, + CREATE_RAW_STATS_TOO_LOW, + CREATE_RAW_STATS_EQUAL_TO_ZERO, + CREATE_EXISTS_NAME, + CREATE_TOO_MUCH_CHARACTERS +}; + +// Character attribute modification specific return value +enum AttribmodResponseCode { + ATTRIBMOD_OK = ERRMSG_OK, + ATTRIBMOD_INVALID_ATTRIBUTE = 0x40, + ATTRIBMOD_NO_POINTS_LEFT, + ATTRIBMOD_DENIED +}; +// Object type enumeration +enum { + // A simple item + OBJECT_ITEM = 0, + // An item that can be activated (doors, switchs, sign, ...) + OBJECT_ACTOR, + // Non-Playable-Character is an actor capable of movement and maybe actions + OBJECT_NPC, + // A monster (moving actor with AI. able to toggle map/quest actions, too) + OBJECT_MONSTER, + // A player + OBJECT_PLAYER +}; + +// Moving object flags +enum { + // Payload contains the current position. + MOVING_POSITION = 1, + // Payload contains the destination. + MOVING_DESTINATION = 2 +}; + +// Email change specific return values +enum { + EMAILCHG_EXISTS_EMAIL = 0x40 +}; + +// Chat errors return values +enum { + CHAT_USING_BAD_WORDS = 0x40, + CHAT_UNHANDLED_COMMAND +}; + +// Chat channels event values +enum { + CHAT_EVENT_NEW_PLAYER = 0, + CHAT_EVENT_LEAVING_PLAYER, + CHAT_EVENT_TOPIC_CHANGE, + CHAT_EVENT_MODE_CHANGE, + CHAT_EVENT_KICKED_PLAYER +}; + +// Guild member event values +enum { + GUILD_EVENT_NEW_PLAYER = 0, + GUILD_EVENT_LEAVING_PLAYER, + GUILD_EVENT_ONLINE_PLAYER, + GUILD_EVENT_OFFLINE_PLAYER +}; #endif diff --git a/src/net/tradehandler.cpp b/src/net/tradehandler.cpp index c5465835..5d93016f 100644 --- a/src/net/tradehandler.cpp +++ b/src/net/tradehandler.cpp @@ -1,40 +1,41 @@ /* * The Mana World - * Copyright (C) 2004 The Mana World Development Team + * Copyright 2004 The Mana World Development Team * * This file is part of The Mana World. * - * This program is free software; you can redistribute it and/or modify + * The Mana World 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, + * The Mana World 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, write to the Free Software + * along with The Mana World; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "tradehandler.h" + #include "messagein.h" #include "protocol.h" -#include "tradehandler.h" -#include "../inventory.h" +#include "gameserver/player.h" + +#include "../beingmanager.h" #include "../item.h" #include "../localplayer.h" -#include "../player_relations.h" #include "../gui/chat.h" #include "../gui/confirm_dialog.h" #include "../gui/trade.h" -#include "../utils/gettext.h" - std::string tradePartnerName; +int tradePartnerID; /** * Listener for request trade dialogs @@ -44,174 +45,90 @@ namespace { { void action(const gcn::ActionEvent &event) { - player_node->tradeReply(event.getId() == "yes"); - }; + if (event.getId() == "yes") + Net::GameServer::Player::requestTrade(tradePartnerID); + else + Net::GameServer::Player::acceptTrade(false); + } } listener; } -TradeHandler::TradeHandler() +TradeHandler::TradeHandler(): + mAcceptTradeRequests(true) { static const Uint16 _messages[] = { - SMSG_TRADE_REQUEST, - SMSG_TRADE_RESPONSE, - SMSG_TRADE_ITEM_ADD, - SMSG_TRADE_ITEM_ADD_RESPONSE, - SMSG_TRADE_OK, - SMSG_TRADE_CANCEL, - SMSG_TRADE_COMPLETE, + GPMSG_TRADE_REQUEST, + GPMSG_TRADE_CANCEL, + GPMSG_TRADE_START, + GPMSG_TRADE_COMPLETE, + GPMSG_TRADE_ACCEPT, + GPMSG_TRADE_ADD_ITEM, + GPMSG_TRADE_SET_MONEY, 0 }; handledMessages = _messages; } +void TradeHandler::setAcceptTradeRequests(bool acceptTradeRequests) +{ + mAcceptTradeRequests = acceptTradeRequests; + if (mAcceptTradeRequests) { + chatWindow->chatLog("Accepting incoming trade requests", BY_SERVER); + } else { + chatWindow->chatLog("Ignoring incoming trade requests", BY_SERVER); + } +} -void TradeHandler::handleMessage(MessageIn *msg) +void TradeHandler::handleMessage(MessageIn &msg) { - switch (msg->getId()) + switch (msg.getId()) { - case SMSG_TRADE_REQUEST: - // If a trade window or request window is already open, send a - // trade cancel to any other trade request. - // - // Note that it would be nice if the server would prevent this - // situation, and that the requesting player would get a - // special message about the player being occupied. - tradePartnerName = msg->readString(24); - - if (player_relations.hasPermission(tradePartnerName, PlayerRelation::TRADE)) - { - if (!player_node->tradeRequestOk()) - { - player_node->tradeReply(false); - break; - } - - player_node->setTrading(true); - ConfirmDialog *dlg; - dlg = new ConfirmDialog(_("Request for trade"), - tradePartnerName + - _(" wants to trade with you, do you accept?")); - dlg->addActionListener(&listener); - } - else - { - player_node->tradeReply(false); - break; - } - break; - - case SMSG_TRADE_RESPONSE: - switch (msg->readInt8()) + case GPMSG_TRADE_REQUEST: + { + Being *being = beingManager->findBeing(msg.readInt16()); + if (!being || !mAcceptTradeRequests) { - case 0: // Too far away - chatWindow->chatLog(_("Trading isn't possible. Trade partner is too far away."), - BY_SERVER); - break; - case 1: // Character doesn't exist - chatWindow->chatLog(_("Trading isn't possible. Character doesn't exist."), - BY_SERVER); - break; - case 2: // Invite request check failed... - chatWindow->chatLog(_("Trade cancelled due to an unknown reason."), - BY_SERVER); - break; - case 3: // Trade accepted - tradeWindow->reset(); - tradeWindow->setCaption( - _("Trade: You and ") + tradePartnerName); - tradeWindow->setVisible(true); - break; - case 4: // Trade cancelled - if (player_relations.hasPermission(tradePartnerName, - PlayerRelation::SPEECH_LOG)) - chatWindow->chatLog(_("Trade with ") + tradePartnerName + - _(" cancelled"), BY_SERVER); - // otherwise ignore silently - - tradeWindow->setVisible(false); - player_node->setTrading(false); - break; - default: // Shouldn't happen as well, but to be sure - chatWindow->chatLog(_("Unhandled trade cancel packet"), - BY_SERVER); - break; + Net::GameServer::Player::acceptTrade(false); + break; } - break; + player_node->setTrading(true); + tradePartnerName = being->getName(); + tradePartnerID = being->getId(); + ConfirmDialog *dlg = new ConfirmDialog("Request for trade", + tradePartnerName + " wants to trade with you, do you accept?"); + dlg->addActionListener(&listener); + } break; + + case GPMSG_TRADE_ADD_ITEM: + { + int type = msg.readInt16(); + int amount = msg.readInt8(); + tradeWindow->addItem(type, false, amount); + } break; - case SMSG_TRADE_ITEM_ADD: - { - Sint32 amount = msg->readInt32(); - Sint16 type = msg->readInt16(); - msg->readInt8(); // identified flag - msg->readInt8(); // attribute - msg->readInt8(); // refine - msg->skip(8); // card (4 shorts) - - // TODO: handle also identified, etc - if (type == 0) { - tradeWindow->addMoney(amount); - } else { - tradeWindow->addItem(type, false, amount, false); - } - } + case GPMSG_TRADE_SET_MONEY: + tradeWindow->setMoney(msg.readInt32()); break; - case SMSG_TRADE_ITEM_ADD_RESPONSE: - // Trade: New Item add response (was 0x00ea, now 01b1) - { - const int index = msg->readInt16(); - Item *item = player_node->getInventory()->getItem(index); - if (!item) - { - tradeWindow->receivedOk(true); - return; - } - Sint16 quantity = msg->readInt16(); - - switch (msg->readInt8()) - { - case 0: - // Successfully added item - if (item->isEquipment() && item->isEquipped()) - { - player_node->unequipItem(item); - } - tradeWindow->addItem(item->getId(), true, quantity, - item->isEquipment()); - item->increaseQuantity(-quantity); - break; - case 1: - // Add item failed - player overweighted - chatWindow->chatLog(_("Failed adding item. Trade partner is over weighted."), - BY_SERVER); - break; - case 2: - // Add item failed - player has no free slot - chatWindow->chatLog(_("Failed adding item. Trade partner has no free slot."), - BY_SERVER); - break; - default: - chatWindow->chatLog(_("Failed adding item for unknown reason."), - BY_SERVER); - break; - } - } + case GPMSG_TRADE_START: + tradeWindow->reset(); + tradeWindow->setCaption("Trading with " + tradePartnerName); + tradeWindow->setVisible(true); break; - case SMSG_TRADE_OK: - // 0 means ok from myself, 1 means ok from other; - tradeWindow->receivedOk(msg->readInt8() == 0); + case GPMSG_TRADE_ACCEPT: + tradeWindow->receivedOk(); break; - case SMSG_TRADE_CANCEL: - chatWindow->chatLog(_("Trade canceled."), BY_SERVER); + case GPMSG_TRADE_CANCEL: + chatWindow->chatLog("Trade canceled.", BY_SERVER); tradeWindow->setVisible(false); tradeWindow->reset(); player_node->setTrading(false); break; - case SMSG_TRADE_COMPLETE: - chatWindow->chatLog(_("Trade completed."), BY_SERVER); + case GPMSG_TRADE_COMPLETE: + chatWindow->chatLog("Trade completed.", BY_SERVER); tradeWindow->setVisible(false); tradeWindow->reset(); player_node->setTrading(false); diff --git a/src/net/tradehandler.h b/src/net/tradehandler.h index d479e43f..6ffe17b5 100644 --- a/src/net/tradehandler.h +++ b/src/net/tradehandler.h @@ -24,14 +24,30 @@ #include "messagehandler.h" -class Network; - class TradeHandler : public MessageHandler { public: TradeHandler(); - void handleMessage(MessageIn *msg); + void handleMessage(MessageIn &msg); + + /** + * Returns whether trade requests are accepted. + * + * @see setAcceptTradeRequests + */ + bool acceptTradeRequests() const + { return mAcceptTradeRequests; } + + /** + * Sets whether trade requests are accepted. When set to false, trade + * requests are automatically denied. When true, a popup will ask the + * player whether he wants to trade. + */ + void setAcceptTradeRequests(bool acceptTradeRequests); + + private: + bool mAcceptTradeRequests; }; #endif |