From 3eeae12c498d1a4dbe969462d2ba841f77ee3ccb Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Sun, 2 Jan 2011 01:48:38 +0200 Subject: Initial commit. This code based on mana client http://www.gitorious.org/mana/mana and my private repository. --- src/net/adminhandler.h | 60 ++ src/net/beinghandler.h | 43 ++ src/net/buysellhandler.h | 47 ++ src/net/charhandler.cpp | 37 ++ src/net/charhandler.h | 112 ++++ src/net/chathandler.h | 71 +++ src/net/download.cpp | 355 +++++++++++ src/net/download.h | 123 ++++ src/net/gamehandler.h | 62 ++ src/net/generalhandler.h | 50 ++ src/net/guildhandler.h | 76 +++ src/net/inventoryhandler.h | 72 +++ src/net/logindata.h | 95 +++ src/net/loginhandler.h | 112 ++++ src/net/manaserv/adminhandler.cpp | 93 +++ src/net/manaserv/adminhandler.h | 64 ++ src/net/manaserv/attributes.cpp | 411 +++++++++++++ src/net/manaserv/attributes.h | 72 +++ src/net/manaserv/beinghandler.cpp | 385 ++++++++++++ src/net/manaserv/beinghandler.h | 73 +++ src/net/manaserv/buysellhandler.cpp | 132 ++++ src/net/manaserv/buysellhandler.h | 57 ++ src/net/manaserv/charhandler.cpp | 406 +++++++++++++ src/net/manaserv/charhandler.h | 119 ++++ src/net/manaserv/chathandler.cpp | 472 +++++++++++++++ src/net/manaserv/chathandler.h | 139 +++++ src/net/manaserv/connection.cpp | 113 ++++ src/net/manaserv/connection.h | 89 +++ src/net/manaserv/defines.h | 77 +++ src/net/manaserv/effecthandler.cpp | 80 +++ src/net/manaserv/effecthandler.h | 44 ++ src/net/manaserv/gamehandler.cpp | 154 +++++ src/net/manaserv/gamehandler.h | 74 +++ src/net/manaserv/generalhandler.cpp | 211 +++++++ src/net/manaserv/generalhandler.h | 78 +++ src/net/manaserv/guildhandler.cpp | 360 +++++++++++ src/net/manaserv/guildhandler.h | 84 +++ src/net/manaserv/internal.cpp | 27 + src/net/manaserv/internal.h | 30 + src/net/manaserv/inventoryhandler.cpp | 219 +++++++ src/net/manaserv/inventoryhandler.h | 109 ++++ src/net/manaserv/itemhandler.cpp | 90 +++ src/net/manaserv/itemhandler.h | 40 ++ src/net/manaserv/loginhandler.cpp | 479 +++++++++++++++ src/net/manaserv/loginhandler.h | 99 +++ src/net/manaserv/messagehandler.cpp | 36 ++ src/net/manaserv/messagehandler.h | 44 ++ src/net/manaserv/messagein.cpp | 62 ++ src/net/manaserv/messagein.h | 49 ++ src/net/manaserv/messageout.cpp | 65 ++ src/net/manaserv/messageout.h | 59 ++ src/net/manaserv/network.cpp | 178 ++++++ src/net/manaserv/network.h | 75 +++ src/net/manaserv/npchandler.cpp | 237 ++++++++ src/net/manaserv/npchandler.h | 89 +++ src/net/manaserv/partyhandler.cpp | 197 ++++++ src/net/manaserv/partyhandler.h | 82 +++ src/net/manaserv/playerhandler.cpp | 440 ++++++++++++++ src/net/manaserv/playerhandler.h | 85 +++ src/net/manaserv/protocol.h | 392 ++++++++++++ src/net/manaserv/specialhandler.cpp | 70 +++ src/net/manaserv/specialhandler.h | 56 ++ src/net/manaserv/tradehandler.cpp | 237 ++++++++ src/net/manaserv/tradehandler.h | 82 +++ src/net/messagehandler.h | 50 ++ src/net/messagein.cpp | 227 +++++++ src/net/messagein.h | 118 ++++ src/net/messageout.cpp | 92 +++ src/net/messageout.h | 91 +++ src/net/net.cpp | 198 ++++++ src/net/net.h | 81 +++ src/net/npchandler.h | 66 ++ src/net/packetcounters.cpp | 128 ++++ src/net/packetcounters.h | 55 ++ src/net/partyhandler.h | 82 +++ src/net/playerhandler.h | 75 +++ src/net/serverinfo.h | 117 ++++ src/net/specialhandler.h | 45 ++ src/net/tmwa/adminhandler.cpp | 135 +++++ src/net/tmwa/adminhandler.h | 69 +++ src/net/tmwa/beinghandler.cpp | 1075 +++++++++++++++++++++++++++++++++ src/net/tmwa/beinghandler.h | 53 ++ src/net/tmwa/buysellhandler.cpp | 231 +++++++ src/net/tmwa/buysellhandler.h | 59 ++ src/net/tmwa/charserverhandler.cpp | 386 ++++++++++++ src/net/tmwa/charserverhandler.h | 87 +++ src/net/tmwa/chathandler.cpp | 552 +++++++++++++++++ src/net/tmwa/chathandler.h | 87 +++ src/net/tmwa/gamehandler.cpp | 196 ++++++ src/net/tmwa/gamehandler.h | 85 +++ src/net/tmwa/generalhandler.cpp | 285 +++++++++ src/net/tmwa/generalhandler.h | 83 +++ src/net/tmwa/gui/guildtab.cpp | 150 +++++ src/net/tmwa/gui/guildtab.h | 57 ++ src/net/tmwa/gui/partytab.cpp | 242 ++++++++ src/net/tmwa/gui/partytab.h | 57 ++ src/net/tmwa/guildhandler.cpp | 780 ++++++++++++++++++++++++ src/net/tmwa/guildhandler.h | 84 +++ src/net/tmwa/inventoryhandler.cpp | 609 +++++++++++++++++++ src/net/tmwa/inventoryhandler.h | 177 ++++++ src/net/tmwa/itemhandler.cpp | 93 +++ src/net/tmwa/itemhandler.h | 40 ++ src/net/tmwa/loginhandler.cpp | 342 +++++++++++ src/net/tmwa/loginhandler.h | 105 ++++ src/net/tmwa/messagehandler.cpp | 48 ++ src/net/tmwa/messagehandler.h | 59 ++ src/net/tmwa/messagein.cpp | 85 +++ src/net/tmwa/messagein.h | 52 ++ src/net/tmwa/messageout.cpp | 142 +++++ src/net/tmwa/messageout.h | 65 ++ src/net/tmwa/network.cpp | 483 +++++++++++++++ src/net/tmwa/network.h | 136 +++++ src/net/tmwa/npchandler.cpp | 249 ++++++++ src/net/tmwa/npchandler.h | 90 +++ src/net/tmwa/partyhandler.cpp | 547 +++++++++++++++++ src/net/tmwa/partyhandler.h | 85 +++ src/net/tmwa/playerhandler.cpp | 752 +++++++++++++++++++++++ src/net/tmwa/playerhandler.h | 74 +++ src/net/tmwa/protocol.h | 327 ++++++++++ src/net/tmwa/specialhandler.cpp | 273 +++++++++ src/net/tmwa/specialhandler.h | 57 ++ src/net/tmwa/token.h | 43 ++ src/net/tmwa/tradehandler.cpp | 347 +++++++++++ src/net/tmwa/tradehandler.h | 65 ++ src/net/tradehandler.h | 67 ++ src/net/worldinfo.h | 39 ++ 126 files changed, 20359 insertions(+) create mode 100644 src/net/adminhandler.h create mode 100644 src/net/beinghandler.h create mode 100644 src/net/buysellhandler.h create mode 100644 src/net/charhandler.cpp create mode 100644 src/net/charhandler.h create mode 100644 src/net/chathandler.h create mode 100644 src/net/download.cpp create mode 100644 src/net/download.h create mode 100644 src/net/gamehandler.h create mode 100644 src/net/generalhandler.h create mode 100644 src/net/guildhandler.h create mode 100644 src/net/inventoryhandler.h create mode 100644 src/net/logindata.h create mode 100644 src/net/loginhandler.h create mode 100644 src/net/manaserv/adminhandler.cpp create mode 100644 src/net/manaserv/adminhandler.h create mode 100644 src/net/manaserv/attributes.cpp create mode 100644 src/net/manaserv/attributes.h create mode 100644 src/net/manaserv/beinghandler.cpp create mode 100644 src/net/manaserv/beinghandler.h create mode 100644 src/net/manaserv/buysellhandler.cpp create mode 100644 src/net/manaserv/buysellhandler.h create mode 100644 src/net/manaserv/charhandler.cpp create mode 100644 src/net/manaserv/charhandler.h create mode 100644 src/net/manaserv/chathandler.cpp create mode 100644 src/net/manaserv/chathandler.h create mode 100644 src/net/manaserv/connection.cpp create mode 100644 src/net/manaserv/connection.h create mode 100644 src/net/manaserv/defines.h create mode 100644 src/net/manaserv/effecthandler.cpp create mode 100644 src/net/manaserv/effecthandler.h create mode 100644 src/net/manaserv/gamehandler.cpp create mode 100644 src/net/manaserv/gamehandler.h create mode 100644 src/net/manaserv/generalhandler.cpp create mode 100644 src/net/manaserv/generalhandler.h create mode 100644 src/net/manaserv/guildhandler.cpp create mode 100644 src/net/manaserv/guildhandler.h create mode 100644 src/net/manaserv/internal.cpp create mode 100644 src/net/manaserv/internal.h create mode 100644 src/net/manaserv/inventoryhandler.cpp create mode 100644 src/net/manaserv/inventoryhandler.h create mode 100644 src/net/manaserv/itemhandler.cpp create mode 100644 src/net/manaserv/itemhandler.h create mode 100644 src/net/manaserv/loginhandler.cpp create mode 100644 src/net/manaserv/loginhandler.h create mode 100644 src/net/manaserv/messagehandler.cpp create mode 100644 src/net/manaserv/messagehandler.h create mode 100644 src/net/manaserv/messagein.cpp create mode 100644 src/net/manaserv/messagein.h create mode 100644 src/net/manaserv/messageout.cpp create mode 100644 src/net/manaserv/messageout.h create mode 100644 src/net/manaserv/network.cpp create mode 100644 src/net/manaserv/network.h create mode 100644 src/net/manaserv/npchandler.cpp create mode 100644 src/net/manaserv/npchandler.h create mode 100644 src/net/manaserv/partyhandler.cpp create mode 100644 src/net/manaserv/partyhandler.h create mode 100644 src/net/manaserv/playerhandler.cpp create mode 100644 src/net/manaserv/playerhandler.h create mode 100644 src/net/manaserv/protocol.h create mode 100644 src/net/manaserv/specialhandler.cpp create mode 100644 src/net/manaserv/specialhandler.h create mode 100644 src/net/manaserv/tradehandler.cpp create mode 100644 src/net/manaserv/tradehandler.h create mode 100644 src/net/messagehandler.h create mode 100644 src/net/messagein.cpp create mode 100644 src/net/messagein.h create mode 100644 src/net/messageout.cpp create mode 100644 src/net/messageout.h create mode 100644 src/net/net.cpp create mode 100644 src/net/net.h create mode 100644 src/net/npchandler.h create mode 100644 src/net/packetcounters.cpp create mode 100644 src/net/packetcounters.h create mode 100644 src/net/partyhandler.h create mode 100644 src/net/playerhandler.h create mode 100644 src/net/serverinfo.h create mode 100644 src/net/specialhandler.h create mode 100644 src/net/tmwa/adminhandler.cpp create mode 100644 src/net/tmwa/adminhandler.h create mode 100644 src/net/tmwa/beinghandler.cpp create mode 100644 src/net/tmwa/beinghandler.h create mode 100644 src/net/tmwa/buysellhandler.cpp create mode 100644 src/net/tmwa/buysellhandler.h create mode 100644 src/net/tmwa/charserverhandler.cpp create mode 100644 src/net/tmwa/charserverhandler.h create mode 100644 src/net/tmwa/chathandler.cpp create mode 100644 src/net/tmwa/chathandler.h create mode 100644 src/net/tmwa/gamehandler.cpp create mode 100644 src/net/tmwa/gamehandler.h create mode 100644 src/net/tmwa/generalhandler.cpp create mode 100644 src/net/tmwa/generalhandler.h create mode 100644 src/net/tmwa/gui/guildtab.cpp create mode 100644 src/net/tmwa/gui/guildtab.h create mode 100644 src/net/tmwa/gui/partytab.cpp create mode 100644 src/net/tmwa/gui/partytab.h create mode 100644 src/net/tmwa/guildhandler.cpp create mode 100644 src/net/tmwa/guildhandler.h create mode 100644 src/net/tmwa/inventoryhandler.cpp create mode 100644 src/net/tmwa/inventoryhandler.h create mode 100644 src/net/tmwa/itemhandler.cpp create mode 100644 src/net/tmwa/itemhandler.h create mode 100644 src/net/tmwa/loginhandler.cpp create mode 100644 src/net/tmwa/loginhandler.h create mode 100644 src/net/tmwa/messagehandler.cpp create mode 100644 src/net/tmwa/messagehandler.h create mode 100644 src/net/tmwa/messagein.cpp create mode 100644 src/net/tmwa/messagein.h create mode 100644 src/net/tmwa/messageout.cpp create mode 100644 src/net/tmwa/messageout.h create mode 100644 src/net/tmwa/network.cpp create mode 100644 src/net/tmwa/network.h create mode 100644 src/net/tmwa/npchandler.cpp create mode 100644 src/net/tmwa/npchandler.h create mode 100644 src/net/tmwa/partyhandler.cpp create mode 100644 src/net/tmwa/partyhandler.h create mode 100644 src/net/tmwa/playerhandler.cpp create mode 100644 src/net/tmwa/playerhandler.h create mode 100644 src/net/tmwa/protocol.h create mode 100644 src/net/tmwa/specialhandler.cpp create mode 100644 src/net/tmwa/specialhandler.h create mode 100644 src/net/tmwa/token.h create mode 100644 src/net/tmwa/tradehandler.cpp create mode 100644 src/net/tmwa/tradehandler.h create mode 100644 src/net/tradehandler.h create mode 100644 src/net/worldinfo.h (limited to 'src/net') diff --git a/src/net/adminhandler.h b/src/net/adminhandler.h new file mode 100644 index 000000000..7c24554c9 --- /dev/null +++ b/src/net/adminhandler.h @@ -0,0 +1,60 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef ADMINHANDLER_H +#define ADMINHANDLER_H + +#include + +namespace Net +{ + +class AdminHandler +{ + public: + virtual ~AdminHandler() {} + + virtual void announce(const std::string &text) = 0; + + virtual void localAnnounce(const std::string &text) = 0; + + virtual void hide(bool hide) = 0; + + virtual void kick(int playerId) = 0; + + virtual void kick(const std::string &name) = 0; + + virtual void ban(int playerId) = 0; + + virtual void ban(const std::string &name) = 0; + + virtual void unban(int playerId) = 0; + + virtual void unban(const std::string &name) = 0; + + virtual void mute(int playerId, int type, int limit) = 0; + + // TODO +}; + +} // namespace Net + +#endif // ADMINHANDLER_H diff --git a/src/net/beinghandler.h b/src/net/beinghandler.h new file mode 100644 index 000000000..89f972e6d --- /dev/null +++ b/src/net/beinghandler.h @@ -0,0 +1,43 @@ +/* + * 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 BEINGHANDLER_H +#define BEINGHANDLER_H + +#include "being.h" +#include "net/messagein.h" + +namespace Net +{ + +class BeingHandler +{ + public: + virtual void handleMessage(Net::MessageIn &msg) = 0; + + virtual void requestNameById(int id) = 0; + + virtual void undress(Being *being) = 0; +}; + +} // namespace Net + +#endif // BEINGHANDLER_H diff --git a/src/net/buysellhandler.h b/src/net/buysellhandler.h new file mode 100644 index 000000000..88851e94e --- /dev/null +++ b/src/net/buysellhandler.h @@ -0,0 +1,47 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef BUYSELLHANDLER_H +#define BUYSELLHANDLER_H + +#include "net/messagein.h" + +#include "being.h" +#include "shopitem.h" + +namespace Net +{ + +class BuySellHandler +{ + public: + virtual void handleMessage(Net::MessageIn &msg) = 0; + virtual void requestSellList(std::string nick) = 0; + virtual void requestBuyList(std::string nick) = 0; + virtual void sendBuyRequest(std::string nick, ShopItem* item, + int amount) = 0; + virtual void sendSellRequest(std::string nick, ShopItem* item, + int amount) = 0; +}; + +} // namespace Net + +#endif // BUYSELLHANDLER_H diff --git a/src/net/charhandler.cpp b/src/net/charhandler.cpp new file mode 100644 index 000000000..699d930a2 --- /dev/null +++ b/src/net/charhandler.cpp @@ -0,0 +1,37 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/charhandler.h" + +#include "gui/charselectdialog.h" + +using namespace Net; + +void CharHandler::updateCharSelectDialog() +{ + if (mCharSelectDialog) + mCharSelectDialog->setCharacters(mCharacters); +} + +void CharHandler::unlockCharSelectDialog() +{ + if (mCharSelectDialog) + mCharSelectDialog->unlock(); +} diff --git a/src/net/charhandler.h b/src/net/charhandler.h new file mode 100644 index 000000000..005d995c8 --- /dev/null +++ b/src/net/charhandler.h @@ -0,0 +1,112 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CHARHANDLER_H +#define CHARHANDLER_H + +#include "localplayer.h" +#include "logindata.h" +#include "playerinfo.h" + +#include +#include + +class CharCreateDialog; +class CharSelectDialog; + +namespace Net +{ + +/** + * A structure to hold information about a character. + */ +struct Character +{ + Character() : + slot(0), + dummy(0) + { + } + + ~Character() + { + delete dummy; + dummy = 0; + } + + int slot; /**< The index in the list of characters */ + LocalPlayer *dummy; /**< A dummy representing this character */ + PlayerInfoBackend data; +}; + +typedef std::list Characters; + +class CharHandler +{ + public: + virtual ~CharHandler() + { } + + virtual void setCharSelectDialog(CharSelectDialog *window) = 0; + + virtual void setCharCreateDialog(CharCreateDialog *window) = 0; + + virtual void requestCharacters() = 0; + + virtual void chooseCharacter(Net::Character *character) = 0; + + virtual void newCharacter(const std::string &name, int slot, + bool gender, int hairstyle, int hairColor, + const std::vector &stats) = 0; + + virtual void deleteCharacter(Net::Character *character) = 0; + + virtual void switchCharacter() = 0; + + virtual unsigned int baseSprite() const = 0; + + virtual unsigned int hairSprite() const = 0; + + virtual unsigned int maxSprite() const = 0; + + protected: + CharHandler(): + mSelectedCharacter(0), + mCharSelectDialog(0), + mCharCreateDialog(0) + {} + + void updateCharSelectDialog(); + void unlockCharSelectDialog(); + + /** The list of available characters. */ + Net::Characters mCharacters; + + /** The selected character. */ + Net::Character *mSelectedCharacter; + + CharSelectDialog *mCharSelectDialog; + CharCreateDialog *mCharCreateDialog; +}; + +} // namespace Net + +#endif // CHARHANDLER_H diff --git a/src/net/chathandler.h b/src/net/chathandler.h new file mode 100644 index 000000000..20938c78b --- /dev/null +++ b/src/net/chathandler.h @@ -0,0 +1,71 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CHATHANDLER_H +#define CHATHANDLER_H + +#include + +namespace Net +{ +class ChatHandler +{ + public: + virtual ~ChatHandler() + { } + + virtual void talk(const std::string &text) = 0; + + virtual void talkRaw(const std::string &text) = 0; + + virtual void me(const std::string &text) = 0; + + virtual void privateMessage(const std::string &recipient, + const std::string &text) = 0; + + virtual void channelList() = 0; + + virtual void enterChannel(const std::string &channel, + const std::string &password) = 0; + + virtual void quitChannel(int channelId) = 0; + + virtual void sendToChannel(int channelId, const std::string &text) = 0; + + virtual void userList(const std::string &channel) = 0; + + virtual void setChannelTopic(int channelId, + const std::string &text) = 0; + + virtual void setUserMode(int channelId, const std::string &name, + int mode) = 0; + + virtual void kickUser(int channelId, const std::string &name) = 0; + + virtual void who() = 0; + + virtual void sendRaw(const std::string &args) = 0; + +// virtual ~ChatHandler() {} +}; +} + +#endif // CHATHANDLER_H diff --git a/src/net/download.cpp b/src/net/download.cpp new file mode 100644 index 000000000..2d391b783 --- /dev/null +++ b/src/net/download.cpp @@ -0,0 +1,355 @@ +/* + * The Mana Client + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/download.h" + +#include "configuration.h" +#include "log.h" +#include "main.h" + +#include "utils/stringutils.h" + +#include + +#include +#include + +#include + +const char *DOWNLOAD_ERROR_MESSAGE_THREAD + = "Could not create download thread!"; + +/** + * Calculates the Alder-32 checksum for the given file. + */ +static unsigned long fadler32(FILE *file) +{ + // Obtain file size + fseek(file, 0, SEEK_END); + long fileSize = ftell(file); + rewind(file); + + // Calculate Adler-32 checksum + char *buffer = static_cast(malloc(fileSize)); + const size_t read = fread(buffer, 1, fileSize, file); + unsigned long adler = adler32(0L, Z_NULL, 0); + adler = adler32(static_cast(adler), (Bytef*)buffer, read); + free(buffer); + + return adler; +} + +enum +{ + OPTIONS_NONE = 0, + OPTIONS_MEMORY = 1 +}; + +namespace Net +{ + +Download::Download(void *ptr, const std::string &url, + DownloadUpdate updateFunction, bool ignoreError): + mPtr(ptr), + mUrl(url), + mFileName(""), + mWriteFunction(NULL), + mUpdateFunction(updateFunction), + mThread(NULL), + mCurl(NULL), + mHeaders(NULL), + mIgnoreError(ignoreError) +{ + mError = static_cast(malloc(CURL_ERROR_SIZE + 1)); + mError[0] = 0; + + mOptions.cancel = false; +} + +Download::~Download() +{ + if (mHeaders) + curl_slist_free_all(mHeaders); + + int status; + if (mThread && SDL_GetThreadID(mThread)) + SDL_WaitThread(mThread, &status); + mThread = 0; + free(mError); +} + +void Download::addHeader(const std::string &header) +{ + mHeaders = curl_slist_append(mHeaders, header.c_str()); +} + +void Download::noCache() +{ + addHeader("pragma: no-cache"); + addHeader("Cache-Control: no-cache"); +} + +void Download::setFile(const std::string &filename, Sint64 adler32) +{ + mOptions.memoryWrite = false; + mFileName = filename; + + if (adler32 > -1) + { + mAdler = static_cast(adler32); + mOptions.checkAdler = true; + } + else + { + mOptions.checkAdler = false; + } +} + +void Download::setWriteFunction(WriteFunction write) +{ + mOptions.memoryWrite = true; + mWriteFunction = write; +} + +bool Download::start() +{ + logger->log("Starting download: %s", mUrl.c_str()); + + mThread = SDL_CreateThread(downloadThread, this); + + if (!mThread) + { + logger->log1(DOWNLOAD_ERROR_MESSAGE_THREAD); + strcpy(mError, DOWNLOAD_ERROR_MESSAGE_THREAD); + mUpdateFunction(mPtr, DOWNLOAD_STATUS_THREAD_ERROR, 0, 0); + if (!mIgnoreError) + return false; + } + + return true; +} + +void Download::cancel() +{ + logger->log("Canceling download: %s", mUrl.c_str()); + + mOptions.cancel = true; + if (mThread && SDL_GetThreadID(mThread)) + SDL_WaitThread(mThread, NULL); + + mThread = NULL; +} + +char *Download::getError() +{ + return mError; +} + +int Download::downloadProgress(void *clientp, double dltotal, double dlnow, + double ultotal _UNUSED_, double ulnow _UNUSED_) +{ + Download *d = reinterpret_cast(clientp); + if (!d) + return -5; + + if (d->mOptions.cancel) + { + return d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_CANCELLED, + static_cast(dltotal), + static_cast(dlnow)); + return -5; + } + + return d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_IDLE, + static_cast(dltotal), + static_cast(dlnow)); +} + +int Download::downloadThread(void *ptr) +{ + int attempts = 0; + bool complete = false; + Download *d = reinterpret_cast(ptr); + CURLcode res; + std::string outFilename; + + if (!d) + return 0; + + if (!d->mOptions.memoryWrite) + outFilename = d->mFileName + ".part"; + + while (attempts < 3 && !complete && !d->mOptions.cancel) + { + FILE *file = NULL; + + d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_STARTING, 0, 0); + + if (d->mOptions.cancel) + { + //need terminate thread? + d->mThread = NULL; + return 0; + } + + d->mCurl = curl_easy_init(); + + if (d->mCurl && !d->mOptions.cancel) + { + logger->log("Downloading: %s", d->mUrl.c_str()); + + curl_easy_setopt(d->mCurl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(d->mCurl, CURLOPT_HTTPHEADER, d->mHeaders); +// curl_easy_setopt(d->mCurl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + + if (d->mOptions.memoryWrite) + { + curl_easy_setopt(d->mCurl, CURLOPT_FAILONERROR, 1); + curl_easy_setopt(d->mCurl, CURLOPT_WRITEFUNCTION, + d->mWriteFunction); + curl_easy_setopt(d->mCurl, CURLOPT_WRITEDATA, d->mPtr); + } + else + { + file = fopen(outFilename.c_str(), "w+b"); + curl_easy_setopt(d->mCurl, CURLOPT_WRITEDATA, file); + } + + curl_easy_setopt(d->mCurl, CURLOPT_USERAGENT, + strprintf(PACKAGE_EXTENDED_VERSION, + branding.getStringValue("appShort").c_str()).c_str()); + curl_easy_setopt(d->mCurl, CURLOPT_ERRORBUFFER, d->mError); + curl_easy_setopt(d->mCurl, CURLOPT_URL, d->mUrl.c_str()); + curl_easy_setopt(d->mCurl, CURLOPT_NOPROGRESS, 0); + curl_easy_setopt(d->mCurl, CURLOPT_PROGRESSFUNCTION, + downloadProgress); + curl_easy_setopt(d->mCurl, CURLOPT_PROGRESSDATA, ptr); + curl_easy_setopt(d->mCurl, CURLOPT_NOSIGNAL, 1); + curl_easy_setopt(d->mCurl, CURLOPT_CONNECTTIMEOUT, 30); + curl_easy_setopt(d->mCurl, CURLOPT_TIMEOUT, 1800); + + if ((res = curl_easy_perform(d->mCurl)) != 0 + && !d->mOptions.cancel) + { + switch (res) + { + case CURLE_ABORTED_BY_CALLBACK: + d->mOptions.cancel = true; + break; + case CURLE_COULDNT_CONNECT: + default: + logger->log("curl error %d: %s host: %s", + res, d->mError, d->mUrl.c_str()); + break; + } + + if (d->mOptions.cancel) + break; + + d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_ERROR, 0, 0); + + if (!d->mOptions.memoryWrite) + { + fclose(file); + ::remove(outFilename.c_str()); + } + attempts++; + continue; + } + + curl_easy_cleanup(d->mCurl); + d->mCurl = 0; + + if (!d->mOptions.memoryWrite) + { + // Don't check resources.xml checksum + if (d->mOptions.checkAdler) + { + unsigned long adler = fadler32(file); + + if (d->mAdler != adler) + { + fclose(file); + + // Remove the corrupted file + ::remove(d->mFileName.c_str()); + logger->log("Checksum for file %s failed: (%lx/%lx)", + d->mFileName.c_str(), + adler, d->mAdler); + attempts++; + continue; // Bail out here to avoid the renaming + } + } + fclose(file); + + // Any existing file with this name is deleted first, otherwise + // the rename will fail on Windows. + ::remove(d->mFileName.c_str()); + ::rename(outFilename.c_str(), d->mFileName.c_str()); + + // Check if we can open it and no errors were encountered + // during renaming + file = fopen(d->mFileName.c_str(), "rb"); + if (file) + { + fclose(file); + complete = true; + } + } + else + { + // It's stored in memory, we're done + complete = true; + } + } + + if (d->mCurl) + { + curl_easy_cleanup(d->mCurl); + d->mCurl = 0; + } + + if (d->mOptions.cancel) + { + //need ternibate thread? + d->mThread = NULL; + return 0; + } + attempts++; + } + + if (d->mOptions.cancel) + { + // Nothing to do... + } + else if (!complete || attempts >= 3) + { + d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_ERROR, 0, 0); + } + else + { + d->mUpdateFunction(d->mPtr, DOWNLOAD_STATUS_COMPLETE, 0, 0); + } + + d->mThread = NULL; + return 0; +} + +} // namespace Net diff --git a/src/net/download.h b/src/net/download.h new file mode 100644 index 000000000..11bca3238 --- /dev/null +++ b/src/net/download.h @@ -0,0 +1,123 @@ +/* + * The Mana Client + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef NET_DOWNLOAD_H +#define NET_DOWNLOAD_H + +#include + +#include +#include + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +enum DownloadStatus +{ + DOWNLOAD_STATUS_CANCELLED = -3, + DOWNLOAD_STATUS_THREAD_ERROR = -2, + DOWNLOAD_STATUS_ERROR = -1, + DOWNLOAD_STATUS_STARTING = 0, + DOWNLOAD_STATUS_IDLE, + DOWNLOAD_STATUS_COMPLETE +}; + +typedef int (*DownloadUpdate)(void *ptr, DownloadStatus status, + size_t total, size_t remaining); + +// Matches what CURL expects +typedef size_t (*WriteFunction)( void *ptr, size_t size, size_t nmemb, + void *stream); + +struct SDL_Thread; +typedef void CURL; +struct curl_slist; + +namespace Net +{ +class Download +{ + public: + Download(void *ptr, const std::string &url, + DownloadUpdate updateFunction, bool ignoreError = false); + + ~Download(); + + void addHeader(const std::string &header); + + /** + * Convience method for adding no-cache headers. + */ + void noCache(); + + void setFile(const std::string &filename, Sint64 adler32 = -1); + + void setWriteFunction(WriteFunction write); + + /** + * Starts the download thread. + * @returns true if thread was created + * false if the thread could not be made or download wasn't + * properly setup + */ + bool start(); + + /** + * Cancels the download. Returns immediately, the cancelled status will + * be noted in the next avialable update call. + */ + void cancel(); + + char *getError(); + + void setIgnoreError(bool n) + { mIgnoreError = n; } + + private: + static int downloadThread(void *ptr); + static int downloadProgress(void *clientp, double dltotal, + double dlnow, double ultotal, + double ulnow); + void *mPtr; + std::string mUrl; + struct + { + unsigned cancel : 1; + unsigned memoryWrite: 1; + unsigned checkAdler: 1; + } mOptions; + std::string mFileName; + WriteFunction mWriteFunction; + unsigned long mAdler; + DownloadUpdate mUpdateFunction; + SDL_Thread *mThread; + CURL *mCurl; + curl_slist *mHeaders; + char *mError; + bool mIgnoreError; +}; + +} // namespace Net + +#endif // NET_DOWNLOAD_H diff --git a/src/net/gamehandler.h b/src/net/gamehandler.h new file mode 100644 index 000000000..a0208f6c7 --- /dev/null +++ b/src/net/gamehandler.h @@ -0,0 +1,62 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MAPHANDLER_H +#define MAPHANDLER_H + +#include "logindata.h" + +#include + +namespace Net +{ + +class GameHandler +{ + public: + virtual ~GameHandler() + {} + + virtual void connect() = 0; + + virtual bool isConnected() = 0; + + virtual void disconnect() = 0; + + virtual void who() = 0; + + virtual void quit() = 0; + + virtual void ping(int tick) = 0; + + virtual bool removeDeadBeings() const = 0; + + virtual void disconnect2() = 0; + + /** + * Tells whether the protocol is using the MP status bar + */ + virtual bool canUseMagicBar() const = 0; +}; + +} // namespace Net + +#endif // MAPHANDLER_H diff --git a/src/net/generalhandler.h b/src/net/generalhandler.h new file mode 100644 index 000000000..9454bc7e1 --- /dev/null +++ b/src/net/generalhandler.h @@ -0,0 +1,50 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "client.h" +#include "main.h" + +#ifndef GENERALHANDLER_H +#define GENERALHANDLER_H + +namespace Net +{ + +class GeneralHandler +{ + public: + virtual ~GeneralHandler() + { } + + virtual void load() = 0; + + virtual void reload() = 0; + + virtual void unload() = 0; + + virtual void flushNetwork() = 0; + + virtual void clearHandlers() = 0; +}; + +} // namespace Net + +#endif // GENERALHANDLER_H diff --git a/src/net/guildhandler.h b/src/net/guildhandler.h new file mode 100644 index 000000000..6a269ff74 --- /dev/null +++ b/src/net/guildhandler.h @@ -0,0 +1,76 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GUILDHANDLER_H +#define GUILDHANDLER_H + +#include "guild.h" + +#include + +class Being; + +namespace Net +{ + +class GuildHandler +{ + public: + virtual ~GuildHandler() + { } + + virtual bool isSupported() + { return false; } + + virtual void create(const std::string &name) = 0; + + virtual void invite(int guildId, const std::string &name) = 0; + + virtual void invite(int guildId, Being *being) = 0; + + virtual void inviteResponse(int guildId, bool response) = 0; + + virtual void leave(int guildId) = 0; + + virtual void kick(GuildMember *member, std::string reason = "") = 0; + + virtual void chat(int guildId, const std::string &text) = 0; + + virtual void memberList(int guildId) = 0; + + virtual void info(int guildId) = 0; + + virtual void changeMemberPostion(GuildMember *member, int level) = 0; + + virtual void requestAlliance(int guildId, int otherGuildId) = 0; + + virtual void requestAllianceResponse(int guildId, int otherGuildId, + bool response) = 0; + + virtual void endAlliance(int guildId, int otherGuildId) = 0; + + virtual void changeNotice(int guildId, std::string msg1, + std::string msg2) = 0; +}; + +} + +#endif // GUILDHANDLER_H diff --git a/src/net/inventoryhandler.h b/src/net/inventoryhandler.h new file mode 100644 index 000000000..18ec6968b --- /dev/null +++ b/src/net/inventoryhandler.h @@ -0,0 +1,72 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef INVENTORYHANDLER_H +#define INVENTORYHANDLER_H + +#include "inventory.h" +#include "item.h" + +#include + +namespace Net +{ + +class InventoryHandler +{ + public: + virtual ~InventoryHandler() + { } + + virtual void equipItem(const Item *item) = 0; + + virtual void unequipItem(const Item *item) = 0; + + virtual void useItem(const Item *item) = 0; + + virtual void dropItem(const Item *item, int amount) = 0; + + virtual bool canSplit(const Item *item) = 0; + + virtual void splitItem(const Item *item, int amount) = 0; + + virtual void moveItem(int oldIndex, int newIndex) = 0; + + virtual void openStorage(int type) = 0; + + virtual void closeStorage(int type) = 0; + + //void changeCart() = 0; + + virtual void moveItem(int source, int slot, int amount, + int destination) = 0; + + // TODO: fix/remove me + virtual size_t getSize(int type) const = 0; + + virtual int convertFromServerSlot(int eAthenaSlot) = 0; + +// virtual ~InventoryHandler() {} +}; + +} // namespace Net + +#endif // INVENTORYHANDLER_H diff --git a/src/net/logindata.h b/src/net/logindata.h new file mode 100644 index 000000000..5617cea32 --- /dev/null +++ b/src/net/logindata.h @@ -0,0 +1,95 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LOGINDATA_H +#define LOGINDATA_H + +#include "being.h" + +#include "net/serverinfo.h" + +#include + +class LoginData +{ + public: + LoginData(): + username(""), + password(""), + newPassword(""), + updateHost(""), + updateType(0), + email(""), + captchaResponse(""), + gender(GENDER_UNSPECIFIED), + remember(false), + registerLogin(false) + { + resetCharacterSlots(); + } + + enum UpdateType + { + Upd_Normal = 0, + Upd_Close = 1, + Upd_Skip = 2, + Upd_Custom = 4 + }; + + std::string username; + std::string password; + std::string newPassword; + std::string updateHost; + int updateType; + + std::string email; + std::string captchaResponse; + + Gender gender; + + bool remember; /**< Whether to store the username. */ + bool registerLogin; /**< Whether an account + is being registered. */ + + unsigned short characterSlots; /**< The number of character slots */ + + void clear() + { + username.clear(); + password.clear(); + newPassword.clear(); + updateHost.clear(); + updateType = Upd_Normal; + email.clear(); + captchaResponse.clear(); + gender = GENDER_UNSPECIFIED; + resetCharacterSlots(); + } + /** + * Initialize character slots to 3 for TmwAthena compatibility + */ + void resetCharacterSlots() + { + characterSlots = 3; // Default value, used for TmwAthena. + } +}; + +#endif // LOGINDATA_H diff --git a/src/net/loginhandler.h b/src/net/loginhandler.h new file mode 100644 index 000000000..5511e46a4 --- /dev/null +++ b/src/net/loginhandler.h @@ -0,0 +1,112 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LOGINHANDLER_H +#define LOGINHANDLER_H + +#include "net/logindata.h" +#include "net/serverinfo.h" +#include "net/worldinfo.h" + +#include +#include + +namespace Net +{ + +class LoginHandler +{ + public: + /** + * This enum describes options specific to either eAthena or Manaserv. + * By querying for these flags, the GUI can adapt to the current + * server type dynamically. + */ + enum OptionalAction + { + Unregister = 0x1, + ChangeEmail = 0x2, + SetEmailOnRegister = 0x4, + SetGenderOnRegister = 0x8 + }; + + void setServer(const ServerInfo &server) + { mServer = server; } + + ServerInfo getServer() const + { return mServer; } + + virtual void connect() = 0; + + virtual bool isConnected() = 0; + + virtual void disconnect() = 0; + + /** + * @see OptionalAction + */ + virtual int supportedOptionalActions() const = 0; + + virtual bool isRegistrationEnabled() = 0; + + virtual void getRegistrationDetails() = 0; + + virtual unsigned int getMinUserNameLength() const + { return 4; } + + virtual unsigned int getMaxUserNameLength() const + { return 25; } + + virtual unsigned int getMinPasswordLength() const + { return 4; } + + virtual unsigned int getMaxPasswordLength() const + { return 255; } + + virtual void loginAccount(LoginData *loginData) = 0; + + virtual void logout() = 0; + + virtual void changeEmail(const std::string &email) = 0; + + virtual void changePassword(const std::string &username, + const std::string &oldPassword, + const std::string &newPassword) = 0; + + virtual void chooseServer(unsigned int server) = 0; + + virtual void registerAccount(LoginData *loginData) = 0; + + virtual void unregisterAccount(const std::string &username, + const std::string &password) = 0; + + virtual Worlds getWorlds() const = 0; + + virtual ~LoginHandler () + { } + + protected: + ServerInfo mServer; +}; + +} // namespace Net + +#endif // LOGINHANDLER_H diff --git a/src/net/manaserv/adminhandler.cpp b/src/net/manaserv/adminhandler.cpp new file mode 100644 index 000000000..cd71f00a2 --- /dev/null +++ b/src/net/manaserv/adminhandler.cpp @@ -0,0 +1,93 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/adminhandler.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +extern Net::AdminHandler *adminHandler; + +namespace ManaServ +{ + +extern Connection *chatServerConnection; + +AdminHandler::AdminHandler() +{ + adminHandler = this; +} + +void AdminHandler::announce(const std::string &text) +{ + MessageOut msg(PCMSG_ANNOUNCE); + msg.writeString(text); + chatServerConnection->send(msg); +} + +void AdminHandler::localAnnounce(const std::string &text _UNUSED_) +{ + // TODO +} + +void AdminHandler::hide(bool hide _UNUSED_) +{ + // TODO +} + +void AdminHandler::kick(int playerId _UNUSED_) +{ + // TODO +} + +void AdminHandler::kick(const std::string &name _UNUSED_) +{ + // TODO +} + +void AdminHandler::ban(int playerId _UNUSED_) +{ + // TODO +} + +void AdminHandler::ban(const std::string &name _UNUSED_) +{ + // TODO +} + +void AdminHandler::unban(int playerId _UNUSED_) +{ + // TODO +} + +void AdminHandler::unban(const std::string &name _UNUSED_) +{ + // TODO +} + +void AdminHandler::mute(int playerId _UNUSED_, int type _UNUSED_, + int limit _UNUSED_) +{ + // TODO +} + +} // namespace ManaServ diff --git a/src/net/manaserv/adminhandler.h b/src/net/manaserv/adminhandler.h new file mode 100644 index 000000000..596ccbccd --- /dev/null +++ b/src/net/manaserv/adminhandler.h @@ -0,0 +1,64 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_ADMINHANDLER_H +#define NET_MANASERV_ADMINHANDLER_H + +#include "net/adminhandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class AdminHandler : public Net::AdminHandler +{ + public: + AdminHandler(); + + void announce(const std::string &text); + + void localAnnounce(const std::string &text); + + void hide(bool hide); + + void kick(int playerId); + + void kick(const std::string &name); + + void ban(int playerId); + + void ban(const std::string &name); + + void unban(int playerId); + + void unban(const std::string &name); + + void mute(int playerId, int type, int limit); +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/attributes.cpp b/src/net/manaserv/attributes.cpp new file mode 100644 index 000000000..7cc0e053d --- /dev/null +++ b/src/net/manaserv/attributes.cpp @@ -0,0 +1,411 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/attributes.h" + +#include "log.h" +#include "playerinfo.h" + +#include "gui/statuswindow.h" + +#include "resources/itemdb.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" +#include "utils/xml.h" + +#include +#include + +#define DEFAULT_ATTRIBUTESDB_FILE "attributes.xml" +#define DEFAULT_POINTS 60 +#define DEFAULT_MIN_PTS 1 +#define DEFAULT_MAX_PTS 20 + +namespace ManaServ +{ +namespace Attributes +{ + typedef struct + { + unsigned int id; + std::string name; + std::string description; + /** Whether the attribute value can be modified by the player */ + bool modifiable; + /**< Attribute scope. */ + std::string scope; + /** The playerInfo core Id the attribute is linked with or -1 if not */ + int playerInfoId; + } Attribute; + + /** Map for attributes. */ + typedef std::map AttributeMap; + static AttributeMap attributes; + + /** tags = effects on attributes. */ + typedef std::map< std::string, std::string > TagMap; + static TagMap tags; + + /** List of modifiable attribute names used at character's creation. */ + static std::vector attributeLabels; + + /** Characters creation points. */ + static unsigned int creationPoints = 0; + static unsigned int attributeMinimum = 0; + static unsigned int attributeMaximum = 0; + + unsigned int getCreationPoints() + { return creationPoints; } + + unsigned int getAttributeMinimum() + { return attributeMinimum; } + + unsigned int getAttributeMaximum() + { return attributeMaximum; } + + std::vector& getLabels() + { return attributeLabels; } + + /** + * Fills the list of base attribute labels. + */ + static void fillLabels() + { + // Fill up the modifiable attribute label list. + attributeLabels.clear(); + AttributeMap::const_iterator it, it_end; + for (it = attributes.begin(), it_end = attributes.end(); + it != it_end; it++) + { + if (it->second.modifiable && (it->second.scope == "character" + || it->second.scope == "being")) + { + attributeLabels.push_back(it->second.name + ":"); + } + } + } + + /** + * Fills the list of base attribute labels. + */ + static int getPlayerInfoIdFromAttrType(std::string attrType) + { + toLower(attrType); + if (attrType == "level") + return ::LEVEL; + else if (attrType == "hp") + return ::HP; + else if (attrType == "max-hp") + return ::MAX_HP; + else if (attrType == "mp") + return ::MP; + else if (attrType == "max-mp") + return ::MAX_MP; + else if (attrType == "exp") + return ::EXP; + else if (attrType == "exp-needed") + return ::EXP_NEEDED; + else if (attrType == "money") + return ::MONEY; + else if (attrType == "total-weight") + return ::TOTAL_WEIGHT; + else if (attrType == "max-weight") + return ::MAX_WEIGHT; + else if (attrType == "skill-points") + return ::SKILL_POINTS; + else if (attrType == "char-points") + return ::CHAR_POINTS; + else if (attrType == "corr-points") + return ::CORR_POINTS; + else if (attrType == "none") + return -2; // Used to hide the attribute display. + + return -1; // Not linked to a playerinfo stat. + } + + int getPlayerInfoIdFromAttrId(int attrId) + { + AttributeMap::const_iterator it = attributes.find(attrId); + + if (it != attributes.end()) + return it->second.playerInfoId; + + return -1; + } + + static void loadBuiltins() + { + { + Attribute a; + a.id = 16; + a.name = _("Strength"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("str", _("Strength %+.1f"))); + } + + { + Attribute a; + a.id = 17; + a.name = _("Agility"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("agi", _("Agility %+.1f"))); + } + + { + Attribute a; + a.id = 18; + a.name = _("Dexterity"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("dex", _("Dexterity %+.1f"))); + } + + { + Attribute a; + a.id = 19; + a.name = _("Vitality"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("vit", _("Vitality %+.1f"))); + } + + { + Attribute a; + a.id = 20; + a.name = _("Intelligence"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("int", _("Intelligence %+.1f"))); + } + + { + Attribute a; + a.id = 21; + a.name = _("Willpower"); + a.description = ""; + a.modifiable = true; + a.scope = "character"; + a.playerInfoId = -1; + + attributes[a.id] = a; + tags.insert(std::make_pair("wil", _("Willpower %+.1f"))); + } + } + + void load() + { + logger->log("Initializing attributes database..."); + + XML::Document doc(DEFAULT_ATTRIBUTESDB_FILE); + xmlNodePtr rootNode = doc.rootNode(); + + if (!rootNode || !xmlStrEqual(rootNode->name, BAD_CAST "attributes")) + { + logger->log("Attributes: Error while loading " + DEFAULT_ATTRIBUTESDB_FILE ". Using Built-ins."); + loadBuiltins(); + fillLabels(); + return; + } + + for_each_xml_child_node(node, rootNode) + { + if (xmlStrEqual(node->name, BAD_CAST "attribute")) + { + int id = XML::getProperty(node, "id", 0); + + if (!id) + { + logger->log("Attributes: Invalid or missing stat ID in " + DEFAULT_ATTRIBUTESDB_FILE "!"); + continue; + } + else if (attributes.find(id) != attributes.end()) + { + logger->log("Attributes: Redefinition of stat ID %d", id); + } + + std::string name = XML::getProperty(node, "name", ""); + + if (name.empty()) + { + logger->log("Attributes: Invalid or missing stat name in " + DEFAULT_ATTRIBUTESDB_FILE "!"); + continue; + } + + // Create the attribute. + Attribute a; + a.id = id; + a.name = name; + a.description = XML::getProperty(node, "desc", ""); + a.modifiable = XML::getBoolProperty(node, "modifiable", false); + a.scope = XML::getProperty(node, "scope", "none"); + a.playerInfoId = getPlayerInfoIdFromAttrType( + XML::getProperty(node, "player-info", "")); + + attributes[id] = a; + + unsigned int count = 0; + for_each_xml_child_node(effectNode, node) + { + if (!xmlStrEqual(effectNode->name, BAD_CAST "modifier")) + continue; + ++count; + std::string tag = XML::getProperty(effectNode, "tag", ""); + if (tag.empty()) + { + if (name.empty()) + { + logger->log("Attribute modifier in attribute" + " %u:%s: Empty name definition " + "on empty tag definition, skipping.", + a.id, a.name.c_str()); + --count; + continue; + } + tag = name.substr(0, name.size() > 3 + ? 3 : name.size()); + tag = toLower(tag) + toString(count); + } + + std::string effect = XML::getProperty( + effectNode, "effect", ""); + + if (effect.empty()) + { + if (name.empty()) + { + logger->log("Attribute modifier in attribute" + " %u:%s: Empty name definition " + "on empty effect definition, skipping.", + a.id, a.name.c_str()); + --count; + continue; + } + else + { + effect = name + " %+f"; + } + } + tags.insert(std::make_pair(tag, effect)); + } + logger->log("Found %d tags for attribute %d.", count, id); + + }// End attribute + else if (xmlStrEqual(node->name, BAD_CAST "points")) + { + creationPoints = XML::getProperty( + node, "start", DEFAULT_POINTS); + attributeMinimum = XML::getProperty( + node, "minimum", DEFAULT_MIN_PTS); + attributeMaximum = XML::getProperty( + node, "maximum", DEFAULT_MAX_PTS); + logger->log("Loaded points: start: %i, min: %i, max: %i.", + creationPoints, attributeMinimum, attributeMaximum); + } + else + { + continue; + } + } + logger->log("Found %d tags for %d attributes.", int(tags.size()), + int(attributes.size())); + + fillLabels(); + + // Sanity checks on starting points + float modifiableAttributeCount = (float) attributeLabels.size(); + float averageValue = 1; + if (modifiableAttributeCount) + averageValue = ((float) creationPoints) / modifiableAttributeCount; + + if (averageValue > attributeMaximum || averageValue < attributeMinimum + || creationPoints < 1) + { + logger->log("Attributes: Character's point values make " + "the character's creation impossible. " + "Switch back to defaults."); + creationPoints = DEFAULT_POINTS; + attributeMinimum = DEFAULT_MIN_PTS; + attributeMaximum = DEFAULT_MAX_PTS; + } + } + + void unload() + { + attributes.clear(); + } + + void informItemDB() + { + std::list dbStats; + + TagMap::const_iterator it, it_end; + for (it = tags.begin(), it_end = tags.end(); it != it_end; ++it) + dbStats.push_back(ItemDB::Stat(it->first, it->second)); + + ItemDB::setStatsList(dbStats); + } + + void informStatusWindow() + { + if (!statusWindow) + return; + + AttributeMap::const_iterator it, it_end; + for (it = attributes.begin(), it_end = attributes.end(); + it != it_end; it++) + { + if (it->second.playerInfoId == -1 + && (it->second.scope == "character" + || it->second.scope == "being")) + { + statusWindow->addAttribute(it->second.id, it->second.name, + it->second.modifiable, it->second.description); + } + } + } + +} // namespace Attributes +} // namespace ManaServ diff --git a/src/net/manaserv/attributes.h b/src/net/manaserv/attributes.h new file mode 100644 index 000000000..216bae411 --- /dev/null +++ b/src/net/manaserv/attributes.h @@ -0,0 +1,72 @@ +/* + * The Mana Client + * Copyright (C) 2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_ATTRIBUTES_H +#define NET_MANASERV_ATTRIBUTES_H + +#include +#include + +namespace ManaServ +{ + +namespace Attributes +{ + void load(); + + void unload(); + + void informItemDB(); + + void informStatusWindow(); + + /** + * Returns the list of base attribute labels. + */ + std::vector& getLabels(); + + /** + * Give back the corresponding playerinfo Id from the attribute id + * defined in the xml file. + */ + int getPlayerInfoIdFromAttrId(int attrId); + + /** + * Give the attribute points given to a character + * at its creation. + */ + unsigned int getCreationPoints(); + + /** + * Give the minimum attribute point possible + * at character's creation. + */ + unsigned int getAttributeMinimum(); + + /** + * Give the maximum attribute point possible + * at character's creation. + */ + unsigned int getAttributeMaximum(); + +} // namespace Attributes +} // namespace ManaServ + +#endif // NET_MANASERV_ATTRIBUTES_H diff --git a/src/net/manaserv/beinghandler.cpp b/src/net/manaserv/beinghandler.cpp new file mode 100644 index 000000000..ae6a417e5 --- /dev/null +++ b/src/net/manaserv/beinghandler.cpp @@ -0,0 +1,385 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/beinghandler.h" + +#include "actorspritemanager.h" +#include "being.h" +#include "client.h" +#include "game.h" +#include "localplayer.h" +#include "log.h" +#include "particle.h" + +#include "gui/okdialog.h" + +#include "net/messagein.h" +#include "net/net.h" + +#include "net/manaserv/playerhandler.h" +#include "net/manaserv/protocol.h" + +#include "resources/colordb.h" + +#include "utils/gettext.h" + +extern Net::BeingHandler *beingHandler; + +namespace ManaServ +{ + +BeingHandler::BeingHandler() +{ + static const Uint16 _messages[] = + { + 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; + beingHandler = this; +} + +void BeingHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_BEING_ENTER: + handleBeingEnterMessage(msg); + break; + case GPMSG_BEING_LEAVE: + handleBeingLeaveMessage(msg); + break; + case GPMSG_BEINGS_MOVE: + handleBeingsMoveMessage(msg); + 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; + default: + break; + } +} + +Vector BeingHandler::giveSpeedInPixelsPerTicks(float speedInTilesPerSeconds) +{ + Vector speedInTicks; + Game *game = Game::instance(); + Map *map = 0; + if (game) + { + map = game->getCurrentMap(); + if (map) + { + speedInTicks.x = speedInTilesPerSeconds + * (float)map->getTileWidth() + / 1000 * (float) MILLISECONDS_IN_A_TICK; + speedInTicks.y = speedInTilesPerSeconds + * (float)map->getTileHeight() + / 1000 * (float) MILLISECONDS_IN_A_TICK; + } + } + + if (!game || !map) + { + speedInTicks.x = speedInTicks.y = 0; + logger->log1("Manaserv::BeingHandler: Speed wasn't given back" + " because game/Map not initialized."); + } + // We don't use z for now. + speedInTicks.z = 0; + + return speedInTicks; +} + +static void handleLooks(Being *being, Net::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] = + { + SPRITE_WEAPON, + SPRITE_HAT, + SPRITE_TOPCLOTHES, + SPRITE_BOTTOMCLOTHES + }; + + int mask = msg.readInt8(); + + if (mask & (1 << 7)) + { + // The equipment has to be cleared first. + for (int i = 0; i < nb_slots; ++i) + being->setSprite(slots[i], 0); + } + + // 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, "", (slots[i] == SPRITE_WEAPON)); + } +} + +void BeingHandler::handleBeingEnterMessage(Net::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_CHARACTER: + { + std::string name = msg.readString(); + if (player_node->getName() == name) + { + being = player_node; + being->setId(id); + } + else + { + being = actorSpriteManager->createBeing(id, + ActorSprite::PLAYER, 0); + being->setName(name); + } + int hs = msg.readInt8(), hc = msg.readInt8(); + being->setSprite(SPRITE_HAIR, hs * -1, ColorDB::get(hc)); + being->setGender(msg.readInt8() == GENDER_MALE ? + GENDER_MALE : GENDER_FEMALE); + handleLooks(being, msg); + } break; + + case OBJECT_MONSTER: + case OBJECT_NPC: + { + int subtype = msg.readInt16(); + being = actorSpriteManager->createBeing(id, type == OBJECT_MONSTER + ? ActorSprite::MONSTER : ActorSprite::NPC, subtype); + std::string name = msg.readString(); + if (name.length() > 0) being->setName(name); + } break; + + default: + return; + } + + being->setPosition(px, py); + being->setDestination(px, py); + being->setAction(action); +} + +void BeingHandler::handleBeingLeaveMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being) + return; + + actorSpriteManager->destroy(being); +} + +void BeingHandler::handleBeingsMoveMessage(Net::MessageIn &msg) +{ + while (msg.getUnreadLength()) + { + int id = msg.readInt16(); + int flags = msg.readInt8(); + Being *being = actorSpriteManager->findBeing(id); + int sx = 0; + int sy = 0; + int speed = 0; + + if (flags & MOVING_POSITION) + { + sx = msg.readInt16(); + sy = msg.readInt16(); + speed = msg.readInt8(); + } + if (!being || !(flags & (MOVING_POSITION | MOVING_DESTINATION))) + { + continue; + } + if (speed) + { + /* + * The being's speed is transfered in tiles per second * 10 + * to keep it transferable in a Byte. + * We set it back to tiles per second and in a float. + * Then, we translate it in pixels per ticks, to correspond + * with the Being::logic() function calls + * @see MILLISECONDS_IN_A_TICK + */ + being->setWalkSpeed( + giveSpeedInPixelsPerTicks((float) speed / 10)); + } + + // Ignore messages from the server for the local player + if (being == player_node) + continue; + + if (flags & MOVING_POSITION) + { + being->setDestination(sx, sy); + } + } +} + +void BeingHandler::handleBeingAttackMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + const int direction = msg.readInt8(); + const int attackType = msg.readInt8(); + + if (!being) + return; + + 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; + default: break; + } + + being->setAction(Being::ATTACK, attackType); +} + +void BeingHandler::handleBeingsDamageMessage(Net::MessageIn &msg) +{ + while (msg.getUnreadLength()) + { + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + int damage = msg.readInt16(); + if (being) + { + being->takeDamage(0, damage, Being::HIT); + } + } +} + +void BeingHandler::handleBeingActionChangeMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + Being::Action action = (Being::Action) msg.readInt8(); + if (!being) + return; + + 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(std::string(" ") + _("Press OK to respawn.")); + OkDialog *dlg = new OkDialog(_("You Died"), message, false); + dlg->addActionListener(&(ManaServ::respawnListener)); + } +} + +void BeingHandler::handleBeingLooksChangeMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being || being->getType() != ActorSprite::PLAYER) + return; + handleLooks(being, msg); + if (msg.getUnreadLength()) + { + int style = msg.readInt16(); + int color = msg.readInt16(); + being->setSprite(SPRITE_HAIR, style * -1, ColorDB::get(color)); + } +} + +void BeingHandler::handleBeingDirChangeMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being) + return; + int data = msg.readInt8(); + + // The direction for the player's character is handled on client side. + if (being != player_node) + { + 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; + default: break; + } + } +} + +void BeingHandler::requestNameById(int id _UNUSED_) +{ +} + +void BeingHandler::undress(Being *being _UNUSED_) +{ +} + +} // namespace ManaServ diff --git a/src/net/manaserv/beinghandler.h b/src/net/manaserv/beinghandler.h new file mode 100644 index 000000000..7ed657520 --- /dev/null +++ b/src/net/manaserv/beinghandler.h @@ -0,0 +1,73 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_BEINGHANDLER_H +#define NET_MANASERV_BEINGHANDLER_H + +#include "net/manaserv/messagehandler.h" + +#include "net/beinghandler.h" + +#include "vector.h" +#include "map.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class BeingHandler : public MessageHandler, public Net::BeingHandler +{ + public: + BeingHandler(); + + void handleMessage(Net::MessageIn &msg); + + /** + * Translate a given speed in tiles per seconds + * into pixels per ticks. + * Used to optimize Being::logic() calls. + * @see MILLISECONDS_IN_A_TICKS + */ + static Vector giveSpeedInPixelsPerTicks(float speedInTilesPerSeconds); + + void requestNameById(int id); + + void undress(Being *being); + + private: + void handleBeingAttackMessage(Net::MessageIn &msg); + void handleBeingEnterMessage(Net::MessageIn &msg); + void handleBeingLeaveMessage(Net::MessageIn &msg); + void handleBeingsMoveMessage(Net::MessageIn &msg); + void handleBeingsDamageMessage(Net::MessageIn &msg); + void handleBeingActionChangeMessage(Net::MessageIn &msg); + void handleBeingLooksChangeMessage(Net::MessageIn &msg); + void handleBeingDirChangeMessage(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/buysellhandler.cpp b/src/net/manaserv/buysellhandler.cpp new file mode 100644 index 000000000..dbfdfd46d --- /dev/null +++ b/src/net/manaserv/buysellhandler.cpp @@ -0,0 +1,132 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/buysellhandler.h" + +#include "actorspritemanager.h" +#include "item.h" +#include "localplayer.h" +#include "playerinfo.h" +#include "shopitem.h" + +#include "gui/buy.h" +#include "gui/chat.h" +#include "gui/sell.h" + +#include "net/messagein.h" +#include "net/net.h" + +#include "net/manaserv/protocol.h" + + +extern Net::BuySellHandler *buySellHandler; + +namespace ManaServ +{ + +BuySellHandler::BuySellHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_NPC_BUY, + GPMSG_NPC_SELL, + 0 + }; + handledMessages = _messages; + buySellHandler = this; +} + +void BuySellHandler::handleMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being || being->getType() != ActorSprite::NPC) + { + return; + } + + int npcId = being->getId(); + + switch (msg.getId()) + { + case GPMSG_NPC_BUY: + { + BuyDialog* dialog = new BuyDialog(npcId); + + dialog->reset(); + dialog->setMoney(PlayerInfo::getAttribute(MONEY)); + + while (msg.getUnreadLength()) + { + int itemId = msg.readInt16(); + int amount = msg.readInt16(); + int value = msg.readInt16(); + dialog->addItem(itemId, amount, value); + } + break; + } + + case GPMSG_NPC_SELL: + { + SellDialog* dialog = new SellDialog(npcId); + + dialog->reset(); + dialog->setMoney(PlayerInfo::getAttribute(MONEY)); + + while (msg.getUnreadLength()) + { + int itemId = msg.readInt16(); + int amount = msg.readInt16(); + int value = msg.readInt16(); + dialog->addItem(new Item(itemId, amount, false), value); + } + break; + } + + default: + break; + } +} + +void BuySellHandler::requestSellList(std::string nick _UNUSED_) +{ + // TODO +} + +void BuySellHandler::requestBuyList(std::string nick _UNUSED_) +{ + // TODO +} + +void BuySellHandler::sendBuyRequest(std::string nick _UNUSED_, + ShopItem* item _UNUSED_, + int amount _UNUSED_) +{ + // TODO +} + +void BuySellHandler::sendSellRequest(std::string nick _UNUSED_, + ShopItem* item _UNUSED_, + int amount _UNUSED_) +{ + // TODO +} + +} // namespace ManaServ diff --git a/src/net/manaserv/buysellhandler.h b/src/net/manaserv/buysellhandler.h new file mode 100644 index 000000000..0ceebe61f --- /dev/null +++ b/src/net/manaserv/buysellhandler.h @@ -0,0 +1,57 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_BUYSELLHANDLER_H +#define NET_MANASERV_BUYSELLHANDLER_H + +#include "net/manaserv/messagehandler.h" + +#include "net/buysellhandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class BuySellHandler : public MessageHandler, public Net::BuySellHandler +{ + public: + BuySellHandler(); + + void handleMessage(Net::MessageIn &msg); + + void requestSellList(std::string nick); + + void requestBuyList(std::string nick); + + void sendBuyRequest(std::string nick, ShopItem* item, int amount); + + void sendSellRequest(std::string nick, ShopItem* item, int amount); + +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/charhandler.cpp b/src/net/manaserv/charhandler.cpp new file mode 100644 index 000000000..dd5430c03 --- /dev/null +++ b/src/net/manaserv/charhandler.cpp @@ -0,0 +1,406 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/charhandler.h" + +#include "client.h" +#include "localplayer.h" +#include "log.h" + +#include "gui/charcreatedialog.h" +#include "gui/okdialog.h" + +#include "net/logindata.h" +#include "net/loginhandler.h" +#include "net/net.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/gamehandler.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" +#include "net/manaserv/attributes.h" + +#include "resources/colordb.h" + +#include "utils/dtor.h" +#include "utils/gettext.h" + +extern Net::CharHandler *charHandler; +extern ManaServ::GameHandler *gameHandler; + +namespace ManaServ +{ + +extern Connection *accountServerConnection; +extern Connection *gameServerConnection; +extern Connection *chatServerConnection; +extern std::string netToken; +extern ServerInfo gameServer; +extern ServerInfo chatServer; + +CharHandler::CharHandler() +{ + static const Uint16 _messages[] = + { + APMSG_CHAR_CREATE_RESPONSE, + APMSG_CHAR_DELETE_RESPONSE, + APMSG_CHAR_INFO, + APMSG_CHAR_SELECT_RESPONSE, + 0 + }; + handledMessages = _messages; + charHandler = this; +} + +CharHandler::~CharHandler() +{ + clear(); +} + +void CharHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case APMSG_CHAR_CREATE_RESPONSE: + handleCharacterCreateResponse(msg); + break; + + case APMSG_CHAR_DELETE_RESPONSE: + handleCharacterDeleteResponse(msg); + break; + + case APMSG_CHAR_INFO: + handleCharacterInfo(msg); + break; + + case APMSG_CHAR_SELECT_RESPONSE: + handleCharacterSelectResponse(msg); + break; + + default: + break; + } +} + +void CharHandler::handleCharacterInfo(Net::MessageIn &msg) +{ + CachedCharacterInfo info; + info.slot = msg.readInt8(); + info.name = msg.readString(); + info.gender = msg.readInt8() == GENDER_MALE ? GENDER_MALE : + GENDER_FEMALE; + info.hairStyle = msg.readInt8(); + info.hairColor = msg.readInt8(); + info.level = msg.readInt16(); + info.characterPoints = msg.readInt16(); + info.correctionPoints = msg.readInt16(); + + + while (msg.getUnreadLength() > 0) + { + int id = msg.readInt32(); + CachedAttrbiute attr; + attr.base = msg.readInt32() / 256.0; + attr.mod = msg.readInt32() / 256.0; + + info.attribute[id] = attr; + } + + mCachedCharacterInfos.push_back(info); + + updateCharacters(); +} + +void CharHandler::handleCharacterCreateResponse(Net::MessageIn &msg) +{ + const int errMsg = msg.readInt8(); + + if (errMsg != ERRMSG_OK) + { + // Character creation failed + std::string errorMessage = ""; + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + errorMessage = _("Not logged in."); + break; + case CREATE_TOO_MUCH_CHARACTERS: + errorMessage = _("No empty slot."); + break; + case ERRMSG_INVALID_ARGUMENT: + errorMessage = _("Invalid name."); + break; + case CREATE_EXISTS_NAME: + errorMessage = _("Character's name already exists."); + break; + case CREATE_INVALID_HAIRSTYLE: + errorMessage = _("Invalid hairstyle."); + break; + case CREATE_INVALID_HAIRCOLOR: + errorMessage = _("Invalid hair color."); + break; + case CREATE_INVALID_GENDER: + errorMessage = _("Invalid gender."); + break; + case CREATE_ATTRIBUTES_TOO_HIGH: + errorMessage = _("Character's stats are too high."); + break; + case CREATE_ATTRIBUTES_TOO_LOW: + errorMessage = _("Character's stats are too low."); + break; + case CREATE_ATTRIBUTES_OUT_OF_RANGE: + errorMessage = strprintf( _("At least one stat" + "is out of the permitted range: (%u - %u)."), + Attributes::getAttributeMinimum(), + Attributes::getAttributeMaximum()); + break; + case CREATE_INVALID_SLOT: + errorMessage = _("Invalid slot number."); + break; + default: + errorMessage = _("Unknown error."); + break; + } + new OkDialog(_("Error"), errorMessage); + + if (mCharCreateDialog) + mCharCreateDialog->unlock(); + } + else + { + // Close the character create dialog + if (mCharCreateDialog) + { + mCharCreateDialog->scheduleDelete(); + mCharCreateDialog = 0; + } + } +} + +void CharHandler::handleCharacterDeleteResponse(Net::MessageIn &msg) +{ + int errMsg = msg.readInt8(); + if (errMsg == ERRMSG_OK) + { + // Character deletion successful + delete mSelectedCharacter; + mCharacters.remove(mSelectedCharacter); +// mSelectedCharacter = 0; + updateCharSelectDialog(); + new OkDialog(_("Info"), _("Player deleted.")); + } + else + { + // Character deletion failed + std::string errorMessage = ""; + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + errorMessage = _("Not logged in."); + break; + case ERRMSG_INVALID_ARGUMENT: + errorMessage = _("Selection out of range."); + break; + default: + errorMessage = strprintf(_("Unknown error (%d)."), errMsg); + } + new OkDialog(_("Error"), errorMessage); + } + mSelectedCharacter = 0; + unlockCharSelectDialog(); +} + +void CharHandler::handleCharacterSelectResponse(Net::MessageIn &msg) +{ + int errMsg = msg.readInt8(); + + if (errMsg == ERRMSG_OK) + { + netToken = msg.readString(32); + + gameServer.hostname.assign(msg.readString()); + gameServer.port = msg.readInt16(); + + chatServer.hostname.assign(msg.readString()); + chatServer.port = msg.readInt16(); + + logger->log("Game server: %s:%d", gameServer.hostname.c_str(), + gameServer.port); + logger->log("Chat server: %s:%d", chatServer.hostname.c_str(), + chatServer.port); + + // Prevent the selected local player from being deleted + player_node = mSelectedCharacter->dummy; + PlayerInfo::setBackend(mSelectedCharacter->data); + mSelectedCharacter->dummy = 0; + + Client::setState(STATE_CONNECT_GAME); + } + else if (errMsg == ERRMSG_FAILURE) + { + errorMessage = _("No gameservers are available."); + delete_all(mCharacters); + mCharacters.clear(); + Client::setState(STATE_ERROR); + } +} + +void CharHandler::setCharSelectDialog(CharSelectDialog *window) +{ + mCharSelectDialog = window; + updateCharacters(); +} + +void CharHandler::setCharCreateDialog(CharCreateDialog *window) +{ + mCharCreateDialog = window; + + if (!mCharCreateDialog) + return; + + mCharCreateDialog->setAttributes(Attributes::getLabels(), + Attributes::getCreationPoints(), + Attributes::getAttributeMinimum(), + Attributes::getAttributeMaximum()); +} + +void CharHandler::requestCharacters() +{ + if (!accountServerConnection->isConnected()) + { + Net::getLoginHandler()->connect(); + } + else + { + // The characters are already there, continue to character selection + Client::setState(STATE_CHAR_SELECT); + } +} + +void CharHandler::chooseCharacter(Net::Character *character) +{ + mSelectedCharacter = character; + + MessageOut msg(PAMSG_CHAR_SELECT); + msg.writeInt8(mSelectedCharacter->slot); + accountServerConnection->send(msg); +} + +void CharHandler::newCharacter(const std::string &name, + int slot, + bool gender, + int hairstyle, + int hairColor, + const std::vector &stats) +{ + MessageOut msg(PAMSG_CHAR_CREATE); + + msg.writeString(name); + msg.writeInt8(hairstyle); + msg.writeInt8(hairColor); + msg.writeInt8(gender); + msg.writeInt8(slot); + + std::vector::const_iterator it, it_end; + for (it = stats.begin(), it_end = stats.end(); it != it_end; it++) + msg.writeInt16((*it)); + + accountServerConnection->send(msg); +} + +void CharHandler::deleteCharacter(Net::Character *character) +{ + mSelectedCharacter = character; + + MessageOut msg(PAMSG_CHAR_DELETE); + msg.writeInt8(mSelectedCharacter->slot); + accountServerConnection->send(msg); +} + +void CharHandler::switchCharacter() +{ + gameHandler->quit(true); +} + +unsigned int CharHandler::baseSprite() const +{ + return SPRITE_BASE; +} + +unsigned int CharHandler::hairSprite() const +{ + return SPRITE_HAIR; +} + +unsigned int CharHandler::maxSprite() const +{ + return SPRITE_VECTOREND; +} + +void CharHandler::updateCharacters() +{ + // Delete previous characters + delete_all(mCharacters); + mCharacters.clear(); + + if (!mCharSelectDialog) + return; + + // Create new characters and initialize them from the cached infos + for (unsigned i = 0; i < mCachedCharacterInfos.size(); ++i) + { + const CachedCharacterInfo &info = mCachedCharacterInfos.at(i); + + Net::Character *character = new Net::Character; + character->slot = info.slot; + LocalPlayer *player = character->dummy = new LocalPlayer; + player->setName(info.name); + player->setGender(info.gender); + player->setSprite(SPRITE_HAIR, info.hairStyle * -1, + ColorDB::get(info.hairColor)); + character->data.mAttributes[LEVEL] = info.level; + character->data.mAttributes[CHAR_POINTS] = info.characterPoints; + character->data.mAttributes[CORR_POINTS] = info.correctionPoints; + + for (CachedAttributes::const_iterator it = info.attribute.begin(), + it_end = info.attribute.end(); it != it_end; it++) + { + character->data.mStats[i].base = it->second.base; + character->data.mStats[i].mod = it->second.mod; + } + + mCharacters.push_back(character); + } + + updateCharSelectDialog(); +} + +void CharHandler::clear() +{ + setCharCreateDialog(0); + setCharSelectDialog(0); + + mCachedCharacterInfos.clear(); + updateCharacters(); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/charhandler.h b/src/net/manaserv/charhandler.h new file mode 100644 index 000000000..512cca451 --- /dev/null +++ b/src/net/manaserv/charhandler.h @@ -0,0 +1,119 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_CHARSERVERHANDLER_H +#define NET_MANASERV_CHARSERVERHANDLER_H + +#include "gui/charselectdialog.h" + +#include "net/charhandler.h" + +#include "net/manaserv/messagehandler.h" + +#include + +class LoginData; + +namespace ManaServ +{ + +/** + * Deals with incoming messages related to character selection. + */ +class CharHandler : public MessageHandler, public Net::CharHandler +{ + public: + CharHandler(); + + ~CharHandler(); + + void handleMessage(Net::MessageIn &msg); + + void setCharSelectDialog(CharSelectDialog *window); + + /** + * 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); + + void requestCharacters(); + + void chooseCharacter(Net::Character *character); + + void newCharacter(const std::string &name, int slot, + bool gender, int hairstyle, int hairColor, + const std::vector &stats); + + void deleteCharacter(Net::Character *character); + + void switchCharacter(); + + unsigned int baseSprite() const; + + unsigned int hairSprite() const; + + unsigned int maxSprite() const; + + void clear(); + + private: + /** + * Character information needs to be cached since we receive it before + * we have loaded the dynamic data, so we can't resolve load any + * sprites yet. + */ + struct CachedAttrbiute + { + double base; + double mod; + }; + + typedef std::map CachedAttributes; + + struct CachedCharacterInfo + { + int slot; + std::string name; + Gender gender; + int hairStyle; + int hairColor; + int level; + int characterPoints; + int correctionPoints; + CachedAttributes attribute; + }; + + void handleCharacterInfo(Net::MessageIn &msg); + void handleCharacterCreateResponse(Net::MessageIn &msg); + void handleCharacterDeleteResponse(Net::MessageIn &msg); + void handleCharacterSelectResponse(Net::MessageIn &msg); + + void updateCharacters(); + + /** Cached character information */ + std::vector mCachedCharacterInfos; +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/chathandler.cpp b/src/net/manaserv/chathandler.cpp new file mode 100644 index 000000000..5e588c583 --- /dev/null +++ b/src/net/manaserv/chathandler.cpp @@ -0,0 +1,472 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/chathandler.h" + +#include "actorspritemanager.h" +#include "being.h" +#include "client.h" +#include "channel.h" +#include "channelmanager.h" + +#include "gui/chat.h" + +#include "gui/widgets/channeltab.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include +#include + +extern Being *player_node; + +extern Net::ChatHandler *chatHandler; + +namespace ManaServ +{ + +extern Connection *chatServerConnection; +extern Connection *gameServerConnection; +extern std::string netToken; +extern ServerInfo chatServer; + +ChatHandler::ChatHandler() +{ + static const Uint16 _messages[] = + { + 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, + CPMSG_WHO_RESPONSE, + CPMSG_DISCONNECT_RESPONSE, + 0 + }; + handledMessages = _messages; + chatHandler = this; +} + +void ChatHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_SAY: + handleGameChatMessage(msg); + break; + + case CPMSG_ENTER_CHANNEL_RESPONSE: + handleEnterChannelResponse(msg); + break; + + case CPMSG_LIST_CHANNELS_RESPONSE: + handleListChannelsResponse(msg); + break; + + case CPMSG_PRIVMSG: + handlePrivateMessage(msg); + break; + + case CPMSG_ANNOUNCEMENT: + handleAnnouncement(msg); + break; + + case CPMSG_PUBMSG: + handleChatMessage(msg); + break; + + case CPMSG_QUIT_CHANNEL_RESPONSE: + handleQuitChannelResponse(msg); + break; + + case CPMSG_LIST_CHANNELUSERS_RESPONSE: + handleListChannelUsersResponse(msg); + break; + + case CPMSG_CHANNEL_EVENT: + handleChannelEvent(msg); + break; + + case CPMSG_WHO_RESPONSE: + handleWhoResponse(msg); + break; + case CPMSG_DISCONNECT_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful logout + if (errMsg == ERRMSG_OK) + { + // TODO: Handle logout + } + else + { + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + errorMessage = "Chatserver: Not logged in"; + break; + default: + errorMessage = "Chatserver: Unknown error"; + break; + } + Client::setState(STATE_ERROR); + } + } + break; + default: + break; + } +} + +void ChatHandler::handleGameChatMessage(Net::MessageIn &msg) +{ + short id = msg.readInt16(); + std::string chatMsg = msg.readString(); + + if (id == 0) + { + localChatTab->chatLog(chatMsg, BY_SERVER); + return; + } + + Being *being = actorSpriteManager->findBeing(id); + + std::string mes; + if (being) + { + mes = being->getName() + " : " + chatMsg; + being->setSpeech(chatMsg, SPEECH_TIME); + } + else + mes = "Unknown : " + chatMsg; + + localChatTab->chatLog(mes, being == player_node + ? BY_PLAYER : BY_OTHER); +} + +void ChatHandler::handleEnterChannelResponse(Net::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); + ChatTab *tab = channel->getTab(); + tab->chatLog(strprintf(_("Topic: %s"), announcement.c_str()), + BY_CHANNEL); + + std::string user; + std::string userModes; + tab->chatLog(_("Players in this channel:"), BY_CHANNEL); + while (msg.getUnreadLength()) + { + user = msg.readString(); + if (user == "") + return; + userModes = msg.readString(); + if (userModes.find('o') != std::string::npos) + { + user = "@" + user; + } + tab->chatLog(user, BY_CHANNEL); + } + + } + else + { + localChatTab->chatLog(_("Error joining channel."), BY_SERVER); + } +} + +void ChatHandler::handleListChannelsResponse(Net::MessageIn &msg) +{ + localChatTab->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(); + localChatTab->chatLog(channelName, BY_SERVER); + } + localChatTab->chatLog(_("End of channel list."), BY_SERVER); +} + +void ChatHandler::handlePrivateMessage(Net::MessageIn &msg) +{ + std::string userNick = msg.readString(); + std::string chatMsg = msg.readString(); + + chatWindow->whisper(userNick, chatMsg); +} + +void ChatHandler::handleAnnouncement(Net::MessageIn &msg) +{ + std::string chatMsg = msg.readString(); + localChatTab->chatLog(chatMsg, BY_GM); +} + +void ChatHandler::handleChatMessage(Net::MessageIn &msg) +{ + short channelId = msg.readInt16(); + std::string userNick = msg.readString(); + std::string chatMsg = msg.readString(); + + Channel *channel = channelManager->findById(channelId); + channel->getTab()->chatLog(userNick, chatMsg); +} + +void ChatHandler::handleQuitChannelResponse(Net::MessageIn &msg) +{ + if (msg.readInt8() == ERRMSG_OK) + { + short channelId = msg.readInt16(); + Channel *channel = channelManager->findById(channelId); + channelManager->removeChannel(channel); + } +} + +void ChatHandler::handleListChannelUsersResponse(Net::MessageIn &msg) +{ + std::string channelName = msg.readString(); + std::string userNick; + std::string userModes; + Channel *channel = channelManager->findByName(channelName); + channel->getTab()->chatLog(_("Players in this channel:"), + BY_CHANNEL); + while (msg.getUnreadLength()) + { + userNick = msg.readString(); + if (userNick == "") + { + break; + } + userModes = msg.readString(); + if (userModes.find('o') != std::string::npos) + { + userNick = "@" + userNick; + } + localChatTab->chatLog(userNick, BY_CHANNEL, channel); + } +} + +void ChatHandler::handleChannelEvent(Net::MessageIn &msg) +{ + short channelId = msg.readInt16(); + char eventId = msg.readInt8(); + std::string line = msg.readString(); + Channel *channel = channelManager->findById(channelId); + + if (channel) + { + switch(eventId) + { + case CHAT_EVENT_NEW_PLAYER: + channel->getTab()->chatLog(strprintf(_("%s entered the " + "channel."), line.c_str()), BY_CHANNEL); + break; + + case CHAT_EVENT_LEAVING_PLAYER: + channel->getTab()->chatLog(strprintf(_("%s left the channel."), + line.c_str()), BY_CHANNEL); + break; + + case CHAT_EVENT_TOPIC_CHANGE: + channel->getTab()->chatLog(strprintf(_("Topic: %s"), + line.c_str()), BY_CHANNEL); + break; + + 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()); + channel->getTab()->chatLog(strprintf(_("%s has set mode %s " + "on user %s."), user1.c_str(), mode.c_str(), + user2.c_str()), BY_CHANNEL); + } + break; + + case CHAT_EVENT_KICKED_PLAYER: + { + int first = line.find(":"); + std::string user1 = line.substr(0, first); + std::string user2 = line.substr(first + 1, line.length()); + channel->getTab()->chatLog(strprintf(_("%s has kicked %s."), + user1.c_str(), user2.c_str()), BY_CHANNEL); + } + break; + + default: + channel->getTab()->chatLog(_("Unknown channel event."), + BY_CHANNEL); + } + } +} + +void ChatHandler::handleWhoResponse(Net::MessageIn &msg) +{ + std::string userNick; + + while (msg.getUnreadLength()) + { + userNick = msg.readString(); + if (userNick == "") + break; + localChatTab->chatLog(userNick, BY_SERVER); + } +} + +void ChatHandler::connect() +{ + MessageOut msg(PCMSG_CONNECT); + msg.writeString(netToken, 32); + chatServerConnection->send(msg); +} + +bool ChatHandler::isConnected() +{ + return chatServerConnection->isConnected(); +} + +void ChatHandler::disconnect() +{ + chatServerConnection->disconnect(); +} + +void ChatHandler::talk(const std::string &text) +{ + MessageOut msg(PGMSG_SAY); + msg.writeString(text); + gameServerConnection->send(msg); +} + +void ChatHandler::talkRaw(const std::string &text) +{ + MessageOut msg(PGMSG_SAY); + msg.writeString(text); + gameServerConnection->send(msg); +} + +void ChatHandler::me(const std::string &text _UNUSED_) +{ + // TODO +} + +void ChatHandler::privateMessage(const std::string &recipient, + const std::string &text) +{ + MessageOut msg(PCMSG_PRIVMSG); + msg.writeString(recipient); + msg.writeString(text); + chatServerConnection->send(msg); +} + +void ChatHandler::channelList() +{ + MessageOut msg(PCMSG_LIST_CHANNELS); + chatServerConnection->send(msg); +} + +void ChatHandler::enterChannel(const std::string &channel, + const std::string &password) +{ + MessageOut msg(PCMSG_ENTER_CHANNEL); + msg.writeString(channel); + msg.writeString(password); + chatServerConnection->send(msg); +} + +void ChatHandler::quitChannel(int channelId) +{ + MessageOut msg(PCMSG_QUIT_CHANNEL); + msg.writeInt16(channelId); + chatServerConnection->send(msg); +} + +void ChatHandler::sendToChannel(int channelId, const std::string &text) +{ + MessageOut msg(PCMSG_CHAT); + msg.writeString(text); + msg.writeInt16(channelId); + chatServerConnection->send(msg); +} + +void ChatHandler::userList(const std::string &channel) +{ + MessageOut msg(PCMSG_LIST_CHANNELUSERS); + msg.writeString(channel); + chatServerConnection->send(msg); +} + +void ChatHandler::setChannelTopic(int channelId, const std::string &text) +{ + MessageOut msg(PCMSG_TOPIC_CHANGE); + msg.writeInt16(channelId); + msg.writeString(text); + chatServerConnection->send(msg); +} + +void ChatHandler::setUserMode(int channelId, const std::string &name, int mode) +{ + MessageOut msg(PCMSG_USER_MODE); + msg.writeInt16(channelId); + msg.writeString(name); + msg.writeInt8(mode); + chatServerConnection->send(msg); +} + +void ChatHandler::kickUser(int channelId, const std::string &name) +{ + MessageOut msg(PCMSG_KICK_USER); + msg.writeInt16(channelId); + msg.writeString(name); + chatServerConnection->send(msg); +} + +void ChatHandler::who() +{ + MessageOut msg(PCMSG_WHO); + chatServerConnection->send(msg); +} + +void ChatHandler::sendRaw(const std::string &args _UNUSED_) +{ + +} +} // namespace ManaServ diff --git a/src/net/manaserv/chathandler.h b/src/net/manaserv/chathandler.h new file mode 100644 index 000000000..8ffa6d28d --- /dev/null +++ b/src/net/manaserv/chathandler.h @@ -0,0 +1,139 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_CHATHANDLER_H +#define NET_MANASERV_CHATHANDLER_H + +#include "net/chathandler.h" +#include "net/serverinfo.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class ChatHandler : public MessageHandler, public Net::ChatHandler +{ + public: + ChatHandler(); + + /** + * Handle the given message appropriately. + */ + void handleMessage(Net::MessageIn &msg); + + void connect(); + + bool isConnected(); + + void disconnect(); + + void talk(const std::string &text); + + void talkRaw(const std::string &text); + + void me(const std::string &text); + + void privateMessage(const std::string &recipient, + const std::string &text); + + void channelList(); + + void enterChannel(const std::string &channel, + const std::string &password); + + void quitChannel(int channelId); + + void sendToChannel(int channelId, const std::string &text); + + void userList(const std::string &channel); + + void setChannelTopic(int channelId, const std::string &text); + + void setUserMode(int channelId, const std::string &name, int mode); + + void kickUser(int channelId, const std::string &name); + + void who(); + + void sendRaw(const std::string &args); + + private: + /** + * Handle chat messages sent from the game server. + */ + void handleGameChatMessage(Net::MessageIn &msg); + + /** + * Handle channel entry responses. + */ + void handleEnterChannelResponse(Net::MessageIn &msg); + + /** + * Handle list channels responses. + */ + void handleListChannelsResponse(Net::MessageIn &msg); + + /** + * Handle private messages. + */ + void handlePrivateMessage(Net::MessageIn &msg); + + /** + * Handle announcements. + */ + void handleAnnouncement(Net::MessageIn &msg); + + /** + * Handle chat messages. + */ + void handleChatMessage(Net::MessageIn &msg); + + /** + * Handle quit channel responses. + */ + void handleQuitChannelResponse(Net::MessageIn &msg); + + /** + * Handle list channel users responses. + */ + void handleListChannelUsersResponse(Net::MessageIn &msg); + + /** + * Handle channel events. + */ + void handleChannelEvent(Net::MessageIn &msg); + + /** + * Handle who responses. + */ + void handleWhoResponse(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/connection.cpp b/src/net/manaserv/connection.cpp new file mode 100644 index 000000000..944d8a94c --- /dev/null +++ b/src/net/manaserv/connection.cpp @@ -0,0 +1,113 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/connection.h" + +#include "log.h" + +#include "net/manaserv/internal.h" +#include "net/manaserv/messageout.h" + +#include + +namespace ManaServ +{ + +Connection::Connection(ENetHost *client): + mConnection(0), mClient(client) +{ + mPort = 0; + connections++; +} + +Connection::~Connection() +{ + connections--; +} + +bool Connection::connect(const std::string &address, short port) +{ + logger->log("Net::Connection::connect(%s, %i)", address.c_str(), port); + + if (address.empty()) + { + logger->log1("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. +#if defined(ENET_VERSION) && ENET_VERSION >= ENET_CUTOFF + mConnection = enet_host_connect(mClient, &enetAddress, 1, 0); +#else + mConnection = enet_host_connect(mClient, &enetAddress, 1); +#endif + + if (!mConnection) + { + logger->log1("Unable to initiate connection to the server."); + mState = NET_ERROR; + return false; + } + + mPort = port; + + return true; +} + +void Connection::disconnect() +{ + if (!mConnection) + return; + + enet_peer_disconnect(mConnection, 0); + enet_host_flush(mClient); + enet_peer_reset(mConnection); + + mConnection = 0; +} + +bool Connection::isConnected() +{ + return (mConnection) ? + (mConnection->state == ENET_PEER_STATE_CONNECTED) : false; +} + +void Connection::send(const ManaServ::MessageOut &msg) +{ + if (!isConnected()) + { + logger->log1("Warning: cannot send message to not connected server!"); + return; + } + + ENetPacket *packet = enet_packet_create(msg.getData(), + msg.getDataSize(), + ENET_PACKET_FLAG_RELIABLE); + enet_peer_send(mConnection, 0, packet); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/connection.h b/src/net/manaserv/connection.h new file mode 100644 index 000000000..260a177ee --- /dev/null +++ b/src/net/manaserv/connection.h @@ -0,0 +1,89 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_CONNECTION_H +#define NET_MANASERV_CONNECTION_H + +#include "enet/enet.h" + +#include + +#ifdef ENET_VERSION_CREATE +#define ENET_CUTOFF ENET_VERSION_CREATE(1, 3, 0) +#else +#define ENET_CUTOFF 0xFFFFFFFF +#endif + +namespace ManaServ +{ + class MessageOut; + + /** + * \ingroup Network + */ + class Connection + { + public: + enum State + { + OK = 0, + 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 ManaServ::MessageOut &msg); + + private: + friend Connection *ManaServ::getConnection(); + Connection(ENetHost *client); + + short mPort; + ENetPeer *mConnection; + ENetHost *mClient; + State mState; + }; +} + +#endif // NET_MANASERV_CONNECTION_H diff --git a/src/net/manaserv/defines.h b/src/net/manaserv/defines.h new file mode 100644 index 000000000..f09175f65 --- /dev/null +++ b/src/net/manaserv/defines.h @@ -0,0 +1,77 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MANASERV_DEFINES_H +#define MANASERV_DEFINES_H + +/** + * Attributes used during combat. Available to all the beings. + */ +enum +{ + BASE_ATTR_BEGIN = 0, + BASE_ATTR_PHY_ATK_MIN = BASE_ATTR_BEGIN, + BASE_ATTR_PHY_ATK_DELTA, + /**< Physical attack power. */ + BASE_ATTR_MAG_ATK, /**< Magical attack power. */ + BASE_ATTR_PHY_RES, /**< Resistance to physical damage. */ + BASE_ATTR_MAG_RES, /**< Resistance to magical damage. */ + BASE_ATTR_EVADE, /**< Ability to avoid hits. */ + BASE_ATTR_HIT, /**< Ability to hit stuff. */ + BASE_ATTR_HP, /**< Hit Points (Base value: maximum, + Modded value: current) */ + BASE_ATTR_HP_REGEN, /**< number of HP regenerated every 10 game ticks */ + BASE_ATTR_END, + BASE_ATTR_NB = BASE_ATTR_END - BASE_ATTR_BEGIN, + + BASE_ELEM_BEGIN = BASE_ATTR_END, + BASE_ELEM_NEUTRAL = BASE_ELEM_BEGIN, + BASE_ELEM_FIRE, + BASE_ELEM_WATER, + BASE_ELEM_EARTH, + BASE_ELEM_AIR, + BASE_ELEM_SACRED, + BASE_ELEM_DEATH, + BASE_ELEM_END, + BASE_ELEM_NB = BASE_ELEM_END - BASE_ELEM_BEGIN, + + NB_BEING_ATTRIBUTES = BASE_ELEM_END +}; + +/** + * Attributes of characters. Used to derive being attributes. + */ +enum +{ + CHAR_ATTR_BEGIN = NB_BEING_ATTRIBUTES, + CHAR_ATTR_STRENGTH = CHAR_ATTR_BEGIN, + CHAR_ATTR_AGILITY, + CHAR_ATTR_DEXTERITY, + CHAR_ATTR_VITALITY, + CHAR_ATTR_INTELLIGENCE, + CHAR_ATTR_WILLPOWER, + CHAR_ATTR_END, + CHAR_ATTR_NB = CHAR_ATTR_END - CHAR_ATTR_BEGIN, + + NB_CHARACTER_ATTRIBUTES = CHAR_ATTR_END +}; + +#endif // MANASERV_DEFINES_H diff --git a/src/net/manaserv/effecthandler.cpp b/src/net/manaserv/effecthandler.cpp new file mode 100644 index 000000000..5e0083744 --- /dev/null +++ b/src/net/manaserv/effecthandler.cpp @@ -0,0 +1,80 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/effecthandler.h" + +#include "actorspritemanager.h" +#include "effectmanager.h" +#include "log.h" + +#include "net/messagein.h" + +#include "net/manaserv/protocol.h" + +namespace ManaServ +{ + +EffectHandler::EffectHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_CREATE_EFFECT_POS, + GPMSG_CREATE_EFFECT_BEING, + 0 + }; + handledMessages = _messages; +} + +void EffectHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_CREATE_EFFECT_POS: + handleCreateEffectPos(msg); + break; + case GPMSG_CREATE_EFFECT_BEING: + handleCreateEffectBeing(msg); + break; + default: + break; + } +} + +void EffectHandler::handleCreateEffectPos(Net::MessageIn &msg) +{ + int id = msg.readInt16(); + Uint16 x = msg.readInt16(); + Uint16 y = msg.readInt16(); + effectManager->trigger(id, x, y); +} + +void EffectHandler::handleCreateEffectBeing(Net::MessageIn &msg) +{ + int eid = msg.readInt16(); + int bid = msg.readInt16(); + Being* b = actorSpriteManager->findBeing(bid); + if (b) + effectManager->trigger(eid, b); + else + logger->log("Warning: CreateEffect called for unknown being #%d", bid); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/effecthandler.h b/src/net/manaserv/effecthandler.h new file mode 100644 index 000000000..5f6c07580 --- /dev/null +++ b/src/net/manaserv/effecthandler.h @@ -0,0 +1,44 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_EFFECTSHANDLER_H +#define NET_MANASERV_EFFECTSHANDLER_H + +#include "net/manaserv/messagehandler.h" + +namespace ManaServ +{ + +class EffectHandler : public MessageHandler +{ + public: + EffectHandler(); + + void handleMessage(Net::MessageIn &msg); + + private: + void handleCreateEffectPos(Net::MessageIn &msg); + void handleCreateEffectBeing(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_EFFECTSHANDLER_H diff --git a/src/net/manaserv/gamehandler.cpp b/src/net/manaserv/gamehandler.cpp new file mode 100644 index 000000000..9ca6c0d6b --- /dev/null +++ b/src/net/manaserv/gamehandler.cpp @@ -0,0 +1,154 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/gamehandler.h" + +#include "client.h" +#include "localplayer.h" + +#include "net/manaserv/chathandler.h" +#include "net/manaserv/connection.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +extern Net::GameHandler *gameHandler; + +extern ManaServ::ChatHandler *chatHandler; + +namespace ManaServ +{ + +extern Connection *chatServerConnection; +extern Connection *gameServerConnection; +extern std::string netToken; +extern ServerInfo gameServer; +extern ServerInfo chatServer; + +GameHandler::GameHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_DISCONNECT_RESPONSE, + 0 + }; + handledMessages = _messages; + gameHandler = this; +} + +void GameHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_DISCONNECT_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful logout + if (errMsg == ERRMSG_OK) + { + netToken = msg.readString(32); + + if (!netToken.empty()) + { + Client::setState(STATE_SWITCH_CHARACTER); + } + else + { + // TODO: Handle logout + } + } + // Logout failed + else + { + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + errorMessage = "Gameserver: Not logged in"; + break; + default: + errorMessage = "Gameserver: Unknown error"; + break; + } + Client::setState(STATE_ERROR); + } + } + break; + default: + break; + } +} + +void GameHandler::connect() +{ + gameServerConnection->connect(gameServer.hostname, gameServer.port); + + // Will already be connected if we just changed gameservers + if (!chatServerConnection->isConnected()) + chatServerConnection->connect(chatServer.hostname, chatServer.port); +} + +bool GameHandler::isConnected() +{ + return gameServerConnection->isConnected() && + chatHandler->isConnected(); +} + +void GameHandler::disconnect() +{ + gameServerConnection->disconnect(); + // No need if we're just changing gameservers + if (Client::getState() != STATE_CHANGE_MAP) + chatHandler->disconnect(); +} + +void GameHandler::who() +{ + // TODO +} + +void GameHandler::quit(bool reconnectAccount) +{ + MessageOut msg(PGMSG_DISCONNECT); + msg.writeInt8((unsigned char) reconnectAccount); + gameServerConnection->send(msg); +} + +void GameHandler::ping(int tick _UNUSED_) +{ + // TODO +} + +void GameHandler::gameLoading() +{ + MessageOut msg(PGMSG_CONNECT); + msg.writeString(netToken, 32); + gameServerConnection->send(msg); + + chatHandler->connect(); + + // Attack range from item DB + player_node->setAttackRange(-1); +} + +void GameHandler::disconnect2() +{ +} + +} // namespace ManaServ diff --git a/src/net/manaserv/gamehandler.h b/src/net/manaserv/gamehandler.h new file mode 100644 index 000000000..246a3c736 --- /dev/null +++ b/src/net/manaserv/gamehandler.h @@ -0,0 +1,74 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_MAPHANDLER_H +#define NET_MANASERV_MAPHANDLER_H + +#include "net/gamehandler.h" +#include "net/serverinfo.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class GameHandler : public MessageHandler, public Net::GameHandler +{ + public: + GameHandler(); + + void handleMessage(Net::MessageIn &msg); + + void connect(); + + bool isConnected(); + + void disconnect(); + + void who(); + + void quit(bool reconnectAccount); + + void quit() { quit(false); } + + void ping(int tick); + + bool removeDeadBeings() const { return false; } + + void clear(); + + void gameLoading(); + + /** The ManaServ protocol doesn't use the MP status bar. */ + bool canUseMagicBar() const { return false; } + + void disconnect2(); +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_MAPHANDLER_H diff --git a/src/net/manaserv/generalhandler.cpp b/src/net/manaserv/generalhandler.cpp new file mode 100644 index 000000000..acf731dec --- /dev/null +++ b/src/net/manaserv/generalhandler.cpp @@ -0,0 +1,211 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/generalhandler.h" + +#include "client.h" + +#include "gui/changeemaildialog.h" +#include "gui/charselectdialog.h" +#include "gui/inventorywindow.h" +#include "gui/register.h" +#include "gui/skilldialog.h" +#include "gui/specialswindow.h" + +#include "net/manaserv/beinghandler.h" +#include "net/manaserv/buysellhandler.h" +#include "net/manaserv/charhandler.h" +#include "net/manaserv/chathandler.h" +#include "net/manaserv/connection.h" +#include "net/manaserv/effecthandler.h" +#include "net/manaserv/gamehandler.h" +#include "net/manaserv/guildhandler.h" +#include "net/manaserv/inventoryhandler.h" +#include "net/manaserv/itemhandler.h" +#include "net/manaserv/loginhandler.h" +#include "net/manaserv/network.h" +#include "net/manaserv/npchandler.h" +#include "net/manaserv/partyhandler.h" +#include "net/manaserv/playerhandler.h" +#include "net/manaserv/specialhandler.h" +#include "net/manaserv/attributes.h" +#include "net/manaserv/tradehandler.h" + +#include "utils/gettext.h" + +#include + +extern Net::GeneralHandler *generalHandler; + +extern ManaServ::LoginHandler *loginHandler; + +namespace ManaServ +{ + +Connection *accountServerConnection = 0; +Connection *chatServerConnection = 0; +Connection *gameServerConnection = 0; +std::string netToken = ""; +ServerInfo gameServer; +ServerInfo chatServer; + +GeneralHandler::GeneralHandler(): + mBeingHandler(new BeingHandler), + mBuySellHandler(new BuySellHandler), + mCharHandler(new CharHandler), + mChatHandler(new ChatHandler), + mEffectHandler(new EffectHandler), + mGameHandler(new GameHandler), + mGuildHandler(new GuildHandler), + mInventoryHandler(new InventoryHandler), + mItemHandler(new ItemHandler), + mLoginHandler(new LoginHandler), + mNpcHandler(new NpcHandler), + mPartyHandler(new PartyHandler), + mPlayerHandler(new PlayerHandler), + mTradeHandler(new TradeHandler), + mSpecialHandler(new SpecialHandler) +{ + initialize(); + + accountServerConnection = getConnection(); + gameServerConnection = getConnection(); + chatServerConnection = getConnection(); + + generalHandler = this; + + listen(CHANNEL_CLIENT); + listen(CHANNEL_GAME); +} + +void GeneralHandler::load() +{ + registerHandler(mBeingHandler.get()); + registerHandler(mBuySellHandler.get()); + registerHandler(mCharHandler.get()); + registerHandler(mChatHandler.get()); + registerHandler(mEffectHandler.get()); + registerHandler(mGameHandler.get()); + registerHandler(mGuildHandler.get()); + registerHandler(mInventoryHandler.get()); + registerHandler(mItemHandler.get()); + registerHandler(mLoginHandler.get()); + registerHandler(mNpcHandler.get()); + registerHandler(mPartyHandler.get()); + registerHandler(mPlayerHandler.get()); + registerHandler(mTradeHandler.get()); +} + +void GeneralHandler::reload() +{ + static_cast(Net::getCharHandler())->clear(); + + if (accountServerConnection) + accountServerConnection->disconnect(); + + if (gameServerConnection) + gameServerConnection->disconnect(); + + if (chatServerConnection) + chatServerConnection->disconnect(); + + netToken.clear(); + gameServer.clear(); + chatServer.clear(); + + Attributes::unload(); + Attributes::load(); + Attributes::informItemDB(); +} + +void GeneralHandler::unload() +{ + clearHandlers(); + + if (accountServerConnection) + accountServerConnection->disconnect(); + if (gameServerConnection) + gameServerConnection->disconnect(); + if (chatServerConnection) + chatServerConnection->disconnect(); + + delete accountServerConnection; + accountServerConnection = 0; + delete gameServerConnection; + gameServerConnection = 0; + delete chatServerConnection; + chatServerConnection = 0; + + Attributes::unload(); + finalize(); +} + +void GeneralHandler::flushNetwork() +{ + flush(); + + if (Client::getState() == STATE_SWITCH_CHARACTER && + Net::getLoginHandler()->isConnected()) + { + loginHandler->reconnect(); + Client::setState(STATE_GET_CHARACTERS); + } +} + +void GeneralHandler::clearHandlers() +{ + clearNetworkHandlers(); +} + +void GeneralHandler::event(Channels channel, + const Mana::Event &event) +{ + if (channel == CHANNEL_CLIENT) + { + int newState = event.getInt("newState"); + + if (newState == STATE_GAME) + { + GameHandler *game = static_cast( + Net::getGameHandler()); + game->gameLoading(); + } + else if (newState == STATE_LOAD_DATA) + { + Attributes::load(); + Attributes::informItemDB(); + } + } + else if (channel == CHANNEL_GAME) + { + if (event.getName() == EVENT_GUIWINDOWSLOADED) + { + inventoryWindow->setSplitAllowed(true); + skillDialog->loadSkills("mana-skills.xml"); + + PlayerInfo::setAttribute(EXP_NEEDED, 100); + + Attributes::informStatusWindow(); + } + } +} + +} // namespace ManaServ diff --git a/src/net/manaserv/generalhandler.h b/src/net/manaserv/generalhandler.h new file mode 100644 index 000000000..944a3d305 --- /dev/null +++ b/src/net/manaserv/generalhandler.h @@ -0,0 +1,78 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_GENERALHANDLER_H +#define NET_MANASERV_GENERALHANDLER_H + +#include "listener.h" + +#include "net/generalhandler.h" +#include "net/net.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class GeneralHandler : public Net::GeneralHandler, public Mana::Listener +{ + public: + GeneralHandler(); + + void load(); + + void reload(); + + void unload(); + + void flushNetwork(); + + void clearHandlers(); + + void event(Channels channel, const Mana::Event &event); + + protected: + MessageHandlerPtr mBeingHandler; + MessageHandlerPtr mBuySellHandler; + MessageHandlerPtr mCharHandler; + MessageHandlerPtr mChatHandler; + MessageHandlerPtr mEffectHandler; + MessageHandlerPtr mGameHandler; + MessageHandlerPtr mGuildHandler; + MessageHandlerPtr mInventoryHandler; + MessageHandlerPtr mItemHandler; + MessageHandlerPtr mLoginHandler; + MessageHandlerPtr mNpcHandler; + MessageHandlerPtr mPartyHandler; + MessageHandlerPtr mPlayerHandler; + MessageHandlerPtr mTradeHandler; + MessageHandlerPtr mSpecialHandler; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_GENERALHANDLER_H diff --git a/src/net/manaserv/guildhandler.cpp b/src/net/manaserv/guildhandler.cpp new file mode 100644 index 000000000..26af3eff1 --- /dev/null +++ b/src/net/manaserv/guildhandler.cpp @@ -0,0 +1,360 @@ +/* + * The Mana Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/guildhandler.h" + +#include "event.h" +#include "guild.h" +#include "log.h" +#include "localplayer.h" +#include "channel.h" +#include "channelmanager.h" + +#include "gui/widgets/channeltab.h" +#include "gui/chat.h" +#include "gui/socialwindow.h" + +#include "net/messagein.h" +#include "net/net.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include + +extern Net::GuildHandler *guildHandler; + +namespace ManaServ +{ + +extern Connection *chatServerConnection; + +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; + + guildHandler = this; +} + +void GuildHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case CPMSG_GUILD_CREATE_RESPONSE: + { + logger->log1("Received CPMSG_GUILD_CREATE_RESPONSE"); + if (msg.readInt8() == ERRMSG_OK) + { + // TODO - Acknowledge guild was created + SERVER_NOTICE(_("Guild created.")) + joinedGuild(msg); + } + else + { + SERVER_NOTICE(_("Error creating guild.")) + } + } break; + + case CPMSG_GUILD_INVITE_RESPONSE: + { + logger->log1("Received CPMSG_GUILD_INVITE_RESPONSE"); + if (msg.readInt8() == ERRMSG_OK) + { + // TODO - Acknowledge invite was sent + SERVER_NOTICE(_("Invite sent.")) + } + } break; + + case CPMSG_GUILD_ACCEPT_RESPONSE: + { + logger->log1("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->log1("Received CPMSG_GUILD_GET_MEMBERS_RESPONSE"); + if (msg.readInt8() == ERRMSG_OK) + { + std::string name; + bool online; + Guild *guild; + GuildMember *member; + + short guildId = msg.readInt16(); + guild = player_node->getGuild(guildId); + + if (!guild) + return; + + guild->clearMembers(); + + while (msg.getUnreadLength()) + { + name = msg.readString(); + online = msg.readInt8(); + if (name != "") + { + member = guild->addMember(name); + member->setOnline(online); + } + } + } + } break; + + case CPMSG_GUILD_UPDATE_LIST: + { + logger->log1("Received CPMSG_GUILD_UPDATE_LIST"); + short guildId = msg.readInt16(); + std::string name = msg.readString(); + char eventId = msg.readInt8(); + GuildMember *member; + + Guild *guild = player_node->getGuild(guildId); + if (guild) + { + switch(eventId) + { + case GUILD_EVENT_NEW_PLAYER: + member = guild->addMember(name); + member->setOnline(true); + break; + + case GUILD_EVENT_LEAVING_PLAYER: + guild->removeMember(name); + break; + + case GUILD_EVENT_ONLINE_PLAYER: + member = guild->getMember(name); + if (member) + { + member->setOnline(true); + } + break; + + case GUILD_EVENT_OFFLINE_PLAYER: + member = guild->getMember(name); + if (member) + { + member->setOnline(false); + } + break; + + default: + logger->log1("Invalid guild event"); + } + } + } break; + + case CPMSG_GUILD_INVITED: + { + logger->log1("Received CPMSG_GUILD_INVITED"); + std::string inviterName = msg.readString(); + std::string guildName = msg.readString(); + int guildId = msg.readInt16(); + + // Open a dialog asking if the player accepts joining the guild. + socialWindow->showGuildInvite(guildName, guildId, inviterName); + } break; + + case CPMSG_GUILD_PROMOTE_MEMBER_RESPONSE: + { + logger->log1("Received CPMSG_GUILD_PROMOTE_MEMBER_RESPONSE"); + + if (msg.readInt8() == ERRMSG_OK) + { + // promotion succeeded + SERVER_NOTICE(_("Member was promoted successfully.")) + } + else + { + // promotion failed + SERVER_NOTICE(_("Failed to promote member.")) + } + } + + case CPMSG_GUILD_REJOIN: + { + logger->log1("Received CPMSG_GUILD_REJOIN"); + + joinedGuild(msg); + } break; + + case CPMSG_GUILD_QUIT_RESPONSE: + { + logger->log1("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) + { + Channel *channel = channelManager->findByName( + guild->getName()); + channelManager->removeChannel(channel); + player_node->removeGuild(guildId); + } + } + } break; + default: break; + } +} + +void GuildHandler::joinedGuild(Net::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 + Guild *guild = Guild::getGuild(guildId); + guild->setName(guildName); + guild->setRights(permissions); + player_node->addGuild(guild); + Net::getGuildHandler()->memberList(guildId); + + // Automatically create the guild channel + // COMMENT: Should this go here?? + Channel *channel = new Channel(channelId, guildName, announcement); + channelManager->addChannel(channel); + channel->getTab()->chatLog(strprintf(_("Topic: %s"), announcement.c_str()), + BY_CHANNEL); +} + +void GuildHandler::create(const std::string &name) +{ + MessageOut msg(PCMSG_GUILD_CREATE); + msg.writeString(name); + chatServerConnection->send(msg); +} + +void GuildHandler::invite(int guildId, const std::string &name) +{ + MessageOut msg(PCMSG_GUILD_INVITE); + msg.writeInt16(guildId); + msg.writeString(name); + chatServerConnection->send(msg); +} + +void GuildHandler::invite(int guildId, Being *being) +{ + invite(guildId, being->getName()); +} + +void GuildHandler::inviteResponse(int guildId _UNUSED_, bool response _UNUSED_) +{ + /*MessageOut msg(PCMSG_GUILD_ACCEPT); + msg.writeString(name); + chatServerConnection->send(msg);*/ +} + +void GuildHandler::leave(int guildId) +{ + MessageOut msg(PCMSG_GUILD_QUIT); + msg.writeInt16(guildId); + chatServerConnection->send(msg); +} + +void GuildHandler::kick(GuildMember *member _UNUSED_, + std::string reason _UNUSED_) +{ + // TODO +} + +void GuildHandler::chat(int guildId _UNUSED_, const std::string &text _UNUSED_) +{ + // TODO +} + +void GuildHandler::memberList(int guildId) +{ + MessageOut msg(PCMSG_GUILD_GET_MEMBERS); + msg.writeInt16(guildId); + chatServerConnection->send(msg); +} + +void GuildHandler::info(int guildId _UNUSED_) +{ + // TODO +} + +void GuildHandler::changeMemberPostion(GuildMember *member _UNUSED_, + int level _UNUSED_) +{ + /*MessageOut msg(PCMSG_GUILD_PROMOTE_MEMBER); + msg.writeInt16(guildId); + msg.writeString(name); + msg.writeInt8(level); + chatServerConnection->send(msg);*/ +} + +void GuildHandler::requestAlliance(int guildId _UNUSED_, + int otherGuildId _UNUSED_) +{ + // TODO +} + +void GuildHandler::requestAllianceResponse(int guildId _UNUSED_, + int otherGuildId _UNUSED_, + bool response _UNUSED_) +{ + // TODO +} + +void GuildHandler::endAlliance(int guildId _UNUSED_, int otherGuildId _UNUSED_) +{ + // TODO +} + +void GuildHandler::changeNotice(int guildId _UNUSED_, + std::string msg1 _UNUSED_, + std::string msg2 _UNUSED_) +{ + // TODO +} + +} // namespace ManaServ diff --git a/src/net/manaserv/guildhandler.h b/src/net/manaserv/guildhandler.h new file mode 100644 index 000000000..18862398b --- /dev/null +++ b/src/net/manaserv/guildhandler.h @@ -0,0 +1,84 @@ +/* + * The Mana Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_GUILDHANDLER_H +#define NET_MANASERV_GUILDHANDLER_H + +#include "net/guildhandler.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class GuildHandler : public Net::GuildHandler, public MessageHandler +{ +public: + GuildHandler(); + + bool isSupported() + { return true; } + + void handleMessage(Net::MessageIn &msg); + + void create(const std::string &name); + + void invite(int guildId, const std::string &name); + + void invite(int guidId, Being *being); + + void inviteResponse(int guidId, bool response); + + void leave(int guildId); + + void kick(GuildMember *member, std::string reason = ""); + + void chat(int guildId, const std::string &text); + + void memberList(int guildId); + + void info(int guildId _UNUSED_); + + void changeMemberPostion(GuildMember *member, int level); + + void requestAlliance(int guildId, int otherGuildId); + + void requestAllianceResponse(int guildId, int otherGuildId, + bool response); + + void endAlliance(int guildId, int otherGuildId); + + void changeNotice(int guildId, std::string msg1, + std::string msg2); + +protected: + void joinedGuild(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/internal.cpp b/src/net/manaserv/internal.cpp new file mode 100644 index 000000000..fcba3fb40 --- /dev/null +++ b/src/net/manaserv/internal.cpp @@ -0,0 +1,27 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/internal.h" + +namespace ManaServ +{ + int connections = 0; +} diff --git a/src/net/manaserv/internal.h b/src/net/manaserv/internal.h new file mode 100644 index 000000000..f600c207e --- /dev/null +++ b/src/net/manaserv/internal.h @@ -0,0 +1,30 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_INTERNAL_H +#define NET_MANASERV_INTERNAL_H + +namespace ManaServ +{ + extern int connections; +} + +#endif diff --git a/src/net/manaserv/inventoryhandler.cpp b/src/net/manaserv/inventoryhandler.cpp new file mode 100644 index 000000000..1d7736f1a --- /dev/null +++ b/src/net/manaserv/inventoryhandler.cpp @@ -0,0 +1,219 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/inventoryhandler.h" + +#include "equipment.h" +#include "inventory.h" +#include "item.h" +#include "itemshortcut.h" +#include "localplayer.h" +#include "playerinfo.h" + +#include "gui/chat.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "resources/iteminfo.h" + +#include "log.h" // <<< REMOVE ME! + +extern Net::InventoryHandler *inventoryHandler; + +namespace ManaServ +{ + +extern Connection *gameServerConnection; + +InventoryHandler::InventoryHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_INVENTORY_FULL, + GPMSG_INVENTORY, + GPMSG_EQUIP, + 0 + }; + handledMessages = _messages; + inventoryHandler = this; +} + +void InventoryHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_INVENTORY_FULL: + { + PlayerInfo::clearInventory(); + PlayerInfo::getEquipment()->setBackend(&mEquips); + int count = msg.readInt16(); + while (count--) + { + unsigned int slot = msg.readInt16(); + int id = msg.readInt16(); + unsigned int amount = msg.readInt16(); + PlayerInfo::setInventoryItem(slot, id, amount, 0); + } + while (msg.getUnreadLength()) + { + unsigned int slot = msg.readInt8(); + unsigned int ref = msg.readInt16(); + + mEquips.addEquipment(slot, ref); + } + } + break; + + case GPMSG_INVENTORY: + while (msg.getUnreadLength()) + { + unsigned int slot = msg.readInt16(); + int id = msg.readInt16(); + unsigned int amount = id ? msg.readInt16() : 0; + PlayerInfo::setInventoryItem(slot, id, amount, 0); + } + break; + + case GPMSG_EQUIP: + while (msg.getUnreadLength()) + { + unsigned int ref = msg.readInt16(); + int count = msg.readInt8(); + while (count--) + { + unsigned int slot = msg.readInt8(); + unsigned int used = msg.readInt8(); + + mEquips.setEquipment(slot, used, ref); + } + } + break; + default: + break; + } +} + +void InventoryHandler::equipItem(const Item *item) +{ + MessageOut msg(PGMSG_EQUIP); + msg.writeInt8(item->getInvIndex()); + gameServerConnection->send(msg); +} + +void InventoryHandler::unequipItem(const Item *item) +{ + MessageOut msg(PGMSG_UNEQUIP); + msg.writeInt8(item->getInvIndex()); + gameServerConnection->send(msg); + +/* + // Tidy equipment directly to avoid weapon still shown bug, for instance + int equipSlot = item->getInvIndex(); + logger->log("Unequipping %d", equipSlot); + mEquips.setEquipment(equipSlot, 0); +*/ +} + +void InventoryHandler::useItem(const Item *item) +{ + MessageOut msg(PGMSG_USE_ITEM); + msg.writeInt8(item->getInvIndex()); + gameServerConnection->send(msg); +} + +void InventoryHandler::dropItem(const Item *item, int amount) +{ + MessageOut msg(PGMSG_DROP); + msg.writeInt8(item->getInvIndex()); + msg.writeInt8(amount); + gameServerConnection->send(msg); +} + +bool InventoryHandler::canSplit(const Item *item) +{ + return item && !item->isEquipment() && item->getQuantity() > 1; +} + +void InventoryHandler::splitItem(const Item *item, int amount) +{ + int newIndex = PlayerInfo::getInventory()->getFreeSlot(); + if (newIndex > Inventory::NO_SLOT_INDEX) + { + MessageOut msg(PGMSG_MOVE_ITEM); + msg.writeInt8(item->getInvIndex()); + msg.writeInt8(newIndex); + msg.writeInt8(amount); + gameServerConnection->send(msg); + } +} + +void InventoryHandler::moveItem(int oldIndex, int newIndex) +{ + if (oldIndex == newIndex) + return; + + MessageOut msg(PGMSG_MOVE_ITEM); + msg.writeInt8(oldIndex); + msg.writeInt8(newIndex); + msg.writeInt8(PlayerInfo::getInventory()->getItem(oldIndex) + ->getQuantity()); + gameServerConnection->send(msg); +} + +void InventoryHandler::openStorage(int type _UNUSED_) +{ + // TODO +} + +void InventoryHandler::closeStorage(int type _UNUSED_) +{ + // TODO +} + +void InventoryHandler::moveItem(int source _UNUSED_, int slot _UNUSED_, + int amount _UNUSED_, int destination _UNUSED_) +{ + // TODO +} + +size_t InventoryHandler::getSize(int type) const +{ + switch (type) + { + case Inventory::INVENTORY: + case Inventory::TRADE: + return 50; + case Inventory::STORAGE: + return 300; + default: + return 0; + } +} + +int InventoryHandler::convertFromServerSlot(int eAthenaSlot) +{ + return eAthenaSlot; +} + +} // namespace ManaServ diff --git a/src/net/manaserv/inventoryhandler.h b/src/net/manaserv/inventoryhandler.h new file mode 100644 index 000000000..07d168ce7 --- /dev/null +++ b/src/net/manaserv/inventoryhandler.h @@ -0,0 +1,109 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_INVENTORYHANDLER_H +#define NET_MANASERV_INVENTORYHANDLER_H + +#include "equipment.h" + +#include "net/inventoryhandler.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class EquipBackend : public Equipment::Backend +{ + public: + EquipBackend() + { memset(mEquipment, 0, sizeof(mEquipment)); } + + Item *getEquipment(int index) const + { return mEquipment[index]; } + + void clear() + { + for (int i = 0; i < EQUIPMENT_SIZE; ++i) + delete mEquipment[i]; + + std::fill_n(mEquipment, EQUIPMENT_SIZE, (Item*) 0); + } + + void setEquipment(unsigned int slot, unsigned int used, int reference) + { + printf("Equip: %d at %dx%d\n", reference, slot, used); + } + + void addEquipment(unsigned int slot, int reference) + { + printf("Equip: %d at %d\n", reference, slot); + } + + private: + Item *mEquipment[EQUIPMENT_SIZE]; +}; + +class InventoryHandler : public MessageHandler, Net::InventoryHandler +{ + public: + InventoryHandler(); + + void handleMessage(Net::MessageIn &msg); + + void equipItem(const Item *item); + + void unequipItem(const Item *item); + + void useItem(const Item *item); + + void dropItem(const Item *item, int amount); + + bool canSplit(const Item *item); + + void splitItem(const Item *item, int amount); + + void moveItem(int oldIndex, int newIndex); + + void openStorage(int type); + + void closeStorage(int type); + + void moveItem(int source, int slot, int amount, + int destination); + + size_t getSize(int type) const; + + int convertFromServerSlot(int eAthenaSlot); + + private: + EquipBackend mEquips; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_INVENTORYHANDLER_H diff --git a/src/net/manaserv/itemhandler.cpp b/src/net/manaserv/itemhandler.cpp new file mode 100644 index 000000000..73320ecfb --- /dev/null +++ b/src/net/manaserv/itemhandler.cpp @@ -0,0 +1,90 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/itemhandler.h" + +#include "actorspritemanager.h" + +#include "net/manaserv/protocol.h" +#include "net/manaserv/messagein.h" + +#include "game.h" +#include "map.h" +#include "log.h" + +namespace ManaServ +{ + +ItemHandler::ItemHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_ITEMS, + GPMSG_ITEM_APPEAR, + 0 + }; + handledMessages = _messages; +} + +void ItemHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + 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 + + if (itemId) + { + if (Game *game = Game::instance()) + { + if (Map *map = game->getCurrentMap()) + { + actorSpriteManager->createItem(id, itemId, + x / map->getTileWidth(), + y / map->getTileHeight(), + 0); + } + else + { + logger->log( + "ItemHandler: An item wasn't created " + "because of Game/Map not initialized..."); + } + } + } + else if (FloorItem *item = actorSpriteManager->findItem(id)) + { + actorSpriteManager->destroy(item); + } + } + } break; + default: break; + } +} + +} // namespace ManaServ diff --git a/src/net/manaserv/itemhandler.h b/src/net/manaserv/itemhandler.h new file mode 100644 index 000000000..58f3695fd --- /dev/null +++ b/src/net/manaserv/itemhandler.h @@ -0,0 +1,40 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_ITEMHANDLER_H +#define NET_MANASERV_ITEMHANDLER_H + +#include "net/manaserv/messagehandler.h" + +namespace ManaServ +{ + +class ItemHandler : public MessageHandler +{ + public: + ItemHandler(); + + void handleMessage(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_ITEMHANDLER_H diff --git a/src/net/manaserv/loginhandler.cpp b/src/net/manaserv/loginhandler.cpp new file mode 100644 index 000000000..1588d762d --- /dev/null +++ b/src/net/manaserv/loginhandler.cpp @@ -0,0 +1,479 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/loginhandler.h" + +#include "client.h" +#include "log.h" + +#include "net/logindata.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "utils/gettext.h" +#include "utils/sha256.h" + +extern Net::LoginHandler *loginHandler; + +namespace ManaServ +{ + +extern Connection *accountServerConnection; +extern std::string netToken; + +LoginHandler::LoginHandler() +{ + static const Uint16 _messages[] = + { + APMSG_LOGIN_RESPONSE, + APMSG_REGISTER_RESPONSE, + APMSG_RECONNECT_RESPONSE, + APMSG_PASSWORD_CHANGE_RESPONSE, + APMSG_EMAIL_CHANGE_RESPONSE, + APMSG_LOGOUT_RESPONSE, + APMSG_UNREGISTER_RESPONSE, + APMSG_REGISTER_INFO_RESPONSE, + 0 + }; + handledMessages = _messages; + loginHandler = this; +} + +void LoginHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + 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) + { + Client::setState(STATE_CHAR_SELECT); + } + // Login failed + else + { + switch (errMsg) + { + case ERRMSG_INVALID_ARGUMENT: + errorMessage = _("Wrong magic_token."); + break; + case ERRMSG_FAILURE: + errorMessage = _("Already logged in."); + break; + case LOGIN_BANNED: + errorMessage = _("Account banned."); + break; + default: + errorMessage = _("Unknown error."); + break; + } + Client::setState(STATE_ERROR); + } + } + break; + + case APMSG_PASSWORD_CHANGE_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful pass change + if (errMsg == ERRMSG_OK) + { + Client::setState(STATE_CHANGEPASSWORD_SUCCESS); + } + // 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; + } + Client::setState(STATE_ACCOUNTCHANGE_ERROR); + } + } + break; + + case APMSG_EMAIL_CHANGE_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful pass change + if (errMsg == ERRMSG_OK) + { + Client::setState(STATE_CHANGEEMAIL_SUCCESS); + } + // 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; + } + Client::setState(STATE_ACCOUNTCHANGE_ERROR); + } + } + break; + case APMSG_LOGOUT_RESPONSE: + { + int errMsg = msg.readInt8(); + + // Successful logout + if (errMsg == ERRMSG_OK) + { + // TODO: handle logout + } + // Logout failed + else + { + switch (errMsg) + { + case ERRMSG_NO_LOGIN: + errorMessage = "Accountserver: Not logged in"; + break; + default: + errorMessage = "Accountserver: Unknown error"; + break; + } + Client::setState(STATE_ERROR); + } + } + break; + case APMSG_UNREGISTER_RESPONSE: + { + int errMsg = msg.readInt8(); + // Successful unregistration + if (errMsg == ERRMSG_OK) + { + Client::setState(STATE_UNREGISTER); + } + // Unregistration failed + else + { + switch (errMsg) + { + case ERRMSG_INVALID_ARGUMENT: + errorMessage = + "Accountserver: Wrong username or password"; + break; + default: + errorMessage = "Accountserver: Unknown error"; + break; + } + Client::setState(STATE_ACCOUNTCHANGE_ERROR); + } + } + break; + + case APMSG_REGISTER_INFO_RESPONSE: + { + int allowed = msg.readInt8(); + + if (allowed) + { + mMinUserNameLength = msg.readInt8(); + mMaxUserNameLength = msg.readInt8(); + std::string captchaURL = msg.readString(); + std::string captchaInstructions = msg.readString(); + + printf("%s: %s\n", captchaURL.c_str(), captchaInstructions.c_str()); + + Client::setState(STATE_REGISTER); + } + else + { + errorMessage = msg.readString(); + + if (errorMessage.empty()) + errorMessage = _("Client registration is not allowed. " + "Please contact server administration."); + Client::setState(STATE_LOGIN_ERROR); + } + } + break; + default: + break; + } +} + +void LoginHandler::handleLoginResponse(Net::MessageIn &msg) +{ + const int errMsg = msg.readInt8(); + + if (errMsg == ERRMSG_OK) + { + readServerInfo(msg); + // No worlds atm, but future use :-D + Client::setState(STATE_WORLD_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_BANNED: + errorMessage = _("Account banned"); + break; + case LOGIN_INVALID_TIME: + errorMessage = _("Login attempt too soon after previous " + "attempt."); + break; + default: + errorMessage = _("Unknown error."); + break; + } + Client::setState(STATE_LOGIN_ERROR); + } +} + +void LoginHandler::handleRegisterResponse(Net::MessageIn &msg) +{ + const int errMsg = msg.readInt8(); + + if (errMsg == ERRMSG_OK) + { + readServerInfo(msg); + Client::setState(STATE_WORLD_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; + case REGISTER_CAPTCHA_WRONG: + errorMessage = _("You took too long with the captcha or your " + "response was incorrect."); + break; + default: + errorMessage = _("Unknown error."); + break; + } + Client::setState(STATE_LOGIN_ERROR); + } +} + +void LoginHandler::readServerInfo(Net::MessageIn &msg) +{ + // Safety check for outdated manaserv versions (remove me later) + if (msg.getUnreadLength() == 0) + return; + + // Set the update host when included in the message + const std::string updateHost = msg.readString(); + if (!updateHost.empty()) + mLoginData->updateHost = updateHost; + else + logger->log1("Warning: server does not have an update host set!"); + + // Read the client data folder for dynamic data loading. + // This is only used by the QT client. + msg.readString(); + + // Read the number of character slots + mLoginData->characterSlots = msg.readInt8(); +} + +void LoginHandler::connect() +{ + accountServerConnection->connect(mServer.hostname, mServer.port); +} + +bool LoginHandler::isConnected() +{ + return accountServerConnection->isConnected(); +} + +void LoginHandler::disconnect() +{ + accountServerConnection->disconnect(); + + if (Client::getState() == STATE_CONNECT_GAME) + { + Client::setState(STATE_GAME); + } +} + +bool LoginHandler::isRegistrationEnabled() +{ + return true; +} + +void LoginHandler::getRegistrationDetails() +{ + MessageOut msg(PAMSG_REQUEST_REGISTER_INFO); + accountServerConnection->send(msg); +} + +unsigned int LoginHandler::getMinUserNameLength() const +{ + return mMinUserNameLength; +} + +unsigned int LoginHandler::getMaxUserNameLength() const +{ + return mMaxUserNameLength; +} + +void LoginHandler::loginAccount(LoginData *loginData) +{ + mLoginData = loginData; + + MessageOut msg(PAMSG_LOGIN); + + msg.writeInt32(0); // client version + msg.writeString(loginData->username); + msg.writeString(sha256(loginData->username + loginData->password)); + + accountServerConnection->send(msg); +} + +void LoginHandler::logout() +{ + MessageOut msg(PAMSG_LOGOUT); + accountServerConnection->send(msg); +} + +void LoginHandler::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); + + accountServerConnection->send(msg); +} + +void LoginHandler::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)); + + accountServerConnection->send(msg); +} + +void LoginHandler::chooseServer(unsigned int server _UNUSED_) +{ + // TODO +} + +void LoginHandler::registerAccount(LoginData *loginData) +{ + mLoginData = loginData; + + MessageOut msg(PAMSG_REGISTER); + + msg.writeInt32(0); // client version + msg.writeString(loginData->username); + // Use a hashed password for privacy reasons + msg.writeString(sha256(loginData->username + loginData->password)); + msg.writeString(loginData->email); + msg.writeString(loginData->captchaResponse); + + accountServerConnection->send(msg); +} + +void LoginHandler::unregisterAccount(const std::string &username, + const std::string &password) +{ + MessageOut msg(PAMSG_UNREGISTER); + + msg.writeString(username); + msg.writeString(sha256(username + password)); + + accountServerConnection->send(msg); +} + +Worlds LoginHandler::getWorlds() const +{ + return Worlds(); +} + +void LoginHandler::reconnect() +{ + MessageOut msg(PAMSG_RECONNECT); + msg.writeString(netToken, 32); + accountServerConnection->send(msg); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/loginhandler.h b/src/net/manaserv/loginhandler.h new file mode 100644 index 000000000..1c47184a7 --- /dev/null +++ b/src/net/manaserv/loginhandler.h @@ -0,0 +1,99 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_LOGINHANDLER_H +#define NET_MANASERV_LOGINHANDLER_H + +#include "net/loginhandler.h" +#include "net/serverinfo.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +class LoginData; + +namespace ManaServ +{ + +class LoginHandler : public MessageHandler, public Net::LoginHandler +{ + public: + LoginHandler(); + + void handleMessage(Net::MessageIn &msg); + + void connect(); + + bool isConnected(); + + void disconnect(); + + int supportedOptionalActions() const + { return Unregister | ChangeEmail | SetEmailOnRegister; } + + bool isRegistrationEnabled(); + + void getRegistrationDetails(); + + unsigned int getMinUserNameLength() const; + + unsigned int getMaxUserNameLength() const; + + void loginAccount(LoginData *loginData); + + void logout(); + + void changeEmail(const std::string &email); + + void changePassword(const std::string &username, + const std::string &oldPassword, + const std::string &newPassword); + + void chooseServer(unsigned int server); + + void registerAccount(LoginData *loginData); + + void unregisterAccount(const std::string &username, + const std::string &password); + + Worlds getWorlds() const; + + void reconnect(); + + private: + void handleLoginResponse(Net::MessageIn &msg); + void handleRegisterResponse(Net::MessageIn &msg); + + void readServerInfo(Net::MessageIn &msg); + + LoginData *mLoginData; + unsigned int mMinUserNameLength; + unsigned int mMaxUserNameLength; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_LOGINHANDLER_H diff --git a/src/net/manaserv/messagehandler.cpp b/src/net/manaserv/messagehandler.cpp new file mode 100644 index 000000000..2f4d53dc6 --- /dev/null +++ b/src/net/manaserv/messagehandler.cpp @@ -0,0 +1,36 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/messagehandler.h" + +#include "net/manaserv/network.h" + +#include + +namespace ManaServ +{ + +MessageHandler::~MessageHandler() +{ + unregisterHandler(this); +} + +} diff --git a/src/net/manaserv/messagehandler.h b/src/net/manaserv/messagehandler.h new file mode 100644 index 000000000..2181438f3 --- /dev/null +++ b/src/net/manaserv/messagehandler.h @@ -0,0 +1,44 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_MESSAGEHANDLER_H +#define NET_MANASERV_MESSAGEHANDLER_H + +#include "net/messagehandler.h" + +namespace ManaServ +{ + +/** + * \ingroup Network + */ +class MessageHandler : public Net::MessageHandler +{ + public: + ~MessageHandler(); + +}; + +typedef const std::auto_ptr MessageHandlerPtr; + +} + +#endif // NET_MANASERV_MESSAGEHANDLER_H diff --git a/src/net/manaserv/messagein.cpp b/src/net/manaserv/messagein.cpp new file mode 100644 index 000000000..458aae740 --- /dev/null +++ b/src/net/manaserv/messagein.cpp @@ -0,0 +1,62 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/messagein.h" + +#include "enet/enet.h" + +namespace ManaServ +{ + +MessageIn::MessageIn(const char *data, unsigned int length): + Net::MessageIn(data, length) +{ + // Read the message ID + mId = readInt16(); +} + +Sint16 MessageIn::readInt16() +{ + Sint16 value = -1; + if (mPos + 2 <= mLength) + { + uint16_t t; + memcpy(&t, mData + mPos, 2); + value = (unsigned short) ENET_NET_TO_HOST_16(t); + } + mPos += 2; + return value; +} + +int MessageIn::readInt32() +{ + int value = -1; + if (mPos + 4 <= mLength) + { + uint32_t t; + memcpy(&t, mData + mPos, 4); + value = ENET_NET_TO_HOST_32(t); + } + mPos += 4; + return value; +} + +} diff --git a/src/net/manaserv/messagein.h b/src/net/manaserv/messagein.h new file mode 100644 index 000000000..e3528e5bf --- /dev/null +++ b/src/net/manaserv/messagein.h @@ -0,0 +1,49 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_MESSAGEIN_H +#define NET_MANASERV_MESSAGEIN_H + +#include "net/messagein.h" + +namespace ManaServ +{ + +/** + * Used for parsing an incoming message. + * + * \ingroup Network + */ +class MessageIn : public Net::MessageIn +{ + public: + /** + * Constructor. + */ + MessageIn(const char *data, unsigned int length); + + Sint16 readInt16(); /**< Reads a short. */ + int readInt32(); /**< Reads a long. */ +}; + +} + +#endif // NET_MANASERV_MESSAGEIN_H diff --git a/src/net/manaserv/messageout.cpp b/src/net/manaserv/messageout.cpp new file mode 100644 index 000000000..f4856e640 --- /dev/null +++ b/src/net/manaserv/messageout.cpp @@ -0,0 +1,65 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/messageout.h" + +#include "enet/enet.h" + +#include +#include + +namespace ManaServ +{ + +MessageOut::MessageOut(short id): + Net::MessageOut(id) +{ + writeInt16(id); +} + +MessageOut::~MessageOut() +{ + free(mData); +} + +void MessageOut::expand(size_t bytes) +{ + mData = (char*)realloc(mData, mPos + bytes); + mDataSize = mPos + bytes; +} + +void MessageOut::writeInt16(Sint16 value) +{ + expand(2); + uint16_t t = ENET_HOST_TO_NET_16(value); + memcpy(mData + mPos, &t, 2); + mPos += 2; +} + +void MessageOut::writeInt32(Sint32 value) +{ + expand(4); + uint32_t t = ENET_HOST_TO_NET_32(value); + memcpy(mData + mPos, &t, 4); + mPos += 4; +} + +} // namespace ManaServ diff --git a/src/net/manaserv/messageout.h b/src/net/manaserv/messageout.h new file mode 100644 index 000000000..00ae069de --- /dev/null +++ b/src/net/manaserv/messageout.h @@ -0,0 +1,59 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_MESSAGEOUT_H +#define NET_MANASERV_MESSAGEOUT_H + +#include "net/messageout.h" + +namespace ManaServ +{ + +class MessageOut : public Net::MessageOut +{ + public: + /** + * Constructor. + */ + MessageOut(short id); + + /** + * Destructor. + */ + ~MessageOut(); + + void writeInt16(Sint16 value); /**< Writes a short. */ + void writeInt32(Sint32 value); /**< Writes a long. */ + + protected: + /** + * 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); +}; + +} + +#endif // NET_MANASERV_MESSAGEOUT_H diff --git a/src/net/manaserv/network.cpp b/src/net/manaserv/network.cpp new file mode 100644 index 000000000..c8389bb82 --- /dev/null +++ b/src/net/manaserv/network.cpp @@ -0,0 +1,178 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/network.h" + +#include "log.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/internal.h" +#include "net/manaserv/messagehandler.h" +#include "net/manaserv/messagein.h" + +#include "enet/enet.h" + +#include + +/** + * The local host which is shared for all outgoing connections. + */ +namespace +{ + ENetHost *client; +} + +namespace ManaServ +{ + +typedef std::map MessageHandlers; +typedef MessageHandlers::iterator MessageHandlerIterator; +static MessageHandlers mMessageHandlers; + +void initialize() +{ + if (enet_initialize()) + { + logger->error("Failed to initialize ENet."); + } + +#if defined(ENET_VERSION) && ENET_VERSION >= ENET_CUTOFF + client = enet_host_create(NULL, 3, 0, 0, 0); +#else + client = enet_host_create(NULL, 3, 0, 0); +#endif + + if (!client) + { + logger->error("Failed to create the local host."); + } +} + +void finalize() +{ + if (!client) + return; // Wasn't initialized at all + + if (connections) + { + logger->error("Tried to shutdown the network subsystem while there " + "are network connections left!"); + } + + clearNetworkHandlers(); + enet_deinitialize(); +} + +Connection *getConnection() +{ + if (!client) + { + logger->error("Tried to instantiate a network object before " + "initializing the network subsystem!"); + } + + return new Connection(client); +} + +void registerHandler(MessageHandler *handler) +{ + for (const Uint16 *i = handler->handledMessages; *i; i++) + { + mMessageHandlers[*i] = handler; + } +} + +void unregisterHandler(MessageHandler *handler) +{ + for (const Uint16 *i = handler->handledMessages; *i; i++) + { + mMessageHandlers.erase(*i); + } +} + +void clearNetworkHandlers() +{ + mMessageHandlers.clear(); +} + + +/** + * Dispatches a message to the appropriate message handler and + * destroys it afterwards. + */ +namespace +{ + void dispatchMessage(ENetPacket *packet) + { + MessageIn msg((const char *)packet->data, packet->dataLength); + + MessageHandlerIterator iter = mMessageHandlers.find(msg.getId()); + + 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()); + } + + // Clean up the packet now that we're done using it. + enet_packet_destroy(packet); + } +} + +void flush() +{ + ENetEvent event; + + // Check if there are any new events + while (enet_host_service(client, &event, 0) > 0) + { + switch (event.type) + { + 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 ENET_EVENT_TYPE_RECEIVE: + dispatchMessage(event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + logger->log1("Disconnected."); + // Reset the server information. + event.peer->data = 0; + break; + + case ENET_EVENT_TYPE_NONE: + default: + break; + } + } +} + +} diff --git a/src/net/manaserv/network.h b/src/net/manaserv/network.h new file mode 100644 index 000000000..149f484e5 --- /dev/null +++ b/src/net/manaserv/network.h @@ -0,0 +1,75 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_NETWORK_H +#define NET_MANASERV_NETWORK_H + +#include + +/** + * \ingroup Network + */ +namespace ManaServ +{ + class MessageHandler; + class MessageOut; + + 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 clearNetworkHandlers(); + + /* + * Handles all events and dispatches incoming messages to the + * registered handlers + */ + void flush(); +} // namespace ManaServ + +#endif diff --git a/src/net/manaserv/npchandler.cpp b/src/net/manaserv/npchandler.cpp new file mode 100644 index 000000000..6d4010e83 --- /dev/null +++ b/src/net/manaserv/npchandler.cpp @@ -0,0 +1,237 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/npchandler.h" + +#include "actorspritemanager.h" + +#include "gui/npcdialog.h" +#include "gui/npcpostdialog.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +extern Net::NpcHandler *npcHandler; + +namespace ManaServ +{ + +extern Connection *gameServerConnection; + +NpcHandler::NpcHandler() +{ + static const Uint16 _messages[] = + { + GPMSG_NPC_CHOICE, + GPMSG_NPC_POST, + GPMSG_NPC_MESSAGE, + GPMSG_NPC_ERROR, + GPMSG_NPC_CLOSE, + GPMSG_NPC_NUMBER, + GPMSG_NPC_STRING, + 0 + }; + handledMessages = _messages; + npcHandler = this; +} + +void NpcHandler::handleMessage(Net::MessageIn &msg) +{ + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being || being->getType() != ActorSprite::NPC) + { + return; + } + + int npcId = being->getId(); + NpcDialogs::iterator diag = mNpcDialogs.find(npcId); + NpcDialog *dialog; + + if (diag == mNpcDialogs.end()) + { + if (msg.getId() == GPMSG_NPC_ERROR || msg.getId() == GPMSG_NPC_CLOSE) + return; // Dialog is pointless in these cases + + dialog = new NpcDialog(npcId); + Wrapper wrap; + wrap.dialog = dialog; + mNpcDialogs[npcId] = wrap; + } + else + { + dialog = diag->second.dialog; + } + + switch (msg.getId()) + { + case GPMSG_NPC_CHOICE: + dialog->choiceRequest(); + while (msg.getUnreadLength()) + { + dialog->addChoice(msg.readString()); + } + break; + + case GPMSG_NPC_NUMBER: + { + int min_num = msg.readInt32(); + int max_num = msg.readInt32(); + dialog->integerRequest(msg.readInt32(), min_num, max_num); + break; + } + + case GPMSG_NPC_STRING: + dialog->textRequest(""); + break; + + case GPMSG_NPC_POST: + { + new NpcPostDialog(npcId); + break; + } + + case GPMSG_NPC_ERROR: + dialog->close(); + if (diag != mNpcDialogs.end()) + { + mNpcDialogs.erase(diag); + } + break; + + case GPMSG_NPC_MESSAGE: + dialog->addText(msg.readString(msg.getUnreadLength())); + dialog->showNextButton(); + break; + + case GPMSG_NPC_CLOSE: + dialog->showCloseButton(); + break; + + default: + break; + } +} + +void NpcHandler::talk(int npcId) +{ + MessageOut msg(PGMSG_NPC_TALK); + msg.writeInt16(npcId); + gameServerConnection->send(msg); +} + +void NpcHandler::nextDialog(int npcId) +{ + MessageOut msg(PGMSG_NPC_TALK_NEXT); + msg.writeInt16(npcId); + gameServerConnection->send(msg); +} + +void NpcHandler::closeDialog(int npcId) +{ + MessageOut msg(PGMSG_NPC_TALK_NEXT); + msg.writeInt16(npcId); + gameServerConnection->send(msg); + + NpcDialogs::iterator it = mNpcDialogs.find(npcId); + if (it != mNpcDialogs.end()) + { + (*it).second.dialog->close(); + mNpcDialogs.erase(it); + } +} + +void NpcHandler::listInput(int npcId, unsigned char value) +{ + MessageOut msg(PGMSG_NPC_SELECT); + msg.writeInt16(npcId); + msg.writeInt8(value); + gameServerConnection->send(msg); +} + +void NpcHandler::integerInput(int npcId, int value) +{ + MessageOut msg(PGMSG_NPC_NUMBER); + msg.writeInt16(npcId); + msg.writeInt32(value); + gameServerConnection->send(msg); +} + +void NpcHandler::stringInput(int npcId, const std::string &value) +{ + MessageOut msg(PGMSG_NPC_STRING); + msg.writeInt16(npcId); + msg.writeString(value); + gameServerConnection->send(msg); +} + +void NpcHandler::sendLetter(int npcId _UNUSED_, const std::string &recipient, + const std::string &text) +{ + MessageOut msg(PGMSG_NPC_POST_SEND); + msg.writeString(recipient); + msg.writeString(text); + gameServerConnection->send(msg); +} + +void NpcHandler::startShopping(int beingId _UNUSED_) +{ + // TODO +} + +void NpcHandler::buy(int beingId _UNUSED_) +{ + // TODO +} + +void NpcHandler::sell(int beingId _UNUSED_) +{ + // TODO +} + +void NpcHandler::buyItem(int beingId _UNUSED_, int itemId, int amount) +{ + MessageOut msg(PGMSG_NPC_BUYSELL); + msg.writeInt16(itemId); + msg.writeInt16(amount); + gameServerConnection->send(msg); +} + +void NpcHandler::sellItem(int beingId _UNUSED_, int itemId, int amount) +{ + MessageOut msg(PGMSG_NPC_BUYSELL); + msg.writeInt16(itemId); + msg.writeInt16(amount); + gameServerConnection->send(msg); +} + +void NpcHandler::endShopping(int beingId _UNUSED_) +{ + // TODO +} + +void NpcHandler::clearDialogs() +{ + mNpcDialogs.clear(); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/npchandler.h b/src/net/manaserv/npchandler.h new file mode 100644 index 000000000..5f47ee388 --- /dev/null +++ b/src/net/manaserv/npchandler.h @@ -0,0 +1,89 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_NPCHANDLER_H +#define NET_MANASERV_NPCHANDLER_H + +#include "net/npchandler.h" + +#include "net/manaserv/messagehandler.h" + +#include + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +class NpcDialog; + +namespace ManaServ +{ + +class NpcHandler : public MessageHandler, public Net::NpcHandler +{ + public: + NpcHandler(); + + void handleMessage(Net::MessageIn &msg); + + void talk(int npcId); + + void nextDialog(int npcId); + + void closeDialog(int npcId); + + void listInput(int npcId, unsigned char value); + + void integerInput(int npcId, int value); + + void stringInput(int npcId, const std::string &value); + + void sendLetter(int npcId, const std::string &recipient, + const std::string &text); + + void startShopping(int beingId); + + void buy(int beingId); + + void sell(int beingId); + + void buyItem(int beingId, int itemId, int amount); + + void sellItem(int beingId, int itemId, int amount); + + void endShopping(int beingId); + + void clearDialogs(); + + private: + typedef struct + { + NpcDialog* dialog; + } Wrapper; + typedef std::map NpcDialogs; + NpcDialogs mNpcDialogs; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_NPCHANDLER_H diff --git a/src/net/manaserv/partyhandler.cpp b/src/net/manaserv/partyhandler.cpp new file mode 100644 index 000000000..3aec20b6b --- /dev/null +++ b/src/net/manaserv/partyhandler.cpp @@ -0,0 +1,197 @@ +/* + * The Mana Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/partyhandler.h" + +#include "event.h" +#include "log.h" +#include "localplayer.h" + +#include "gui/socialwindow.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include + +#define PARTY_ID 1 + +extern Net::PartyHandler *partyHandler; + +namespace ManaServ +{ + +extern Connection *chatServerConnection; + +PartyHandler::PartyHandler(): + mParty(Party::getParty(PARTY_ID)) +{ + static const Uint16 _messages[] = + { + CPMSG_PARTY_INVITE_RESPONSE, + CPMSG_PARTY_INVITED, + CPMSG_PARTY_ACCEPT_INVITE_RESPONSE, + CPMSG_PARTY_QUIT_RESPONSE, + CPMSG_PARTY_NEW_MEMBER, + CPMSG_PARTY_MEMBER_LEFT, + CPMSG_PARTY_REJECTED, + 0 + }; + handledMessages = _messages; + partyHandler = this; +} + +void PartyHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case CPMSG_PARTY_INVITE_RESPONSE: + { + if (msg.readInt8() == ERRMSG_OK) + { + + } + } break; + + case CPMSG_PARTY_INVITED: + { + socialWindow->showPartyInvite(msg.readString()); + } break; + + case CPMSG_PARTY_ACCEPT_INVITE_RESPONSE: + { + if (msg.readInt8() == ERRMSG_OK) + { + // + SERVER_NOTICE(_("Joined party.")); + } + } + + case CPMSG_PARTY_QUIT_RESPONSE: + { + if (msg.readInt8() == ERRMSG_OK) + { + mParty->clearMembers(); + player_node->setParty(NULL); + } + } break; + + case CPMSG_PARTY_NEW_MEMBER: + { + int id = msg.readInt16(); // being id + std::string name = msg.readString(); + + SERVER_NOTICE(strprintf(_("%s joined the party."), + name.c_str())); + + if (id == player_node->getId()) + player_node->setParty(mParty); + + mParty->addMember(id, name); + } break; + + case CPMSG_PARTY_MEMBER_LEFT: + { + mParty->removeMember(msg.readString()); + } break; + + case CPMSG_PARTY_REJECTED: + { + std::string name = msg.readString(); + SERVER_NOTICE(strprintf( + _("%s rejected your invite."), name.c_str())); + } break; + default: + break; + } +} + +void PartyHandler::create(const std::string &name _UNUSED_) +{ + // TODO +} + +void PartyHandler::join(int partyId _UNUSED_) +{ + // TODO +} + +void PartyHandler::invite(Being *being) +{ + invite(being->getName()); +} + +void PartyHandler::invite(const std::string &name) +{ + MessageOut msg(PCMSG_PARTY_INVITE); + + msg.writeString(name); + + chatServerConnection->send(msg); +} + +void PartyHandler::inviteResponse(const std::string &inviter, bool accept) +{ + MessageOut msg = MessageOut(accept ? PCMSG_PARTY_ACCEPT_INVITE : + PCMSG_PARTY_REJECT_INVITE); + + msg.writeString(inviter); + + chatServerConnection->send(msg); +} + +void PartyHandler::leave() +{ + MessageOut msg(PCMSG_PARTY_QUIT); + + chatServerConnection->send(msg); +} + +void PartyHandler::kick(Being *being _UNUSED_) +{ + // TODO +} + +void PartyHandler::kick(const std::string &name _UNUSED_) +{ + // TODO +} + +void PartyHandler::chat(const std::string &text _UNUSED_) +{ + // TODO +} + +void PartyHandler::requestPartyMembers() +{ + //MessageOut msg(PCMSG_GUILD_GET_MEMBERS); + + //msg.writeInt16(guildId); + + //chatServerConnection->send(msg); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/partyhandler.h b/src/net/manaserv/partyhandler.h new file mode 100644 index 000000000..b2c8ce49e --- /dev/null +++ b/src/net/manaserv/partyhandler.h @@ -0,0 +1,82 @@ +/* + * The Mana Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_PARTYHANDLER_H +#define NET_MANASERV_PARTYHANDLER_H + +#include "net/partyhandler.h" + +#include "net/manaserv/messagehandler.h" + +#include "party.h" + +#include + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class PartyHandler : public MessageHandler, public Net::PartyHandler +{ +public: + PartyHandler(); + + void handleMessage(Net::MessageIn &msg); + + void create(const std::string &name = ""); + + void join(int partyId); + + void invite(Being *being); + + void invite(const std::string &name); + + void inviteResponse(const std::string &inviter, bool accept); + + void leave(); + + void kick(Being *being); + + void kick(const std::string &name); + + void chat(const std::string &text); + + void requestPartyMembers(); + + PartyShare getShareExperience() { return PARTY_SHARE_NO; } + + void setShareExperience(PartyShare share _UNUSED_) {} + + PartyShare getShareItems() { return PARTY_SHARE_NO; } + + void setShareItems(PartyShare share _UNUSED_) {} +private: + Party *mParty; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_PARTYHANDLER_H diff --git a/src/net/manaserv/playerhandler.cpp b/src/net/manaserv/playerhandler.cpp new file mode 100644 index 000000000..3af82486c --- /dev/null +++ b/src/net/manaserv/playerhandler.cpp @@ -0,0 +1,440 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/playerhandler.h" +#include "net/manaserv/beinghandler.h" + +#include "client.h" +#include "effectmanager.h" +#include "game.h" +#include "localplayer.h" +#include "log.h" +#include "particle.h" +#include "playerinfo.h" +#include "configuration.h" + +#include "gui/chat.h" +#include "gui/gui.h" +#include "gui/okdialog.h" +#include "gui/viewport.h" + +#include "net/net.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/defines.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/npchandler.h" +#include "net/manaserv/protocol.h" +#include "net/manaserv/attributes.h" + +/** + * Max. distance we are willing to scroll after a teleport; + * everything beyond will reset the port hard. + * 32 is the nominal tile width/height. + * @todo: Make this parameter read from config. + */ +static const int MAP_TELEPORT_SCROLL_DISTANCE = 8 * 32; + +extern Net::PlayerHandler *playerHandler; + +namespace ManaServ +{ + +void RespawnRequestListener::action(const gcn::ActionEvent &event _UNUSED_) +{ + Net::getPlayerHandler()->respawn(); + + ManaServ::NpcHandler *handler = + static_cast(Net::getNpcHandler()); + handler->clearDialogs(); +} + +extern Connection *gameServerConnection; + +PlayerHandler::PlayerHandler() +{ + static const Uint16 _messages[] = + { + 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, + GPMSG_SPECIAL_STATUS, + 0 + }; + handledMessages = _messages; + playerHandler = this; +} + +void PlayerHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_PLAYER_MAP_CHANGE: + handleMapChangeMessage(msg); + break; + + 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; + + case GPMSG_PLAYER_ATTRIBUTE_CHANGE: + { + logger->log1("ATTRIBUTE UPDATE:"); + while (msg.getUnreadLength()) + { + int attrId = msg.readInt16(); + double base = msg.readInt32() / 256.0; + double value = msg.readInt32() / 256.0; + + // Set the core player attribute the stat + // depending on attribute link. + int playerInfoId = + Attributes::getPlayerInfoIdFromAttrId(attrId); + if (playerInfoId > -1) + { + PlayerInfo::setAttribute(playerInfoId, value); + } + else + { + PlayerInfo::setStatBase(attrId, base); + PlayerInfo::setStatMod(attrId, value - base); + } + } + } break; + + case GPMSG_PLAYER_EXP_CHANGE: + { + logger->log1("EXP Update"); + while (msg.getUnreadLength()) + { + int skill = msg.readInt16(); + int current = msg.readInt32(); + int next = msg.readInt32(); + + PlayerInfo::setStatExperience(skill, current, next); + } + } break; + + case GPMSG_LEVELUP: + { + PlayerInfo::setAttribute(LEVEL, msg.readInt16()); + PlayerInfo::setAttribute(CHAR_POINTS, msg.readInt16()); + PlayerInfo::setAttribute(CORR_POINTS, msg.readInt16()); + Particle* effect = particleEngine->addEffect( + paths.getStringValue("particles") + + paths.getStringValue("levelUpEffectFile") + , 0, 0); + player_node->controlParticle(effect); + } break; + + + case GPMSG_LEVEL_PROGRESS: + { + PlayerInfo::setAttribute(EXP, msg.readInt8()); + } break; + + + case GPMSG_RAISE_ATTRIBUTE_RESPONSE: + { + int errCode = msg.readInt8(); + int attrNum = msg.readInt16(); + 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 = PlayerInfo::getStatBase(attrNum) - 1; + PlayerInfo::setAttribute(CHAR_POINTS, 0); + PlayerInfo::setStatBase(attrNum, attrValue); + } break; + case ATTRIBMOD_DENIED: + { + // undo attribute change + logger->log("Warning: Server denied increase of attribute" + " %d (reason unknown) ", attrNum); + int points = PlayerInfo::getAttribute(CHAR_POINTS) - 1; + PlayerInfo::setAttribute(CHAR_POINTS, points); + + int attrValue = PlayerInfo::getStatBase(attrNum) - 1; + PlayerInfo::setStatBase(attrNum, attrValue); + } break; + default: + break; + } + } break; + + case GPMSG_LOWER_ATTRIBUTE_RESPONSE: + { + int errCode = msg.readInt8(); + int attrNum = msg.readInt16(); + 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 = PlayerInfo::getStatBase(attrNum) + 1; + PlayerInfo::setAttribute(CHAR_POINTS, 0); + PlayerInfo::setAttribute(CORR_POINTS, 0); + PlayerInfo::setStatBase(attrNum, attrValue); + break; + } break; + case ATTRIBMOD_DENIED: + { + // undo attribute change + logger->log("Warning: Server denied reduction of attribute" + " %d (reason unknown) ", attrNum); + int charaPoints = PlayerInfo::getAttribute( + CHAR_POINTS) - 1; + + PlayerInfo::setAttribute(CHAR_POINTS, charaPoints); + + int correctPoints = PlayerInfo::getAttribute(CORR_POINTS) + + 1; + + PlayerInfo::setAttribute(CORR_POINTS, correctPoints); + + int attrValue = PlayerInfo::getStatBase(attrNum) + 1; + PlayerInfo::setStatBase(attrNum, attrValue); + } break; + default: + break; + } + + } break; + + case GPMSG_SPECIAL_STATUS : + { + while (msg.getUnreadLength()) + { + // { B specialID, L current, L max, L recharge } + int id = msg.readInt8(); + int current = msg.readInt32(); + int max = msg.readInt32(); + int recharge = msg.readInt32(); + PlayerInfo::setSpecialStatus(id, current, max, recharge); + } + } break; + /* + case SMSG_PLAYER_ARROW_MESSAGE: + { + Sint16 type = msg.readInt16(); + + switch (type) + { + case 0: + localChatTab->chatLog(_("Equip arrows first."), + BY_SERVER); + break; + default: + logger->log("0x013b: Unhandled message %i", type); + break; + } + } + break; + */ + default: + break; + } +} + +void PlayerHandler::handleMapChangeMessage(Net::MessageIn &msg) +{ + const std::string mapName = msg.readString(); + const unsigned short x = msg.readInt16(); + const unsigned short y = msg.readInt16(); + + Game *game = Game::instance(); + const bool sameMap = (game->getCurrentMapName() == mapName); + + logger->log("Changing map to %s (%d, %d)", mapName.c_str(), x, y); + + // Switch the actual map, deleting the previous one + game->changeMap(mapName); + + const Vector &playerPos = player_node->getPosition(); + float scrollOffsetX = 0.0f; + float scrollOffsetY = 0.0f; + + /* Scroll if neccessary */ + if (!sameMap + || (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); + player_node->setDestination(x, y); + + logger->log("Adjust scrolling by %d,%d", (int) scrollOffsetX, + (int) scrollOffsetY); + viewport->scrollBy(scrollOffsetX, scrollOffsetY); +} + +void PlayerHandler::attack(int id, bool keep _UNUSED_) +{ + MessageOut msg(PGMSG_ATTACK); + msg.writeInt16(id); + gameServerConnection->send(msg); +} + +void PlayerHandler::stopAttack() +{ + +} + +void PlayerHandler::emote(int emoteId _UNUSED_) +{ + // TODO +} + +void PlayerHandler::increaseAttribute(int attr) +{ + MessageOut msg(PGMSG_RAISE_ATTRIBUTE); + msg.writeInt16(attr); + gameServerConnection->send(msg); +} + +void PlayerHandler::decreaseAttribute(int attr) +{ + MessageOut msg(PGMSG_LOWER_ATTRIBUTE); + msg.writeInt16(attr); + gameServerConnection->send(msg); +} + +void PlayerHandler::increaseSkill(int skillId _UNUSED_) +{ + // Not used atm +} + +void PlayerHandler::pickUp(FloorItem *floorItem) +{ + if (floorItem) + { + int id = floorItem->getId(); + MessageOut msg(PGMSG_PICKUP); + msg.writeInt16(id >> 16); + msg.writeInt16(id & 0xFFFF); + gameServerConnection->send(msg); + } +} + +void PlayerHandler::setDirection(char direction) +{ + MessageOut msg(PGMSG_DIRECTION_CHANGE); + msg.writeInt8(direction); + gameServerConnection->send(msg); +} + +void PlayerHandler::setDestination(int x, int y, int /* direction */) +{ + MessageOut msg(PGMSG_WALK); + msg.writeInt16(x); + msg.writeInt16(y); + gameServerConnection->send(msg); +} + +void PlayerHandler::changeAction(Being::Action action) +{ + player_node->setAction(action); + + MessageOut msg(PGMSG_ACTION_CHANGE); + msg.writeInt8(action); + gameServerConnection->send(msg); +} + +void PlayerHandler::respawn() +{ + MessageOut msg(PGMSG_RESPAWN); + gameServerConnection->send(msg); +} + +void PlayerHandler::ignorePlayer(const std::string &player _UNUSED_, + bool ignore _UNUSED_) +{ + // TODO +} + +void PlayerHandler::ignoreAll(bool ignore _UNUSED_) +{ + // TODO +} + +bool PlayerHandler::canUseMagic() +{ + return true; +} + +bool PlayerHandler::canCorrectAttributes() +{ + return true; +} + +int PlayerHandler::getJobLocation() +{ + return -1; +} + +Vector PlayerHandler::getDefaultWalkSpeed() +{ + // Return translation in pixels per ticks. + return ManaServ::BeingHandler::giveSpeedInPixelsPerTicks(6.0f); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/playerhandler.h b/src/net/manaserv/playerhandler.h new file mode 100644 index 000000000..ddd510714 --- /dev/null +++ b/src/net/manaserv/playerhandler.h @@ -0,0 +1,85 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_PLAYERHANDLER_H +#define NET_MANASERV_PLAYERHANDLER_H + +#include "net/playerhandler.h" + +#include "net/manaserv/messagehandler.h" + +#include + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +struct RespawnRequestListener : public gcn::ActionListener +{ + void action(const gcn::ActionEvent &event); +}; + +static RespawnRequestListener respawnListener; + +class PlayerHandler : public MessageHandler, public Net::PlayerHandler +{ + public: + PlayerHandler(); + + void handleMessage(Net::MessageIn &msg); + + void attack(int id, bool keep = false); + void stopAttack(); + void emote(int emoteId); + + void increaseAttribute(int attr); + void decreaseAttribute(int attr); + void increaseSkill(int skillId); + + void pickUp(FloorItem *floorItem); + void setDirection(char direction); + void setDestination(int x, int y, int direction = -1); + void changeAction(Being::Action action); + + void respawn(); + + void ignorePlayer(const std::string &player, bool ignore); + void ignoreAll(bool ignore); + + bool canUseMagic(); + bool canCorrectAttributes(); + + int getJobLocation(); + + Vector getDefaultWalkSpeed(); + + private: + void handleMapChangeMessage(Net::MessageIn &msg); +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_PLAYERHANDLER_H diff --git a/src/net/manaserv/protocol.h b/src/net/manaserv/protocol.h new file mode 100644 index 000000000..2b5efd69c --- /dev/null +++ b/src/net/manaserv/protocol.h @@ -0,0 +1,392 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef MANASERV_PROTOCOL_H +#define MANASERV_PROTOCOL_H + +/** + * 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 + * - GAMSG_*: from game server to account server + * + * 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) + * + * TODO - Document specific error codes for each packet + */ +enum { + // Login/Register + PAMSG_REGISTER = 0x0000, // D version, S username, S password, S email, S captcha response + APMSG_REGISTER_RESPONSE = 0x0002, // B error, S updatehost, S Client data URL, B Character slots + PAMSG_UNREGISTER = 0x0003, // S username, S password + APMSG_UNREGISTER_RESPONSE = 0x0004, // B error + PAMSG_REQUEST_REGISTER_INFO = 0x0005, // + APMSG_REGISTER_INFO_RESPONSE = 0x0006, // B byte registration Allowed, byte minNameLength, byte maxNameLength, string captchaURL, string captchaInstructions + PAMSG_LOGIN = 0x0010, // D version, S username, S password + APMSG_LOGIN_RESPONSE = 0x0012, // B error, S updatehost, S Client data URL, B Character slots + PAMSG_LOGOUT = 0x0013, // - + APMSG_LOGOUT_RESPONSE = 0x0014, // B error + PAMSG_CHAR_CREATE = 0x0020, // S name, B hair style, B hair color, B gender, B slot, W*6 stats + APMSG_CHAR_CREATE_RESPONSE = 0x0021, // B error + PAMSG_CHAR_DELETE = 0x0022, // B slot + APMSG_CHAR_DELETE_RESPONSE = 0x0023, // B error + // B slot, S name, B gender, B hair style, B hair color, W level, + // W character points, W correction points, + // {D attr id, D base value (in 1/256ths) D mod value (in 256ths) }* + APMSG_CHAR_INFO = 0x0024, // ^ + PAMSG_CHAR_SELECT = 0x0026, // B slot + 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, // { W slot, W item id [, W amount] (if item id is nonzero) }* + GPMSG_INVENTORY_FULL = 0x0121, // W inventory slot count { W slot, W itemId, W amount } { B equip slot, W invy slot}* + GPMSG_EQUIP = 0x0122, // { W Invy slot, B equip slot type count { B equip slot, B number used} }* + GPMSG_PLAYER_ATTRIBUTE_CHANGE = 0x0130, // { W attribute, D base value (in 1/256ths), D modified value (in 1/256ths)}* + GPMSG_PLAYER_EXP_CHANGE = 0x0140, // { W skill, D exp got, D exp needed }* + GPMSG_LEVELUP = 0x0150, // W new level, W character points, W correction points + GPMSG_LEVEL_PROGRESS = 0x0151, // B percent completed to next levelup + PGMSG_RAISE_ATTRIBUTE = 0x0160, // W attribute + GPMSG_RAISE_ATTRIBUTE_RESPONSE = 0x0161, // B error, W attribute + PGMSG_LOWER_ATTRIBUTE = 0x0170, // W attribute + GPMSG_LOWER_ATTRIBUTE_RESPONSE = 0x0171, // B error, W attribute + PGMSG_RESPAWN = 0x0180, // - + GPMSG_BEING_ENTER = 0x0200, // B type, W being id, B action, W*2 position + // character: 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_BEING_HEALTH_CHANGE = 0x0274, // W being id, W hp, W max hp + GPMSG_BEINGS_MOVE = 0x0280, // { W being id, B flags [, W*2 position, B speed] }* + GPMSG_ITEMS = 0x0281, // { W item id, W*2 position }* + PGMSG_ATTACK = 0x0290, // W being id + GPMSG_BEING_ATTACK = 0x0291, // W being id, B direction, B attacktype + PGMSG_USE_SPECIAL = 0x0292, // B specialID + GPMSG_SPECIAL_STATUS = 0x0293, // { B specialID, D current, D max, D recharge } + 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_CLOSE = 0x02B9, // W being id + GPMSG_NPC_POST = 0x02D0, // W being id + PGMSG_NPC_POST_SEND = 0x02D1, // W being id, { S name, S text, W item id } + GPMSG_NPC_POST_GET = 0x02D2, // W being id, { S name, S text, W item id } + PGMSG_NPC_NUMBER = 0x02D3, // W being id, D number + PGMSG_NPC_STRING = 0x02D4, // W being id, S string + GPMSG_NPC_NUMBER = 0x02D5, // W being id, D max, D min, D default + GPMSG_NPC_STRING = 0x02D6, // W being 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_AGREED = 0x02C6, // - + GPMSG_TRADE_AGREED = 0x02C7, // - + PGMSG_TRADE_CONFIRM = 0x02C8, // - + GPMSG_TRADE_CONFIRM = 0x02C9, // - + PGMSG_TRADE_ADD_ITEM = 0x02CA, // B slot, B amount + GPMSG_TRADE_ADD_ITEM = 0x02CB, // W item id, B amount + PGMSG_TRADE_SET_MONEY = 0x02CC, // D amount + GPMSG_TRADE_SET_MONEY = 0x02CD, // D amount + GPMSG_TRADE_BOTH_CONFIRM = 0x02CE, // - + PGMSG_USE_ITEM = 0x0300, // B slot + GPMSG_USE_RESPONSE = 0x0301, // B error + GPMSG_BEINGS_DAMAGE = 0x0310, // { W being id, W amount }* + GPMSG_CREATE_EFFECT_POS = 0x0320, // W effect id, W*2 position + GPMSG_CREATE_EFFECT_BEING = 0x0321, // W effect id, W BeingID + + // 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 + PCMSG_GUILD_KICK_MEMBER = 0x0370, // W guild, S name + CPMSG_GUILD_KICK_MEMBER_RESPONSE = 0x0371, // B error + + CPMSG_GUILD_INVITED = 0x0388, // S char name, S guild name, W id + CPMSG_GUILD_REJOIN = 0x0389, // S name, W guild, W rights, W channel, S announce + + // Party + PCMSG_PARTY_INVITE = 0x03A0, // S name + CPMSG_PARTY_INVITE_RESPONSE = 0x03A1, // B error, S name + 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_REJECT_INVITE = 0x03A7, // S name + CPMSG_PARTY_REJECTED = 0x03A8, // 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 + PCMSG_WHO = 0x0415, // - + CPMSG_WHO_RESPONSE = 0x0416, // { S user } + + // -- 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 modes + PCMSG_USER_MODE = 0x0465, // W channel id, S name, B mode + PCMSG_KICK_USER = 0x0466, // W channel id, S name + + // Inter-server + GAMSG_REGISTER = 0x0500, // S address, W port, S password, D items db revision, { W map id }* + AGMSG_REGISTER_RESPONSE = 0x0501, // C item version, C password response + AGMSG_ACTIVE_MAP = 0x0502, // W map id + AGMSG_PLAYER_ENTER = 0x0510, // B*32 token, D id, S name, serialised character data + GAMSG_PLAYER_DATA = 0x0520, // D id, serialised character data + GAMSG_REDIRECT = 0x0530, // D id + AGMSG_REDIRECT_RESPONSE = 0x0531, // D id, B*32 token, S game address, W game port + GAMSG_PLAYER_RECONNECT = 0x0532, // D id, B*32 token + GAMSG_PLAYER_SYNC = 0x0533, // serialised sync data + GAMSG_SET_QUEST = 0x0540, // D id, S name, S value + GAMSG_GET_QUEST = 0x0541, // D id, S name + AGMSG_GET_QUEST_RESPONSE = 0x0542, // D id, S name, S value + GAMSG_BAN_PLAYER = 0x0550, // D id, W duration + GAMSG_CHANGE_PLAYER_LEVEL = 0x0555, // D id, W level + GAMSG_CHANGE_ACCOUNT_LEVEL = 0x0556, // D id, W level + GAMSG_STATISTICS = 0x0560, // { W map id, W thing nb, W monster nb, W player nb, { D character id }* }* + CGMSG_CHANGED_PARTY = 0x0590, // D character id, D party id + GCMSG_REQUEST_POST = 0x05A0, // D character id + CGMSG_POST_RESPONSE = 0x05A1, // D receiver id, { S sender name, S letter, W num attachments { W attachment item id, W quantity } } + GCMSG_STORE_POST = 0x05A5, // D sender id, S receiver name, S letter, { W attachment item id, W quantity } + CGMSG_STORE_POST_RESPONSE = 0x05A6, // D id, B error + GAMSG_TRANSACTION = 0x0600, // D character id, D action, S message + + 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 + ERRMSG_LIMIT_REACHED // limit reached +}; + +// used in AGMSG_REGISTER_RESPONSE to show state of item db +enum { + DATA_VERSION_OK = 0x00, + DATA_VERSION_OUTDATED = 0x01 +}; + +// used in AGMSG_REGISTER_RESPNSE to show if password was accepted +enum { + PASSWORD_OK = 0x00, + PASSWORD_BAD = 0x01 +}; + +// used to identify part of sync message +enum { + SYNC_CHARACTER_POINTS = 0x01, // D charId, D charPoints, D corrPoints + SYNC_CHARACTER_ATTRIBUTE = 0x02, // D charId, D attrId, DF base, DF mod + SYNC_CHARACTER_SKILL = 0x03, // D charId, B skillId, D skill value + SYNC_ONLINE_STATUS = 0x04, // D charId, B 0x00 = offline, 0x01 = online + SYNC_END_OF_BUFFER = 0xFF // shows, that the buffer ends here. +}; + +// Login specific return values +enum { + LOGIN_INVALID_VERSION = 0x40, // the user is using an incompatible protocol + LOGIN_INVALID_TIME = 0x50, // the user tried logging in too fast + LOGIN_BANNED // the user is currently banned +}; + +// 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 + REGISTER_CAPTCHA_WRONG // user didn't solve the captcha correctly +}; + +// Character creation specific return values +enum { + CREATE_INVALID_HAIRSTYLE = 0x40, + CREATE_INVALID_HAIRCOLOR, + CREATE_INVALID_GENDER, + CREATE_ATTRIBUTES_TOO_HIGH, + CREATE_ATTRIBUTES_TOO_LOW, + CREATE_ATTRIBUTES_OUT_OF_RANGE, + CREATE_EXISTS_NAME, + CREATE_TOO_MUCH_CHARACTERS, + CREATE_INVALID_SLOT +}; + +// 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 ThingType +{ + // A simple item. + OBJECT_ITEM = 0, + // An item that toggle map/quest actions (doors, switchs, ...) + // and can speak (map panels). + OBJECT_ACTOR, + // Non-Playable-Character is an actor capable of movement and maybe actions. + OBJECT_NPC, + // A monster (moving actor with AI. Should be able to toggle map/quest + // actions, too). + OBJECT_MONSTER, + // A normal being. + OBJECT_CHARACTER, + // A effect to be shown. + OBJECT_EFFECT, + // Server-only object. + OBJECT_OTHER +}; + +// 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 +}; + + +enum +{ + SPRITE_BASE = 0, + SPRITE_SHOE, + SPRITE_BOTTOMCLOTHES, + SPRITE_TOPCLOTHES, + SPRITE_HAIR, + SPRITE_HAT, + SPRITE_WEAPON, + SPRITE_VECTOREND +}; + +#endif // MANASERV_PROTOCOL_H diff --git a/src/net/manaserv/specialhandler.cpp b/src/net/manaserv/specialhandler.cpp new file mode 100644 index 000000000..8508c9b56 --- /dev/null +++ b/src/net/manaserv/specialhandler.cpp @@ -0,0 +1,70 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/specialhandler.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +extern Net::SpecialHandler *specialHandler; + +namespace ManaServ +{ + +extern Connection *gameServerConnection; + +SpecialHandler::SpecialHandler() +{ + specialHandler = this; +} + +void SpecialHandler::handleMessage(Net::MessageIn &msg _UNUSED_) +{ + // TODO +} + +void SpecialHandler::use(int id) +{ + MessageOut msg(PGMSG_USE_SPECIAL); + msg.writeInt8(id); + gameServerConnection->send(msg); +} + +void SpecialHandler::use(int id _UNUSED_, int level _UNUSED_, + int beingId _UNUSED_) +{ + // TODO +} + +void SpecialHandler::use(int id _UNUSED_, int level _UNUSED_, int x _UNUSED_, + int y _UNUSED_) +{ + // TODO +} + +void SpecialHandler::use(int id _UNUSED_, const std::string &map _UNUSED_) +{ + // TODO +} + +} // namespace ManaServ diff --git a/src/net/manaserv/specialhandler.h b/src/net/manaserv/specialhandler.h new file mode 100644 index 000000000..1f48688bf --- /dev/null +++ b/src/net/manaserv/specialhandler.h @@ -0,0 +1,56 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_SKILLHANDLER_H +#define NET_MANASERV_SKILLHANDLER_H + +#include "net/specialhandler.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class SpecialHandler : public MessageHandler, public Net::SpecialHandler +{ + public: + SpecialHandler(); + + void handleMessage(Net::MessageIn &msg); + + void use(int id); + + void use(int id, int level, int beingId); + + void use(int id, int level, int x, int y); + + void use(int id, const std::string &map); +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_SKILLHANDLER_H diff --git a/src/net/manaserv/tradehandler.cpp b/src/net/manaserv/tradehandler.cpp new file mode 100644 index 000000000..5a9fdfabd --- /dev/null +++ b/src/net/manaserv/tradehandler.cpp @@ -0,0 +1,237 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/manaserv/tradehandler.h" + +#include "actorspritemanager.h" +#include "event.h" +#include "item.h" +#include "localplayer.h" +#include "playerinfo.h" +#include "playerrelations.h" + +#include "gui/confirmdialog.h" +#include "gui/trade.h" + +#include "net/net.h" + +#include "net/manaserv/connection.h" +#include "net/manaserv/messagein.h" +#include "net/manaserv/messageout.h" +#include "net/manaserv/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +extern std::string tradePartnerName; +int tradePartnerID; + +extern Net::TradeHandler *tradeHandler; + +namespace ManaServ +{ + +extern Connection *gameServerConnection; + +/** + * Listener for request trade dialogs + */ +namespace +{ + struct RequestTradeListener : public gcn::ActionListener + { + void action(const gcn::ActionEvent &event) + { + if (event.getId() == "yes") + { + ManaServ::MessageOut msg(PGMSG_TRADE_REQUEST); + msg.writeInt16(tradePartnerID); + gameServerConnection->send(msg); + } + else if (event.getId() == "ignore") + { + player_relations.ignoreTrade(tradePartnerName); + Net::getTradeHandler()->cancel(); + } + else + { + Net::getTradeHandler()->cancel(); + } + } + } listener; +} + +TradeHandler::TradeHandler(): + mAcceptTradeRequests(true) +{ + static const Uint16 _messages[] = + { + GPMSG_TRADE_REQUEST, + GPMSG_TRADE_CANCEL, + GPMSG_TRADE_START, + GPMSG_TRADE_COMPLETE, + GPMSG_TRADE_AGREED, + GPMSG_TRADE_BOTH_CONFIRM, + GPMSG_TRADE_CONFIRM, + GPMSG_TRADE_ADD_ITEM, + GPMSG_TRADE_SET_MONEY, + 0 + }; + handledMessages = _messages; + tradeHandler = this; +} + +void TradeHandler::setAcceptTradeRequests(bool acceptTradeRequests) +{ + mAcceptTradeRequests = acceptTradeRequests; + if (mAcceptTradeRequests) + SERVER_NOTICE(_("Accepting incoming trade requests.")) + else + SERVER_NOTICE(_("Ignoring incoming trade requests.")) +} + +void TradeHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case GPMSG_TRADE_REQUEST: + { + Being *being = actorSpriteManager->findBeing(msg.readInt16()); + if (!being || !mAcceptTradeRequests) + { + respond(false); + break; + } + PlayerInfo::setTrading(true); + tradePartnerName = being->getName(); + tradePartnerID = being->getId(); + ConfirmDialog *dlg = new ConfirmDialog(_("Request for Trade"), + strprintf(_("%s wants to trade with you, do you accept?"), + tradePartnerName.c_str()), true); + dlg->addActionListener(&listener); + } break; + + case GPMSG_TRADE_ADD_ITEM: + { + int type = msg.readInt16(); + int amount = msg.readInt8(); + tradeWindow->addItem(type, false, amount, 0); + } break; + + case GPMSG_TRADE_SET_MONEY: + tradeWindow->setMoney(msg.readInt32()); + break; + + case GPMSG_TRADE_START: + tradeWindow->reset(); + tradeWindow->setCaption(strprintf(_("Trading with %s"), + tradePartnerName.c_str())); + tradeWindow->setVisible(true); + break; + + case GPMSG_TRADE_BOTH_CONFIRM: + tradeWindow->receivedOk(false); + break; + + case GPMSG_TRADE_AGREED: + tradeWindow->receivedOk(false); + break; + + case GPMSG_TRADE_CANCEL: + SERVER_NOTICE(_("Trade canceled.")) + tradeWindow->setVisible(false); + tradeWindow->reset(); + PlayerInfo::setTrading(false); + break; + + case GPMSG_TRADE_COMPLETE: + SERVER_NOTICE(_("Trade completed.")) + tradeWindow->setVisible(false); + tradeWindow->reset(); + PlayerInfo::setTrading(false); + break; + + default: + break; + } +} + +void TradeHandler::request(Being *being) +{ + tradePartnerName = being->getName(); + tradePartnerID = being->getId(); + + MessageOut msg(PGMSG_TRADE_REQUEST); + msg.writeInt16(tradePartnerID); + gameServerConnection->send(msg); +} + +void TradeHandler::respond(bool accept) +{ + MessageOut msg(accept ? PGMSG_TRADE_REQUEST : PGMSG_TRADE_CANCEL); + gameServerConnection->send(msg); + + if (!accept) + PlayerInfo::setTrading(false); +} + +void TradeHandler::addItem(Item *item, int amount) +{ + MessageOut msg(PGMSG_TRADE_ADD_ITEM); + msg.writeInt8(item->getInvIndex()); + msg.writeInt8(amount); + gameServerConnection->send(msg); + + tradeWindow->addItem(item->getId(), true, amount, 0); + item->increaseQuantity(-amount); +} + +void TradeHandler::removeItem(int slotNum _UNUSED_, int amount _UNUSED_) +{ + // TODO +} + +void TradeHandler::setMoney(int amount) +{ + MessageOut msg(PGMSG_TRADE_SET_MONEY); + msg.writeInt32(amount); + gameServerConnection->send(msg); +} + +void TradeHandler::confirm() +{ + MessageOut msg(PGMSG_TRADE_CONFIRM); + gameServerConnection->send(msg); +} + +void TradeHandler::finish() +{ + MessageOut msg(PGMSG_TRADE_AGREED); + gameServerConnection->send(msg); +} + +void TradeHandler::cancel() +{ + MessageOut msg(PGMSG_TRADE_CANCEL); + gameServerConnection->send(msg); +} + +} // namespace ManaServ diff --git a/src/net/manaserv/tradehandler.h b/src/net/manaserv/tradehandler.h new file mode 100644 index 000000000..3bbf15470 --- /dev/null +++ b/src/net/manaserv/tradehandler.h @@ -0,0 +1,82 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MANASERV_TRADEHANDLER_H +#define NET_MANASERV_TRADEHANDLER_H + +#include "net/tradehandler.h" + +#include "net/manaserv/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace ManaServ +{ + +class TradeHandler : public MessageHandler, public Net::TradeHandler +{ + public: + TradeHandler(); + + void handleMessage(Net::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); + + void request(Being *being); + + void respond(bool accept); + + void addItem(Item *item, int amount); + + void removeItem(int slotNum, int amount); + + void setMoney(int amount); + + void confirm(); + + void finish(); + + void cancel(); + + private: + bool mAcceptTradeRequests; +}; + +} // namespace ManaServ + +#endif // NET_MANASERV_TRADEHANDLER_H diff --git a/src/net/messagehandler.h b/src/net/messagehandler.h new file mode 100644 index 000000000..3a454b1f9 --- /dev/null +++ b/src/net/messagehandler.h @@ -0,0 +1,50 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MESSAGEHANDLER_H +#define NET_MESSAGEHANDLER_H + +#include "net/messagein.h" + +#include + +#include + +namespace Net +{ + +/** + * \ingroup Network + */ +class MessageHandler +{ + public: + const Uint16 *handledMessages; + + virtual void handleMessage(MessageIn &msg) = 0; + + virtual ~MessageHandler() + { } +}; + +} + +#endif // NET_MESSAGEHANDLER_H diff --git a/src/net/messagein.cpp b/src/net/messagein.cpp new file mode 100644 index 000000000..0ac391ee2 --- /dev/null +++ b/src/net/messagein.cpp @@ -0,0 +1,227 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/messagein.h" + +#include "net/packetcounters.h" + +#include "log.h" + +#include "utils/stringutils.h" + +#define MAKEWORD(low, high) \ + ((unsigned short)(((unsigned char)(low)) | \ + ((unsigned short)((unsigned char)(high))) << 8)) + +namespace Net +{ + +MessageIn::MessageIn(const char *data, unsigned int length): + mData(data), + mLength(length), + mPos(0) +{ + PacketCounters::incInPackets(); + DEBUGLOG("MessageIn"); +} + +unsigned char MessageIn::readInt8() +{ + unsigned char value = -1; + if (mPos < mLength) + value = static_cast(mData[mPos]); + + mPos += 1; + PacketCounters::incInBytes(1); + DEBUGLOG("readInt8: " + toString(static_cast(value))); + 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 = static_cast(p[0] | ((p[1] & 0x07) << 8)); + y = static_cast((p[1] >> 3) | ((p[2] & 0x3F) << 5)); + } + mPos += 3; + PacketCounters::incInBytes(3); + DEBUGLOG("readCoordinates: " + toString(static_cast(x)) + "," + + toString(static_cast(y))); +} + +void MessageIn::readCoordinates(Uint16 &x, Uint16 &y, Uint8 &direction) +{ + if (mPos + 3 <= mLength) + { + const char *data = mData + mPos; + Sint16 temp; + + temp = MAKEWORD(data[1] & 0x00c0, data[0] & 0x00ff); + x = static_cast(temp >> 6); + temp = MAKEWORD(data[2] & 0x00f0, data[1] & 0x003f); + y = static_cast(temp >> 4); + + direction = data[2] & 0x000f; + + // Translate from eAthena format + switch (direction) + { + case 0: + direction = 1; + break; + case 1: + direction = 3; + break; + case 2: + direction = 2; + break; + case 3: + direction = 6; + break; + case 4: + direction = 4; + break; + case 5: + direction = 12; + break; + case 6: + direction = 8; + break; + case 7: + direction = 9; + break; + default: + // OOPSIE! Impossible or unknown + direction = 0; + } + } + mPos += 3; + PacketCounters::incInBytes(3); + DEBUGLOG("readCoordinates: " + toString((int)x) + "," + toString((int)y)); +} + +void MessageIn::readCoordinatePair(Uint16 &srcX, Uint16 &srcY, + Uint16 &dstX, Uint16 &dstY) +{ + if (mPos + 5 <= mLength) + { + const char *data = mData + mPos; + Sint16 temp; + + temp = MAKEWORD(data[3], data[2] & 0x000f); + dstX = static_cast(temp >> 2); + + dstY = MAKEWORD(data[4], data[3] & 0x0003); + + temp = MAKEWORD(data[1], data[0]); + srcX = static_cast(temp >> 6); + + temp = MAKEWORD(data[2], data[1] & 0x003f); + srcY = static_cast(temp >> 4); + } + mPos += 5; + DEBUGLOG("readCoordinatePair: " + toString((int)srcX) + "," + + toString((int)srcY) + " " + toString((int)dstX) + "," + + toString((int)dstY)); + PacketCounters::incInBytes(5); +} + +void MessageIn::skip(unsigned int length) +{ + mPos += length; + PacketCounters::incInBytes(length); + DEBUGLOG("skip: " + toString((int)length)); +} + +std::string MessageIn::readString(int length) +{ + // Get string length + if (length < 0) + length = readInt16(); + + // Make sure the string isn't erroneous + if (length < 0 || mPos + length > mLength) + { + mPos = mLength + 1; + DEBUGLOG("readString error"); + return ""; + } + + // Read the string + char const *stringBeg = mData + mPos; + char const *stringEnd + = static_cast(memchr(stringBeg, '\0', length)); + + std::string readString(stringBeg, + stringEnd ? stringEnd - stringBeg : length); + mPos += length; + PacketCounters::incInBytes(length); + DEBUGLOG("readString: " + readString); + return readString; +} + +std::string MessageIn::readRawString(int length) +{ + // Get string length + if (length < 0) + length = readInt16(); + + // Make sure the string isn't erroneous + if (length < 0 || mPos + length > mLength) + { + mPos = mLength + 1; + return ""; + } + + // Read the string + char const *stringBeg = mData + mPos; + char const *stringEnd + = static_cast(memchr(stringBeg, '\0', length)); + std::string readString(stringBeg, + stringEnd ? stringEnd - stringBeg : length); + + mPos += length; + PacketCounters::incInBytes(length); + DEBUGLOG("readString: " + readString); + + if (stringEnd) + { + long len2 = length - (stringEnd - stringBeg) - 1; + char const *stringBeg2 = stringEnd + 1; + char const *stringEnd2 + = static_cast(memchr(stringBeg2, '\0', len2)); + std::string hiddenPart = std::string(stringBeg2, + stringEnd2 ? stringEnd2 - stringBeg2 : len2); + if (hiddenPart.length() > 0) + { + DEBUGLOG("readString2: " + hiddenPart); + + return readString + "|" + hiddenPart; + } + } + + return readString; +} + +} diff --git a/src/net/messagein.h b/src/net/messagein.h new file mode 100644 index 000000000..8781b3050 --- /dev/null +++ b/src/net/messagein.h @@ -0,0 +1,118 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MESSAGEIN_H +#define NET_MESSAGEIN_H + +#include + +#include + +namespace Net +{ + +/** + * Used for parsing an incoming message. + * + * \ingroup Network + */ +class MessageIn +{ + public: + /** + * Returns the message ID. + */ + int getId() const + { return mId; } + + /** + * Returns the message length. + */ + unsigned int getLength() const + { return mLength; } + + /** + * Returns the length of unread data. + */ + unsigned int getUnreadLength() const + { return mLength - mPos; } + + virtual unsigned char readInt8(); /**< Reads a byte. */ + virtual Sint16 readInt16() = 0; /**< Reads a short. */ + virtual int readInt32() = 0; /**< Reads a long. */ + + /** + * Reads a 3-byte block containing tile-based coordinates. Used by + * manaserv. + */ + virtual void readCoordinates(Uint16 &x, Uint16 &y); + + /** + * Reads a special 3 byte block used by eAthena, containing x and y + * coordinates and direction. + */ + virtual void readCoordinates(Uint16 &x, Uint16 &y, Uint8 &direction); + + /** + * Reads a special 5 byte block used by eAthena, containing a source + * and destination coordinate pair. + */ + virtual void readCoordinatePair(Uint16 &srcX, Uint16 &srcY, + Uint16 &dstX, Uint16 &dstY); + + /** + * Skips a given number of bytes. + */ + virtual void skip(unsigned int length); + + /** + * Reads a string. If a length is not given (-1), it is assumed + * that the length of the string is stored in a short at the + * start of the string. + */ + virtual std::string readString(int length = -1); + + virtual std::string readRawString(int length); + + virtual ~MessageIn() + { } + + protected: + /** + * Constructor. + */ + MessageIn(const char *data, unsigned int length); + + const char *mData; /**< The message data. */ + unsigned int mLength; /**< The length of the data. */ + 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 // NET_MESSAGEIN_H diff --git a/src/net/messageout.cpp b/src/net/messageout.cpp new file mode 100644 index 000000000..5152b73e2 --- /dev/null +++ b/src/net/messageout.cpp @@ -0,0 +1,92 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/messageout.h" + +#include "net/packetcounters.h" + +#include "log.h" + +#include "utils/stringutils.h" + +#include +#include + +namespace Net +{ + +MessageOut::MessageOut(short id _UNUSED_): + mData(0), + mDataSize(0), + mPos(0) +{ + PacketCounters::incOutPackets(); + DEBUGLOG("MessageOut"); +} + +void MessageOut::writeInt8(Sint8 value) +{ + DEBUGLOG("writeInt8: " + toString((int)value)); + expand(1); + mData[mPos] = value; + mPos += 1; + PacketCounters::incOutBytes(1); +} + +void MessageOut::writeString(const std::string &string, int length) +{ + DEBUGLOG("writeString: " + string); + int stringLength = static_cast(string.length()); + if (length < 0) + { + // Write the length at the start if not fixed + writeInt16(static_cast(stringLength)); + length = stringLength; + } + else if (length < stringLength) + { + // Make sure the length of the string is no longer than specified + stringLength = length; + } + expand(length); + + // Write the actual string + memcpy(mData + mPos, string.c_str(), stringLength); + + // Pad remaining space with zeros + if (length > stringLength) + memset(mData + mPos + stringLength, '\0', length - stringLength); + + mPos += length; + PacketCounters::incOutBytes(length); +} + +char *MessageOut::getData() const +{ + return mData; +} + +unsigned int MessageOut::getDataSize() const +{ + return mDataSize; +} + +} diff --git a/src/net/messageout.h b/src/net/messageout.h new file mode 100644 index 000000000..11a9ff552 --- /dev/null +++ b/src/net/messageout.h @@ -0,0 +1,91 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_MESSAGEOUT_H +#define NET_MESSAGEOUT_H + +#include + +#include + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace Net +{ + +/** + * Used for building an outgoing message. + * + * \ingroup Network + */ +class MessageOut +{ + public: + virtual void writeInt8(Sint8 value); /**< Writes a byte. */ + virtual void writeInt16(Sint16 value) = 0; /**< Writes a short. */ + virtual void writeInt32(Sint32 value) = 0; /**< Writes a long. */ + + /** + * Writes a string. If a fixed length is not given (-1), it is stored + * as a short at the start of the string. + */ + virtual void writeString(const std::string &string, int length = -1); + + /** + * Returns the content of the message. + */ + virtual char *getData() const; + + /** + * Returns the length of the data. + */ + virtual unsigned int getDataSize() const; + + virtual ~MessageOut() + { } + + protected: + /** + * Constructor. + */ + MessageOut(short id); + + /** + * 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. + */ + virtual void expand(size_t size) = 0; + + char *mData; /**< Data building up. */ + unsigned int mDataSize; /**< Size of data. */ + unsigned int mPos; /**< Position in the data. */ +}; + +} + +#endif // NET_MESSAGEOUT_H diff --git a/src/net/net.cpp b/src/net/net.cpp new file mode 100644 index 000000000..36e414643 --- /dev/null +++ b/src/net/net.cpp @@ -0,0 +1,198 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/net.h" + +#include "main.h" + +#include "net/adminhandler.h" +#include "net/beinghandler.h" +#include "net/buysellhandler.h" +#include "net/charhandler.h" +#include "net/chathandler.h" +#include "net/generalhandler.h" +#include "net/guildhandler.h" +#include "net/inventoryhandler.h" +#include "net/loginhandler.h" +#include "net/gamehandler.h" +#include "net/npchandler.h" +#include "net/partyhandler.h" +#include "net/playerhandler.h" +#include "net/specialhandler.h" +#include "net/tradehandler.h" + +#include "net/tmwa/generalhandler.h" + +#include "net/manaserv/generalhandler.h" + +Net::AdminHandler *adminHandler = NULL; +Net::CharHandler *charHandler = NULL; +Net::ChatHandler *chatHandler = NULL; +Net::GeneralHandler *generalHandler = NULL; +Net::InventoryHandler *inventoryHandler = NULL; +Net::LoginHandler *loginHandler = NULL; +Net::GameHandler *gameHandler = NULL; +Net::GuildHandler *guildHandler = NULL; +Net::NpcHandler *npcHandler = NULL; +Net::PartyHandler *partyHandler = NULL; +Net::PlayerHandler *playerHandler = NULL; +Net::SpecialHandler *specialHandler = NULL; +Net::TradeHandler *tradeHandler = NULL; +Net::BeingHandler *beingHandler = NULL; +Net::BuySellHandler *buySellHandler = NULL; + +Net::AdminHandler *Net::getAdminHandler() +{ + return adminHandler; +} + +Net::CharHandler *Net::getCharHandler() +{ + return charHandler; +} + +Net::ChatHandler *Net::getChatHandler() +{ + return chatHandler; +} + +Net::GameHandler *Net::getGameHandler() +{ + return gameHandler; +} + +Net::GeneralHandler *Net::getGeneralHandler() +{ + return generalHandler; +} + +Net::GuildHandler *Net::getGuildHandler() +{ + return guildHandler; +} + +Net::InventoryHandler *Net::getInventoryHandler() +{ + return inventoryHandler; +} + +Net::LoginHandler *Net::getLoginHandler() +{ + return loginHandler; +} + +Net::NpcHandler *Net::getNpcHandler() +{ + return npcHandler; +} + +Net::PartyHandler *Net::getPartyHandler() +{ + return partyHandler; +} + +Net::PlayerHandler *Net::getPlayerHandler() +{ + return playerHandler; +} + +Net::SpecialHandler *Net::getSpecialHandler() +{ + return specialHandler; +} + +Net::TradeHandler *Net::getTradeHandler() +{ + return tradeHandler; +} + +Net::BeingHandler *Net::getBeingHandler() +{ + return beingHandler; +} + +Net::BuySellHandler *Net::getBuySellHandler() +{ + return buySellHandler; +} + +namespace Net +{ +ServerInfo::Type networkType = ServerInfo::UNKNOWN; + +void connectToServer(const ServerInfo &server) +{ + if (server.type == ServerInfo::UNKNOWN) + { + // TODO: Query the server about itself and choose the netcode based on + // that + } + + if (networkType == server.type && getGeneralHandler() != NULL) + { + getGeneralHandler()->reload(); + } + else + { + if (networkType != ServerInfo::UNKNOWN && getGeneralHandler() != NULL) + getGeneralHandler()->unload(); + + switch (server.type) + { + case ServerInfo::MANASERV: + new ManaServ::GeneralHandler; + break; + + case ServerInfo::TMWATHENA: + new TmwAthena::GeneralHandler; + break; + + default: + // Shouldn't happen... + break; + } + + getGeneralHandler()->load(); + + networkType = server.type; + } + + if (getLoginHandler()) + { + getLoginHandler()->setServer(server); + getLoginHandler()->connect(); + } +} + +void unload() +{ + GeneralHandler *handler = getGeneralHandler(); + if (handler) + handler->unload(); +} + +ServerInfo::Type getNetworkType() +{ + return networkType; +} + +} // namespace Net + diff --git a/src/net/net.h b/src/net/net.h new file mode 100644 index 000000000..95fe04f36 --- /dev/null +++ b/src/net/net.h @@ -0,0 +1,81 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_H +#define NET_H + +/** + * \namespace Net + * + * The network communication layer. It is composed of a host of interfaces that + * interact with different aspects of the game. They have different + * implementations depending on the type of server the client is connecting to. + */ + +#include "net/serverinfo.h" + +namespace Net +{ + +class AdminHandler; +class BeingHandler; +class CharHandler; +class ChatHandler; +class GameHandler; +class GeneralHandler; +class GuildHandler; +class InventoryHandler; +class LoginHandler; +class NpcHandler; +class PartyHandler; +class PlayerHandler; +class SpecialHandler; +class TradeHandler; +class BuySellHandler; + +AdminHandler *getAdminHandler(); +BeingHandler *getBeingHandler(); +CharHandler *getCharHandler(); +ChatHandler *getChatHandler(); +GameHandler *getGameHandler(); +GeneralHandler *getGeneralHandler(); +GuildHandler *getGuildHandler(); +InventoryHandler *getInventoryHandler(); +LoginHandler *getLoginHandler(); +NpcHandler *getNpcHandler(); +PartyHandler *getPartyHandler(); +PlayerHandler *getPlayerHandler(); +SpecialHandler *getSpecialHandler(); +TradeHandler *getTradeHandler(); +BuySellHandler *getBuySellHandler(); + +ServerInfo::Type getNetworkType(); + +/** + * Handles server detection and connection + */ +void connectToServer(const ServerInfo &server); + +void unload(); + +} // namespace Net + +#endif // NET_H diff --git a/src/net/npchandler.h b/src/net/npchandler.h new file mode 100644 index 000000000..1b08a83ec --- /dev/null +++ b/src/net/npchandler.h @@ -0,0 +1,66 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NPCHANDLER_H +#define NPCHANDLER_H + +#include + +namespace Net +{ + +class NpcHandler +{ + public: + virtual ~NpcHandler() + { } + + virtual void talk(int npcId) = 0; + + virtual void nextDialog(int npcId) = 0; + + virtual void closeDialog(int npcId) = 0; + + virtual void listInput(int npcId, unsigned char value) = 0; + + virtual void integerInput(int npcId, int value) = 0; + + virtual void stringInput(int npcId, const std::string &value) = 0; + + virtual void sendLetter(int npcId, const std::string &recipient, + const std::string &text) = 0; + + virtual void startShopping(int beingId) = 0; + + virtual void buy(int beingId) = 0; + + virtual void sell(int beingId) = 0; + + virtual void buyItem(int beingId, int itemId, int amount) = 0; + + virtual void sellItem(int beingId, int itemId, int amount) = 0; + + virtual void endShopping(int beingId) = 0; +}; + +} // namespace Net + +#endif // NPCHANDLER_H diff --git a/src/net/packetcounters.cpp b/src/net/packetcounters.cpp new file mode 100644 index 000000000..40ab72024 --- /dev/null +++ b/src/net/packetcounters.cpp @@ -0,0 +1,128 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/packetcounters.h" + +extern volatile int cur_time; +extern volatile bool runCounters; + +int PacketCounters::mInCurrentSec = 0; +int PacketCounters::mInBytes = 0; +int PacketCounters::mInBytesCalc = 0; +int PacketCounters::mInPackets = 0; +int PacketCounters::mInPacketsCalc = 0; +int PacketCounters::mOutCurrentSec = 0; +int PacketCounters::mOutBytes = 0; +int PacketCounters::mOutBytesCalc = 0; +int PacketCounters::mOutPackets = 0; +int PacketCounters::mOutPacketsCalc = 0; + +void PacketCounters::incInBytes(int cnt) +{ + if (!runCounters) + return; + + updateCounter(PacketCounters::mInCurrentSec, PacketCounters::mInBytesCalc, + PacketCounters::mInBytes); + + PacketCounters::mInBytes += cnt; +} + +void PacketCounters::incInPackets() +{ + if (!runCounters) + return; + + updateCounter(PacketCounters::mInCurrentSec, + PacketCounters::mInPacketsCalc, PacketCounters::mInPackets); + + PacketCounters::mInPackets ++; +} + +int PacketCounters::getInBytes() +{ + return PacketCounters::mInBytesCalc; +} + +int PacketCounters::getInPackets() +{ + return PacketCounters::mInPacketsCalc; +} + +void PacketCounters::incOutBytes(int cnt) +{ + if (!runCounters) + return; + + updateCounter(PacketCounters::mOutCurrentSec, + PacketCounters::mOutBytesCalc, PacketCounters::mOutBytes); + + PacketCounters::mOutBytes += cnt; +} + +void PacketCounters::incOutPackets() +{ + if (!runCounters) + return; + + updateCounter(PacketCounters::mOutCurrentSec, + PacketCounters::mOutPacketsCalc, + PacketCounters::mOutPackets); + + PacketCounters::mOutPackets ++; +} + +int PacketCounters::getOutBytes() +{ + return PacketCounters::mOutBytesCalc; +} + +int PacketCounters::getOutPackets() +{ + return PacketCounters::mOutPacketsCalc; +} + + +void PacketCounters::updateCounter(int ¤tSec, int &calc, int &counter) +{ + int idx = cur_time % 60; + if (currentSec != idx) + { + currentSec = idx; + calc = counter; + counter = 0; + } +} + +void PacketCounters::update() +{ + if (!runCounters) + return; + + updateCounter(PacketCounters::mInCurrentSec, PacketCounters::mInBytesCalc, + PacketCounters::mInBytes); + updateCounter(PacketCounters::mInCurrentSec, + PacketCounters::mInPacketsCalc, PacketCounters::mInPackets); + updateCounter(PacketCounters::mOutCurrentSec, + PacketCounters::mOutBytesCalc, PacketCounters::mOutBytes); + updateCounter(PacketCounters::mOutCurrentSec, + PacketCounters::mOutPacketsCalc, PacketCounters::mOutPackets); +} diff --git a/src/net/packetcounters.h b/src/net/packetcounters.h new file mode 100644 index 000000000..1e1aa7d83 --- /dev/null +++ b/src/net/packetcounters.h @@ -0,0 +1,55 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PACKETCOUNTERS_H +#define PACKETCOUNTERS_H + +class PacketCounters +{ +public: +// PacketCounters(); + static void incInBytes(int cnt); + static void incInPackets(); + static int getInBytes(); + static int getInPackets(); + static void incOutBytes(int cnt); + static void incOutPackets(); + static int getOutBytes(); + static int getOutPackets(); + static void update(); + + static int mInCurrentSec; + static int mInBytes; + static int mInBytesCalc; + static int mInPackets; + static int mInPacketsCalc; + static int mOutCurrentSec; + static int mOutBytes; + static int mOutBytesCalc; + static int mOutPackets; + static int mOutPacketsCalc; + +private: + static void updateCounter(int ¤tSec, int &calc, int &counter); + +}; + +#endif diff --git a/src/net/partyhandler.h b/src/net/partyhandler.h new file mode 100644 index 000000000..461334ece --- /dev/null +++ b/src/net/partyhandler.h @@ -0,0 +1,82 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PARTYHANDLER_H +#define PARTYHANDLER_H + +#include + +class Being; + +enum PartyShare +{ + PARTY_SHARE_UNKNOWN = -1, + PARTY_SHARE_NO, + PARTY_SHARE, + PARTY_SHARE_NOT_POSSIBLE = 2 +}; + +namespace Net +{ + +class PartyHandler +{ + public: + virtual ~PartyHandler() + { } + + virtual void create(const std::string &name = "") = 0; + + virtual void join(int partyId) = 0; + + virtual void invite(Being *player) = 0; + + virtual void invite(const std::string &name) = 0; + + virtual void inviteResponse(const std::string &inviter, + bool accept) = 0; + + virtual void leave() = 0; + + virtual void kick(Being *player) = 0; + + virtual void kick(const std::string &name) = 0; + + virtual void chat(const std::string &text) = 0; + + virtual void requestPartyMembers() = 0; + + virtual PartyShare getShareExperience() = 0; + + virtual void setShareExperience(PartyShare share) = 0; + + virtual PartyShare getShareItems() = 0; + + virtual void setShareItems(PartyShare share) = 0; + + // virtual void options() = 0; + + // virtual void message() = 0; +}; + +} // namespace Net + +#endif // PARTYHANDLER_H diff --git a/src/net/playerhandler.h b/src/net/playerhandler.h new file mode 100644 index 000000000..8fa84b38f --- /dev/null +++ b/src/net/playerhandler.h @@ -0,0 +1,75 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PLAYERHANDLER_H +#define PLAYERHANDLER_H + +#include "being.h" +#include "flooritem.h" +#include "localplayer.h" + +namespace Net +{ + +class PlayerHandler +{ + public: + virtual ~PlayerHandler() + { } + + virtual void attack(int id, bool keep = false) = 0; + + virtual void stopAttack() = 0; + + virtual void emote(int emoteId) = 0; + + virtual void increaseAttribute(int attr) = 0; + + virtual void decreaseAttribute(int attr) = 0; + + virtual void increaseSkill(int skillId) = 0; + + virtual void pickUp(FloorItem *floorItem) = 0; + + virtual void setDirection(char direction) = 0; + + virtual void setDestination(int x, int y, int direction = -1) = 0; + + virtual void changeAction(Being::Action action) = 0; + + virtual void respawn() = 0; + + virtual void ignorePlayer(const std::string &player, bool ignore) = 0; + + virtual void ignoreAll(bool ignore) = 0; + + virtual bool canUseMagic() = 0; + + virtual bool canCorrectAttributes() = 0; + + virtual int getJobLocation() = 0; + + virtual Vector getDefaultWalkSpeed() = 0; +}; + +} // namespace Net + +#endif // PLAYERHANDLER_H diff --git a/src/net/serverinfo.h b/src/net/serverinfo.h new file mode 100644 index 000000000..113d8a9b2 --- /dev/null +++ b/src/net/serverinfo.h @@ -0,0 +1,117 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SERVERINFO_H +#define SERVERINFO_H + +#include "utils/stringutils.h" + +#include +#include + +class ServerInfo +{ +public: + enum Type + { + UNKNOWN = 0, + MANASERV, + TMWATHENA + }; + + typedef std::pair VersionString; + + Type type; + std::string name; + std::string hostname; + unsigned short port; + + std::string description; + VersionString version; + + bool save; + + ServerInfo() + { + type = TMWATHENA; + port = 0; + save = false; + version.first = 0; + } + + ServerInfo(const ServerInfo &info) + { + type = info.type; + name = info.name; + hostname = info.hostname; + port = info.port; + description = info.description; + version.first = info.version.first; + version.second = info.version.second; + save = info.save; + } + + bool isValid() const + { + return !(hostname.empty() || port == 0 || type == UNKNOWN); + } + + void clear() + { + type = UNKNOWN; + name.clear(); + hostname.clear(); + port = 0; + description.clear(); + version.first = 0; + version.second.clear(); + save = false; + } + + bool operator==(const ServerInfo &other) const + { + return (type == other.type && hostname == other.hostname && + port == other.port); + } + + bool operator!=(const ServerInfo &other) const + { + return (type != other.type || hostname != other.hostname || + port != other.port); + } + + static Type parseType(const std::string &type) + { + if (compareStrI(type, "tmwathena") == 0) + return TMWATHENA; + // Used for backward compatibility + else if (compareStrI(type, "eathena") == 0) + return TMWATHENA; + else if (compareStrI(type, "manaserv") == 0) + return MANASERV; + + return UNKNOWN; + } +}; + +typedef std::vector ServerInfos; + +#endif // SERVERINFO_H diff --git a/src/net/specialhandler.h b/src/net/specialhandler.h new file mode 100644 index 000000000..cc3a09356 --- /dev/null +++ b/src/net/specialhandler.h @@ -0,0 +1,45 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef SPECIALHANDLER_H +#define SPECIALHANDLER_H + +#include + +namespace Net +{ +class SpecialHandler +{ + public: + virtual ~SpecialHandler () + { } + + virtual void use(int id) = 0; + + virtual void use(int id, int level, int beingId) = 0; + + virtual void use(int id, int level, int x, int y) = 0; + + virtual void use(int id, const std::string &map) = 0; +}; +} + +#endif // SPECIALHANDLER_H diff --git a/src/net/tmwa/adminhandler.cpp b/src/net/tmwa/adminhandler.cpp new file mode 100644 index 000000000..114c9a897 --- /dev/null +++ b/src/net/tmwa/adminhandler.cpp @@ -0,0 +1,135 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/adminhandler.h" + +#include "actorspritemanager.h" +#include "being.h" +#include "event.h" +#include "game.h" +#include "playerrelations.h" + +#include "net/chathandler.h" +#include "net/net.h" + +#include "net/tmwa/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include + +extern Net::AdminHandler *adminHandler; + +namespace TmwAthena +{ + +AdminHandler::AdminHandler() +{ + static const Uint16 _messages[] = + { + SMSG_ADMIN_KICK_ACK, + 0 + }; + handledMessages = _messages; + adminHandler = this; +} + +void AdminHandler::handleMessage(Net::MessageIn &msg) +{ + int id; + switch (msg.getId()) + { + case SMSG_ADMIN_KICK_ACK: + id = msg.readInt32(); + if (id == 0) + SERVER_NOTICE(_("Kick failed!")) + else + SERVER_NOTICE(_("Kick succeeded!")) + default: + break; + } +} + +void AdminHandler::announce(const std::string &text) +{ + MessageOut outMsg(CMSG_ADMIN_ANNOUNCE); + outMsg.writeInt16(static_cast(text.length() + 4)); + outMsg.writeString(text, static_cast(text.length())); +} + +void AdminHandler::localAnnounce(const std::string &text) +{ + MessageOut outMsg(CMSG_ADMIN_LOCAL_ANNOUNCE); + outMsg.writeInt16(static_cast(text.length() + 4)); + outMsg.writeString(text, static_cast(text.length())); +} + +void AdminHandler::hide(bool hide _UNUSED_) +{ + MessageOut outMsg(CMSG_ADMIN_HIDE); + outMsg.writeInt32(0); //unused +} + +void AdminHandler::kick(int playerId) +{ + MessageOut outMsg(CMSG_ADMIN_KICK); + outMsg.writeInt32(playerId); +} + +void AdminHandler::kick(const std::string &name) +{ + Net::getChatHandler()->talk("@kick " + name); +} + +void AdminHandler::ban(int playerId _UNUSED_) +{ + // Not supported +} + +void AdminHandler::ban(const std::string &name) +{ + Net::getChatHandler()->talk("@ban " + name); +} + +void AdminHandler::unban(int playerId _UNUSED_) +{ + // Not supported +} + +void AdminHandler::unban(const std::string &name) +{ + Net::getChatHandler()->talk("@unban " + name); +} + +void AdminHandler::mute(int playerId _UNUSED_, int type _UNUSED_, + int limit _UNUSED_) +{ + return; // Still looking into this +/* + MessageOut outMsg(CMSG_ADMIN_MUTE); + outMsg.writeInt32(playerId); + outMsg.writeInt8(type); + outMsg.writeInt16(limit); +*/ +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/adminhandler.h b/src/net/tmwa/adminhandler.h new file mode 100644 index 000000000..a7febf441 --- /dev/null +++ b/src/net/tmwa/adminhandler.h @@ -0,0 +1,69 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_ADMINHANDLER_H +#define NET_TA_ADMINHANDLER_H + +#include "net/adminhandler.h" +#include "net/net.h" + +#include "net/tmwa/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace TmwAthena +{ + +class AdminHandler : public MessageHandler, public Net::AdminHandler +{ + public: + AdminHandler(); + + void handleMessage(Net::MessageIn &msg); + + void announce(const std::string &text); + + void localAnnounce(const std::string &text); + + void hide(bool hide); + + void kick(int playerId); + + void kick(const std::string &name); + + void ban(int playerId); + + void ban(const std::string &name); + + void unban(int playerId); + + void unban(const std::string &name); + + void mute(int playerId, int type, int limit); +}; + +} // namespace TmwAthena + +#endif // NET_TA_ADMINHANDLER_H diff --git a/src/net/tmwa/beinghandler.cpp b/src/net/tmwa/beinghandler.cpp new file mode 100644 index 000000000..ea4978a20 --- /dev/null +++ b/src/net/tmwa/beinghandler.cpp @@ -0,0 +1,1075 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/beinghandler.h" + +#include "actorspritemanager.h" +#include "being.h" +#include "client.h" +#include "effectmanager.h" +#include "guild.h" +#include "keyboardconfig.h" +#include "localplayer.h" +#include "log.h" +#include "party.h" +#include "playerrelations.h" +#include "configuration.h" + +#include "gui/botcheckerwindow.h" +#include "gui/outfitwindow.h" +#include "gui/socialwindow.h" +#include "gui/killstats.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include "net/playerhandler.h" +#include "net/net.h" + +#include "net/tmwa/protocol.h" + +#include "resources/colordb.h" + +#include + +extern Net::BeingHandler *beingHandler; + +namespace TmwAthena +{ + +const int EMOTION_TIME = 500; /**< Duration of emotion icon */ + +Being *createBeing(int id, short job); + +BeingHandler::BeingHandler(bool enableSync): + mSync(enableSync) +{ + static const Uint16 _messages[] = + { + SMSG_BEING_VISIBLE, + SMSG_BEING_MOVE, + SMSG_BEING_MOVE2, + SMSG_BEING_REMOVE, + SMSG_SKILL_DAMAGE, + SMSG_BEING_ACTION, + SMSG_BEING_SELFEFFECT, + SMSG_BEING_EMOTION, + SMSG_BEING_CHANGE_LOOKS, + SMSG_BEING_CHANGE_LOOKS2, + SMSG_BEING_NAME_RESPONSE, + SMSG_PLAYER_GUILD_PARTY_INFO, + SMSG_BEING_CHANGE_DIRECTION, + SMSG_PLAYER_UPDATE_1, + SMSG_PLAYER_UPDATE_2, + SMSG_PLAYER_MOVE, + SMSG_PLAYER_STOP, + SMSG_PLAYER_MOVE_TO_ATTACK, + SMSG_PLAYER_STATUS_CHANGE, + SMSG_BEING_STATUS_CHANGE, + SMSG_BEING_RESURRECT, + SMSG_SOLVE_CHAR_NAME, + SMSG_BEING_SPAWN, + SMSG_SKILL_CASTING, + SMSG_SKILL_CAST_CANCEL, + SMSG_SKILL_NO_DAMAGE, + SMSG_BEING_IP_RESPONSE, + SMSG_PVP_MAP_MODE, + SMSG_PVP_SET, + 0 + }; + handledMessages = _messages; + beingHandler = this; +} + +Being *createBeing(int id, short job) +{ + if (!actorSpriteManager) + return 0; + + ActorSprite::Type type = ActorSprite::UNKNOWN; + if (job <= 25 || (job >= 4001 && job <= 4049)) + type = ActorSprite::PLAYER; + else if (job >= 46 && job <= 1000) + type = ActorSprite::NPC; + else if (job > 1000 && job <= 2000) + type = ActorSprite::MONSTER; + else if (job == 45) + type = ActorSprite::PORTAL; + + Being *being = actorSpriteManager->createBeing(id, type, job); + + if (type == ActorSprite::PLAYER || type == ActorSprite::NPC) + { + if (!being->updateFromCache()) + { + MessageOut outMsg(0x0094); + outMsg.writeInt32(id); //readLong(2)); + } + else + { + if (player_node) + player_node->checkNewName(being); + } + } + if (type == Being::PLAYER) + { + if (botCheckerWindow) + botCheckerWindow->updateList(); + if (socialWindow) + socialWindow->updateActiveList(); + } + return being; +} + +void BeingHandler::requestNameById(int id) +{ + MessageOut outMsg(0x0094); + outMsg.writeInt32(id); //readLong(2)); +} + +void BeingHandler::handleMessage(Net::MessageIn &msg) +{ + if (!actorSpriteManager) + return; + + int id; + short job, speed, gender; + Uint16 headTop, headMid, headBottom; + Uint16 shoes, gloves; + Uint16 weapon, shield; + Uint16 gmstatus; + int param1; + Uint16 stunMode; + int level; + Uint32 statusEffects; + int type, guild; + Uint16 status; + Being *srcBeing, *dstBeing; + int hairStyle, hairColor, flag; + int hp, maxHP, oldHP; + + 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 = actorSpriteManager->findBeing(id); + + if (dstBeing && dstBeing->getType() == Being::MONSTER + && !dstBeing->isAlive()) + { + actorSpriteManager->destroy(dstBeing); + actorSpriteManager->erase(dstBeing); + dstBeing = 0; + } + + 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; + + if (actorSpriteManager->isBlocked(id) == true) + break; + + dstBeing = createBeing(id, job); + + if (!dstBeing) + break; + + if (job == 1022 && killStats) + killStats->jackoAlive(dstBeing->getId()); + } + else + { + // undeleting marked for deletion being + if (dstBeing->getType() == Being::NPC) + { + actorSpriteManager->undelete(dstBeing); + } + } + + if (dstBeing->getType() == Being::PLAYER) + dstBeing->setMoveTime(); + + if (msg.getId() == SMSG_BEING_VISIBLE) + { + dstBeing->clearPath(); + dstBeing->setActionTime(tick_time); + dstBeing->setAction(Being::STAND); + } + + + // Prevent division by 0 when calculating frame + if (speed == 0) + speed = 150; + + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + dstBeing->setSubtype(job); + if (dstBeing->getType() == ActorSprite::MONSTER && player_node) + player_node->checkNewName(dstBeing); + + hairStyle = msg.readInt16(); + weapon = msg.readInt16(); + headBottom = msg.readInt16(); + + if (msg.getId() == SMSG_BEING_MOVE) + msg.readInt32(); // server tick + + shield = msg.readInt16(); + headTop = msg.readInt16(); + headMid = msg.readInt16(); + hairColor = msg.readInt16(); + shoes = msg.readInt16(); // clothes color - "abused" as shoes + + if (dstBeing->getType() == ActorSprite::MONSTER) + { + hp = msg.readInt32(); + maxHP = msg.readInt32(); + if (hp && maxHP) + { + oldHP = dstBeing->getHP(); + if (!oldHP || oldHP > hp) + dstBeing->setHP(hp); + dstBeing->setMaxHP(maxHP); + } + gloves = 0; + guild = 0; + } + else + { + gloves = msg.readInt16(); // head dir - "abused" as gloves + guild = msg.readInt32(); // guild + msg.readInt16(); // guild emblem + } +// logger->log("being guild: " + toString(guild)); +/* + if (guild == 0) + dstBeing->clearGuilds(); + else + dstBeing->setGuild(Guild::getGuild(static_cast(guild))); +*/ + + msg.readInt16(); // manner + dstBeing->setStatusEffectBlock(32, msg.readInt16()); // opt3 + msg.readInt8(); // karma + gender = msg.readInt8(); + + if (dstBeing->getType() == ActorSprite::PLAYER) + { + dstBeing->setGender((gender == 0) + ? GENDER_FEMALE : GENDER_MALE); + // Set these after the gender, as the sprites may be gender-specific + dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1, + ColorDB::get(hairColor)); + dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom); + dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid); + dstBeing->setSprite(SPRITE_HAT, headTop); + dstBeing->setSprite(SPRITE_SHOE, shoes); + dstBeing->setSprite(SPRITE_GLOVES, gloves); + dstBeing->setSprite(SPRITE_WEAPON, weapon, "", true); + if (!config.getBoolValue("hideShield")) + dstBeing->setSprite(SPRITE_SHIELD, shield); + } + + if (msg.getId() == SMSG_BEING_MOVE) + { + Uint16 srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY); + dstBeing->setAction(Being::STAND); + dstBeing->setTileCoords(srcX, srcY); + dstBeing->setDestination(dstX, dstY); + +// if (player_node && player_node->getTarget() == dstBeing) +// player_node->targetMoved(); + } + else + { + Uint8 dir; + Uint16 x, y; + msg.readCoordinates(x, y, dir); + dstBeing->setTileCoords(x, y); + + if (job == 45 && socialWindow && outfitWindow) + { + int num = socialWindow->getPortalIndex(x, y); + if (num >= 0) + { + dstBeing->setName(keyboard.getKeyShortString( + outfitWindow->keyName(num))); + } + else + { + dstBeing->setName(""); + } + } + + dstBeing->setDirection(getDirection(dir)); + } + + msg.readInt8(); // unknown + msg.readInt8(); // unknown +// msg.readInt8(); // unknown / sit + msg.readInt16(); + + 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 = actorSpriteManager->findBeing(msg.readInt32()); + + /* + * 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) + break; + + Uint16 srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY); + msg.readInt32(); // Server tick + + dstBeing->setAction(Being::STAND); + dstBeing->setTileCoords(srcX, srcY); + dstBeing->setDestination(dstX, dstY); + if (dstBeing->getType() == Being::PLAYER) + dstBeing->setMoveTime(); + +// if (player_node && player_node->getTarget() == dstBeing) +// { +// logger->log("SMSG_BEING_MOVE2"); +// player_node->targetMoved(); +// } + + break; + + case SMSG_BEING_SPAWN: + // skipping this packet + msg.readInt32(); // id + msg.readInt16(); // speed + msg.readInt16(); // opt1 + msg.readInt16(); // opt2 + msg.readInt16(); // option + msg.readInt16(); // disguise + break; + + case SMSG_BEING_REMOVE: + // A being should be removed or has died + + id = msg.readInt32(); + dstBeing = actorSpriteManager->findBeing(id); + if (!dstBeing) + break; + + player_node->followMoveTo(dstBeing, player_node->getNextDestX(), + player_node->getNextDestY()); + + // If this is player's current target, clear it. + if (dstBeing == player_node->getTarget()) + player_node->stopAttack(); + + if (msg.readInt8() == 1) + { + dstBeing->setAction(Being::DEAD); + if (dstBeing->getName() == "Jack O" && killStats) + killStats->jackoDead(id); + } + else + { + if (dstBeing->getType() == Being::PLAYER) + { + if (botCheckerWindow) + botCheckerWindow->updateList(); + if (socialWindow) + socialWindow->updateActiveList(); + } + actorSpriteManager->destroy(dstBeing); + } + break; + + case SMSG_BEING_RESURRECT: + // A being changed mortality status + + id = msg.readInt32(); + dstBeing = actorSpriteManager->findBeing(id); + if (!dstBeing) + break; + + // If this is player's current target, clear it. + if (dstBeing == player_node->getTarget()) + player_node->stopAttack(); + + if (msg.readInt8() == 1) + dstBeing->setAction(Being::STAND); + + break; + + case SMSG_SKILL_DAMAGE: + msg.readInt16(); // Skill Id + srcBeing = actorSpriteManager->findBeing(msg.readInt32()); + dstBeing = actorSpriteManager->findBeing(msg.readInt32()); + msg.readInt32(); // Server tick + msg.readInt32(); // src speed + msg.readInt32(); // dst speed + param1 = msg.readInt32(); // Damage + msg.readInt16(); // Skill level + msg.readInt16(); // Div + msg.readInt8(); // Skill hit/type (?) + if (dstBeing) + { + // Perhaps a new skill attack type should be created and used? + dstBeing->takeDamage(srcBeing, param1, Being::HIT); + } + if (srcBeing) + srcBeing->handleAttack(dstBeing, param1, Being::HIT); + break; + + case SMSG_BEING_ACTION: + srcBeing = actorSpriteManager->findBeing(msg.readInt32()); + dstBeing = actorSpriteManager->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 Being::HIT: // Damage + case Being::CRITICAL: // Critical Damage + case Being::MULTI: // Critical Damage + case Being::REFLECT: // Reflected Damage + case Being::FLEE: // Lucky Dodge + if (dstBeing) + { + dstBeing->takeDamage(srcBeing, param1, + (Being::AttackType)type); + } + if (srcBeing) + { + srcBeing->handleAttack(dstBeing, param1, + (Being::AttackType)type); + if (srcBeing->getType() == Being::PLAYER) + srcBeing->setAttackTime(); + } + break; + + case 0x02: // Sit + if (srcBeing) + { + srcBeing->setAction(Being::SIT); + if (srcBeing->getType() == Being::PLAYER) + { + srcBeing->setMoveTime(); + if (player_node) + { + player_node->imitateAction( + srcBeing, Being::SIT); + } + } + } + break; + + case 0x03: // Stand up + if (srcBeing) + { + srcBeing->setAction(Being::STAND); + if (srcBeing->getType() == Being::PLAYER) + { + srcBeing->setMoveTime(); + if (player_node) + { + player_node->imitateAction( + srcBeing, Being::STAND); + } + } + } + break; + default: + break; +/* + logger->log("QQQ1 SMSG_BEING_ACTION:"); + if (srcBeing) + logger->log("srcBeing:" + toString(srcBeing->getId())); + if (dstBeing) + logger->log("dstBeing:" + toString(dstBeing->getId())); + logger->log("type: " + toString(type)); +*/ + } + break; + + case SMSG_BEING_SELFEFFECT: + { + if (!effectManager) + return; + + id = (Uint32)msg.readInt32(); + Being* being = actorSpriteManager->findBeing(id); + if (!being) + break; + + int effectType = msg.readInt32(); + + effectManager->trigger(effectType, being); + + if (being && effectType == 3 + && being->getType() == Being::PLAYER + && socialWindow) + { //reset received damage + socialWindow->resetDamage(being->getName()); + } + + break; + } + + case SMSG_BEING_EMOTION: + if (!player_node) + break; + + if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) + break; + + if (player_relations.hasPermission(dstBeing, + PlayerRelation::EMOTE)) + { + unsigned char emote = msg.readInt8(); + if (emote) + { + dstBeing->setEmote(emote, EMOTION_TIME); + player_node->imitateEmote(dstBeing, emote); + } + } + if (dstBeing->getType() == Being::PLAYER) + dstBeing->setOtherTime(); + + 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 = actorSpriteManager->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(); + } + + if (dstBeing->getType() == Being::PLAYER) + dstBeing->setOtherTime(); + + if (!player_node) + break; + + switch (type) + { + case 0: // change race + dstBeing->setSubtype(id); + break; + case 1: // eAthena LOOK_HAIR + dstBeing->setSpriteID(SPRITE_HAIR, id *-1); + break; + case 2: // Weapon ID in id, Shield ID in id2 + dstBeing->setSprite(SPRITE_WEAPON, id, "", true); + if (!config.getBoolValue("hideShield")) + dstBeing->setSprite(SPRITE_SHIELD, id2); + player_node->imitateOutfit(dstBeing, SPRITE_SHIELD); + break; + case 3: // Change lower headgear for eAthena, pants for us + dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, id); + player_node->imitateOutfit(dstBeing, SPRITE_BOTTOMCLOTHES); + break; + case 4: // Change upper headgear for eAthena, hat for us + dstBeing->setSprite(SPRITE_HAT, id); + player_node->imitateOutfit(dstBeing, SPRITE_HAT); + break; + case 5: // Change middle headgear for eathena, armor for us + dstBeing->setSprite(SPRITE_TOPCLOTHES, id); + player_node->imitateOutfit(dstBeing, SPRITE_TOPCLOTHES); + break; + case 6: // eAthena LOOK_HAIR_COLOR + dstBeing->setSpriteColor(SPRITE_HAIR, ColorDB::get(id)); + break; + case 8: // eAthena LOOK_SHIELD + if (!config.getBoolValue("hideShield")) + dstBeing->setSprite(SPRITE_SHIELD, id); + player_node->imitateOutfit(dstBeing, SPRITE_SHIELD); + break; + case 9: // eAthena LOOK_SHOES + dstBeing->setSprite(SPRITE_SHOE, id); + player_node->imitateOutfit(dstBeing, SPRITE_SHOE); + break; + case 10: // LOOK_GLOVES + dstBeing->setSprite(SPRITE_GLOVES, id); + player_node->imitateOutfit(dstBeing, SPRITE_GLOVES); + break; + case 11: // LOOK_CAPE + dstBeing->setSprite(SPRITE_CAPE, id); + player_node->imitateOutfit(dstBeing, SPRITE_CAPE); + break; + case 12: + dstBeing->setSprite(SPRITE_MISC1, id); + player_node->imitateOutfit(dstBeing, SPRITE_MISC1); + break; + case 13: + dstBeing->setSprite(SPRITE_MISC2, id); + player_node->imitateOutfit(dstBeing, SPRITE_MISC2); + break; + default: + logger->log("QQQ3 CHANGE_LOOKS: unsupported type: " + "%d, id: %d", type, id); + if (dstBeing) + { + logger->log("ID: " + toString(dstBeing->getId())); + logger->log("name: " + toString(dstBeing->getName())); + } + break; + } + } + break; + + case SMSG_BEING_NAME_RESPONSE: + { + int beingId = msg.readInt32(); + if ((dstBeing = actorSpriteManager->findBeing(beingId))) + { + if (beingId == player_node->getId()) + { + player_node->pingResponse(); + } + else + { + dstBeing->setName(msg.readString(24)); + dstBeing->updateGuild(); + dstBeing->addToCache(); + + if (dstBeing->getType() == Being::PLAYER) + dstBeing->updateColors(); + + if (player_node) + { + Party *party = player_node->getParty(); + if (party && party->isMember(dstBeing->getId())) + { + PartyMember *member = party->getMember( + dstBeing->getId()); + + if (member) + member->setName(dstBeing->getName()); + } + player_node->checkNewName(dstBeing); + } + } + } + } + break; + case SMSG_BEING_IP_RESPONSE: + { + if ((dstBeing = actorSpriteManager->findBeing( + msg.readInt32()))) + { + dstBeing->setIp(ipToString(msg.readInt32())); + } + } + break; + case SMSG_SOLVE_CHAR_NAME: + { + logger->log1("SMSG_SOLVE_CHAR_NAME"); + logger->log(toString(msg.readInt32())); + logger->log(msg.readString(24)); + } + break; + case SMSG_PLAYER_GUILD_PARTY_INFO: + if ((dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) + { + dstBeing->setPartyName(msg.readString(24)); + dstBeing->setGuildName(msg.readString(24)); + dstBeing->setGuildPos(msg.readString(24)); + dstBeing->addToCache(); + msg.readString(24); // Discard this + } + break; + case SMSG_BEING_CHANGE_DIRECTION: + { + if (!(dstBeing = actorSpriteManager->findBeing(msg.readInt32()))) + break; + + msg.readInt16(); // unused + + unsigned char dir = getDirection(msg.readInt8()); + dstBeing->setDirection(dir); + if (player_node) + player_node->imitateDirection(dstBeing, dir); + break; + } + case SMSG_PLAYER_UPDATE_1: + case SMSG_PLAYER_UPDATE_2: + case SMSG_PLAYER_MOVE: + if (!actorSpriteManager || !player_node) + break; + + // 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 = actorSpriteManager->findBeing(id); + + if (!dstBeing) + { + if (actorSpriteManager->isBlocked(id) == true) + break; + + dstBeing = createBeing(id, job); + + if (!dstBeing) + break; + } + + if (Party *party = player_node->getParty()) + { + if (party->isMember(id)) + dstBeing->setParty(party); + } + + dstBeing->setWalkSpeed(Vector(speed, speed, 0)); + dstBeing->setSubtype(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(); + shoes = msg.readInt16(); + gloves = msg.readInt16(); //sd->head_dir + guild = msg.readInt32(); // guild + + if (guild == 0) + dstBeing->clearGuilds(); + else + dstBeing->setGuild(Guild::getGuild(static_cast(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(SPRITE_WEAPON, weapon, "", true); + if (!config.getBoolValue("hideShield")) + dstBeing->setSprite(SPRITE_SHIELD, shield); + //dstBeing->setSprite(SPRITE_SHOE, shoes); + dstBeing->setSprite(SPRITE_BOTTOMCLOTHES, headBottom); + dstBeing->setSprite(SPRITE_TOPCLOTHES, headMid); + dstBeing->setSprite(SPRITE_HAT, headTop); + //dstBeing->setSprite(SPRITE_GLOVES, gloves); + //dstBeing->setSprite(SPRITE_CAPE, cape); + //dstBeing->setSprite(SPRITE_MISC1, misc1); + //dstBeing->setSprite(SPRITE_MISC2, misc2); + dstBeing->setSprite(SPRITE_HAIR, hairStyle * -1, + ColorDB::get(hairColor)); + + player_node->imitateOutfit(dstBeing); + + if (msg.getId() == SMSG_PLAYER_MOVE) + { + Uint16 srcX, srcY, dstX, dstY; + msg.readCoordinatePair(srcX, srcY, dstX, dstY); + + player_node->followMoveTo(dstBeing, srcX, srcY, dstX, dstY); + + dstBeing->setTileCoords(srcX, srcY); + dstBeing->setDestination(dstX, dstY); + + if (player_node->getCurrentAction() != Being::STAND) + player_node->imitateAction(dstBeing, Being::STAND); + if (player_node->getDirection() != dstBeing->getDirection()) + { + player_node->imitateDirection(dstBeing, + dstBeing->getDirection()); + } + } + else + { + Uint8 dir; + Uint16 x, y; + msg.readCoordinates(x, y, dir); + dstBeing->setTileCoords(x, y); + dir = getDirection(dir); + dstBeing->setDirection(dir); + + player_node->imitateDirection(dstBeing, dir); + } + + gmstatus = msg.readInt16(); + + if (gmstatus & 0x80) + dstBeing->setGM(true); + + if (msg.getId() == SMSG_PLAYER_UPDATE_1) + { + int type = msg.readInt8(); + switch (type) + { + case 0: + dstBeing->setAction(Being::STAND); + player_node->imitateAction(dstBeing, Being::STAND); + break; + + case 1: + dstBeing->setAction(Being::DEAD); + break; + + case 2: + dstBeing->setAction(Being::SIT); + player_node->imitateAction(dstBeing, Being::SIT); + break; + + default: + //need set stay state? + logger->log("QQQ2 SMSG_PLAYER_UPDATE_1:" + + toString(id) + " " + toString(type)); + if (dstBeing) + { + logger->log("dstBeing id:" + + toString(dstBeing->getId())); + logger->log("dstBeing name:" + + dstBeing->getName()); + } + break; + + } + } + else if (msg.getId() == SMSG_PLAYER_MOVE) + { + msg.readInt8(); // unknown + } + + level = msg.readInt8(); // Lv + if (level) + dstBeing->setLevel(level); + + msg.readInt8(); // unknown + + dstBeing->setActionTime(tick_time); + dstBeing->reset(); + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); + dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); + + if (msg.getId() == SMSG_PLAYER_MOVE + && dstBeing->getType() == Being::PLAYER) + { + dstBeing->setMoveTime(); + } + + 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 = actorSpriteManager->findBeing(id); + if (dstBeing) + { + Uint16 x, y; + x = msg.readInt16(); + y = msg.readInt16(); + dstBeing->setTileCoords(x, y); + if (dstBeing->getCurrentAction() == Being::MOVE) + 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... + */ + if (player_node) + player_node->fixAttackTarget(); + break; + + case SMSG_PLAYER_STATUS_CHANGE: + // Change in players' flags + + id = msg.readInt32(); + dstBeing = actorSpriteManager->findBeing(id); + if (!dstBeing) + break; + + stunMode = msg.readInt16(); + statusEffects = msg.readInt16(); + statusEffects |= ((Uint32) msg.readInt16()) << 16; + msg.readInt8(); // Unused? + + dstBeing->setStunMode(stunMode); + dstBeing->setStatusEffectBlock(0, (statusEffects >> 16) & 0xffff); + dstBeing->setStatusEffectBlock(16, statusEffects & 0xffff); + break; + + case SMSG_BEING_STATUS_CHANGE: + // Status change + status = msg.readInt16(); + id = msg.readInt32(); + flag = msg.readInt8(); // 0: stop, 1: start + + dstBeing = actorSpriteManager->findBeing(id); + if (dstBeing) + dstBeing->setStatusEffect(status, flag); + break; + + case SMSG_SKILL_CASTING: + msg.readInt32(); // src id + msg.readInt32(); // dst id + msg.readInt16(); // dst x + msg.readInt16(); // dst y + msg.readInt16(); // skill num + msg.readInt32(); // skill get pl + msg.readInt32(); // cast time + break; + + case SMSG_SKILL_CAST_CANCEL: + msg.readInt32(); // id + break; + + case SMSG_SKILL_NO_DAMAGE: + msg.readInt16(); // skill id + msg.readInt16(); // heal + msg.readInt32(); // dst id + msg.readInt32(); // src id + msg.readInt8(); // fail + break; + + case SMSG_PVP_MAP_MODE: + { + Game *game = Game::instance(); + if (!game) + break; + + Map *map = game->getCurrentMap(); + if (map) + map->setPvpMode(msg.readInt16()); + break; + } + + case SMSG_PVP_SET: + { + int id = msg.readInt32(); // id + int rank = msg.readInt32(); // rank + msg.readInt32(); // num + dstBeing = actorSpriteManager->findBeing(id); + if (dstBeing) + dstBeing->setPvpRank(rank); + break; + } + + default: + break; + } +} + +Uint8 BeingHandler::getDirection(Uint8 dir) +{ + if (dir == 0) + dir = 8; + return dir; +} + +void BeingHandler::undress(Being *being) +{ + being->setSprite(SPRITE_BOTTOMCLOTHES, 0); + being->setSprite(SPRITE_TOPCLOTHES, 0); + being->setSprite(SPRITE_HAT, 0); + being->setSprite(SPRITE_SHOE, 0); + being->setSprite(SPRITE_GLOVES, 0); +// being->setSprite(SPRITE_WEAPON, 0, "", true); +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/beinghandler.h b/src/net/tmwa/beinghandler.h new file mode 100644 index 000000000..f16b35178 --- /dev/null +++ b/src/net/tmwa/beinghandler.h @@ -0,0 +1,53 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_BEINGHANDLER_H +#define NET_TA_BEINGHANDLER_H + +#include "net/beinghandler.h" +#include "net/net.h" + +#include "net/tmwa/messagehandler.h" + +namespace TmwAthena +{ + +class BeingHandler : public MessageHandler, public Net::BeingHandler +{ + public: + BeingHandler(bool enableSync); + + virtual void handleMessage(Net::MessageIn &msg); + + virtual void requestNameById(int id); + + virtual void undress(Being *being); + + Uint8 getDirection(Uint8 dir); + + private: + // Should we honor server "Stop Walking" packets + bool mSync; +}; + +} // namespace TmwAthena + +#endif // NET_TA_BEINGHANDLER_H diff --git a/src/net/tmwa/buysellhandler.cpp b/src/net/tmwa/buysellhandler.cpp new file mode 100644 index 000000000..bdc372238 --- /dev/null +++ b/src/net/tmwa/buysellhandler.cpp @@ -0,0 +1,231 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/buysellhandler.h" + +#include "actorspritemanager.h" +#include "configuration.h" +#include "event.h" +#include "inventory.h" +#include "item.h" +#include "localplayer.h" +#include "playerinfo.h" +#include "shopitem.h" + +#include "gui/buy.h" +#include "gui/buysell.h" +#include "gui/sell.h" +#include "gui/shopwindow.h" + +#include "gui/widgets/chattab.h" + +#include "net/chathandler.h" +#include "net/messagein.h" +#include "net/net.h" + +#include "net/tmwa/chathandler.h" +#include "net/tmwa/protocol.h" + +#include "utils/gettext.h" + +extern Net::BuySellHandler *buySellHandler; + +namespace TmwAthena +{ + +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 + }; + mNpcId = 0; + handledMessages = _messages; + buySellHandler = this; +} + +void BuySellHandler::handleMessage(Net::MessageIn &msg) +{ + int n_items; + + switch (msg.getId()) + { + case SMSG_NPC_BUY_SELL_CHOICE: + if (!BuySellDialog::isActive()) + { + mNpcId = msg.readInt32(); + new BuySellDialog(mNpcId); + } + break; + + case SMSG_NPC_BUY: + msg.readInt16(); // length + n_items = (msg.getLength() - 4) / 11; + mBuyDialog = new BuyDialog(mNpcId); + mBuyDialog->setMoney(PlayerInfo::getAttribute(MONEY)); + + for (int k = 0; k < n_items; k++) + { + int value = msg.readInt32(); + msg.readInt32(); // DCvalue + msg.readInt8(); // type + int itemId = msg.readInt16(); + mBuyDialog->addItem(itemId, 0, value); + } + break; + + case SMSG_NPC_SELL: + msg.readInt16(); // length + n_items = (msg.getLength() - 4) / 10; + if (n_items > 0) + { + SellDialog *dialog = new SellDialog(mNpcId); + dialog->setMoney(PlayerInfo::getAttribute(MONEY)); + + for (int k = 0; k < n_items; k++) + { + int index = msg.readInt16() - INVENTORY_OFFSET; + int value = msg.readInt32(); + msg.readInt32(); // OCvalue + + Item *item = PlayerInfo::getInventory()->getItem(index); + + if (item && !(item->isEquipped())) + dialog->addItem(item, value); + } + } + else + { + SERVER_NOTICE(_("Nothing to sell.")) + } + break; + + case SMSG_NPC_BUY_RESPONSE: + if (msg.readInt8() == 0) + { + SERVER_NOTICE(_("Thanks for buying.")) + } + else + { + // Reset player money since buy dialog already assumed purchase + // would go fine + if (mBuyDialog) + mBuyDialog->setMoney(PlayerInfo::getAttribute(MONEY)); + SERVER_NOTICE(_("Unable to buy.")) + } + break; + + case SMSG_NPC_SELL_RESPONSE: + if (msg.readInt8() == 0) + SERVER_NOTICE(_("Thanks for selling.")) + else + SERVER_NOTICE(_("Unable to sell.")) + + break; + default: + break; + } + +} + +void BuySellHandler::requestSellList(std::string nick) +{ + if (nick.empty() != 0 || !shopWindow) + return; + + std::string data = "!selllist " + toString(tick_time); + shopWindow->setAcceptPlayer(nick); + + if (config.getBoolValue("hideShopMessages")) + { + Net::getChatHandler()->privateMessage(nick, data); + } + else + { + if (chatWindow) + chatWindow->whisper(nick, data, BY_PLAYER); + } +//was true +} + +void BuySellHandler::requestBuyList(std::string nick) +{ + if (nick.empty() || !shopWindow) + return; + + std::string data = "!buylist " + toString(tick_time); + shopWindow->setAcceptPlayer(nick); + + if (config.getBoolValue("hideShopMessages")) + { + Net::getChatHandler()->privateMessage(nick, data); + } + else + { + if (chatWindow) + chatWindow->whisper(nick, data, BY_PLAYER); + } +//was true +} + +void BuySellHandler::sendBuyRequest(std::string nick, ShopItem* item, + int amount) +{ + if (!chatWindow || nick.empty() || !item || + amount < 1 || amount > item->getQuantity()) + { + return; + } + std::string data = strprintf("!buyitem %d %d %d", + item->getId(), item->getPrice(), amount); + + if (config.getBoolValue("hideShopMessages")) + Net::getChatHandler()->privateMessage(nick, data); + else + chatWindow->whisper(nick, data, BY_PLAYER); +//was true +} + +void BuySellHandler::sendSellRequest(std::string nick, ShopItem* item, + int amount) +{ + if (!chatWindow || nick.empty() || !item || + amount < 1 || amount > item->getQuantity()) + { + return; + } + + std::string data = strprintf("!sellitem %d %d %d", + item->getId(), item->getPrice(), amount); + + if (config.getBoolValue("hideShopMessages")) + Net::getChatHandler()->privateMessage(nick, data); + else + chatWindow->whisper(nick, data, BY_PLAYER); +//was true +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/buysellhandler.h b/src/net/tmwa/buysellhandler.h new file mode 100644 index 000000000..e9ae2b9ff --- /dev/null +++ b/src/net/tmwa/buysellhandler.h @@ -0,0 +1,59 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_BUYSELLHANDLER_H +#define NET_TA_BUYSELLHANDLER_H + +#include "net/buysellhandler.h" + +#include "being.h" + +#include "net/net.h" + +#include "net/tmwa/messagehandler.h" + +class BuyDialog; + +namespace TmwAthena +{ + +class BuySellHandler : public MessageHandler, public Net::BuySellHandler +{ + public: + BuySellHandler(); + + virtual void handleMessage(Net::MessageIn &msg); + + virtual void requestSellList(std::string nick); + virtual void requestBuyList(std::string nick); + virtual void sendBuyRequest(std::string nick, ShopItem* item, + int amount); + virtual void sendSellRequest(std::string nick, ShopItem* item, + int amount); + + private: + int mNpcId; + BuyDialog *mBuyDialog; +}; + +} // namespace TmwAthena + +#endif // NET_TA_BUYSELLHANDLER_H diff --git a/src/net/tmwa/charserverhandler.cpp b/src/net/tmwa/charserverhandler.cpp new file mode 100644 index 000000000..e500f667b --- /dev/null +++ b/src/net/tmwa/charserverhandler.cpp @@ -0,0 +1,386 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/charserverhandler.h" + +#include "client.h" +#include "configuration.h" +#include "game.h" +#include "log.h" + +#include "gui/charcreatedialog.h" +#include "gui/okdialog.h" + +#include "net/logindata.h" +#include "net/messagein.h" +#include "net/messageout.h" +#include "net/net.h" + +#include "net/tmwa/gamehandler.h" +#include "net/tmwa/loginhandler.h" +#include "net/tmwa/network.h" +#include "net/tmwa/protocol.h" + +#include "resources/colordb.h" + +#include "utils/dtor.h" +#include "utils/gettext.h" +#include "utils/stringutils.h" + +extern Net::CharHandler *charHandler; + +namespace TmwAthena +{ + +extern ServerInfo charServer; +extern ServerInfo mapServer; + +CharServerHandler::CharServerHandler() +{ + static const Uint16 _messages[] = + { + SMSG_CHAR_LOGIN, + SMSG_CHAR_LOGIN_ERROR, + SMSG_CHAR_CREATE_SUCCEEDED, + SMSG_CHAR_CREATE_FAILED, + SMSG_CHAR_DELETE_SUCCEEDED, + SMSG_CHAR_DELETE_FAILED, + SMSG_CHAR_MAP_INFO, + SMSG_CHANGE_MAP_SERVER, + 0 + }; + handledMessages = _messages; + charHandler = this; +} + +void CharServerHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case SMSG_CHAR_LOGIN: + { + msg.skip(2); // Length word + msg.skip(20); // Unused + + delete_all(mCharacters); + mCharacters.clear(); + + // Derive number of characters from message length + const int count = (msg.getLength() - 24) / 106; + + for (int i = 0; i < count; ++i) + { + Net::Character *character = new Net::Character; + readPlayerData(msg, character); + mCharacters.push_back(character); + logger->log("CharServer: Player: %s (%d)", + character->dummy->getName().c_str(), character->slot); + } + + Client::setState(STATE_CHAR_SELECT); + } + break; + + case SMSG_CHAR_LOGIN_ERROR: + switch (msg.readInt8()) + { + case 0: + errorMessage = _("Access denied. Most likely, there are " + "too many players on this server."); + break; + case 1: + errorMessage = _("Cannot use this ID."); + break; + default: + errorMessage = _("Unknown char-server failure."); + break; + } + Client::setState(STATE_ERROR); + break; + + case SMSG_CHAR_CREATE_SUCCEEDED: + { + Net::Character *character = new Net::Character; + readPlayerData(msg, character); + mCharacters.push_back(character); + + updateCharSelectDialog(); + + // Close the character create dialog + if (mCharCreateDialog) + { + mCharCreateDialog->scheduleDelete(); + mCharCreateDialog = 0; + } + } + break; + + case SMSG_CHAR_CREATE_FAILED: + new OkDialog(_("Error"), _("Failed to create character. Most " + "likely the name is already taken.")); + if (mCharCreateDialog) + mCharCreateDialog->unlock(); + break; + + case SMSG_CHAR_DELETE_SUCCEEDED: + delete mSelectedCharacter; + mCharacters.remove(mSelectedCharacter); + mSelectedCharacter = 0; + updateCharSelectDialog(); + unlockCharSelectDialog(); + new OkDialog(_("Info"), _("Character deleted.")); + break; + + case SMSG_CHAR_DELETE_FAILED: + unlockCharSelectDialog(); + new OkDialog(_("Error"), _("Failed to delete character.")); + break; + + case SMSG_CHAR_MAP_INFO: + { +// msg.skip(4); // CharID, must be the same as player_node->charID + PlayerInfo::setCharId(msg.readInt32()); + GameHandler *gh = static_cast(Net::getGameHandler()); + gh->setMap(msg.readString(16)); + mapServer.hostname = ipToString(msg.readInt32()); + mapServer.port = msg.readInt16(); + + // Prevent the selected local player from being deleted + player_node = mSelectedCharacter->dummy; + PlayerInfo::setBackend(mSelectedCharacter->data); + + mSelectedCharacter->dummy = 0; + + delete_all(mCharacters); + mCharacters.clear(); + updateCharSelectDialog(); + + if (mNetwork) + mNetwork->disconnect(); + Client::setState(STATE_CONNECT_GAME); + } + break; + + case SMSG_CHANGE_MAP_SERVER: + { + GameHandler *gh = static_cast(Net::getGameHandler()); + if (!gh || !mNetwork) + return; + gh->setMap(msg.readString(16)); + int x = msg.readInt16(); + int y = msg.readInt16(); + mapServer.hostname = ipToString(msg.readInt32()); + mapServer.port = msg.readInt16(); + + mNetwork->disconnect(); + Client::setState(STATE_CHANGE_MAP); + if (player_node) + { + player_node->setTileCoords(x, y); + player_node->setMap(0); + } + } + break; + + default: + break; + } +} + +void CharServerHandler::readPlayerData(Net::MessageIn &msg, + Net::Character *character) +{ + const Token &token = + static_cast(Net::getLoginHandler())->getToken(); + + LocalPlayer *tempPlayer = new LocalPlayer(msg.readInt32(), 0); + tempPlayer->setGender(token.sex); + + character->data.mAttributes[EXP] = msg.readInt32(); + character->data.mAttributes[MONEY] = msg.readInt32(); + character->data.mStats[JOB].exp = msg.readInt32(); + + int temp = msg.readInt32(); + character->data.mStats[JOB].base = temp; + character->data.mStats[JOB].mod = temp; + + tempPlayer->setSprite(SPRITE_SHOE, msg.readInt16()); + tempPlayer->setSprite(SPRITE_GLOVES, msg.readInt16()); + tempPlayer->setSprite(SPRITE_CAPE, msg.readInt16()); + tempPlayer->setSprite(SPRITE_MISC1, msg.readInt16()); + + msg.readInt32(); // option + msg.readInt32(); // karma + msg.readInt32(); // manner + msg.skip(2); // unknown + + character->data.mAttributes[HP] = msg.readInt16(); + character->data.mAttributes[MAX_HP] = msg.readInt16(); + character->data.mAttributes[MP] = msg.readInt16(); + character->data.mAttributes[MAX_MP] = msg.readInt16(); + + msg.readInt16(); // speed + tempPlayer->setSubtype(msg.readInt16()); // class (used for race) + int hairStyle = msg.readInt16(); + Uint16 weapon = msg.readInt16(); + tempPlayer->setSprite(SPRITE_WEAPON, weapon, "", true); + + character->data.mAttributes[LEVEL] = msg.readInt16(); + + msg.readInt16(); // skill point + tempPlayer->setSprite(SPRITE_BOTTOMCLOTHES, msg.readInt16()); + //to avoid show error (error.xml) need remove this sprite + if (!config.getBoolValue("hideShield")) + tempPlayer->setSprite(SPRITE_SHIELD, msg.readInt16()); + else + msg.readInt16(); + + tempPlayer->setSprite(SPRITE_HAT, msg.readInt16()); // head option top + tempPlayer->setSprite(SPRITE_TOPCLOTHES, msg.readInt16()); + tempPlayer->setSprite(SPRITE_HAIR, hairStyle * -1, + ColorDB::get(msg.readInt16())); + tempPlayer->setSprite(SPRITE_MISC2, msg.readInt16()); + tempPlayer->setName(msg.readString(24)); + + character->dummy = tempPlayer; + + for (int i = 0; i < 6; i++) + character->data.mStats[i + STR].base = msg.readInt8(); + + character->slot = msg.readInt8(); // character slot + msg.readInt8(); // unknown +} + +void CharServerHandler::setCharSelectDialog(CharSelectDialog *window) +{ + mCharSelectDialog = window; + updateCharSelectDialog(); +} + +void CharServerHandler::setCharCreateDialog(CharCreateDialog *window) +{ + mCharCreateDialog = window; + + if (!mCharCreateDialog) + return; + + std::vector attributes; + attributes.push_back(_("Strength:")); + attributes.push_back(_("Agility:")); + attributes.push_back(_("Vitality:")); + attributes.push_back(_("Intelligence:")); + attributes.push_back(_("Dexterity:")); + attributes.push_back(_("Luck:")); + + const Token &token = + static_cast(Net::getLoginHandler())->getToken(); + + mCharCreateDialog->setAttributes(attributes, 30, 1, 9); + mCharCreateDialog->setFixedGender(true, token.sex); +} + +void CharServerHandler::requestCharacters() +{ + connect(); +} + +void CharServerHandler::chooseCharacter(Net::Character *character) +{ + mSelectedCharacter = character; + mCharSelectDialog = 0; + + MessageOut outMsg(CMSG_CHAR_SELECT); + outMsg.writeInt8(static_cast(mSelectedCharacter->slot)); +} + +void CharServerHandler::newCharacter(const std::string &name, int slot, + bool gender _UNUSED_, int hairstyle, + int hairColor, + const std::vector &stats) +{ + MessageOut outMsg(CMSG_CHAR_CREATE); + outMsg.writeString(name, 24); + for (int i = 0; i < 6; i++) + outMsg.writeInt8(static_cast(stats[i])); + + outMsg.writeInt8(static_cast(slot)); + outMsg.writeInt16(static_cast(hairColor)); + outMsg.writeInt16(static_cast(hairstyle)); +} + +void CharServerHandler::deleteCharacter(Net::Character *character) +{ + if (!character) + return; + + mSelectedCharacter = character; + + MessageOut outMsg(CMSG_CHAR_DELETE); + outMsg.writeInt32(mSelectedCharacter->dummy->getId()); + outMsg.writeString("a@a.com", 40); +} + +void CharServerHandler::switchCharacter() +{ + // This is really a map-server packet + MessageOut outMsg(CMSG_PLAYER_RESTART); + outMsg.writeInt8(1); +} + +unsigned int CharServerHandler::baseSprite() const +{ + return SPRITE_BASE; +} + +unsigned int CharServerHandler::hairSprite() const +{ + return SPRITE_HAIR; +} + +unsigned int CharServerHandler::maxSprite() const +{ + return SPRITE_VECTOREND; +} + +void CharServerHandler::connect() +{ + const Token &token = + static_cast(Net::getLoginHandler())->getToken(); + + if (!mNetwork) + return; + + mNetwork->disconnect(); + mNetwork->connect(charServer); + MessageOut outMsg(CMSG_CHAR_SERVER_CONNECT); + outMsg.writeInt32(token.account_ID); + outMsg.writeInt32(token.session_ID1); + outMsg.writeInt32(token.session_ID2); + // [Fate] The next word is unused by the old char server, so we squeeze in + // mana client version information + outMsg.writeInt16(CLIENT_PROTOCOL_VERSION); + outMsg.writeInt8((token.sex == GENDER_MALE) ? 1 : 0); + + // We get 4 useless bytes before the real answer comes in (what are these?) + mNetwork->skip(4); +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/charserverhandler.h b/src/net/tmwa/charserverhandler.h new file mode 100644 index 000000000..d6ce0a2dc --- /dev/null +++ b/src/net/tmwa/charserverhandler.h @@ -0,0 +1,87 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_CHARSERVERHANDLER_H +#define NET_TA_CHARSERVERHANDLER_H + +#include "net/charhandler.h" +#include "net/serverinfo.h" + +#include "net/tmwa/messagehandler.h" +#include "net/tmwa/token.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +class LoginData; + +namespace TmwAthena +{ + +/** + * Deals with incoming messages from the character server. + */ +class CharServerHandler : public MessageHandler, public Net::CharHandler +{ + public: + CharServerHandler(); + + virtual void handleMessage(Net::MessageIn &msg); + + void setCharSelectDialog(CharSelectDialog *window); + + /** + * 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); + + void requestCharacters(); + + void chooseCharacter(Net::Character *character); + + void newCharacter(const std::string &name, int slot, + bool gender, int hairstyle, int hairColor, + const std::vector &stats); + + void deleteCharacter(Net::Character *character); + + void switchCharacter(); + + unsigned int baseSprite() const; + + unsigned int hairSprite() const; + + unsigned int maxSprite() const; + + void connect(); + + private: + void readPlayerData(Net::MessageIn &msg, Net::Character *character); +}; + +} // namespace TmwAthena + +#endif // NET_TA_CHARSERVERHANDLER_H diff --git a/src/net/tmwa/chathandler.cpp b/src/net/tmwa/chathandler.cpp new file mode 100644 index 000000000..4dcfb2b34 --- /dev/null +++ b/src/net/tmwa/chathandler.cpp @@ -0,0 +1,552 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/chathandler.h" + +#include "actorspritemanager.h" +#include "being.h" +#include "configuration.h" +#include "event.h" +#include "game.h" +#include "localplayer.h" +#include "playerrelations.h" +#include "log.h" + +#include "gui/chat.h" +#include "gui/shopwindow.h" + +#include "gui/widgets/chattab.h" + +#include "net/messagein.h" +#include "net/messageout.h" + +#include "net/tmwa/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include + +extern Net::ChatHandler *chatHandler; + +namespace TmwAthena +{ + +ChatHandler::ChatHandler() +{ + static const Uint16 _messages[] = + { + SMSG_BEING_CHAT, + SMSG_PLAYER_CHAT, + SMSG_WHISPER, + SMSG_WHISPER_RESPONSE, + SMSG_GM_CHAT, + SMSG_MVP, // MVP + 0 + }; + handledMessages = _messages; + chatHandler = this; +} + +void ChatHandler::handleMessage(Net::MessageIn &msg) +{ + if (!localChatTab) + return; + + Being *being; + std::string chatMsg; + std::string nick; + int chatMsgLength; + + switch (msg.getId()) + { + case SMSG_WHISPER_RESPONSE: + { + if (mSentWhispers.empty()) + { + nick = "user"; + } + else + { + nick = mSentWhispers.front(); + mSentWhispers.pop(); + } + + int type = msg.readInt8(); + switch (type) + { + case 0x00: + // Success (don't need to report) + break; + case 0x01: + if (chatWindow) + { + chatWindow->whisper(nick, + strprintf(_("Whisper could not be " + "sent, %s is offline."), nick.c_str()), BY_SERVER); + } + break; + case 0x02: + if (chatWindow) + { + chatWindow->whisper(nick, + strprintf(_("Whisper could not " + "be sent, ignored by %s."), nick.c_str()), + BY_SERVER); + } + break; + default: + if (logger) + { + logger->log("QQQ SMSG_WHISPER_RESPONSE:" + + toString(type)); + } + } + 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") + { + if (player_relations.hasPermission( + nick, PlayerRelation::WHISPER)) + { + bool tradeBot = config.getBoolValue("tradebot"); + bool showMsg = !config.getBoolValue("hideShopMessages"); + if (player_relations.hasPermission( + nick, PlayerRelation::TRADE)) + { + if (shopWindow) + { //commands to shop from player + if (chatMsg.find("!selllist ") == 0) + { + if (tradeBot) + { + if (showMsg && chatWindow) + chatWindow->whisper(nick, chatMsg); + shopWindow->giveList(nick, + ShopWindow::SELL); + } + } + else if (chatMsg.find("!buylist ") == 0) + { + if (tradeBot) + { + if (showMsg && chatWindow) + chatWindow->whisper(nick, chatMsg); + shopWindow->giveList(nick, + ShopWindow::BUY); + } + } + else if (chatMsg.find("!buyitem ") == 0) + { + if (showMsg && chatWindow) + chatWindow->whisper(nick, chatMsg); + if (tradeBot) + { + shopWindow->processRequest(nick, chatMsg, + ShopWindow::BUY); + } + } + else if (chatMsg.find("!sellitem ") == 0) + { + if (showMsg && chatWindow) + chatWindow->whisper(nick, chatMsg); + if (tradeBot) + { + shopWindow->processRequest(nick, chatMsg, + ShopWindow::SELL); + } + } + else if (chatMsg.length() > 3 + && chatMsg.find("\302\202") == 0) + { + chatMsg = chatMsg.erase(0, 2); + if (showMsg && chatWindow) + chatWindow->whisper(nick, chatMsg); + if (chatMsg.find("B1") == 0 + || chatMsg.find("S1") == 0) + { + shopWindow->showList(nick, chatMsg); + } + } + else if (chatWindow) + { + chatWindow->whisper(nick, chatMsg); + } + } + else if (chatWindow) + { + chatWindow->whisper(nick, chatMsg); + } + } + else + { + if (chatWindow && (showMsg + || (chatMsg.find("!selllist") + != 0 && chatMsg.find("!buylist") != 0))) + { + chatWindow->whisper(nick, chatMsg); + } + } + } + } + else if (localChatTab) + { + localChatTab->chatLog(chatMsg, BY_SERVER); + } + + break; + } + + // Received speech from being + case SMSG_BEING_CHAT: + { + if (!actorSpriteManager) + return; + + chatMsgLength = msg.readInt16() - 8; + being = actorSpriteManager->findBeing(msg.readInt32()); + + if (!being || chatMsgLength <= 0) + break; + + std::string str2; + chatMsg = msg.readRawString(chatMsgLength); + + if (being->getType() == Being::PLAYER) + being->setTalkTime(); + + std::string::size_type pos = chatMsg.find(" : ", 0); + std::string sender_name = ((pos == std::string::npos) + ? "" : chatMsg.substr(0, pos)); + + if (sender_name != being->getName() + && being->getType() == Being::PLAYER) + { + if (!being->getName().empty()) + sender_name = being->getName(); + } + else + { + chatMsg.erase(0, pos + 3); + } + + trim(chatMsg); + + // 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) + { + chatWindow->resortChatLog(removeColors(sender_name) + " : " + + chatMsg, BY_OTHER); + } + + 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; + + std::string str2; + chatMsg = msg.readRawString(chatMsgLength); + std::string::size_type pos = chatMsg.find(" : ", 0); + + if (msg.getId() == SMSG_PLAYER_CHAT) + { + if (chatWindow) + chatWindow->resortChatLog(chatMsg, BY_PLAYER); +// if (localChatTab) +// localChatTab->chatLog(chatMsg, BY_PLAYER); + + const std::string senseStr = "You sense the following: "; + if (actorSpriteManager && !chatMsg.find(senseStr)) + { + actorSpriteManager->parseLevels( + chatMsg.substr(senseStr.size())); + } + + if (pos != std::string::npos) + chatMsg.erase(0, pos + 3); + + trim(chatMsg); + + if (player_node) + player_node->setSpeech(chatMsg, SPEECH_TIME); + } + else if (localChatTab) + { + localChatTab->chatLog(chatMsg, BY_GM); + } + break; + } + + case SMSG_MVP: + { + // Display MVP player + int id = msg.readInt32(); // id + if (localChatTab && actorSpriteManager) + { + being = actorSpriteManager->findBeing(id); + if (!being) + { + localChatTab->chatLog(_("MVP player."), BY_SERVER); + } + else + { + localChatTab->chatLog(_("MVP player: ") + + being->getName(), BY_SERVER); + } + } + break; + } + + default: + break; + } +} + +void ChatHandler::talk(const std::string &text) +{ + if (!player_node) + return; + + std::string mes = player_node->getName() + " : " + text; +// std::string mes = player_node->getName() + "zzzz : " + text; + + MessageOut outMsg(CMSG_CHAT_MESSAGE); + // Added + 1 in order to let eAthena parse admin commands correctly + outMsg.writeInt16(static_cast(mes.length() + 4 + 1)); + outMsg.writeString(mes, static_cast(mes.length() + 1)); +} + +void ChatHandler::talkRaw(const std::string &mes) +{ + MessageOut outMsg(CMSG_CHAT_MESSAGE); + // Added + 1 in order to let eAthena parse admin commands correctly + outMsg.writeInt16(static_cast(mes.length() + 4 + 1)); + outMsg.writeString(mes, static_cast(mes.length() + 1)); +} + +void ChatHandler::me(const std::string &text) +{ + std::string action = strprintf("*%s*", text.c_str()); + + talk(action); +} + +void ChatHandler::privateMessage(const std::string &recipient, + const std::string &text) +{ + MessageOut outMsg(CMSG_CHAT_WHISPER); + outMsg.writeInt16(static_cast(text.length() + 28)); + outMsg.writeString(recipient, 24); + outMsg.writeString(text, static_cast(text.length())); + mSentWhispers.push(recipient); +} + +void ChatHandler::channelList() +{ + SERVER_NOTICE(_("Channels are not supported!")) +} + +void ChatHandler::enterChannel(const std::string &channel _UNUSED_, + const std::string &password _UNUSED_) +{ + SERVER_NOTICE(_("Channels are not supported!")) +} + +void ChatHandler::quitChannel(int channelId _UNUSED_) +{ + SERVER_NOTICE(_("Channels are not supported!")) +} + +void ChatHandler::sendToChannel(int channelId _UNUSED_, + const std::string &text _UNUSED_) +{ + SERVER_NOTICE(_("Channels are not supported!")) +} + +void ChatHandler::userList(const std::string &channel _UNUSED_) +{ + SERVER_NOTICE(_("Channels are not supported!")) +} + +void ChatHandler::setChannelTopic(int channelId _UNUSED_, + const std::string &text _UNUSED_) +{ + SERVER_NOTICE(_("Channels are not supported!")) +} + +void ChatHandler::setUserMode(int channelId _UNUSED_, + const std::string &name _UNUSED_, + int mode _UNUSED_) +{ + SERVER_NOTICE(_("Channels are not supported!")) +} + +void ChatHandler::kickUser(int channelId _UNUSED_, + const std::string &name _UNUSED_) +{ + SERVER_NOTICE(_("Channels are not supported!")) +} + +void ChatHandler::who() +{ + MessageOut outMsg(CMSG_WHO_REQUEST); +} + +void ChatHandler::sendRaw(const std::string &args) +{ + std::string line = args; + std::string str; + MessageOut *outMsg = 0; + + if (line == "") + return; + + std::string::size_type pos = line.find(" "); + if (pos != std::string::npos) + { + str = line.substr(0, pos); + outMsg = new MessageOut(static_cast(atoi(str.c_str()))); + line = line.substr(pos + 1); + pos = line.find(" "); + } + else + { + outMsg = new MessageOut(static_cast(atoi(line.c_str()))); + delete outMsg; + return; + } + + while (pos != std::string::npos) + { + str = line.substr(0, pos); + processRaw(*outMsg, str); + line = line.substr(pos + 1); + pos = line.find(" "); + } + if (outMsg) + { + if (line != "") + processRaw(*outMsg, line); + delete outMsg; + } +} + +void ChatHandler::processRaw(MessageOut &outMsg, std::string &line) +{ + std::string::size_type pos = line.find(":"); + if (pos == std::string::npos) + { + int i = atoi(line.c_str()); + if (line.length() <= 3) + outMsg.writeInt8(static_cast(i)); + else if (line.length() <= 5) + outMsg.writeInt16(static_cast(i)); + else + outMsg.writeInt32(i); + } + else + { + std::string header = line.substr(0, pos); + std::string data = line.substr(pos + 1); + if (header.length() != 1) + return; + + int i = 0; + + switch (header[0]) + { + case '1': + case '2': + case '4': + i = atoi(data.c_str()); + break; + default: + break; + } + + switch (header[0]) + { + case '1': + outMsg.writeInt8(static_cast(i)); + break; + case '2': + outMsg.writeInt16(static_cast(i)); + break; + case '4': + outMsg.writeInt32(i); + break; + case 'c': + { + pos = line.find(","); + if (pos != std::string::npos) + { + unsigned short x = static_cast( + atoi(data.substr(0, pos).c_str())); + data = data.substr(pos + 1); + pos = line.find(","); + if (pos == std::string::npos) + break; + + unsigned short y = static_cast( + atoi(data.substr(0, pos).c_str())); + int dir = atoi(data.substr(pos + 1).c_str()); + outMsg.writeCoordinates(x, y, + static_cast(dir)); + } + break; + } + case 't': + outMsg.writeString(data, static_cast(data.length())); + break; + default: + break; + } + } +} + +} // namespace TmwAthena + diff --git a/src/net/tmwa/chathandler.h b/src/net/tmwa/chathandler.h new file mode 100644 index 000000000..1432da3a2 --- /dev/null +++ b/src/net/tmwa/chathandler.h @@ -0,0 +1,87 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_CHATHANDLER_H +#define NET_TA_CHATHANDLER_H + +#include "net/chathandler.h" +#include "net/net.h" + +#include "net/tmwa/messagehandler.h" + +#include + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace TmwAthena +{ + +class ChatHandler : public MessageHandler, public Net::ChatHandler +{ + public: + ChatHandler(); + + void handleMessage(Net::MessageIn &msg); + + void talk(const std::string &text); + + void talkRaw(const std::string &text); + + void me(const std::string &text); + + void privateMessage(const std::string &recipient, + const std::string &text); + + void channelList(); + + void enterChannel(const std::string &channel, + const std::string &password); + + void quitChannel(int channelId); + + void sendToChannel(int channelId, const std::string &text); + + void userList(const std::string &channel); + + void setChannelTopic(int channelId, const std::string &text); + + void setUserMode(int channelId, const std::string &name, int mode); + + void kickUser(int channelId, const std::string &name); + + void who(); + + void sendRaw(const std::string &args); + + void processRaw(MessageOut &outMsg, std::string &line); + + private: + typedef std::queue WhisperQueue; + WhisperQueue mSentWhispers; +}; + +} // namespace TmwAthena + +#endif // NET_TA_CHATHANDLER_H diff --git a/src/net/tmwa/gamehandler.cpp b/src/net/tmwa/gamehandler.cpp new file mode 100644 index 000000000..5f949ce6e --- /dev/null +++ b/src/net/tmwa/gamehandler.cpp @@ -0,0 +1,196 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/gamehandler.h" + +#include "client.h" +#include "event.h" +#include "game.h" +#include "localplayer.h" +#include "log.h" + +#include "gui/okdialog.h" + +#include "net/messagein.h" +#include "net/messageout.h" + +#include "net/tmwa/loginhandler.h" +#include "net/tmwa/network.h" +#include "net/tmwa/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +extern Net::GameHandler *gameHandler; + +namespace TmwAthena +{ + +extern ServerInfo mapServer; + +GameHandler::GameHandler() +{ + static const Uint16 _messages[] = + { + SMSG_MAP_LOGIN_SUCCESS, + SMSG_SERVER_PING, + SMSG_WHO_ANSWER, + SMSG_CHAR_SWITCH_RESPONSE, + SMSG_MAP_QUIT_RESPONSE, + 0 + }; + handledMessages = _messages; + gameHandler = this; + + listen(CHANNEL_GAME); +} + +void GameHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case SMSG_MAP_LOGIN_SUCCESS: + { + unsigned char direction; + Uint16 x, y; + msg.readInt32(); // server tick + msg.readCoordinates(x, y, direction); + msg.skip(2); // unknown + logger->log("Protocol: Player start position: (%d, %d)," + " Direction: %d", x, y, direction); + // Switch now or we'll have problems + Client::setState(STATE_GAME); + if (player_node) + player_node->setTileCoords(x, y); + break; + } + + case SMSG_SERVER_PING: + // We ignore this for now + // int tick = msg.readInt32() + break; + + case SMSG_WHO_ANSWER: + SERVER_NOTICE(strprintf(_("Online users: %d"), msg.readInt32())) + break; + + case SMSG_CHAR_SWITCH_RESPONSE: + if (msg.readInt8()) + Client::setState(STATE_SWITCH_CHARACTER); + break; + + case SMSG_MAP_QUIT_RESPONSE: + if (msg.readInt8()) + new OkDialog(_("Game"), _("Request to quit denied!"), NULL); + break; + + default: + break; + } +} + +void GameHandler::event(Channels channel, const Mana::Event &event) +{ + if (channel == CHANNEL_GAME) + { + if (event.getName() == EVENT_ENGINESINITALIZED) + Game::instance()->changeMap(mMap); + else if (event.getName() == EVENT_MAPLOADED) + MessageOut outMsg(CMSG_MAP_LOADED); + } +} + +void GameHandler::connect() +{ + if (!mNetwork) + return; + + mNetwork->connect(mapServer); + + const Token &token = + static_cast(Net::getLoginHandler())->getToken(); + + + if (Client::getState() == STATE_CONNECT_GAME) + { + mCharID = player_node->getId(); + // Change the player's ID to the account ID to match what eAthena uses + player_node->setId(token.account_ID); + } + + // Send login infos + MessageOut outMsg(CMSG_MAP_SERVER_CONNECT); + outMsg.writeInt32(token.account_ID); + outMsg.writeInt32(mCharID); + outMsg.writeInt32(token.session_ID1); + outMsg.writeInt32(token.session_ID2); + outMsg.writeInt8((token.sex == GENDER_MALE) ? 1 : 0); + +/* + if (player_node) + { + // Change the player's ID to the account ID to match what eAthena uses + player_node->setId(token.account_ID); + } +*/ + // We get 4 useless bytes before the real answer comes in (what are these?) + mNetwork->skip(4); +} + +bool GameHandler::isConnected() +{ + if (!mNetwork) + return false; + return mNetwork->isConnected(); +} + +void GameHandler::disconnect() +{ + if (mNetwork) + mNetwork->disconnect(); +} + +void GameHandler::who() +{ +} + +void GameHandler::quit() +{ + MessageOut outMsg(CMSG_CLIENT_QUIT); +} + +void GameHandler::ping(int tick) +{ + MessageOut msg(CMSG_CLIENT_PING); + msg.writeInt32(tick); +} + +void GameHandler::setMap(const std::string map) +{ + mMap = map.substr(0, map.rfind(".")); +} + +void GameHandler::disconnect2() +{ + MessageOut outMsg(CMSG_CLIENT_DISCONNECT); +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/gamehandler.h b/src/net/tmwa/gamehandler.h new file mode 100644 index 000000000..d59320bfa --- /dev/null +++ b/src/net/tmwa/gamehandler.h @@ -0,0 +1,85 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_MAPHANDLER_H +#define NET_TA_MAPHANDLER_H + +#include "listener.h" + +#include "net/gamehandler.h" +#include "net/net.h" +#include "net/serverinfo.h" + +#include "net/tmwa/messagehandler.h" +#include "net/tmwa/token.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace TmwAthena +{ + +class GameHandler : public MessageHandler, public Net::GameHandler, + public Mana::Listener +{ + public: + GameHandler(); + + void handleMessage(Net::MessageIn &msg); + + void event(Channels channel, const Mana::Event &event); + + void connect(); + + bool isConnected(); + + void disconnect(); + + void who(); + + void quit(); + + void ping(int tick); + + bool removeDeadBeings() const + { return true; } + + void clear(); + + void setMap(const std::string map); + + /** The tmwAthena protocol is making use of the MP status bar. */ + bool canUseMagicBar() const + { return true; } + + void disconnect2(); + + private: + std::string mMap; + int mCharID; /// < Saved for map-server switching +}; + +} // namespace TmwAthena + +#endif // NET_TA_MAPHANDLER_H diff --git a/src/net/tmwa/generalhandler.cpp b/src/net/tmwa/generalhandler.cpp new file mode 100644 index 000000000..83d8ceb8d --- /dev/null +++ b/src/net/tmwa/generalhandler.cpp @@ -0,0 +1,285 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/generalhandler.h" + +#include "client.h" +#include "configuration.h" +#include "log.h" + +#include "gui/charselectdialog.h" +#include "gui/inventorywindow.h" +#include "gui/register.h" +#include "gui/skilldialog.h" +#include "gui/socialwindow.h" +#include "gui/statuswindow.h" + +#include "net/messagein.h" +#include "net/messageout.h" +#include "net/serverinfo.h" + +#include "net/tmwa/adminhandler.h" +#include "net/tmwa/beinghandler.h" +#include "net/tmwa/buysellhandler.h" +#include "net/tmwa/chathandler.h" +#include "net/tmwa/charserverhandler.h" +#include "net/tmwa/gamehandler.h" +#include "net/tmwa/guildhandler.h" +#include "net/tmwa/inventoryhandler.h" +#include "net/tmwa/itemhandler.h" +#include "net/tmwa/loginhandler.h" +#include "net/tmwa/network.h" +#include "net/tmwa/npchandler.h" +#include "net/tmwa/partyhandler.h" +#include "net/tmwa/playerhandler.h" +#include "net/tmwa/protocol.h" +#include "net/tmwa/tradehandler.h" +#include "net/tmwa/specialhandler.h" + +#include "net/tmwa/gui/guildtab.h" +#include "net/tmwa/gui/partytab.h" + +#include "resources/itemdb.h" + +#include "utils/gettext.h" + +#include +#include + +extern Net::GeneralHandler *generalHandler; + +namespace TmwAthena +{ + +ServerInfo charServer; +ServerInfo mapServer; + +extern Guild *taGuild; +extern Party *taParty; + +GeneralHandler::GeneralHandler(): + mAdminHandler(new AdminHandler), + mBeingHandler(new BeingHandler(config.getBoolValue("EnableSync"))), + mBuySellHandler(new BuySellHandler), + mCharHandler(new CharServerHandler), + mChatHandler(new ChatHandler), + mGameHandler(new GameHandler), + mGuildHandler(new GuildHandler), + mInventoryHandler(new InventoryHandler), + mItemHandler(new ItemHandler), + mLoginHandler(new LoginHandler), + mNpcHandler(new NpcHandler), + mPartyHandler(new PartyHandler), + mPlayerHandler(new PlayerHandler), + mSpecialHandler(new SpecialHandler), + mTradeHandler(new TradeHandler) +{ + static const Uint16 _messages[] = + { + SMSG_CONNECTION_PROBLEM, + 0 + }; + handledMessages = _messages; + generalHandler = this; + + std::list stats; + stats.push_back(ItemDB::Stat("str", _("Strength %+d"))); + stats.push_back(ItemDB::Stat("agi", _("Agility %+d"))); + stats.push_back(ItemDB::Stat("vit", _("Vitality %+d"))); + stats.push_back(ItemDB::Stat("int", _("Intelligence %+d"))); + stats.push_back(ItemDB::Stat("dex", _("Dexterity %+d"))); + stats.push_back(ItemDB::Stat("luck", _("Luck %+d"))); + + ItemDB::setStatsList(stats); + + listen(CHANNEL_GAME); +} + +GeneralHandler::~GeneralHandler() +{ + delete mNetwork; + mNetwork = 0; +} + +void GeneralHandler::handleMessage(Net::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: + if (Client::getState() == STATE_GAME) + errorMessage = _("Someone else is trying to use this " + "account."); + else + 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; + } + Client::setState(STATE_ERROR); + break; + + default: + break; + } +} + +void GeneralHandler::load() +{ + (new Network)->registerHandler(this); + + if (!mNetwork) + return; + + mNetwork->registerHandler(mAdminHandler.get()); + mNetwork->registerHandler(mBeingHandler.get()); + mNetwork->registerHandler(mBuySellHandler.get()); + mNetwork->registerHandler(mChatHandler.get()); + mNetwork->registerHandler(mCharHandler.get()); + mNetwork->registerHandler(mGameHandler.get()); + mNetwork->registerHandler(mGuildHandler.get()); + mNetwork->registerHandler(mInventoryHandler.get()); + mNetwork->registerHandler(mItemHandler.get()); + mNetwork->registerHandler(mLoginHandler.get()); + mNetwork->registerHandler(mNpcHandler.get()); + mNetwork->registerHandler(mPlayerHandler.get()); + mNetwork->registerHandler(mSpecialHandler.get()); + mNetwork->registerHandler(mTradeHandler.get()); + mNetwork->registerHandler(mPartyHandler.get()); +} + +void GeneralHandler::reload() +{ + if (mNetwork) + mNetwork->disconnect(); + + static_cast(mLoginHandler.get())->clearWorlds(); + static_cast( + mCharHandler.get())->setCharCreateDialog(0); + static_cast( + mCharHandler.get())->setCharSelectDialog(0); +} + +void GeneralHandler::unload() +{ + if (mNetwork) + mNetwork->clearHandlers(); +} + +void GeneralHandler::flushNetwork() +{ + if (!mNetwork) + return; + + mNetwork->flush(); + mNetwork->dispatchMessages(); + + if (mNetwork->getState() == Network::NET_ERROR) + { + if (!mNetwork->getError().empty()) + errorMessage = mNetwork->getError(); + else + errorMessage = _("Got disconnected from server!"); + + Client::setState(STATE_ERROR); + } +} + +void GeneralHandler::clearHandlers() +{ + if (mNetwork) + mNetwork->clearHandlers(); +} + +void GeneralHandler::event(Channels channel, + const Mana::Event &event) +{ + if (channel == CHANNEL_GAME) + { + if (event.getName() == EVENT_GUIWINDOWSLOADED) + { + if (inventoryWindow) + inventoryWindow->setSplitAllowed(false); + if (skillDialog) + skillDialog->loadSkills("ea-skills.xml"); + + if (!statusWindow) + return; + + statusWindow->addAttribute(STR, _("Strength"), true, ""); + statusWindow->addAttribute(AGI, _("Agility"), true, ""); + statusWindow->addAttribute(VIT, _("Vitality"), true, ""); + statusWindow->addAttribute(INT, _("Intelligence"), true, ""); + statusWindow->addAttribute(DEX, _("Dexterity"), true, ""); + statusWindow->addAttribute(LUK, _("Luck"), true, ""); + + statusWindow->addAttribute(ATK, _("Attack"), false, ""); + statusWindow->addAttribute(DEF, _("Defense"), false, ""); + statusWindow->addAttribute(MATK, _("M.Attack"), false, ""); + statusWindow->addAttribute(MDEF, _("M.Defense"), false, ""); + statusWindow->addAttribute(HIT, _("% Accuracy"), false, ""); + statusWindow->addAttribute(FLEE, _("% Evade"), false, ""); + statusWindow->addAttribute(CRIT, _("% Critical"), false, ""); + statusWindow->addAttribute(ATTACK_SPEED, _("Attack Delay"), + false, ""); + statusWindow->addAttribute(WALK_SPEED, _("Walk Delay"), + false, ""); + statusWindow->addAttribute(ATTACK_RANGE, _("Attack Range"), + false, ""); + } + else if (event.getName() == EVENT_GUIWINDOWSUNLOADING) + { + if (socialWindow) + { + socialWindow->removeTab(taGuild); + socialWindow->removeTab(taParty); + } + + delete guildTab; + guildTab = 0; + + delete partyTab; + partyTab = 0; + } + } +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/generalhandler.h b/src/net/tmwa/generalhandler.h new file mode 100644 index 000000000..eded556d0 --- /dev/null +++ b/src/net/tmwa/generalhandler.h @@ -0,0 +1,83 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TMWA_GENERALHANDLER_H +#define NET_TMWA_GENERALHANDLER_H + +#include "listener.h" + +#include "net/generalhandler.h" +#include "net/net.h" + +#include "net/tmwa/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace TmwAthena +{ + +class GeneralHandler : public MessageHandler, public Net::GeneralHandler, + public Mana::Listener +{ + public: + GeneralHandler(); + + ~GeneralHandler(); + + void handleMessage(Net::MessageIn &msg); + + void load(); + + void reload(); + + void unload(); + + void flushNetwork(); + + void clearHandlers(); + + void event(Channels channel, const Mana::Event &event); + + protected: + MessageHandlerPtr mAdminHandler; + MessageHandlerPtr mBeingHandler; + MessageHandlerPtr mBuySellHandler; + MessageHandlerPtr mCharHandler; + MessageHandlerPtr mChatHandler; + MessageHandlerPtr mGameHandler; + MessageHandlerPtr mGuildHandler; + MessageHandlerPtr mInventoryHandler; + MessageHandlerPtr mItemHandler; + MessageHandlerPtr mLoginHandler; + MessageHandlerPtr mNpcHandler; + MessageHandlerPtr mPartyHandler; + MessageHandlerPtr mPlayerHandler; + MessageHandlerPtr mSpecialHandler; + MessageHandlerPtr mTradeHandler; +}; + +} // namespace TmwAthena + +#endif // NET_TA_GENERALHANDLER_H diff --git a/src/net/tmwa/gui/guildtab.cpp b/src/net/tmwa/gui/guildtab.cpp new file mode 100644 index 000000000..4b080277c --- /dev/null +++ b/src/net/tmwa/gui/guildtab.cpp @@ -0,0 +1,150 @@ +/* + * The Mana Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/gui/guildtab.h" + +#include "chatlog.h" +#include "commandhandler.h" +#include "guild.h" +#include "localplayer.h" + +#include "gui/theme.h" + +#include "net/net.h" +#include "net/guildhandler.h" + +#include "resources/iteminfo.h" +#include "resources/itemdb.h" + +#include "utils/dtor.h" +#include "utils/gettext.h" +#include "utils/stringutils.h" + +namespace TmwAthena +{ + +extern Guild *taGuild; + +GuildTab::GuildTab() : + ChatTab(_("Guild")) +{ + setTabColor(&Theme::getThemeColor(Theme::GUILD_CHAT_TAB)); +} + +GuildTab::~GuildTab() +{ +} + +void GuildTab::handleInput(const std::string &msg) +{ + if (!taGuild) + return; + + if (chatWindow) + { + Net::getGuildHandler()->chat(taGuild->getId(), + chatWindow->doReplace(msg)); + } + else + { + Net::getGuildHandler()->chat(taGuild->getId(), msg); + } +} + +void GuildTab::showHelp() +{ + chatLog(_("/help > Display this help.")); + chatLog(_("/invite > Invite a player to your guild")); + chatLog(_("/leave > Leave the guild you are in")); + chatLog(_("/kick > Kick some one from the guild you are in")); +} + +bool GuildTab::handleCommand(const std::string &type, const std::string &args) +{ + if (type == "help") + { + if (args == "invite") + { + chatLog(_("Command: /invite ")); + chatLog(_("This command invites to the guild you're in.")); + chatLog(_("If the has spaces in it, enclose it in " + "double quotes (\").")); + } + else if (args == "leave") + { + chatLog(_("Command: /leave")); + chatLog(_("This command causes the player to leave the guild.")); + } + else + return false; + } +/* + else if (type == "create" || type == "new") + { + if (args.empty()) + chatLog(_("Guild name is missing."), BY_SERVER); + else + Net::getGuildHandler()->create(args); + } +*/ + else if (type == "invite" && taGuild) + { + Net::getGuildHandler()->invite(taGuild->getId(), args); + } + else if (type == "leave" && taGuild) + { + Net::getGuildHandler()->leave(taGuild->getId()); + } + else if (type == "kick" && taGuild) + { + Net::getGuildHandler()->kick(taGuild->getMember(args)); + } + else if (type == "notice" && taGuild) + { + std::string str1 = args.substr(0, 60); + std::string str2 = ""; + if (args.size() > 60) + str2 = args.substr(60); + Net::getGuildHandler()->changeNotice(taGuild->getId(), str1, str2); + } + else + { + return false; + } + + return true; +} + +void GuildTab::getAutoCompleteList(std::vector &names) const +{ + if (taGuild) + taGuild->getNames(names); + names.push_back("/notice "); +} + +void GuildTab::saveToLogFile(std::string &msg) +{ + if (chatLogger) + chatLogger->log("#Guild", msg); +} + +} // namespace TmwAthena + diff --git a/src/net/tmwa/gui/guildtab.h b/src/net/tmwa/gui/guildtab.h new file mode 100644 index 000000000..cce9eb596 --- /dev/null +++ b/src/net/tmwa/gui/guildtab.h @@ -0,0 +1,57 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TA_GUILDTAB_H +#define TA_GUILDTAB_H + +#include "gui/widgets/chattab.h" + +namespace TmwAthena +{ + +/** + * A tab for a guild chat channel. + */ +class GuildTab : public ChatTab +{ + public: + GuildTab(); + ~GuildTab(); + + void showHelp(); + + bool handleCommand(const std::string &type, const std::string &args); + + void saveToLogFile(std::string &msg); + + int getType() const { return ChatTab::TAB_GUILD; } + + protected: + void handleInput(const std::string &msg); + + void getAutoCompleteList(std::vector &names) const; +}; + +extern GuildTab *guildTab; + +} // namespace TmwAthena + +#endif // TA_GUILDTAB_H diff --git a/src/net/tmwa/gui/partytab.cpp b/src/net/tmwa/gui/partytab.cpp new file mode 100644 index 000000000..9cfb4670f --- /dev/null +++ b/src/net/tmwa/gui/partytab.cpp @@ -0,0 +1,242 @@ +/* + * The Mana Client + * Copyright (C) 2008-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/gui/partytab.h" + +#include "chatlog.h" +#include "commandhandler.h" +#include "localplayer.h" +#include "party.h" + +#include "gui/theme.h" + +#include "net/net.h" +#include "net/partyhandler.h" + +#include "resources/iteminfo.h" +#include "resources/itemdb.h" + +#include "utils/dtor.h" +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include "net/chathandler.h" + +namespace TmwAthena +{ + +PartyTab::PartyTab() : + ChatTab(_("Party")) +{ + setTabColor(&Theme::getThemeColor(Theme::PARTY_CHAT_TAB)); +} + +PartyTab::~PartyTab() +{ +} + +void PartyTab::handleInput(const std::string &msg) +{ + if (chatWindow) + Net::getPartyHandler()->chat(chatWindow->doReplace(msg)); + else + Net::getPartyHandler()->chat(msg); +} + +void PartyTab::showHelp() +{ + chatLog(_("/help > Display this help.")); + chatLog(_("/invite > Invite a player to your party")); + chatLog(_("/leave > Leave the party you are in")); + chatLog(_("/kick > Kick some one from the party you are in")); + chatLog(_("/item > Show/change party item sharing options")); + chatLog(_("/exp > Show/change party experience sharing options")); +} + +bool PartyTab::handleCommand(const std::string &type, const std::string &args) +{ + if (type == "help") + { + if (args == "invite") + { + chatLog(_("Command: /invite ")); + chatLog(_("This command invites to party with you.")); + chatLog(_("If the has spaces in it, enclose it in " + "double quotes (\").")); + } + else if (args == "leave") + { + chatLog(_("Command: /leave")); + chatLog(_("This command causes the player to leave the party.")); + } + else if (args == "item") + { + chatLog(_("Command: /item ")); + chatLog( + _("This command changes the party's item sharing policy.")); + chatLog(_(" can be one of \"1\", \"yes\", \"true\" to " + "enable item sharing, or \"0\", \"no\", \"false\" to " + "disable item sharing.")); + chatLog(_("Command: /item")); + chatLog(_("This command displays the party's" + " current item sharing policy.")); + } + else if (args == "exp") + { + chatLog(_("Command: /exp ")); + chatLog(_("This command changes the party's " + "experience sharing policy.")); + chatLog(_(" can be one of \"1\", \"yes\", \"true\" to " + "enable experience sharing, or \"0\"," + " \"no\", \"false\" to disable experience sharing.")); + chatLog(_("Command: /exp")); + chatLog(_("This command displays the party's current " + "experience sharing policy.")); + } + else + { + return false; + } + } + else if (type == "create" || type == "new") + { + if (args.empty()) + chatLog(_("Party name is missing."), BY_SERVER); + else + Net::getPartyHandler()->create(args); + } + else if (type == "invite") + { + Net::getPartyHandler()->invite(args); + } + else if (type == "leave") + { + Net::getPartyHandler()->leave(); + } + else if (type == "kick") + { + Net::getPartyHandler()->kick(args); + } + else if (type == "item") + { + if (args.empty()) + { + switch (Net::getPartyHandler()->getShareItems()) + { + case PARTY_SHARE: + chatLog(_("Item sharing enabled."), BY_SERVER); + return true; + case PARTY_SHARE_NO: + chatLog(_("Item sharing disabled."), BY_SERVER); + return true; + case PARTY_SHARE_NOT_POSSIBLE: + chatLog(_("Item sharing not possible."), BY_SERVER); + return true; + case PARTY_SHARE_UNKNOWN: + chatLog(_("Item sharing unknown."), BY_SERVER); + return true; + default: + break; + } + } + + char opt = CommandHandler::parseBoolean(args); + + switch (opt) + { + case 1: + Net::getPartyHandler()->setShareItems(PARTY_SHARE); + break; + case 0: + Net::getPartyHandler()->setShareItems(PARTY_SHARE_NO); + break; + case -1: + chatLog(strprintf(BOOLEAN_OPTIONS, "item")); + default: + break; + } + } + else if (type == "exp") + { + if (args.empty()) + { + switch (Net::getPartyHandler()->getShareExperience()) + { + case PARTY_SHARE: + chatLog(_("Experience sharing enabled."), BY_SERVER); + return true; + case PARTY_SHARE_NO: + chatLog(_("Experience sharing disabled."), BY_SERVER); + return true; + case PARTY_SHARE_NOT_POSSIBLE: + chatLog(_("Experience sharing not possible."), BY_SERVER); + return true; + case PARTY_SHARE_UNKNOWN: + chatLog(_("Experience sharing unknown."), BY_SERVER); + return true; + default: + break; + } + } + + char opt = CommandHandler::parseBoolean(args); + + switch (opt) + { + case 1: + Net::getPartyHandler()->setShareExperience(PARTY_SHARE); + break; + case 0: + Net::getPartyHandler()->setShareExperience(PARTY_SHARE_NO); + break; + case -1: + chatLog(strprintf(BOOLEAN_OPTIONS, "exp")); + default: + break; + } + } + else + { + return false; + } + + return true; +} + +void PartyTab::getAutoCompleteList(std::vector &names) const +{ + if (!player_node) + return; + + Party *p = player_node->getParty(); + + if (p) + p->getNames(names); +} + +void PartyTab::saveToLogFile(std::string &msg) +{ + if (chatLogger) + chatLogger->log("#Party", msg); +} + +} // namespace TmwAthena + diff --git a/src/net/tmwa/gui/partytab.h b/src/net/tmwa/gui/partytab.h new file mode 100644 index 000000000..d5f4436db --- /dev/null +++ b/src/net/tmwa/gui/partytab.h @@ -0,0 +1,57 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TA_PARTYTAB_H +#define TA_PARTYTAB_H + +#include "gui/widgets/chattab.h" + +namespace TmwAthena +{ + +/** + * A tab for a party chat channel. + */ +class PartyTab : public ChatTab +{ + public: + PartyTab(); + ~PartyTab(); + + void showHelp(); + + bool handleCommand(const std::string &type, const std::string &args); + + int getType() const { return ChatTab::TAB_PARTY; } + + void saveToLogFile(std::string &msg); + + protected: + void handleInput(const std::string &msg); + + virtual void getAutoCompleteList(std::vector&) const; +}; + +extern PartyTab *partyTab; + +} // namespace TmwAthena + +#endif // TA_PARTYTAB_H diff --git a/src/net/tmwa/guildhandler.cpp b/src/net/tmwa/guildhandler.cpp new file mode 100644 index 000000000..88cd88009 --- /dev/null +++ b/src/net/tmwa/guildhandler.cpp @@ -0,0 +1,780 @@ +/* + * The Mana Client + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/guildhandler.h" + +#include "actorspritemanager.h" +#include "guild.h" +#include "event.h" +#include "localplayer.h" +#include "log.h" +#include "playerinfo.h" + +#include "gui/socialwindow.h" + +#include "net/tmwa/messagein.h" +#include "net/tmwa/protocol.h" + +#include "net/tmwa/gui/guildtab.h" + +#include "utils/gettext.h" + +extern Net::GuildHandler *guildHandler; + +namespace TmwAthena +{ + +GuildTab *guildTab = 0; +Guild *taGuild; +bool showBasicInfo(false); + +GuildHandler::GuildHandler() +{ + static const Uint16 _messages[] = + { + SMSG_GUILD_CREATE_RESPONSE, + SMSG_GUILD_POSITION_INFO, + SMSG_GUILD_MEMBER_LOGIN, + SMSG_GUILD_MASTER_OR_MEMBER, + SMSG_GUILD_BASIC_INFO, + SMSG_GUILD_ALIANCE_INFO, + SMSG_GUILD_MEMBER_LIST, + SMSG_GUILD_POS_NAME_LIST, + SMSG_GUILD_POS_INFO_LIST, + SMSG_GUILD_POSITION_CHANGED, + SMSG_GUILD_MEMBER_POS_CHANGE, + SMSG_GUILD_EMBLEM, + SMSG_GUILD_SKILL_INFO, + SMSG_GUILD_NOTICE, + SMSG_GUILD_INVITE, + SMSG_GUILD_INVITE_ACK, + SMSG_GUILD_LEAVE, + SMSG_GUILD_EXPULSION, + SMSG_GUILD_EXPULSION_LIST, + SMSG_GUILD_MESSAGE, + SMSG_GUILD_SKILL_UP, + SMSG_GUILD_REQ_ALLIANCE, + SMSG_GUILD_REQ_ALLIANCE_ACK, + SMSG_GUILD_DEL_ALLIANCE, + SMSG_GUILD_OPPOSITION_ACK, + SMSG_GUILD_BROKEN, + 0 + }; + handledMessages = _messages; + + guildHandler = this; +} + +GuildHandler::~GuildHandler() +{ + delete guildTab; + guildTab = 0; +} + +void GuildHandler::handleMessage(Net::MessageIn &msg) +{ + DEBUGLOG("guild message"); + + switch (msg.getId()) + { + case SMSG_GUILD_CREATE_RESPONSE: + { + int flag = msg.readInt8(); + + if (flag == 0) + { + // Success + SERVER_NOTICE(_("Guild created.")) + } + else if (flag == 1) + { + // Already in a guild + SERVER_NOTICE(_("You already in guild.")) + } + else if (flag == 2) + { + // Unable to make (likely name already in use) + SERVER_NOTICE(_("You already in guild.")) + } + else if (flag == 3) + { + // Emperium check failed + SERVER_NOTICE(_("Emperium check failed.")) + } + else + { + // Unknown response + SERVER_NOTICE(_("Unknown server response.")) + } + } + break; + + case SMSG_GUILD_POSITION_INFO: + { + int guildId = msg.readInt32(); + int emblem = msg.readInt32(); + int posMode = msg.readInt32(); + msg.readInt32(); // Unused + msg.readInt8(); // Unused + std::string guildName = msg.readString(24); + + Guild *g = Guild::getGuild(guildId); + if (!g) + break; + + g->setName(guildName); + g->setEmblemId(emblem); + if (!taGuild) + taGuild = g; + if (!guildTab && chatWindow) + { + guildTab = new GuildTab(); + if (player_node) + player_node->addGuild(taGuild); + memberList(guildId); + } + + if (player_node) + { + player_node->setGuild(g); + player_node->setGuildName(g->getName()); + } + + logger->log("Guild position info: %d %d %d %s\n", guildId, + emblem, posMode, guildName.c_str()); + } + break; + + case SMSG_GUILD_MEMBER_LOGIN: + { + int accountId = msg.readInt32(); // Account ID + int charId = msg.readInt32(); // Char ID + int online = msg.readInt32(); // Flag + if (taGuild) + { + GuildMember *m = taGuild->getMember(accountId, charId); + if (m) + m->setOnline(online); + } + break; + } + + case SMSG_GUILD_MASTER_OR_MEMBER: + msg.readInt32(); // Type (0x57 for member, 0xd7 for master) + break; + + case SMSG_GUILD_BASIC_INFO: + { + int guildId = msg.readInt32(); // Guild ID + int level = msg.readInt32(); // Guild level + int members = msg.readInt32(); // 'Connect member' + int maxMembers = msg.readInt32(); // 'Max member' + int avgLevel = msg.readInt32(); // Average level + int exp = msg.readInt32(); // Exp + int nextExp = msg.readInt32(); // Next exp + msg.skip(16); // unused + std::string name = msg.readString(24); // Name + std::string master = msg.readString(24); // Master's name + std::string castle = msg.readString(20); // Castles + // (ie: "Six Castles" or "None Taken") + + if (guildTab && showBasicInfo) + { + showBasicInfo = false; + guildTab->chatLog(strprintf( + _("Guild name: %s"), name.c_str()), BY_SERVER); + guildTab->chatLog(strprintf( + _("Guild master: %s"), master.c_str()), BY_SERVER); + guildTab->chatLog(strprintf( + _("Guild level: %d"), level), BY_SERVER); + guildTab->chatLog(strprintf( + _("Online members: %d"), members), BY_SERVER); + guildTab->chatLog(strprintf( + _("Max members: %d"), maxMembers), BY_SERVER); + guildTab->chatLog(strprintf( + _("Average level: %d"), avgLevel), BY_SERVER); + guildTab->chatLog(strprintf( + _("Guild exp: %d"), exp), BY_SERVER); + guildTab->chatLog(strprintf( + _("Guild next exp: %d"), nextExp), BY_SERVER); + guildTab->chatLog(strprintf( + _("Guild castle: %s"), castle.c_str()), BY_SERVER); + } + + Guild *g = Guild::getGuild(guildId); + if (!g) + break; + g->setName(name); + } + break; + + case SMSG_GUILD_ALIANCE_INFO: + { + int length = msg.readInt16(); + int count = (length - 4) / 32; + + for (int i = 0; i < count; i++) + { + msg.readInt32(); // 'Opposition' + msg.readInt32(); // Other guild ID + msg.readString(24); // Other guild name + } + } + break; + + case SMSG_GUILD_MEMBER_LIST: + { + int length = msg.readInt16(); + int count = (length - 4) / 104; + if (!taGuild) + { + logger->log1("!taGuild"); + break; + } + + taGuild->clearMembers(); + + for (int i = 0; i < count; i++) + { + int id = msg.readInt32(); // Account ID + int charId = msg.readInt32(); // Char ID + msg.readInt16(); // Hair + msg.readInt16(); // Hair color + int gender = msg.readInt16(); // Gender + int race = msg.readInt16(); // Class + int level = msg.readInt16(); // Level + int exp = msg.readInt32(); // Exp + int online = msg.readInt32(); // Online + int pos = msg.readInt32(); // Position + msg.skip(50); // unused + std::string name = msg.readString(24); // Name + + GuildMember *m = taGuild->addMember(id, charId, name); + if (m) + { + m->setOnline(online); + m->setID(id); + m->setCharId(charId); + if (!gender) + m->setGender(GENDER_FEMALE); + else if (gender == 1) + m->setGender(GENDER_MALE); + else + m->setGender(GENDER_UNSPECIFIED); + + m->setLevel(level); + m->setExp(exp); + m->setPos(pos); + m->setRace(race); +// m->setDisplayBold(!pos); + if (actorSpriteManager) + { + Being *being = actorSpriteManager->findBeingByName( + name, Being::PLAYER); + if (being) + { + being->setGuildName(taGuild->getName()); + if (being->getLevel() != level) + { + being->setLevel(level); + being->updateName(); + } + } + } + } + } + taGuild->sort(); + if (actorSpriteManager) + { + actorSpriteManager->updatePlayerGuild(); + actorSpriteManager->updatePlayerColors(); + } + } + break; + + case SMSG_GUILD_POS_NAME_LIST: + { + if (!taGuild) + { + logger->log1("!taGuild"); + break; + } + + int length = msg.readInt16(); + int count = (length - 4) / 28; + + for (int i = 0; i < count; i++) + { + int id = msg.readInt32(); // ID + std::string name = msg.readString(24); // Position name + taGuild->addPos(id, name); + } + } + break; + + case SMSG_GUILD_POS_INFO_LIST: + { + int length = msg.readInt16(); + int count = (length - 4) / 16; + + for (int i = 0; i < count; i++) + { + msg.readInt32(); // ID + msg.readInt32(); // Mode + msg.readInt32(); // Same ID + msg.readInt32(); // Exp mode + } + } + break; + + case SMSG_GUILD_POSITION_CHANGED: + msg.readInt16(); // Always 44 + msg.readInt32(); // ID + msg.readInt32(); // Mode + msg.readInt32(); // Same ID + msg.readInt32(); // Exp mode + msg.readString(24); // Name + break; + + case SMSG_GUILD_MEMBER_POS_CHANGE: + { + msg.readInt16(); // Always 16 + int accountId = msg.readInt32(); // Account ID + int charId = msg.readInt32(); // Char ID + int pos = msg.readInt32(); // Position + if (taGuild) + { + GuildMember *m = taGuild->getMember(accountId, charId); + if (m) + m->setPos(pos); + } + break; + } + + case SMSG_GUILD_EMBLEM: + { + int length = msg.readInt16(); + + msg.readInt32(); // Guild ID + msg.readInt32(); // Emblem ID + msg.skip(length - 12); // Emblem data (unknown format) + } + break; + + case SMSG_GUILD_SKILL_INFO: + { + int length = msg.readInt16(); + int count = (length - 6) / 37; + + msg.readInt16(); // 'Skill point' + + for (int i = 0; i < count; i++) + { + msg.readInt16(); // ID + msg.readInt16(); // 'Info' (unknown atm) + msg.readInt16(); // unused + msg.readInt16(); // Level + msg.readInt16(); // SP + msg.readInt16(); // 'Range' + msg.skip(24); // unused + msg.readInt8(); // Can be increased + } + } + break; + + case SMSG_GUILD_NOTICE: + { + std::string msg1 = msg.readString(60); // Mes1 + std::string msg2 = msg.readString(120); // Mes2 + if (guildTab) + { + guildTab->chatLog(msg1, BY_SERVER); + guildTab->chatLog(msg2, BY_SERVER); + } + break; + } + + case SMSG_GUILD_INVITE: + { + int guildId = msg.readInt32(); + std::string guildName = msg.readString(24); + + if (socialWindow) + socialWindow->showGuildInvite(guildName, guildId, ""); + break; + } + + case SMSG_GUILD_INVITE_ACK: + { + int flag = msg.readInt8(); + if (!guildTab) + break; + + switch (flag) + { + case 0: + guildTab->chatLog(_("Could not inivte user to guild."), + BY_SERVER); + break; + + case 1: + guildTab->chatLog(_("User rejected guild invite."), + BY_SERVER); + break; + + case 2: + guildTab->chatLog(_("User is now part of your guild."), + BY_SERVER); + break; + + case 3: + guildTab->chatLog(_("Your guild is full."), + BY_SERVER); + break; + + default: + guildTab->chatLog(_("Unknown guild invite response."), + BY_SERVER); + break; + } + } + break; + + case SMSG_GUILD_LEAVE: + { + std::string nick = msg.readString(24); // Name + std::string message = msg.readString(40); // Message + + if (taGuild) + taGuild->removeMember(nick); + + if (player_node && nick == player_node->getName()) + { + if (taGuild) + { + taGuild->removeFromMembers(); + taGuild->clearMembers(); + } + SERVER_NOTICE(_("You have left the guild.")) + delete guildTab; + guildTab = 0; + + if (socialWindow && taGuild) + socialWindow->removeTab(taGuild); + if (actorSpriteManager) + actorSpriteManager->updatePlayerColors(); + } + else + { + if (guildTab) + { + guildTab->chatLog(strprintf( + _("%s has left your guild."), + nick.c_str()), BY_SERVER); + } + if (actorSpriteManager) + { + Being *b = actorSpriteManager->findBeingByName( + nick, Being::PLAYER); + + if (b) + b->clearGuilds(); + if (taGuild) + taGuild->removeMember(nick); + } + } + break; + } + + case SMSG_GUILD_EXPULSION: + { + std::string nick = msg.readString(24); // Name (of expulsed?) + std::string message = msg.readString(40); // Message + msg.skip(24); // unused ("dummy") + if (taGuild) + taGuild->removeMember(nick); + + if (player_node && nick == player_node->getName()) + { + if (taGuild) + { + taGuild->removeFromMembers(); + taGuild->clearMembers(); + } + SERVER_NOTICE(_("You was kicked from guild.")); + delete guildTab; + guildTab = 0; + + if (socialWindow && taGuild) + socialWindow->removeTab(taGuild); + if (actorSpriteManager) + actorSpriteManager->updatePlayerColors(); + } + else + { + if (guildTab) + { + guildTab->chatLog(strprintf( + _("%s has kicked from your guild."), + nick.c_str()), BY_SERVER); + } + + if (actorSpriteManager) + { + Being *b = actorSpriteManager->findBeingByName( + nick, Being::PLAYER); + + if (b) + b->clearGuilds(); + if (taGuild) + taGuild->removeMember(nick); + } + } + break; + } + + case SMSG_GUILD_EXPULSION_LIST: + { + int length = msg.readInt16(); + int count = (length - 4) / 88; + + for (int i = 0; i < count; i++) + { + msg.readString(24); // Name (of expulsed?) + msg.readString(24); // 'Acc' (name of expulser?) + msg.readString(24); // Message + } + } + break; + + case SMSG_GUILD_MESSAGE: + { + int msgLength = msg.readInt16() - 4; + + if (msgLength <= 0) + return; + if (guildTab) + { + std::string chatMsg = msg.readString(msgLength); + + std::string::size_type pos = chatMsg.find(" : ", 0); + if (pos != std::string::npos) + { + std::string sender_name = ((pos == std::string::npos) + ? "" : chatMsg.substr(0, pos)); + + chatMsg.erase(0, pos + 3); + + trim(chatMsg); + guildTab->chatLog(sender_name, chatMsg); + } + else + { + guildTab->chatLog(chatMsg); + } + } + } + break; + + case SMSG_GUILD_SKILL_UP: + msg.readInt16(); // Skill ID + msg.readInt16(); // Level + msg.readInt16(); // SP + msg.readInt16(); // 'Range' + msg.readInt8(); // unused? (always 1) + break; + + case SMSG_GUILD_REQ_ALLIANCE: + msg.readInt32(); // Account ID + msg.readString(24); // Name + break; + + case SMSG_GUILD_REQ_ALLIANCE_ACK: + msg.readInt32(); // Flag + break; + + case SMSG_GUILD_DEL_ALLIANCE: + msg.readInt32(); // Guild ID + msg.readInt32(); // Flag + break; + + case SMSG_GUILD_OPPOSITION_ACK: + msg.readInt8(); // Flag + break; + + case SMSG_GUILD_BROKEN: + msg.readInt32(); // Flag + break; + + default: + break; + } +} + +void GuildHandler::create(const std::string &name) +{ + MessageOut msg(CMSG_GUILD_CREATE); + msg.writeInt32(0); // Unused + msg.writeString(name, 24); +} + +void GuildHandler::invite(int guildId _UNUSED_, + const std::string &name _UNUSED_) +{ + if (!actorSpriteManager) + return; + + Being* being = actorSpriteManager->findBeingByName(name, Being::PLAYER); + if (being) + { + MessageOut msg(CMSG_GUILD_INVITE); + msg.writeInt32(being->getId()); + msg.writeInt32(0); // Unused + msg.writeInt32(0); // Unused + } +} + +void GuildHandler::invite(int guildId _UNUSED_, Being *being) +{ + if (!being) + return; + + MessageOut msg(CMSG_GUILD_INVITE); + msg.writeInt32(being->getId()); + msg.writeInt32(0); // Unused + msg.writeInt32(0); // Unused +} + +void GuildHandler::inviteResponse(int guildId, bool response) +{ + MessageOut msg(CMSG_GUILD_INVITE_REPLY); + msg.writeInt32(guildId); + msg.writeInt8(response); + msg.writeInt8(0); // Unused + msg.writeInt16(0); // Unused +} + +void GuildHandler::leave(int guildId) +{ + if (!player_node) + return; + + MessageOut msg(CMSG_GUILD_LEAVE); + msg.writeInt32(guildId); + msg.writeInt32(player_node->getId()); // Account ID + msg.writeInt32(PlayerInfo::getCharId()); // Char ID + msg.writeString("", 40); // Message +} + +void GuildHandler::kick(GuildMember *member, std::string reason) +{ + if (!member || !member->getGuild()) + return; + + MessageOut msg(CMSG_GUILD_EXPULSION); + msg.writeInt32(member->getGuild()->getId()); + msg.writeInt32(member->getID()); // Account ID + msg.writeInt32(member->getCharId()); // Char ID + msg.writeString(reason, 40); // Message +} + +void GuildHandler::chat(int guildId _UNUSED_, const std::string &text) +{ + if (!player_node) + return; + + std::string str = player_node->getName() + " : " + text; + MessageOut msg(CMSG_GUILD_MESSAGE); + msg.writeInt16(str.size() + 4); + msg.writeString(str, str.length()); +} + +void GuildHandler::memberList(int guildId _UNUSED_) +{ + // TODO four types of info requests: + // 0 = basic info + alliance info + // 1 = position name list + member list + // 2 = position name list + position info list + // 3 = skill info + // 4 = expulsion list + + MessageOut msg(CMSG_GUILD_REQUEST_INFO); + msg.writeInt32(1); // Request member list +} + +void GuildHandler::info(int guildId _UNUSED_) +{ + // TODO four types of info requests: + // 0 = basic info + alliance info + // 1 = position name list + member list + // 2 = position name list + position info list + // 3 = skill info + // 4 = expulsion list + + showBasicInfo = true; + MessageOut msg(CMSG_GUILD_REQUEST_INFO); + msg.writeInt32(0); // Request basic info +} + +void GuildHandler::changeMemberPostion(GuildMember *member, int level) +{ + if (!member || !member->getGuild()) + return; + + MessageOut msg(CMSG_GUILD_CHANGE_MEMBER_POS); + msg.writeInt16(16); // size less then 16 <= 4 + 12 + msg.writeInt32(member->getID()); // Account ID + msg.writeInt32(member->getCharId()); // Char ID + msg.writeInt32(level); // pos +} + +void GuildHandler::requestAlliance(int guildId _UNUSED_, + int otherGuildId _UNUSED_) +{ + // TODO +} + +void GuildHandler::requestAllianceResponse(int guildId _UNUSED_, + int otherGuildId _UNUSED_, + bool response _UNUSED_) +{ + // TODO +} + +void GuildHandler::endAlliance(int guildId _UNUSED_, int otherGuildId _UNUSED_) +{ + // TODO +} + +void GuildHandler::changeNotice(int guildId, std::string msg1, + std::string msg2) +{ + MessageOut msg(CMSG_GUILD_CHANGE_NOTICE); + msg.writeInt32(guildId); + msg.writeString(msg1, 60); // msg1 + msg.writeString(msg2, 120); // msg2 +} + +bool GuildHandler::isSupported() +{ + return true; +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/guildhandler.h b/src/net/tmwa/guildhandler.h new file mode 100644 index 000000000..80b03bd01 --- /dev/null +++ b/src/net/tmwa/guildhandler.h @@ -0,0 +1,84 @@ +/* + * The Mana Client + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_GUILDHANDLER_H +#define NET_TA_GUILDHANDLER_H + +#include "net/guildhandler.h" + +#include "net/tmwa/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace TmwAthena +{ + +class GuildHandler : public Net::GuildHandler, public MessageHandler +{ + public: + GuildHandler(); + + ~GuildHandler(); + + void handleMessage(Net::MessageIn &msg); + + void create(const std::string &name); + + void invite(int guildId, const std::string &name); + + void invite(int guildId, Being *being); + + void inviteResponse(int guildId, bool response); + + void leave(int guildId); + + void kick(GuildMember *member, std::string reason = ""); + + void chat(int guildId, const std::string &text); + + void memberList(int guildId); + + void info(int guildId _UNUSED_); + + void changeMemberPostion(GuildMember *member, int level); + + void requestAlliance(int guildId, int otherGuildId); + + void requestAllianceResponse(int guildId, int otherGuildId, + bool response); + + void endAlliance(int guildId, int otherGuildId); + + void changeNotice(int guildId, std::string msg1, std::string msg2); + + bool isSupported(); + + private: + // TmwAthena (and eAthena) only supports one guild per player + Guild *mGuild; +}; + +} + +#endif // NET_TA_GUILDHANDLER_H diff --git a/src/net/tmwa/inventoryhandler.cpp b/src/net/tmwa/inventoryhandler.cpp new file mode 100644 index 000000000..b4b38e623 --- /dev/null +++ b/src/net/tmwa/inventoryhandler.cpp @@ -0,0 +1,609 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/inventoryhandler.h" + +#include "configuration.h" +#include "equipment.h" +#include "event.h" +#include "inventory.h" +#include "item.h" +#include "itemshortcut.h" +#include "localplayer.h" +#include "log.h" + +#include "gui/widgets/chattab.h" + +#include "net/messagein.h" +#include "net/messageout.h" + +#include "net/tmwa/protocol.h" + +#include "resources/iteminfo.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include + +extern Net::InventoryHandler *inventoryHandler; + +const Equipment::Slot EQUIP_POINTS[Equipment::EQUIP_VECTOREND] = +{ + Equipment::EQUIP_LEGS_SLOT, + Equipment::EQUIP_FIGHT1_SLOT, + Equipment::EQUIP_GLOVES_SLOT, + Equipment::EQUIP_RING2_SLOT, + Equipment::EQUIP_RING1_SLOT, + Equipment::EQUIP_FIGHT2_SLOT, + Equipment::EQUIP_FEET_SLOT, + Equipment::EQUIP_NECK_SLOT, + Equipment::EQUIP_HEAD_SLOT, + Equipment::EQUIP_TORSO_SLOT, + Equipment::EQUIP_EVOL_RING1_SLOT, + Equipment::EQUIP_EVOL_RING2_SLOT, + Equipment::EQUIP_PROJECTILE_SLOT, +}; + +const Equipment::Slot EQUIP_CONVERT[] = +{ + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_FEET_SLOT, // SPRITE_SHOE + Equipment::EQUIP_LEGS_SLOT, // SPRITE_BOTTOMCLOTHES + Equipment::EQUIP_TORSO_SLOT, // SPRITE_TOPCLOTHES + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_HEAD_SLOT, // SPRITE_HAT + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_GLOVES_SLOT, // SPRITE_GLOVES + Equipment::EQUIP_FIGHT1_SLOT, // SPRITE_WEAPON + Equipment::EQUIP_FIGHT2_SLOT, // SPRITE_SHIELD + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_PROJECTILE_SLOT, // 0 + Equipment::EQUIP_PROJECTILE_SLOT, // 0 +}; + +namespace TmwAthena +{ + +int getSlot(int eAthenaSlot); + +int getSlot(int eAthenaSlot) +{ + if (eAthenaSlot == 0) + return Equipment::EQUIP_VECTOREND; + + if (eAthenaSlot & 0x8000) + return Equipment::EQUIP_PROJECTILE_SLOT; + + int mask = 1; + int position = 0; + while (!(eAthenaSlot & mask)) + { + mask <<= 1; + position++; + } + return EQUIP_POINTS[position]; +} + +enum +{ + debugInventory = 1 +}; + +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, + SMSG_PLAYER_EQUIPMENT, + SMSG_PLAYER_EQUIP, + SMSG_PLAYER_UNEQUIP, + SMSG_PLAYER_ARROW_EQUIP, + SMSG_PLAYER_ATTACK_RANGE, + 0 + }; + handledMessages = _messages; + inventoryHandler = this; + + mStorage = 0; + mStorageWindow = 0; +} + +InventoryHandler::~InventoryHandler() +{ + if (mStorageWindow) + { + mStorageWindow->close(); + mStorageWindow = 0; + } + + delete mStorage; + mStorage = 0; +} + +void InventoryHandler::handleMessage(Net::MessageIn &msg) +{ + int number, flag; + int index, amount, itemId, equipType, arrow, refine; + int identified, cards[4], itemType; + Inventory *inventory = 0; + if (player_node) + inventory = PlayerInfo::getInventory(); + + switch (msg.getId()) + { + case SMSG_PLAYER_INVENTORY: + case SMSG_PLAYER_STORAGE_ITEMS: + if (msg.getId() == SMSG_PLAYER_INVENTORY) + { + // Clear inventory - this will be a complete refresh + mEquips.clear(); + PlayerInfo::getEquipment()->setBackend(&mEquips); + + if (inventory) + inventory->clear(); + } + else + { + mInventoryItems.clear(); + } + + 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(); + amount = msg.readInt16(); + arrow = msg.readInt16(); + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + + index -= (msg.getId() == SMSG_PLAYER_INVENTORY) ? + INVENTORY_OFFSET : STORAGE_OFFSET; + + if (debugInventory) + { + 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]); + } + + if (msg.getId() == SMSG_PLAYER_INVENTORY) + { + // Trick because arrows are not considered equipment + bool isEquipment = arrow & 0x8000; + + if (inventory) + { + inventory->setItem(index, itemId, amount, + 0, isEquipment); + } + } + else + { + mInventoryItems.push_back(InventoryItem(index, itemId, + amount, 0, false)); + } + } + break; + + case SMSG_PLAYER_STORAGE_EQUIP: + msg.readInt16(); // length + number = (msg.getLength() - 4) / 20; + + for (int loop = 0; loop < number; loop++) + { + index = msg.readInt16() - STORAGE_OFFSET; + itemId = msg.readInt16(); + itemType = msg.readInt8(); + identified = msg.readInt8(); + amount = 1; + msg.readInt16(); // Equip Point? + msg.readInt16(); // Another Equip Point? + msg.readInt8(); // Attribute (broken) + refine = msg.readInt8(); + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + + if (debugInventory) + { + logger->log("Index: %d, ID: %d, Type: %d, Identified: %d, " + "Qty: %d, Cards: %d, %d, %d, %d, Refine: %d", + index, itemId, itemType, identified, amount, + cards[0], cards[1], cards[2], cards[3], + refine); + } + + mInventoryItems.push_back(InventoryItem(index, itemId, amount, + refine, false)); + } + break; + + case SMSG_PLAYER_INVENTORY_ADD: + index = msg.readInt16() - INVENTORY_OFFSET; + amount = msg.readInt16(); + itemId = msg.readInt16(); + identified = msg.readInt8(); + msg.readInt8(); // attribute + refine = msg.readInt8(); + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + equipType = msg.readInt16(); + itemType = msg.readInt8(); + + { + const ItemInfo &itemInfo = ItemDB::get(itemId); + + if (msg.readInt8() > 0) + { + if (player_node) + player_node->pickedUp(itemInfo, 0); + } + else + { + if (player_node) + player_node->pickedUp(itemInfo, amount); + + if (inventory) + { + Item *item = inventory->getItem(index); + + if (item && item->getId() == itemId) + amount += inventory->getItem(index)->getQuantity(); + + inventory->setItem(index, itemId, amount, refine, + equipType != 0); + } + } + } break; + + case SMSG_PLAYER_INVENTORY_REMOVE: + index = msg.readInt16() - INVENTORY_OFFSET; + amount = msg.readInt16(); + if (inventory) + { + 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() - INVENTORY_OFFSET; + msg.readInt16(); // item id + msg.readInt32(); // id + amount = msg.readInt16(); + msg.readInt8(); // type + + if (inventory) + { + if (Item *item = inventory->getItem(index)) + { + if (amount) + item->setQuantity(amount); + else + inventory->removeItemAt(index); + } + } + break; + + case SMSG_ITEM_USE_RESPONSE: + index = msg.readInt16() - INVENTORY_OFFSET; + amount = msg.readInt16(); + + if (msg.readInt8() == 0) + { + SERVER_NOTICE(_("Failed to use item.")) + } + else + { + if (inventory) + { + if (Item *item = inventory->getItem(index)) + { + if (amount) + item->setQuantity(amount); + else + inventory->removeItemAt(index); + } + } + } + break; + + case SMSG_PLAYER_STORAGE_STATUS: + /* + * 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. + */ + { + msg.readInt16(); // Used count + int size = msg.readInt16(); // Max size + + if (!mStorage) + mStorage = new Inventory(Inventory::STORAGE, size); + + InventoryItems::iterator it = mInventoryItems.begin(); + InventoryItems::iterator it_end = mInventoryItems.end(); + for (; it != it_end; it++) + mStorage->setItem((*it).slot, (*it).id, (*it).quantity, + (*it).equip); + mInventoryItems.clear(); + + if (!mStorageWindow) + mStorageWindow = new InventoryWindow(mStorage); + } + break; + + case SMSG_PLAYER_STORAGE_ADD: + // Move an item into storage + index = msg.readInt16() - STORAGE_OFFSET; + amount = msg.readInt32(); + itemId = msg.readInt16(); + identified = msg.readInt8(); + msg.readInt8(); // attribute + refine = msg.readInt8(); + for (int i = 0; i < 4; i++) + cards[i] = msg.readInt16(); + + if (Item *item = mStorage->getItem(index)) + { + item->setId(itemId); + item->increaseQuantity(amount); + } + else + { + if (mStorage) + mStorage->setItem(index, itemId, amount, false); + } + break; + + case SMSG_PLAYER_STORAGE_REMOVE: + // Move an item out of storage + index = msg.readInt16() - STORAGE_OFFSET; + amount = msg.readInt16(); + if (mStorage) + { + if (Item *item = mStorage->getItem(index)) + { + item->increaseQuantity(-amount); + if (item->getQuantity() == 0) + mStorage->removeItemAt(index); + } + } + break; + + case SMSG_PLAYER_STORAGE_CLOSE: + // Storage access has been closed + + // Storage window deletes itself + mStorageWindow = 0; + + if (mStorage) + mStorage->clear(); + + delete mStorage; + mStorage = 0; + break; + + case SMSG_PLAYER_EQUIPMENT: + msg.readInt16(); // length + number = (msg.getLength() - 4) / 20; + + for (int loop = 0; loop < number; loop++) + { + index = msg.readInt16() - INVENTORY_OFFSET; + itemId = msg.readInt16(); + msg.readInt8(); // type + msg.readInt8(); // identify flag + msg.readInt16(); // equip type + equipType = msg.readInt16(); + msg.readInt8(); // attribute + refine = msg.readInt8(); + msg.skip(8); // card + + if (inventory) + inventory->setItem(index, itemId, 1, refine, true); + + if (equipType) + mEquips.setEquipment(getSlot(equipType), index); + } + break; + + case SMSG_PLAYER_EQUIP: + index = msg.readInt16() - INVENTORY_OFFSET; + equipType = msg.readInt16(); + flag = msg.readInt8(); + + if (!flag) + SERVER_NOTICE(_("Unable to equip.")) + else + mEquips.setEquipment(getSlot(equipType), index); + break; + + case SMSG_PLAYER_UNEQUIP: + index = msg.readInt16() - INVENTORY_OFFSET; + equipType = msg.readInt16(); + flag = msg.readInt8(); + + if (flag) + mEquips.setEquipment(getSlot(equipType), -1); + break; + + case SMSG_PLAYER_ATTACK_RANGE: + { + int range = msg.readInt16(); + if (player_node) + player_node->setAttackRange(range); + PlayerInfo::setStatBase(ATTACK_RANGE, range); + PlayerInfo::setStatMod(ATTACK_RANGE, 0); + break; + } + + case SMSG_PLAYER_ARROW_EQUIP: + index = msg.readInt16(); + + if (index <= 1) + break; + + index -= INVENTORY_OFFSET; + + logger->log("Arrows equipped: %i", index); + mEquips.setEquipment(Equipment::EQUIP_PROJECTILE_SLOT, index); + break; + + default: + break; + } +} + +void InventoryHandler::equipItem(const Item *item) +{ + if (!item) + return; + + MessageOut outMsg(CMSG_PLAYER_EQUIP); + outMsg.writeInt16(static_cast( + item->getInvIndex() + INVENTORY_OFFSET)); + outMsg.writeInt16(0); +} + +void InventoryHandler::unequipItem(const Item *item) +{ + if (!item) + return; + + MessageOut outMsg(CMSG_PLAYER_UNEQUIP); + outMsg.writeInt16(static_cast( + item->getInvIndex() + INVENTORY_OFFSET)); +} + +void InventoryHandler::useItem(const Item *item) +{ + if (!item) + return; + + MessageOut outMsg(CMSG_PLAYER_INVENTORY_USE); + outMsg.writeInt16(static_cast( + item->getInvIndex() + INVENTORY_OFFSET)); + outMsg.writeInt32(item->getId()); // unused +} + +void InventoryHandler::dropItem(const Item *item, int amount) +{ + if (!item) + return; + + // TODO: Fix wrong coordinates of drops, serverside? (what's wrong here?) + MessageOut outMsg(CMSG_PLAYER_INVENTORY_DROP); + outMsg.writeInt16(static_cast( + item->getInvIndex() + INVENTORY_OFFSET)); + outMsg.writeInt16(static_cast(amount)); +} + +bool InventoryHandler::canSplit(const Item *item _UNUSED_) +{ + return false; +} + +void InventoryHandler::splitItem(const Item *item _UNUSED_, + int amount _UNUSED_) +{ + // Not implemented for eAthena (possible?) +} + +void InventoryHandler::moveItem(int oldIndex _UNUSED_, int newIndex _UNUSED_) +{ + // Not implemented for eAthena (possible?) +} + +void InventoryHandler::openStorage(int type _UNUSED_) +{ + // Doesn't apply to eAthena, since opening happens through NPCs? +} + +void InventoryHandler::closeStorage(int type _UNUSED_) +{ + MessageOut outMsg(CMSG_CLOSE_STORAGE); +} + +void InventoryHandler::moveItem(int source, int slot, int amount, + int destination) +{ + if (source == Inventory::INVENTORY && destination == Inventory::STORAGE) + { + MessageOut outMsg(CMSG_MOVE_TO_STORAGE); + outMsg.writeInt16(slot + INVENTORY_OFFSET); + outMsg.writeInt32(amount); + } + else if (source == Inventory::STORAGE + && destination == Inventory::INVENTORY) + { + MessageOut outMsg(CSMG_MOVE_FROM_STORAGE); + outMsg.writeInt16(slot + STORAGE_OFFSET); + outMsg.writeInt32(amount); + } +} + +size_t InventoryHandler::getSize(int type) const +{ + switch (type) + { + case Inventory::INVENTORY: + return 100; + case Inventory::STORAGE: + return 0; // Comes from server after items + case Inventory::TRADE: + return 12; + case GUILD_STORAGE: + return 0; // Comes from server after items + default: + return 0; + } +} +int InventoryHandler::convertFromServerSlot(int serverSlot) +{ + if (serverSlot < 0 || serverSlot > 13) + return 0; + + return EQUIP_CONVERT[serverSlot]; +} +} // namespace TmwAthena diff --git a/src/net/tmwa/inventoryhandler.h b/src/net/tmwa/inventoryhandler.h new file mode 100644 index 000000000..1a10b6ba0 --- /dev/null +++ b/src/net/tmwa/inventoryhandler.h @@ -0,0 +1,177 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_INVENTORYHANDLER_H +#define NET_TA_INVENTORYHANDLER_H + +#include "equipment.h" +#include "inventory.h" +#include "playerinfo.h" + +#include "gui/inventorywindow.h" + +#include "net/inventoryhandler.h" +#include "net/net.h" + +#include "net/tmwa/messagehandler.h" + +#include + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace TmwAthena +{ + +class EquipBackend : public Equipment::Backend +{ + public: + EquipBackend() + { + memset(mEquipment, -1, sizeof(mEquipment)); + } + + Item *getEquipment(int index) const + { + int invyIndex = mEquipment[index]; + if (invyIndex == -1) + { + return NULL; + } + return PlayerInfo::getInventory()->getItem(invyIndex); + } + + void clear() + { + for (int i = 0; i < EQUIPMENT_SIZE; i++) + { + if (mEquipment[i] != -1) + { + Item* item = PlayerInfo::getInventory()->getItem(i); + if (item) + { + item->setEquipped(false); + } + } + + mEquipment[i] = -1; + } + } + + void setEquipment(int index, int inventoryIndex) + { + // Unequip existing item + Item* item = PlayerInfo::getInventory() + ->getItem(mEquipment[index]); + + if (item) + item->setEquipped(false); + + mEquipment[index] = inventoryIndex; + + item = PlayerInfo::getInventory()->getItem(inventoryIndex); + if (item) + item->setEquipped(true); + } + + private: + int mEquipment[EQUIPMENT_SIZE]; +}; + +/** + * Used to cache storage data until we get size data for it. + */ +class InventoryItem +{ + public: + int slot; + int id; + int quantity; + int refine; + bool equip; + + InventoryItem(int slot, int id, int quantity, int refine, bool equip) + { + this->slot = slot; + this->id = id; + this->quantity = quantity; + this->refine = refine; + this->equip = equip; + } +}; + +typedef std::list InventoryItems; + +class InventoryHandler : public MessageHandler, public Net::InventoryHandler +{ + public: + enum + { + GUILD_STORAGE = Inventory::TYPE_END, + CART + }; + + InventoryHandler(); + + ~InventoryHandler(); + + void handleMessage(Net::MessageIn &msg); + + void equipItem(const Item *item); + + void unequipItem(const Item *item); + + void useItem(const Item *item); + + void dropItem(const Item *item, int amount); + + bool canSplit(const Item *item); + + void splitItem(const Item *item, int amount); + + void moveItem(int oldIndex, int newIndex); + + void openStorage(int type); + + void closeStorage(int type); + + void moveItem(int source, int slot, int amount, + int destination); + + size_t getSize(int type) const; + + int convertFromServerSlot(int serverSlot); + + private: + EquipBackend mEquips; + InventoryItems mInventoryItems; + Inventory *mStorage; + InventoryWindow *mStorageWindow; +}; + +} // namespace TmwAthena + +int getSlot(int eAthenaSlot); + +#endif // NET_TA_INVENTORYHANDLER_H diff --git a/src/net/tmwa/itemhandler.cpp b/src/net/tmwa/itemhandler.cpp new file mode 100644 index 000000000..7ae124646 --- /dev/null +++ b/src/net/tmwa/itemhandler.cpp @@ -0,0 +1,93 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/itemhandler.h" + +#include "actorspritemanager.h" + +#include "net/messagein.h" + +#include "net/tmwa/protocol.h" + +namespace TmwAthena +{ + +ItemHandler::ItemHandler() +{ + static const Uint16 _messages[] = + { + SMSG_ITEM_VISIBLE, + SMSG_ITEM_DROPPED, + SMSG_ITEM_REMOVE, + 0 + }; + handledMessages = _messages; +} + +void ItemHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case SMSG_ITEM_VISIBLE: + case SMSG_ITEM_DROPPED: + { + int id = msg.readInt32(); + int itemId = msg.readInt16(); + msg.readInt8(); // identify flag + int x = msg.readInt16(); + int y = msg.readInt16(); +// msg.skip(4); // amount,subX,subY / subX,subY,amount + int amount1 = msg.readInt16(); + int amount2 = msg.readInt16(); + + if (actorSpriteManager) + { + if (msg.getId() == SMSG_ITEM_VISIBLE) + { + actorSpriteManager->createItem(id, itemId, + x, y, amount1); + } + else + { + actorSpriteManager->createItem(id, itemId, + x, y, amount2); + } + } + } + break; + + case SMSG_ITEM_REMOVE: + if (actorSpriteManager) + { + if (FloorItem *item = actorSpriteManager->findItem( + msg.readInt32())) + { + actorSpriteManager->destroy(item); + } + } + break; + + default: + break; + } +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/itemhandler.h b/src/net/tmwa/itemhandler.h new file mode 100644 index 000000000..0f740fe58 --- /dev/null +++ b/src/net/tmwa/itemhandler.h @@ -0,0 +1,40 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_ITEMHANDLER_H +#define NET_TA_ITEMHANDLER_H + +#include "net/tmwa/messagehandler.h" + +namespace TmwAthena +{ + +class ItemHandler : public MessageHandler +{ + public: + ItemHandler(); + + virtual void handleMessage(Net::MessageIn &msg); +}; + +} // namespace TmwAthena + +#endif // NET_TA_ITEMHANDLER_H diff --git a/src/net/tmwa/loginhandler.cpp b/src/net/tmwa/loginhandler.cpp new file mode 100644 index 000000000..73fec1322 --- /dev/null +++ b/src/net/tmwa/loginhandler.cpp @@ -0,0 +1,342 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/loginhandler.h" + +#include "client.h" +#include "log.h" +#include "configuration.h" + +#include "net/logindata.h" +#include "net/messagein.h" +#include "net/messageout.h" + +#include "net/tmwa/network.h" +#include "net/tmwa/protocol.h" + +#include "utils/dtor.h" +#include "utils/gettext.h" +#include "utils/stringutils.h" + +extern Net::LoginHandler *loginHandler; + +namespace TmwAthena +{ + +extern ServerInfo charServer; + +LoginHandler::LoginHandler(): + mVersionResponse(false), + mRegistrationEnabled(true) +{ + static const Uint16 _messages[] = + { + SMSG_UPDATE_HOST, + SMSG_LOGIN_DATA, + SMSG_LOGIN_ERROR, + SMSG_CHAR_PASSWORD_RESPONSE, + SMSG_SERVER_VERSION_RESPONSE, + 0 + }; + handledMessages = _messages; + loginHandler = this; +} + +LoginHandler::~LoginHandler() +{ + delete_all(mWorlds); +} + +void LoginHandler::handleMessage(Net::MessageIn &msg) +{ + int code, worldCount; + + switch (msg.getId()) + { + case SMSG_CHAR_PASSWORD_RESPONSE: + { + // 0: acc not found, 1: success, 2: password mismatch, 3: pass too short + int errMsg = msg.readInt8(); + // Successful pass change + if (errMsg == 1) + { + Client::setState(STATE_CHANGEPASSWORD_SUCCESS); + } + // pass change failed + else + { + switch (errMsg) + { + case 0: + errorMessage = + _("Account was not found. Please re-login."); + break; + case 2: + errorMessage = _("Old password incorrect."); + break; + case 3: + errorMessage = _("New password too short."); + break; + default: + errorMessage = _("Unknown error."); + break; + } + Client::setState(STATE_ACCOUNTCHANGE_ERROR); + } + } + break; + + case SMSG_UPDATE_HOST: + int len; + + len = msg.readInt16() - 4; + mUpdateHost = msg.readString(len); + loginData.updateHost = mUpdateHost; + + logger->log("Received update host \"%s\" from login server.", + mUpdateHost.c_str()); + break; + + case SMSG_LOGIN_DATA: + // Skip the length word + msg.skip(2); + + clearWorlds(); + + worldCount = (msg.getLength() - 47) / 32; + + mToken.session_ID1 = msg.readInt32(); + mToken.account_ID = msg.readInt32(); + mToken.session_ID2 = msg.readInt32(); + msg.skip(30); // unknown + mToken.sex = msg.readInt8() ? GENDER_MALE : GENDER_FEMALE; + + for (int i = 0; i < worldCount; i++) + { + WorldInfo *world = new WorldInfo; + + world->address = msg.readInt32(); + world->port = msg.readInt16(); + world->name = msg.readString(20); + world->online_users = msg.readInt32(); + config.setValue("updatehost", mUpdateHost); + world->updateHost = mUpdateHost; + msg.skip(2); // unknown + + logger->log("Network: Server: %s (%s:%d)", + world->name.c_str(), + ipToString(world->address), + world->port); + + mWorlds.push_back(world); + } + Client::setState(STATE_WORLD_SELECT); + break; + + case SMSG_LOGIN_ERROR: + 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.\nPlease 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; + } + Client::setState(STATE_ERROR); + break; + + case SMSG_SERVER_VERSION_RESPONSE: + { + // TODO: verify these! + + msg.readInt8(); // -1 + msg.readInt8(); // T + msg.readInt8(); // M + msg.readInt8(); // W + + unsigned int options = msg.readInt32(); + + mRegistrationEnabled = options; +// mRegistrationEnabled = (options & 1); + + // Leave this last + mVersionResponse = true; + } + break; + + default: + break; + } +} + +void LoginHandler::connect() +{ + if (!mNetwork) + return; + + mNetwork->connect(mServer); + MessageOut outMsg(CMSG_SERVER_VERSION_REQUEST); +} + +bool LoginHandler::isConnected() +{ + if (!mNetwork) + return false; + + return mVersionResponse && mNetwork->isConnected(); +} + +void LoginHandler::disconnect() +{ + if (mNetwork && mNetwork->getServer() == mServer) + mNetwork->disconnect(); +} + +bool LoginHandler::isRegistrationEnabled() +{ + return mRegistrationEnabled; +} + +void LoginHandler::getRegistrationDetails() +{ + // Not supported, so move on + Client::setState(STATE_REGISTER); +} + +void LoginHandler::loginAccount(LoginData *loginData) +{ + + if (loginData) + { + // Since we're attempting to use the tAthena protocol, + // let's reset the character slots to the good value, + // in case we just logged out a Manaserv server + // with a different config. + loginData->resetCharacterSlots(); + + sendLoginRegister(loginData->username, loginData->password); + } +} + +void LoginHandler::logout() +{ + // TODO +} + +void LoginHandler::changeEmail(const std::string &email _UNUSED_) +{ + // TODO +} + +void LoginHandler::changePassword(const std::string &username _UNUSED_, + const std::string &oldPassword, + const std::string &newPassword) +{ + MessageOut outMsg(CMSG_CHAR_PASSWORD_CHANGE); + outMsg.writeString(oldPassword, 24); + outMsg.writeString(newPassword, 24); +} + +void LoginHandler::chooseServer(unsigned int server) +{ + if (server >= mWorlds.size() || !mWorlds[server]) + return; + + charServer.clear(); + charServer.hostname = ipToString(mWorlds[server]->address); + charServer.port = mWorlds[server]->port; + + Client::setState(STATE_UPDATE); +} + +void LoginHandler::registerAccount(LoginData *loginData) +{ + if (!loginData) + return; + + std::string username = loginData->username; + username.append((loginData->gender == GENDER_FEMALE) ? "_F" : "_M"); + + sendLoginRegister(username, loginData->password); +} + +void LoginHandler::unregisterAccount(const std::string &username _UNUSED_, + const std::string &password _UNUSED_) +{ + // TODO +} + +void LoginHandler::sendLoginRegister(const std::string &username, + const std::string &password) +{ + MessageOut outMsg(0x0064); + outMsg.writeInt32(0); // client version + outMsg.writeString(username, 24); + outMsg.writeString(password, 24); + + /* + * eAthena calls the last byte "client version 2", but it isn't used at + * at all. We're retasking it, as a bit mask: + * 0 - can handle the 0x63 "update host" packet + * 1 - defaults to the first char-server (instead of the last) + */ + outMsg.writeInt8(0x03); +} + +Worlds LoginHandler::getWorlds() const +{ + return mWorlds; +} + +void LoginHandler::clearWorlds() +{ + delete_all(mWorlds); + mWorlds.clear(); +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/loginhandler.h b/src/net/tmwa/loginhandler.h new file mode 100644 index 000000000..977b4ce0a --- /dev/null +++ b/src/net/tmwa/loginhandler.h @@ -0,0 +1,105 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TMWA_LOGINHANDLER_H +#define NET_TMWA_LOGINHANDLER_H + +#include "net/loginhandler.h" + +#include "net/tmwa/messagehandler.h" +#include "net/tmwa/token.h" + +#include + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +struct LoginData; + +namespace TmwAthena +{ + +class LoginHandler : public MessageHandler, public Net::LoginHandler +{ + public: + LoginHandler(); + + ~LoginHandler(); + + void handleMessage(Net::MessageIn &msg); + + void connect(); + + bool isConnected(); + + void disconnect(); + + int supportedOptionalActions() const + { return SetGenderOnRegister; } + + bool isRegistrationEnabled(); + + void getRegistrationDetails(); + + unsigned int getMaxPasswordLength() const + { return 25; } + + void loginAccount(LoginData *loginData); + + void logout(); + + void changeEmail(const std::string &email); + + void changePassword(const std::string &username, + const std::string &oldPassword, + const std::string &newPassword); + + void chooseServer(unsigned int server); + + void registerAccount(LoginData *loginData); + + void unregisterAccount(const std::string &username, + const std::string &password); + + Worlds getWorlds() const; + + void clearWorlds(); + + const Token &getToken() const + { return mToken; } + + private: + void sendLoginRegister(const std::string &username, + const std::string &password); + + bool mVersionResponse; + bool mRegistrationEnabled; + std::string mUpdateHost; + Worlds mWorlds; + Token mToken; +}; + +} // namespace TmwAthena + +#endif // NET_TA_LOGINHANDLER_H diff --git a/src/net/tmwa/messagehandler.cpp b/src/net/tmwa/messagehandler.cpp new file mode 100644 index 000000000..85a0caeae --- /dev/null +++ b/src/net/tmwa/messagehandler.cpp @@ -0,0 +1,48 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/messagehandler.h" + +#include "net/tmwa/network.h" + +#include + +namespace TmwAthena +{ + +MessageHandler::MessageHandler() + : mNetwork(NULL) +{ +} + +MessageHandler::~MessageHandler() +{ + if (mNetwork) + mNetwork->unregisterHandler(this); +} + +void MessageHandler::setNetwork(Network *network) +{ + assert(!(network && mNetwork)); + mNetwork = network; +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/messagehandler.h b/src/net/tmwa/messagehandler.h new file mode 100644 index 000000000..d4b0c8094 --- /dev/null +++ b/src/net/tmwa/messagehandler.h @@ -0,0 +1,59 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_MESSAGEHANDLER_H +#define NET_TA_MESSAGEHANDLER_H + +#include "net/messagehandler.h" +#include "net/messagein.h" + +#include "net/tmwa/messageout.h" + +#include + +#include + +namespace TmwAthena +{ + +class Network; + +/** + * \ingroup Network + */ +class MessageHandler : public Net::MessageHandler +{ + public: + MessageHandler(); + + ~MessageHandler(); + + void setNetwork(Network *network); + + protected: + Network *mNetwork; +}; + +typedef const std::auto_ptr MessageHandlerPtr; + +} + +#endif // NET_TA_MESSAGEHANDLER_H diff --git a/src/net/tmwa/messagein.cpp b/src/net/tmwa/messagein.cpp new file mode 100644 index 000000000..23f5e09c1 --- /dev/null +++ b/src/net/tmwa/messagein.cpp @@ -0,0 +1,85 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/messagein.h" + +#include "net/packetcounters.h" + +#include "log.h" + +#include "utils/stringutils.h" + +#include +#include + +#define MAKEWORD(low, high) \ + ((unsigned short)(((unsigned char)(low)) | \ + ((unsigned short)((unsigned char)(high))) << 8)) + +namespace TmwAthena +{ + +MessageIn::MessageIn(const char *data, unsigned int length): + Net::MessageIn(data, length) +{ + // Read the message ID + mId = readInt16(); +} + +Sint16 MessageIn::readInt16() +{ + Sint16 value = -1; + if (mPos + 2 <= mLength) + { +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + Sint16 swap; + memcpy(&swap, mData + mPos, sizeof(Sint16)); + value = SDL_Swap16(swap); +#else + memcpy(&value, mData + mPos, sizeof(Sint16)); +#endif + } + mPos += 2; + PacketCounters::incInBytes(2); + DEBUGLOG("readInt16: " + toString((int)value)); + return value; +} + +int MessageIn::readInt32() +{ + Sint32 value = -1; + if (mPos + 4 <= mLength) + { +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + Sint32 swap; + memcpy(&swap, mData + mPos, sizeof(Sint32)); + value = SDL_Swap32(swap); +#else + memcpy(&value, mData + mPos, sizeof(Sint32)); +#endif + } + mPos += 4; + PacketCounters::incInBytes(4); + DEBUGLOG(strprintf("readInt32: %u", value)); + return value; +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/messagein.h b/src/net/tmwa/messagein.h new file mode 100644 index 000000000..b3dedc7af --- /dev/null +++ b/src/net/tmwa/messagein.h @@ -0,0 +1,52 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_MESSAGEIN_H +#define NET_TA_MESSAGEIN_H + +#include "net/messagein.h" + +#include +#include + +namespace TmwAthena +{ + +/** + * Used for parsing an incoming message. + * + * \ingroup Network + */ + class MessageIn : public Net::MessageIn +{ + public: + /** + * Constructor. + */ + MessageIn(const char *data, unsigned int length); + + Sint16 readInt16(); /**< Reads a short. */ + int readInt32(); /**< Reads a long. */ +}; + +} + +#endif // NET_TA_MESSAGEIN_H diff --git a/src/net/tmwa/messageout.cpp b/src/net/tmwa/messageout.cpp new file mode 100644 index 000000000..42d4f582c --- /dev/null +++ b/src/net/tmwa/messageout.cpp @@ -0,0 +1,142 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/messageout.h" + +#include "net/packetcounters.h" + +#include "net/tmwa/network.h" + +#include "log.h" + +#include "utils/stringutils.h" + +#include +#include + +#include +#include + +namespace TmwAthena +{ + +MessageOut::MessageOut(short id): + Net::MessageOut(id) +{ + mNetwork = TmwAthena::Network::instance(); + mData = mNetwork->mOutBuffer + mNetwork->mOutSize; + writeInt16(id); +} + +void MessageOut::expand(size_t bytes) +{ + mNetwork->mOutSize += static_cast(bytes); + PacketCounters::incOutBytes(static_cast(bytes)); +} + +void MessageOut::writeInt16(Sint16 value) +{ + DEBUGLOG("writeInt16: " + toString((int)value)); + expand(2); +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + Sint16 swap = SDL_Swap16(value); + memcpy(mData + mPos, &swap, sizeof(Sint16)); +#else + memcpy(mData + mPos, &value, sizeof(Sint16)); +#endif + mPos += 2; + PacketCounters::incOutBytes(2); +} + +void MessageOut::writeInt32(Sint32 value) +{ + DEBUGLOG("writeInt32: " + toString(value)); + expand(4); +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + Sint32 swap = SDL_Swap32(value); + memcpy(mData + mPos, &swap, sizeof(Sint32)); +#else + memcpy(mData + mPos, &value, sizeof(Sint32)); +#endif + mPos += 4; + PacketCounters::incOutBytes(4); +} + +#define LOBYTE(w) ((unsigned char)(w)) +#define HIBYTE(w) ((unsigned char)(((unsigned short)(w)) >> 8)) + +void MessageOut::writeCoordinates(unsigned short x, unsigned short y, + unsigned char direction) +{ + DEBUGLOG(strprintf("writeCoordinates: %u,%u %u", x, y, direction)); + char *data = mData + mPos; + mNetwork->mOutSize += 3; + mPos += 3; + + short temp; + temp = x; + temp <<= 6; + data[0] = 0; + data[1] = 1; + data[2] = 2; + data[0] = HIBYTE(temp); + data[1] = (unsigned char) temp; + temp = y; + temp <<= 4; + data[1] |= HIBYTE(temp); + data[2] = LOBYTE(temp); + + // Translate direction to eAthena format + switch (direction) + { + case 1: + direction = 0; + break; + case 3: + direction = 1; + break; + case 2: + direction = 2; + break; + case 6: + direction = 3; + break; + case 4: + direction = 4; + break; + case 12: + direction = 5; + break; + case 8: + direction = 6; + break; + case 9: + direction = 7; + break; + default: + // OOPSIE! Impossible or unknown + direction = (unsigned char) -1; + } + data[2] |= direction; + PacketCounters::incOutBytes(3); +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/messageout.h b/src/net/tmwa/messageout.h new file mode 100644 index 000000000..ae0fc9a49 --- /dev/null +++ b/src/net/tmwa/messageout.h @@ -0,0 +1,65 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_MESSAGEOUT_H +#define NET_TA_MESSAGEOUT_H + +#include "net/messageout.h" + +#include +#include + +namespace TmwAthena +{ + +class Network; + +/** + * Used for building an outgoing message. + * + * \ingroup Network + */ +class MessageOut : public Net::MessageOut +{ + public: + /** + * Constructor. + */ + MessageOut(short id); + + void writeInt16(Sint16 value); /**< Writes a short. */ + void writeInt32(Sint32 value); /**< Writes a long. */ + + /** + * Encodes coordinates and direction in 3 bytes. + */ + void writeCoordinates(unsigned short x, unsigned short y, + unsigned char direction); + + private: + void expand(size_t size); + + Network *mNetwork; +}; + +} + +#endif // NET_TA_MESSAGEOUT_H diff --git a/src/net/tmwa/network.cpp b/src/net/tmwa/network.cpp new file mode 100644 index 000000000..7aa9ad7d2 --- /dev/null +++ b/src/net/tmwa/network.cpp @@ -0,0 +1,483 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/network.h" + +#include "log.h" + +#include "net/messagehandler.h" +#include "net/messagein.h" + +#include "net/tmwa/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#include +#include + +/** 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, 50, 3, -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, +// #0x2000 + 26, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 19, 10, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +const unsigned int BUFFER_SIZE = 655360; + +namespace TmwAthena +{ + +int networkThread(void *data) +{ + Network *network = static_cast(data); + + if (!network || !network->realConnect()) + return -1; + + network->receive(); + + return 0; +} + +Network *Network::mInstance = 0; + +Network::Network(): + mSocket(0), + mInBuffer(new char[BUFFER_SIZE]), + mOutBuffer(new char[BUFFER_SIZE]), + mInSize(0), mOutSize(0), + mToSkip(0), + mState(IDLE), + mWorkerThread(0) +{ + SDLNet_Init(); + + mMutex = SDL_CreateMutex(); + mInstance = this; +} + +Network::~Network() +{ + clearHandlers(); + + if (mState != IDLE && mState != NET_ERROR) + disconnect(); + + SDL_DestroyMutex(mMutex); + mInstance = 0; + + delete[] mInBuffer; + delete[] mOutBuffer; + + SDLNet_Quit(); +} + +bool Network::connect(ServerInfo server) +{ + if (mState != IDLE && mState != NET_ERROR) + { + logger->log1("Tried to connect an already connected socket!"); + assert(false); + return false; + } + + if (server.hostname.empty()) + { + setError(_("Empty address given to Network::connect()!")); + return false; + } + + logger->log("Network::Connecting to %s:%i", server.hostname.c_str(), + server.port); + + mServer.hostname = server.hostname; + mServer.port = server.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_GetThreadID(mWorkerThread)) + { + SDL_WaitThread(mWorkerThread, NULL); + mWorkerThread = NULL; + } + + if (mSocket) + { + // need call SDLNet_TCP_DelSocket? + SDLNet_TCP_Close(mSocket); + mSocket = 0; + } +} + +void Network::registerHandler(MessageHandler *handler) +{ + if (!handler) + return; + + for (const Uint16 *i = handler->handledMessages; *i; ++i) + mMessageHandlers[*i] = handler; + + handler->setNetwork(this); +} + +void Network::unregisterHandler(MessageHandler *handler) +{ + if (!handler) + return; + + 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) + { + if (i->second) + i->second->setNetwork(0); + } + mMessageHandlers.clear(); +} + +void Network::dispatchMessages() +{ + while (messageReady()) + { + MessageIn msg = getNextMessage(); + + MessageHandlerIterator iter = mMessageHandlers.find(msg.getId()); + + if (msg.getLength() == 0) + logger->error("Zero length packet received. Exiting."); + + if (iter != mMessageHandlers.end()) + { + if (iter->second) + 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, msgId; + + SDL_mutexP(mMutex); + if (mInSize >= 2) + { + msgId = readWord(0); + if (msgId == SMSG_SERVER_VERSION_RESPONSE) + len = 10; + else + len = packet_lengths[msgId]; + + if (len == -1 && mInSize > 4) + len = readWord(2); + + } + + bool ret = (mInSize >= static_cast(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; + if (msgId == SMSG_SERVER_VERSION_RESPONSE) + len = 10; + else + len = packet_lengths[msgId]; + + if (len == -1) + len = readWord(2); + +#ifdef DEBUG + logger->log("Received packet 0x%x of length %d\n", msgId, len); +#endif + + MessageIn msg(mInBuffer, len); + SDL_mutexV(mMutex); + + return msg; +} + +bool Network::realConnect() +{ + IPaddress ipAddress; + + if (SDLNet_ResolveHost(&ipAddress, mServer.hostname.c_str(), + mServer.port) == -1) + { + std::string errorMessage = _("Unable to resolve host \"") + + mServer.hostname + "\""; + setError(errorMessage); + logger->log("SDLNet_ResolveHost: %s", errorMessage.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->log1("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->log1("Disconnected."); + } + else if (ret < 0) + { + setError(_("Connection to server terminated. ") + + 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); +} + +Network *Network::instance() +{ + return mInstance; +} + +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 +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/network.h b/src/net/tmwa/network.h new file mode 100644 index 000000000..bdafd6a09 --- /dev/null +++ b/src/net/tmwa/network.h @@ -0,0 +1,136 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_NETWORK_H +#define NET_TA_NETWORK_H + +#include "net/serverinfo.h" + +#include "net/tmwa/messagehandler.h" +#include "net/tmwa/messagein.h" +#include "net/tmwa/messageout.h" + +#include +#include + +#include +#include + +/** + * Protocol version, reported to the eAthena char and mapserver who can adjust + * the protocol accordingly. + */ +#define CLIENT_PROTOCOL_VERSION 1 + +namespace TmwAthena +{ + +class Network +{ + public: + Network(); + + ~Network(); + + bool connect(ServerInfo server); + + void disconnect(); + + ServerInfo getServer() const + { return mServer; } + + 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 = 0, + CONNECTED, + CONNECTING, + DATA, + NET_ERROR + }; + + protected: + friend int networkThread(void *data); + friend class MessageOut; + + static Network *instance(); + + void setError(const std::string &error); + + Uint16 readWord(int pos); + + bool realConnect(); + + void receive(); + + TCPsocket mSocket; + + ServerInfo mServer; + + char *mInBuffer, *mOutBuffer; + unsigned int mInSize, mOutSize; + + unsigned int mToSkip; + + int mState; + std::string mError; + + SDL_Thread *mWorkerThread; + SDL_mutex *mMutex; + + typedef std::map MessageHandlers; + typedef MessageHandlers::iterator MessageHandlerIterator; + MessageHandlers mMessageHandlers; + + static Network *mInstance; +}; + +} // namespace TmwAthena + +#endif // NET_TA_NETWORK_H diff --git a/src/net/tmwa/npchandler.cpp b/src/net/tmwa/npchandler.cpp new file mode 100644 index 000000000..84fe1789c --- /dev/null +++ b/src/net/tmwa/npchandler.cpp @@ -0,0 +1,249 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/npchandler.h" + +#include "actorspritemanager.h" +#include "localplayer.h" + +#include "gui/npcdialog.h" + +#include "net/messagein.h" +#include "net/messageout.h" +#include "net/net.h" +#include "net/npchandler.h" + +#include "net/tmwa/protocol.h" + +#include + +extern Net::NpcHandler *npcHandler; + +namespace TmwAthena +{ + +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; + npcHandler = this; +} + +void NpcHandler::handleMessage(Net::MessageIn &msg) +{ + if (msg.getId() == SMSG_NPC_CHOICE || msg.getId() == SMSG_NPC_MESSAGE) + msg.readInt16(); // length + + int npcId = msg.readInt32(); + NpcDialogs::iterator diag = mNpcDialogs.find(npcId); + NpcDialog *dialog = 0; + + if (diag == mNpcDialogs.end()) + { + // Empty dialogs don't help + if (msg.getId() == SMSG_NPC_CLOSE) + { + closeDialog(npcId); + return; + } + else if (msg.getId() == SMSG_NPC_NEXT) + { + nextDialog(npcId); + return; + } + else + { + dialog = new NpcDialog(npcId); + Wrapper wrap; + wrap.dialog = dialog; + mNpcDialogs[npcId] = wrap; + } + } + else + { + dialog = diag->second.dialog; + } + + switch (msg.getId()) + { + case SMSG_NPC_CHOICE: + if (dialog) + { + dialog->choiceRequest(); + dialog->parseListItems(msg.readString(msg.getLength() - 8)); + } + else + { + msg.readString(msg.getLength() - 8); + } + break; + + case SMSG_NPC_MESSAGE: + if (dialog) + dialog->addText(msg.readString(msg.getLength() - 8)); + else + msg.readString(msg.getLength() - 8); + break; + + case SMSG_NPC_CLOSE: + // Show the close button + if (dialog) + dialog->showCloseButton(); + break; + + case SMSG_NPC_NEXT: + // Show the next button + if (dialog) + dialog->showNextButton(); + break; + + case SMSG_NPC_INT_INPUT: + // Request for an integer + if (dialog) + dialog->integerRequest(0); + break; + + case SMSG_NPC_STR_INPUT: + // Request for a string + if (dialog) + dialog->textRequest(""); + break; + + default: + break; + } + + if (player_node && player_node->getCurrentAction() != Being::SIT) + player_node->setAction(Being::STAND); +} + +void NpcHandler::talk(int npcId) +{ + MessageOut outMsg(CMSG_NPC_TALK); + outMsg.writeInt32(npcId); + outMsg.writeInt8(0); // Unused +} + +void NpcHandler::nextDialog(int npcId) +{ + MessageOut outMsg(CMSG_NPC_NEXT_REQUEST); + outMsg.writeInt32(npcId); +} + +void NpcHandler::closeDialog(int npcId) +{ + MessageOut outMsg(CMSG_NPC_CLOSE); + outMsg.writeInt32(npcId); + + NpcDialogs::iterator it = mNpcDialogs.find(npcId); + if (it != mNpcDialogs.end()) + { + if ((*it).second.dialog) + (*it).second.dialog->close(); + mNpcDialogs.erase(it); + } +} + +void NpcHandler::listInput(int npcId, unsigned char value) +{ + MessageOut outMsg(CMSG_NPC_LIST_CHOICE); + outMsg.writeInt32(npcId); + outMsg.writeInt8(static_cast(value)); +} + +void NpcHandler::integerInput(int npcId, int value) +{ + MessageOut outMsg(CMSG_NPC_INT_RESPONSE); + outMsg.writeInt32(npcId); + outMsg.writeInt32(value); +} + +void NpcHandler::stringInput(int npcId, const std::string &value) +{ + MessageOut outMsg(CMSG_NPC_STR_RESPONSE); + outMsg.writeInt16(value.length() + 9); + outMsg.writeInt32(npcId); + outMsg.writeString(value, value.length()); + outMsg.writeInt8(0); // Prevent problems with string reading +} + +void NpcHandler::sendLetter(int npcId _UNUSED_, + const std::string &recipient _UNUSED_, + const std::string &text _UNUSED_) +{ + // TODO +} + +void NpcHandler::startShopping(int beingId _UNUSED_) +{ + // TODO +} + +void NpcHandler::buy(int beingId) +{ + MessageOut outMsg(CMSG_NPC_BUY_SELL_REQUEST); + outMsg.writeInt32(beingId); + outMsg.writeInt8(0); // Buy +} + +void NpcHandler::sell(int beingId) +{ + MessageOut outMsg(CMSG_NPC_BUY_SELL_REQUEST); + outMsg.writeInt32(beingId); + outMsg.writeInt8(1); // Sell +} + +void NpcHandler::buyItem(int beingId _UNUSED_, int itemId, int amount) +{ + MessageOut outMsg(CMSG_NPC_BUY_REQUEST); + outMsg.writeInt16(8); // One item (length of packet) + outMsg.writeInt16(amount); + outMsg.writeInt16(itemId); +} + +void NpcHandler::sellItem(int beingId _UNUSED_, int itemId, int amount) +{ + MessageOut outMsg(CMSG_NPC_SELL_REQUEST); + outMsg.writeInt16(8); // One item (length of packet) + outMsg.writeInt16(itemId + INVENTORY_OFFSET); + outMsg.writeInt16(amount); +} + +void NpcHandler::endShopping(int beingId _UNUSED_) +{ + // TODO +} + +void NpcHandler::clearDialogs() +{ + mNpcDialogs.clear(); +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/npchandler.h b/src/net/tmwa/npchandler.h new file mode 100644 index 000000000..7931a3d8b --- /dev/null +++ b/src/net/tmwa/npchandler.h @@ -0,0 +1,90 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_NPCHANDLER_H +#define NET_TA_NPCHANDLER_H + +#include "net/net.h" +#include "net/npchandler.h" + +#include "net/tmwa/messagehandler.h" + +#include + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +class NpcDialog; + +namespace TmwAthena +{ + +class NpcHandler : public MessageHandler, public Net::NpcHandler +{ + public: + NpcHandler(); + + void handleMessage(Net::MessageIn &msg); + + void talk(int npcId); + + void nextDialog(int npcId); + + void closeDialog(int npcId); + + void listInput(int npcId, unsigned char value); + + void integerInput(int npcId, int value); + + void stringInput(int npcId, const std::string &value); + + void sendLetter(int npcId, const std::string &recipient, + const std::string &text); + + void startShopping(int beingId); + + void buy(int beingId); + + void sell(int beingId); + + void buyItem(int beingId, int itemId, int amount); + + void sellItem(int beingId, int itemId, int amount); + + void endShopping(int beingId); + + void clearDialogs(); + + private: + typedef struct + { + NpcDialog* dialog; + } Wrapper; + typedef std::map NpcDialogs; + NpcDialogs mNpcDialogs; +}; + +} // namespace TmwAthena + +#endif // NET_TA_NPCHANDLER_H diff --git a/src/net/tmwa/partyhandler.cpp b/src/net/tmwa/partyhandler.cpp new file mode 100644 index 000000000..eecbf0c0e --- /dev/null +++ b/src/net/tmwa/partyhandler.cpp @@ -0,0 +1,547 @@ +/* + * The Mana Client + * Copyright (C) 2008 Lloyd Bryant + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/partyhandler.h" + +#include "actorspritemanager.h" +#include "event.h" +#include "localplayer.h" +#include "log.h" + +#include "gui/socialwindow.h" + +#include "net/messagein.h" +#include "net/messageout.h" + +#include "net/tmwa/protocol.h" + +#include "net/tmwa/gui/partytab.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +#define PARTY_ID 1 + +extern Net::PartyHandler *partyHandler; + +namespace TmwAthena +{ + +PartyTab *partyTab = 0; +Party *taParty; + +PartyHandler::PartyHandler(): + mShareExp(PARTY_SHARE_UNKNOWN), mShareItems(PARTY_SHARE_UNKNOWN) +{ + static const Uint16 _messages[] = + { + SMSG_PARTY_CREATE, + SMSG_PARTY_INFO, + SMSG_PARTY_INVITE_RESPONSE, + SMSG_PARTY_INVITED, + SMSG_PARTY_SETTINGS, + SMSG_PARTY_MOVE, + SMSG_PARTY_LEAVE, + SMSG_PARTY_UPDATE_HP, + SMSG_PARTY_UPDATE_COORDS, + SMSG_PARTY_MESSAGE, + 0 + }; + handledMessages = _messages; + partyHandler = this; + taParty = Party::getParty(1); +} + +PartyHandler::~PartyHandler() +{ + delete partyTab; + partyTab = 0; +} + +void PartyHandler::handleMessage(Net::MessageIn &msg) +{ + switch (msg.getId()) + { + case SMSG_PARTY_CREATE: + if (msg.readInt8()) + SERVER_NOTICE(_("Could not create party.")) + else + SERVER_NOTICE(_("Party successfully created.")) + break; + case SMSG_PARTY_INFO: + { + if (!taParty) + { + logger->log1("error: party empty in SMSG_PARTY_INFO"); + taParty = Party::getParty(1); + } + if (!player_node) + logger->log1("error: player_node==0 in SMSG_PARTY_INFO"); + + if (taParty) + taParty->clearMembers(); + + int length = msg.readInt16(); + if (taParty) + taParty->setName(msg.readString(24)); + + int count = (length - 28) / 46; + if (player_node && taParty) + { + player_node->setParty(taParty); + player_node->setPartyName(taParty->getName()); + } + + for (int i = 0; i < count; i++) + { + int id = msg.readInt32(); + std::string nick = msg.readString(24); + std::string map = msg.readString(16); + bool leader = msg.readInt8() == 0; + bool online = msg.readInt8() == 0; + + if (taParty) + { + PartyMember *member = taParty->addMember(id, nick); + if (member) + { + member->setLeader(leader); + member->setOnline(online); + member->setMap(map); + } + } + } + + if (taParty) + taParty->sort(); + + if (player_node && taParty) + { + player_node->setParty(taParty); + player_node->setPartyName(taParty->getName()); + } + } + break; + case SMSG_PARTY_INVITE_RESPONSE: + { + if (!partyTab) + break; + + std::string nick = msg.readString(24); + + switch (msg.readInt8()) + { + case 0: + partyTab->chatLog(strprintf( + _("%s is already a member of a party."), + nick.c_str()), BY_SERVER); + break; + case 1: + partyTab->chatLog(strprintf( + _("%s refused your invitation."), + nick.c_str()), BY_SERVER); + break; + case 2: + partyTab->chatLog(strprintf( + _("%s is now a member of your party."), + nick.c_str()), BY_SERVER); + break; + case 3: + partyTab->chatLog(strprintf( + _("%s cant joid your party because party is " + "full."), nick.c_str()), BY_SERVER); + break; + default: + partyTab->chatLog(strprintf( + _("QQQ Unknown invite response for %s."), + nick.c_str()), BY_SERVER); + break; + } + break; + } + case SMSG_PARTY_INVITED: + { + int id = msg.readInt32(); + std::string partyName = msg.readString(24); + std::string nick = ""; + Being *being; + + if (actorSpriteManager) + { + if ((being = actorSpriteManager->findBeing(id))) + { + if (being && being->getType() == Being::PLAYER) + nick = being->getName(); + } + } + + if (socialWindow) + socialWindow->showPartyInvite(partyName, nick); + break; + } + case SMSG_PARTY_SETTINGS: + { + if (!partyTab) + { + if (!chatWindow) + break; + + partyTab = new PartyTab(); + } + + // These seem to indicate the sharing mode for exp and items + short exp = msg.readInt16(); + short item = msg.readInt16(); + + if (!partyTab) + break; + + switch (exp) + { + case PARTY_SHARE: + if (mShareExp == PARTY_SHARE) + break; + mShareExp = PARTY_SHARE; + if (partyTab) + { + partyTab->chatLog( + _("Experience sharing enabled."), BY_SERVER); + } + break; + case PARTY_SHARE_NO: + if (mShareExp == PARTY_SHARE_NO) + break; + mShareExp = PARTY_SHARE_NO; + if (partyTab) + { + partyTab->chatLog( + _("Experience sharing disabled."), BY_SERVER); + } + break; + case PARTY_SHARE_NOT_POSSIBLE: + if (mShareExp == PARTY_SHARE_NOT_POSSIBLE) + break; + mShareExp = PARTY_SHARE_NOT_POSSIBLE; + if (partyTab) + { + partyTab->chatLog( + _("Experience sharing not possible."), + BY_SERVER); + } + break; + default: + logger->log("QQQ Unknown party exp option: %d\n", exp); + break; + } + + switch (item) + { + case PARTY_SHARE: + if (mShareItems == PARTY_SHARE) + break; + mShareItems = PARTY_SHARE; + if (partyTab) + { + partyTab->chatLog( + _("Item sharing enabled."), BY_SERVER); + } + break; + case PARTY_SHARE_NO: + if (mShareItems == PARTY_SHARE_NO) + break; + mShareItems = PARTY_SHARE_NO; + if (partyTab) + { + partyTab->chatLog( + _("Item sharing disabled."), BY_SERVER); + } + break; + case PARTY_SHARE_NOT_POSSIBLE: + if (mShareItems == PARTY_SHARE_NOT_POSSIBLE) + break; + mShareItems = PARTY_SHARE_NOT_POSSIBLE; + if (partyTab) + { + partyTab->chatLog( + _("Item sharing not possible."), BY_SERVER); + } + break; + default: + logger->log("QQQ Unknown party item option: %d\n", + exp); + break; + } + break; + } + case SMSG_PARTY_MOVE: + { + int id = msg.readInt32(); // id + PartyMember *m = 0; + if (taParty) + m = taParty->getMember(id); + if (m) + { + msg.skip(4); + m->setX(msg.readInt16()); // x + m->setY(msg.readInt16()); // y + m->setOnline(msg.readInt8()); // online (if 0) + msg.readString(24); // party + msg.readString(24); // nick + m->setMap(msg.readString(16)); // map + } + else + { + msg.skip(4); + msg.readInt16(); // x + msg.readInt16(); // y + msg.readInt8(); // online (if 0) + msg.readString(24); // party + msg.readString(24); // nick + msg.readString(16); // map + } + } + break; + case SMSG_PARTY_LEAVE: + { + int id = msg.readInt32(); + std::string nick = msg.readString(24); + msg.readInt8(); // fail + if (player_node && id == player_node->getId()) + { + if (taParty) + { + taParty->removeFromMembers(); + taParty->clearMembers(); + } + SERVER_NOTICE(_("You have left the party.")) + delete partyTab; + partyTab = 0; + + if (socialWindow && taParty) + socialWindow->removeTab(taParty); + } + else + { + if (partyTab) + { + partyTab->chatLog(strprintf( + _("%s has left your party."), + nick.c_str()), BY_SERVER); + } + if (actorSpriteManager) + { + Being *b = actorSpriteManager->findBeing(id); + if (b && b->getType() == Being::PLAYER) + b->setParty(0); + } + if (taParty) + taParty->removeMember(id); + } + break; + } + case SMSG_PARTY_UPDATE_HP: + { + int id = msg.readInt32(); + int hp = msg.readInt16(); + int maxhp = msg.readInt16(); + PartyMember *m = 0; + if (taParty) + m = taParty->getMember(id); + if (m) + { + m->setHp(hp); + m->setMaxHp(maxhp); + } + + // The server only sends this when the member is in range, so + // lets make sure they get the party hilight. + if (actorSpriteManager && taParty) + { + if (Being *b = actorSpriteManager->findBeing(id)) + b->setParty(taParty); + } + } + break; + case SMSG_PARTY_UPDATE_COORDS: + { + int id = msg.readInt32(); // id + PartyMember *m = 0; + if (taParty) + m = taParty->getMember(id); + if (m) + { + m->setX(msg.readInt16()); // x + m->setY(msg.readInt16()); // y + } + else + { + msg.readInt16(); // x + msg.readInt16(); // y + } + } + break; + case SMSG_PARTY_MESSAGE: + { + int msgLength = msg.readInt16() - 8; + if (msgLength <= 0) + return; + + int id = msg.readInt32(); + std::string chatMsg = msg.readString(msgLength); + + if (taParty) + { + PartyMember *member = taParty->getMember(id); + if (partyTab) + { + if (member) + { + partyTab->chatLog(member->getName(), chatMsg); + } + else + { + partyTab->chatLog(strprintf( + _("An unknown member tried to say: %s"), + chatMsg.c_str()), BY_SERVER); + } + } + } + } + break; + + default: + break; + } +} + +void PartyHandler::create(const std::string &name) +{ + MessageOut outMsg(CMSG_PARTY_CREATE); + outMsg.writeString(name.substr(0, 23), 24); +} + +void PartyHandler::join(int partyId _UNUSED_) +{ + // TODO? +} + +void PartyHandler::invite(Being *being) +{ + if (being) + { + MessageOut outMsg(CMSG_PARTY_INVITE); + outMsg.writeInt32(being->getId()); + } +} + +void PartyHandler::invite(const std::string &name) +{ + if (!actorSpriteManager) + return; + + Being* being = actorSpriteManager->findBeingByName(name, Being::PLAYER); + if (being) + { + MessageOut outMsg(CMSG_PARTY_INVITE); + outMsg.writeInt32(being->getId()); + } +} + +void PartyHandler::inviteResponse(const std::string &inviter _UNUSED_, + bool accept) +{ + if (player_node) + { + MessageOut outMsg(CMSG_PARTY_INVITED); + outMsg.writeInt32(player_node->getId()); + outMsg.writeInt32(accept ? 1 : 0); + } +} + +void PartyHandler::leave() +{ + MessageOut outMsg(CMSG_PARTY_LEAVE); +} + +void PartyHandler::kick(Being *being) +{ + if (being) + { + MessageOut outMsg(CMSG_PARTY_KICK); + outMsg.writeInt32(being->getId()); + outMsg.writeString("", 24); //Unused + } +} + +void PartyHandler::kick(const std::string &name) +{ + if (!taParty) + return; + + PartyMember *m = taParty->getMember(name); + if (!m) + { + if (partyTab) + { + partyTab->chatLog(strprintf(_("%s is not in your party!"), + name.c_str()), BY_SERVER); + } + return; + } + + MessageOut outMsg(CMSG_PARTY_KICK); + outMsg.writeInt32(m->getID()); + outMsg.writeString(name, 24); //Unused +} + +void PartyHandler::chat(const std::string &text) +{ + MessageOut outMsg(CMSG_PARTY_MESSAGE); + outMsg.writeInt16(text.length() + 4); + outMsg.writeString(text, text.length()); +} + +void PartyHandler::requestPartyMembers() +{ + // Our eAthena doesn't have this message + // Not needed anyways +} + +void PartyHandler::setShareExperience(PartyShare share) +{ + if (share == PARTY_SHARE_NOT_POSSIBLE) + return; + + MessageOut outMsg(CMSG_PARTY_SETTINGS); + outMsg.writeInt16(share); + outMsg.writeInt16(mShareItems); +} + +void PartyHandler::setShareItems(PartyShare share) +{ + if (share == PARTY_SHARE_NOT_POSSIBLE) + return; + + MessageOut outMsg(CMSG_PARTY_SETTINGS); + outMsg.writeInt16(mShareExp); + outMsg.writeInt16(share); +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/partyhandler.h b/src/net/tmwa/partyhandler.h new file mode 100644 index 000000000..8a199ded3 --- /dev/null +++ b/src/net/tmwa/partyhandler.h @@ -0,0 +1,85 @@ +/* + * The Mana Client + * Copyright (C) 2008 Lloyd Bryant + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_PARTYHANDLER_H +#define NET_TA_PARTYHANDLER_H + +#include "net/net.h" +#include "net/partyhandler.h" + +#include "net/tmwa/messagehandler.h" + +#include "party.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace TmwAthena +{ + +class PartyHandler : public MessageHandler, public Net::PartyHandler +{ + public: + PartyHandler(); + + ~PartyHandler(); + + void handleMessage(Net::MessageIn &msg); + + void create(const std::string &name = ""); + + void join(int partyId); + + void invite(Being *being); + + void invite(const std::string &name); + + void inviteResponse(const std::string &inviter, bool accept); + + void leave(); + + void kick(Being *being); + + void kick(const std::string &name); + + void chat(const std::string &text); + + void requestPartyMembers(); + + PartyShare getShareExperience() + { return mShareExp; } + + void setShareExperience(PartyShare share); + + PartyShare getShareItems() + { return mShareItems; } + + void setShareItems(PartyShare share); + + private: + PartyShare mShareExp, mShareItems; +}; + +} // namespace TmwAthena + +#endif // NET_TA_PARTYHANDLER_H diff --git a/src/net/tmwa/playerhandler.cpp b/src/net/tmwa/playerhandler.cpp new file mode 100644 index 000000000..1d0d3e67d --- /dev/null +++ b/src/net/tmwa/playerhandler.cpp @@ -0,0 +1,752 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/playerhandler.h" + +#include "event.h" +#include "game.h" +#include "localplayer.h" +#include "log.h" +#include "party.h" +#include "playerinfo.h" +#include "units.h" + +#include "gui/buy.h" +#include "gui/buysell.h" +#include "gui/gui.h" +#include "gui/npcdialog.h" +#include "gui/okdialog.h" +#include "gui/sell.h" +#include "gui/statuswindow.h" +#include "gui/viewport.h" + +#include "net/messagein.h" +#include "net/messageout.h" + +#include "net/tmwa/protocol.h" +#include "net/tmwa/npchandler.h" + +#include "utils/stringutils.h" +#include "utils/gettext.h" + +extern OkDialog *weightNotice; +extern OkDialog *deathNotice; + +// 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; + +// TODO Move somewhere else +namespace +{ + /** + * Listener used for handling the overweigth message. + */ + struct WeightListener : public gcn::ActionListener + { + void action(const gcn::ActionEvent &event _UNUSED_) + { + weightNotice = NULL; + } + } weightListener; + + /** + * Listener used for handling death message. + */ + struct DeathListener : public gcn::ActionListener + { + void action(const gcn::ActionEvent &event _UNUSED_) + { + Net::getPlayerHandler()->respawn(); + deathNotice = NULL; + + BuyDialog::closeAll(); + BuySellDialog::closeAll(); + NpcDialog::closeAll(); + SellDialog::closeAll(); + + if (viewport) + viewport->closePopupMenu(); + + TmwAthena::NpcHandler *handler = + static_cast(Net::getNpcHandler()); + if (handler) + handler->clearDialogs(); + if (player_node) + player_node->respawn(); + } + } deathListener; + +} // anonymous namespace + +static const char *randomDeathMessage() +{ + static char const *const deadMsg[] = + { + N_("You are dead."), + N_("We regret to inform you that your character was killed in " + "battle."), + N_("You are not that alive anymore."), + N_("The cold hands of the grim reaper are grabbing for your soul."), + N_("Game Over!"), + N_("Insert coin to continue."), + N_("No, kids. Your character did not really die. It... " + "err... went to a better place."), + N_("Your plan of breaking your enemies weapon by " + "bashing it with your throat failed."), + N_("I guess this did not run too well."), + // NetHack reference: + N_("Do you want your possessions identified?"), + // Secret of Mana reference: + N_("Sadly, no trace of you was ever found..."), + // Final Fantasy VI reference: + N_("Annihilated."), + // Earthbound reference: + N_("Looks like you got your head handed to you."), + // Leisure Suit Larry 1 reference: + N_("You screwed up again, dump your body down the tubes " + "and get you another one."), + // Monty Python references (Dead Parrot sketch mostly): + N_("You're not dead yet. You're just resting."), + N_("You are no more."), + N_("You have ceased to be."), + N_("You've expired and gone to meet your maker."), + N_("You're a stiff."), + N_("Bereft of life, you rest in peace."), + N_("If you weren't so animated, you'd be pushing up the daisies."), + N_("Your metabolic processes are now history."), + N_("You're off the twig."), + N_("You've kicked the bucket."), + N_("You've shuffled off your mortal coil, run down the " + "curtain and joined the bleedin' choir invisibile."), + N_("You are an ex-player."), + N_("You're pining for the fjords.") + }; + + const int random = rand() % (sizeof(deadMsg) / sizeof(deadMsg[0])); + return gettext(deadMsg[random]); +} + +extern Net::PlayerHandler *playerHandler; + +namespace TmwAthena +{ + +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; + playerHandler = this; +} + +void PlayerHandler::handleMessage(Net::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. + */ + Uint16 srcX, srcY, dstX, dstY; + msg.readInt32(); //tick + msg.readCoordinatePair(srcX, srcY, dstX, dstY); + if (player_node) + player_node->setRealPos(dstX, dstY); +// if (debugChatTab) +// debugChatTab->chatLog("move resp: " + toString((int)srcX) + "," + toString((int)srcY) + " " +// + toString((int)dstX) + "," + toString((int)dstY)); + + break; + + case SMSG_PLAYER_WARP: + { + std::string mapPath = msg.readString(16); + int x = msg.readInt16(); + int y = msg.readInt16(); + + logger->log("Warping to %s (%d, %d)", mapPath.c_str(), x, y); + + if (!player_node) + logger->log1("SMSG_PLAYER_WARP player_node null"); + + /* + * We must clear the local player's target *before* the call + * to changeMap, as it deletes all beings. + */ + if (player_node) + player_node->stopAttack(); + + Game *game = Game::instance(); + + const std::string ¤tMapName = game->getCurrentMapName(); + bool sameMap = (currentMapName == mapPath); + + // Switch the actual map, deleting the previous one if necessary + mapPath = mapPath.substr(0, mapPath.rfind(".")); + game->changeMap(mapPath); + + float scrollOffsetX = 0.0f; + float scrollOffsetY = 0.0f; + + if (player_node) + { + /* Scroll if neccessary */ + if (!sameMap + || (abs(x - player_node->getTileX()) + > MAP_TELEPORT_SCROLL_DISTANCE) + || (abs(y - player_node->getTileY()) + > MAP_TELEPORT_SCROLL_DISTANCE)) + { + Map *map = game->getCurrentMap(); + if (map) + { + scrollOffsetX = (x - player_node->getTileX()) + * map->getTileWidth(); + scrollOffsetY = (y - player_node->getTileY()) + * map->getTileHeight(); + } + } + + player_node->setAction(Being::STAND); + player_node->setTileCoords(x, y); + player_node->naviageClean(); +// player_node->updateNavigateList(); + } + + logger->log("Adjust scrolling by %d:%d", (int) scrollOffsetX, + (int) scrollOffsetY); + + if (viewport) + viewport->scrollBy(scrollOffsetX, scrollOffsetY); + } + break; + + case SMSG_PLAYER_STAT_UPDATE_1: + { + int type = msg.readInt16(); + int value = msg.readInt32(); + if (!player_node) + return; + + switch (type) + { + case 0x0000: + player_node->setWalkSpeed(Vector(value, value, 0)); + PlayerInfo::setStatBase(WALK_SPEED, value); + PlayerInfo::setStatMod(WALK_SPEED, 0); + break; + case 0x0004: break; // manner + case 0x0005: + PlayerInfo::setAttribute(HP, value); + if (player_node->isInParty() && Party::getParty(1)) + { + PartyMember *m = Party::getParty(1) + ->getMember(player_node->getId()); + if (m) + { + m->setHp(value); + m->setMaxHp(PlayerInfo::getAttribute(MAX_HP)); + } + } + break; + case 0x0006: + PlayerInfo::setAttribute(MAX_HP, value); + + if (player_node->isInParty() && Party::getParty(1)) + { + PartyMember *m = Party::getParty(1)->getMember( + player_node->getId()); + if (m) + { + m->setHp(PlayerInfo::getAttribute(HP)); + m->setMaxHp(value); + } + } + break; + case 0x0007: + PlayerInfo::setAttribute(MP, value); + break; + case 0x0008: + PlayerInfo::setAttribute(MAX_MP, value); + break; + case 0x0009: + PlayerInfo::setAttribute(CHAR_POINTS, value); + break; + case 0x000b: + PlayerInfo::setAttribute(LEVEL, value); + if (player_node) + { + player_node->setLevel(value); + player_node->updateName(); + } + break; + case 0x000c: + PlayerInfo::setAttribute(SKILL_POINTS, value); + break; + case 0x0018: + if (!weightNotice) + { + const int max + = PlayerInfo::getAttribute(MAX_WEIGHT) / 2; + const int total + = PlayerInfo::getAttribute(TOTAL_WEIGHT); + if (value >= max && total < max) + { + weightNotice = new OkDialog(_("Message"), + _("You are carrying more than " + "half your weight. You are " + "unable to regain health.")); + weightNotice->addActionListener( + &weightListener); + } + else if (value < max && total >= max) + { + weightNotice = new OkDialog(_("Message"), + _("You are carrying less than " + "half your weight. You are " + "can regain health.")); + weightNotice->addActionListener( + &weightListener); + } + } + PlayerInfo::setAttribute(TOTAL_WEIGHT, value); + break; + case 0x0019: + PlayerInfo::setAttribute(MAX_WEIGHT, value); + break; + + case 0x0029: + PlayerInfo::setStatBase(ATK, value); + break; + case 0x002a: + PlayerInfo::setStatMod(ATK, value); + break; + + case 0x002b: + PlayerInfo::setStatBase(MATK, value); + break; + case 0x002c: + PlayerInfo::setStatMod(MATK, value); + break; + + case 0x002d: + PlayerInfo::setStatBase(DEF, value); + break; + case 0x002e: + PlayerInfo::setStatMod(DEF, value); + break; + + case 0x002f: + PlayerInfo::setStatBase(MDEF, value); + break; + case 0x0030: + PlayerInfo::setStatMod(MDEF, value); + break; + + case 0x0031: + PlayerInfo::setStatBase(HIT, value); + break; + + case 0x0032: + PlayerInfo::setStatBase(FLEE, value); + break; + case 0x0033: + PlayerInfo::setStatMod(FLEE, value); + break; + + case 0x0034: + PlayerInfo::setStatBase(CRIT, value); + break; + + case 0x0035: + player_node->setAttackSpeed(value); + PlayerInfo::setStatBase(ATTACK_SPEED, value); + PlayerInfo::setStatMod(ATTACK_SPEED, 0); + break; + + case 0x0037: + PlayerInfo::setStatBase(JOB, value); + break; + + case 500: + player_node->setGMLevel(value); + break; + + default: + logger->log("QQQQ PLAYER_STAT_UPDATE_1 " + + toString(type) + "," + toString(value)); + break; + } + + if (PlayerInfo::getAttribute(HP) == 0 && !deathNotice) + { + deathNotice = new OkDialog(_("Message"), + randomDeathMessage(), + false); + deathNotice->addActionListener(&deathListener); + player_node->setAction(Being::DEAD); + } + } + break; + + case SMSG_PLAYER_STAT_UPDATE_2: + { + int type = msg.readInt16(); + switch (type) + { + case 0x0001: + PlayerInfo::setAttribute(EXP, msg.readInt32()); + break; + case 0x0002: + PlayerInfo::setStatExperience(JOB, msg.readInt32(), + PlayerInfo::getStatExperience(JOB).second); + break; + case 0x0014: + { + int oldMoney = PlayerInfo::getAttribute(MONEY); + int newMoney = msg.readInt32(); + if (newMoney > oldMoney) + { + SERVER_NOTICE(strprintf(_("You picked up %s."), + Units::formatCurrency(newMoney - + oldMoney).c_str())) + } + else if (newMoney < oldMoney) + { + SERVER_NOTICE(strprintf(_("You spent %s."), + Units::formatCurrency(oldMoney - + newMoney).c_str())) + } + + PlayerInfo::setAttribute(MONEY, newMoney); + break; + } + case 0x0016: + PlayerInfo::setAttribute(EXP_NEEDED, msg.readInt32()); + break; + case 0x0017: + PlayerInfo::setStatExperience(JOB, + PlayerInfo::getStatExperience(JOB).first, + msg.readInt32()); + break; + default: + logger->log("QQQQ PLAYER_STAT_UPDATE_2 " + toString(type)); + break; + } + break; + } + case SMSG_PLAYER_STAT_UPDATE_3: // Update a base attribute + { + int type = msg.readInt32(); + int base = msg.readInt32(); + int bonus = msg.readInt32(); + + PlayerInfo::setStatBase(type, base, false); + PlayerInfo::setStatMod(type, bonus); + } + break; + + case SMSG_PLAYER_STAT_UPDATE_4: // Attribute increase ack + { + int type = msg.readInt16(); + int ok = msg.readInt8(); + int value = msg.readInt8(); + + if (ok != 1) + { + int oldValue = PlayerInfo::getStatBase(type); + int points = PlayerInfo::getAttribute(CHAR_POINTS); + points += oldValue - value; + PlayerInfo::setAttribute(CHAR_POINTS, points); + SERVER_NOTICE(_("Cannot raise skill!")) + } + + PlayerInfo::setStatBase(type, value); + } + break; + + // Updates stats and status points + case SMSG_PLAYER_STAT_UPDATE_5: + PlayerInfo::setAttribute(CHAR_POINTS, msg.readInt16()); + { + int val = msg.readInt8(); + PlayerInfo::setStatBase(STR, val); + if (statusWindow) + statusWindow->setPointsNeeded(STR, msg.readInt8()); + else + msg.readInt8(); + + val = msg.readInt8(); + PlayerInfo::setStatBase(AGI, val); + if (statusWindow) + statusWindow->setPointsNeeded(AGI, msg.readInt8()); + else + msg.readInt8(); + + val = msg.readInt8(); + PlayerInfo::setStatBase(VIT, val); + if (statusWindow) + statusWindow->setPointsNeeded(VIT, msg.readInt8()); + else + msg.readInt8(); + + val = msg.readInt8(); + PlayerInfo::setStatBase(INT, val); + if (statusWindow) + statusWindow->setPointsNeeded(INT, msg.readInt8()); + else + msg.readInt8(); + + val = msg.readInt8(); + PlayerInfo::setStatBase(DEX, val); + if (statusWindow) + statusWindow->setPointsNeeded(DEX, msg.readInt8()); + else + msg.readInt8(); + + val = msg.readInt8(); + PlayerInfo::setStatBase(LUK, val); + if (statusWindow) + statusWindow->setPointsNeeded(LUK, msg.readInt8()); + else + msg.readInt8(); + + PlayerInfo::setStatBase(ATK, msg.readInt16(), false); + PlayerInfo::setStatMod(ATK, msg.readInt16()); + + val = msg.readInt16(); + PlayerInfo::setStatBase(MATK, val, false); + + val = msg.readInt16(); + PlayerInfo::setStatMod(MATK, val); + + PlayerInfo::setStatBase(DEF, msg.readInt16(), false); + PlayerInfo::setStatMod(DEF, msg.readInt16()); + + PlayerInfo::setStatBase(MDEF, msg.readInt16(), false); + PlayerInfo::setStatMod(MDEF, msg.readInt16()); + + PlayerInfo::setStatBase(HIT, msg.readInt16()); + + PlayerInfo::setStatBase(FLEE, msg.readInt16(), false); + PlayerInfo::setStatMod(FLEE, msg.readInt16()); + + PlayerInfo::setStatBase(CRIT, msg.readInt16()); + } + + msg.readInt16(); // manner + break; + + case SMSG_PLAYER_STAT_UPDATE_6: + { + int type = msg.readInt16(); + if (statusWindow) + { + switch (type) + { + case 0x0020: + statusWindow->setPointsNeeded(STR, msg.readInt8()); + break; + case 0x0021: + statusWindow->setPointsNeeded(AGI, msg.readInt8()); + break; + case 0x0022: + statusWindow->setPointsNeeded(VIT, msg.readInt8()); + break; + case 0x0023: + statusWindow->setPointsNeeded(INT, msg.readInt8()); + break; + case 0x0024: + statusWindow->setPointsNeeded(DEX, msg.readInt8()); + break; + case 0x0025: + statusWindow->setPointsNeeded(LUK, msg.readInt8()); + break; + default: + logger->log("QQQQ PLAYER_STAT_UPDATE_6 " + + toString(type)); + break; + } + } + break; + } + case SMSG_PLAYER_ARROW_MESSAGE: + { + int type = msg.readInt16(); + + switch (type) + { + case 0: + { + SERVER_NOTICE(_("Equip arrows first.")) + } + break; + case 3: + // arrows equiped + break; + default: + logger->log("QQQQ 0x013b: Unhandled message %i", type); + break; + } + } + break; + + default: + break; + } +} + +void PlayerHandler::attack(int id, bool keep) +{ + MessageOut outMsg(CMSG_PLAYER_ATTACK); + outMsg.writeInt32(id); + if (keep) + outMsg.writeInt8(7); + else + outMsg.writeInt8(0); +} + +void PlayerHandler::stopAttack() +{ + MessageOut outMsg(CMSG_PLAYER_STOP_ATTACK); +} + +void PlayerHandler::emote(int emoteId) +{ + MessageOut outMsg(CMSG_PLAYER_EMOTE); + outMsg.writeInt8(emoteId); +} + +void PlayerHandler::increaseAttribute(int attr) +{ + if (attr >= STR && attr <= LUK) + { + MessageOut outMsg(CMSG_STAT_UPDATE_REQUEST); + outMsg.writeInt16(attr); + outMsg.writeInt8(1); + } +} + +void PlayerHandler::decreaseAttribute(int attr _UNUSED_) +{ + // Supported by eA? +} + +void PlayerHandler::increaseSkill(int skillId) +{ + if (PlayerInfo::getAttribute(SKILL_POINTS) <= 0) + return; + + MessageOut outMsg(CMSG_SKILL_LEVELUP_REQUEST); + outMsg.writeInt16(skillId); +} + +void PlayerHandler::pickUp(FloorItem *floorItem) +{ + if (!floorItem) + return; + + MessageOut outMsg(CMSG_ITEM_PICKUP); + outMsg.writeInt32(floorItem->getId()); +} + +void PlayerHandler::setDirection(char direction) +{ + MessageOut outMsg(CMSG_PLAYER_CHANGE_DIR); + outMsg.writeInt16(0); + outMsg.writeInt8(direction); +} + +void PlayerHandler::setDestination(int x, int y, int direction) +{ + MessageOut outMsg(CMSG_PLAYER_CHANGE_DEST); + outMsg.writeCoordinates(x, y, direction); +} + +void PlayerHandler::changeAction(Being::Action action) +{ + char type; + switch (action) + { + case Being::SIT: type = 2; break; + case Being::STAND: type = 3; break; + default: return; + } + + MessageOut outMsg(CMSG_PLAYER_CHANGE_ACT); + outMsg.writeInt32(0); + outMsg.writeInt8(type); +} + +void PlayerHandler::respawn() +{ + MessageOut outMsg(CMSG_PLAYER_RESTART); + outMsg.writeInt8(0); +} + +void PlayerHandler::ignorePlayer(const std::string &player _UNUSED_, + bool ignore _UNUSED_) +{ + // TODO +} + +void PlayerHandler::ignoreAll(bool ignore _UNUSED_) +{ + // TODO +} + +bool PlayerHandler::canUseMagic() +{ + return PlayerInfo::getStatEffective(MATK) > 0; +} + +bool PlayerHandler::canCorrectAttributes() +{ + return false; +} + +int PlayerHandler::getJobLocation() +{ + return JOB; +} + +Vector PlayerHandler::getDefaultWalkSpeed() +{ + // Return an normalized speed for any side + // as the offset is calculated elsewhere. + return Vector(150, 150, 0); +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/playerhandler.h b/src/net/tmwa/playerhandler.h new file mode 100644 index 000000000..6e1c6dc78 --- /dev/null +++ b/src/net/tmwa/playerhandler.h @@ -0,0 +1,74 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_PLAYERHANDLER_H +#define NET_TA_PLAYERHANDLER_H + +#include "net/net.h" +#include "net/playerhandler.h" + +#include "net/tmwa/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace TmwAthena +{ + +class PlayerHandler : public MessageHandler, public Net::PlayerHandler +{ + public: + PlayerHandler(); + + void handleMessage(Net::MessageIn &msg); + + void attack(int id, bool keep = false); + void stopAttack(); + void emote(int emoteId); + + void increaseAttribute(int attr); + void decreaseAttribute(int attr); + void increaseSkill(int skillId); + + void pickUp(FloorItem *floorItem); + void setDirection(char direction); + void setDestination(int x, int y, int direction = -1); + void changeAction(Being::Action action); + + void respawn(); + + void ignorePlayer(const std::string &player, bool ignore); + void ignoreAll(bool ignore); + + bool canUseMagic(); + bool canCorrectAttributes(); + + int getJobLocation(); + + Vector getDefaultWalkSpeed(); +}; + +} // namespace TmwAthena + +#endif // NET_TA_PLAYERHANDLER_H diff --git a/src/net/tmwa/protocol.h b/src/net/tmwa/protocol.h new file mode 100644 index 000000000..6001fae3b --- /dev/null +++ b/src/net/tmwa/protocol.h @@ -0,0 +1,327 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TA_PROTOCOL_H +#define TA_PROTOCOL_H + +enum +{ + JOB = 0xa, + + STR = 0xd, + AGI, + VIT, + INT, + DEX, + LUK, + + ATK, + DEF, + MATK, + MDEF, + HIT, + FLEE, + CRIT + +// KARMA, +// MANNER +}; + +enum +{ + SPRITE_BASE = 0, + SPRITE_SHOE, + SPRITE_BOTTOMCLOTHES, + SPRITE_TOPCLOTHES, + SPRITE_MISC1, + SPRITE_MISC2, + SPRITE_HAIR, + SPRITE_HAT, + SPRITE_CAPE, + SPRITE_GLOVES, + SPRITE_WEAPON, + SPRITE_SHIELD, + SPRITE_VECTOREND +}; + +static const int INVENTORY_OFFSET = 2; +static const int STORAGE_OFFSET = 1; + +/********************************* + * Packets from server to client * + *********************************/ +#define SMSG_SERVER_VERSION_RESPONSE 0x7531 + +#define SMSG_SERVER_PING 0x007f /**< Contains server tick */ +#define SMSG_CONNECTION_PROBLEM 0x0081 + +#define SMSG_UPDATE_HOST 0x0063 /**< Custom update host packet */ +#define SMSG_LOGIN_DATA 0x0069 +#define SMSG_LOGIN_ERROR 0x006a + +#define SMSG_CHAR_LOGIN 0x006b +#define SMSG_CHAR_LOGIN_ERROR 0x006c +#define SMSG_CHAR_CREATE_SUCCEEDED 0x006d +#define SMSG_CHAR_CREATE_FAILED 0x006e +#define SMSG_CHAR_DELETE_SUCCEEDED 0x006f +#define SMSG_CHAR_DELETE_FAILED 0x0070 +#define SMSG_CHAR_MAP_INFO 0x0071 +#define SMSG_CHAR_PASSWORD_RESPONSE 0x0062 /**< Custom packet reply to password change request */ + +#define SMSG_CHAR_SWITCH_RESPONSE 0x00b3 +#define SMSG_CHANGE_MAP_SERVER 0x0092 + +#define SMSG_MAP_LOGIN_SUCCESS 0x0073 /**< Contains starting location */ +#define SMSG_MAP_QUIT_RESPONSE 0x018b +#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_PLAYER_SKILL_UP 0x010e +#define SMSG_SKILL_FAILED 0x0110 +#define SMSG_SKILL_DAMAGE 0x01de +#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_BEING_CHANGE_DIRECTION 0x009c +#define SMSG_BEING_RESURRECT 0x0148 + +#define SMSG_PLAYER_STATUS_CHANGE 0x0119 +#define SMSG_PLAYER_GUILD_PARTY_INFO 0x0195 +#define SMSG_BEING_STATUS_CHANGE 0x0196 + +#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_RESPONSE 0x00fd +#define SMSG_PARTY_INVITED 0x00fe +#define SMSG_PARTY_SETTINGS 0x0101 +#define SMSG_PARTY_MOVE 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 */ + +#define SMSG_ADMIN_KICK_ACK 0x00cd + +#define SMSG_GUILD_CREATE_RESPONSE 0x0167 +#define SMSG_GUILD_POSITION_INFO 0x016c +#define SMSG_GUILD_MEMBER_LOGIN 0x016d +#define SMSG_GUILD_MASTER_OR_MEMBER 0x014e +#define SMSG_GUILD_BASIC_INFO 0x01b6 +#define SMSG_GUILD_ALIANCE_INFO 0x014c +#define SMSG_GUILD_MEMBER_LIST 0x0154 +#define SMSG_GUILD_POS_NAME_LIST 0x0166 +#define SMSG_GUILD_POS_INFO_LIST 0x0160 +#define SMSG_GUILD_POSITION_CHANGED 0x0174 +#define SMSG_GUILD_MEMBER_POS_CHANGE 0x0156 +#define SMSG_GUILD_EMBLEM 0x0152 +#define SMSG_GUILD_SKILL_INFO 0x0162 +#define SMSG_GUILD_NOTICE 0x016f +#define SMSG_GUILD_INVITE 0x016a +#define SMSG_GUILD_INVITE_ACK 0x0169 +#define SMSG_GUILD_LEAVE 0x015a +#define SMSG_GUILD_EXPULSION 0x015c +#define SMSG_GUILD_EXPULSION_LIST 0x0163 +#define SMSG_GUILD_MESSAGE 0x017f +#define SMSG_GUILD_SKILL_UP 0x010e +#define SMSG_GUILD_REQ_ALLIANCE 0x0171 +#define SMSG_GUILD_REQ_ALLIANCE_ACK 0x0173 +#define SMSG_GUILD_DEL_ALLIANCE 0x0184 +#define SMSG_GUILD_OPPOSITION_ACK 0x0181 +#define SMSG_GUILD_BROKEN 0x015e + +#define SMSG_MVP 0x010c + +/********************************** + * Packets from client to server * + **********************************/ +#define CMSG_SERVER_VERSION_REQUEST 0x7530 + +#define CMSG_CHAR_PASSWORD_CHANGE 0x0061 /**< Custom change password packet */ +#define CMSG_CHAR_SERVER_CONNECT 0x0065 +#define CMSG_CHAR_SELECT 0x0066 +#define CMSG_CHAR_CREATE 0x0067 +#define CMSG_CHAR_DELETE 0x0068 + +#define CMSG_MAP_SERVER_CONNECT 0x0072 +#define CMSG_CLIENT_PING 0x007e /**< Send to server with tick */ +#define CMSG_MAP_LOADED 0x007d +#define CMSG_CLIENT_QUIT 0x018A + +#define CMSG_CHAT_MESSAGE 0x008c +#define CMSG_CHAT_WHISPER 0x0096 +#define CMSG_CHAT_ANNOUNCE 0x0099 +#define CMSG_CHAT_WHO 0x00c1 + +#define CMSG_SKILL_LEVELUP_REQUEST 0x0112 +#define CMSG_STAT_UPDATE_REQUEST 0x00bb +#define CMSG_SKILL_USE_BEING 0x0113 +#define CMSG_SKILL_USE_POSITION 0x0116 +// Variant of 0x116 with 80 char string at end (unsure of use) +#define CMSG_SKILL_USE_POSITION_MORE 0x0190 +#define CMSG_SKILL_USE_MAP 0x011b + +#define CMSG_PLAYER_INVENTORY_USE 0x00a7 +#define CMSG_PLAYER_INVENTORY_DROP 0x00a2 +#define CMSG_PLAYER_EQUIP 0x00a9 +#define CMSG_PLAYER_UNEQUIP 0x00ab + +#define CMSG_ITEM_PICKUP 0x009f +#define CMSG_PLAYER_CHANGE_DIR 0x009b +#define CMSG_PLAYER_CHANGE_DEST 0x0085 +#define CMSG_PLAYER_CHANGE_ACT 0x0089 +#define CMSG_PLAYER_RESTART 0x00b2 +#define CMSG_PLAYER_EMOTE 0x00bf +#define CMSG_PLAYER_ATTACK 0x0089 +#define CMSG_PLAYER_STOP_ATTACK 0x0118 +#define CMSG_WHO_REQUEST 0x00c1 + +#define CMSG_NPC_TALK 0x0090 +#define CMSG_NPC_NEXT_REQUEST 0x00b9 +#define CMSG_NPC_CLOSE 0x0146 +#define CMSG_NPC_LIST_CHOICE 0x00b8 +#define CMSG_NPC_INT_RESPONSE 0x0143 +#define CMSG_NPC_STR_RESPONSE 0x01d5 +#define CMSG_NPC_BUY_SELL_REQUEST 0x00c5 +#define CMSG_NPC_BUY_REQUEST 0x00c8 +#define CMSG_NPC_SELL_REQUEST 0x00c9 + +#define CMSG_TRADE_REQUEST 0x00e4 +#define CMSG_TRADE_RESPONSE 0x00e6 +#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_PARTY_CREATE 0x00f9 +#define CMSG_PARTY_INVITE 0x00fc +#define CMSG_PARTY_INVITED 0x00ff +#define CMSG_PARTY_LEAVE 0x0100 +#define CMSG_PARTY_SETTINGS 0x0102 +#define CMSG_PARTY_KICK 0x0103 +#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 */ + +#define CMSG_ADMIN_ANNOUNCE 0x0099 +#define CMSG_ADMIN_LOCAL_ANNOUNCE 0x019C +#define CMSG_ADMIN_HIDE 0x019D +#define CMSG_ADMIN_KICK 0x00CC +#define CMSG_ADMIN_MUTE 0x0149 + +#define CMSG_GUILD_CHECK_MASTER 0x014d +#define CMSG_GUILD_REQUEST_INFO 0x014f +#define CMSG_GUILD_REQUEST_EMBLEM 0x0151 +#define CMSG_GUILD_CHANGE_EMBLEM 0x0153 +#define CMSG_GUILD_CHANGE_MEMBER_POS 0x0155 +#define CMSG_GUILD_LEAVE 0x0159 +#define CMSG_GUILD_EXPULSION 0x015b +#define CMSG_GUILD_BREAK 0x015d +#define CMSG_GUILD_CHANGE_POS_INFO 0x0161 +#define CMSG_GUILD_CREATE 0x0165 +#define CMSG_GUILD_INVITE 0x0168 +#define CMSG_GUILD_INVITE_REPLY 0x016b +#define CMSG_GUILD_CHANGE_NOTICE 0x016e +#define CMSG_GUILD_ALLIANCE_REQUEST 0x0170 +#define CMSG_GUILD_ALLIANCE_REPLY 0x0172 +#define CMSG_GUILD_MESSAGE 0x017e +#define CMSG_GUILD_OPPOSITION 0x0180 +#define CMSG_GUILD_ALLIANCE_DELETE 0x0183 + +#define CMSG_SOLVE_CHAR_NAME 0x0193 +#define SMSG_SOLVE_CHAR_NAME 0x0194 +#define CMSG_CLIENT_DISCONNECT 0x7532 +#define SMSG_SKILL_CASTING 0x013e +#define SMSG_SKILL_CAST_CANCEL 0x01b9 +#define SMSG_SKILL_NO_DAMAGE 0x011a + +#define SMSG_BEING_IP_RESPONSE 0x020c +#define SMSG_PVP_MAP_MODE 0x0199 +#define SMSG_PVP_SET 0x019a + +#endif diff --git a/src/net/tmwa/specialhandler.cpp b/src/net/tmwa/specialhandler.cpp new file mode 100644 index 000000000..d502cc85f --- /dev/null +++ b/src/net/tmwa/specialhandler.cpp @@ -0,0 +1,273 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/specialhandler.h" + +#include "log.h" +#include "playerinfo.h" + +#include "gui/skilldialog.h" + +#include "gui/widgets/chattab.h" + +#include "net/messagein.h" +#include "net/messageout.h" + +#include "net/tmwa/protocol.h" + +#include "utils/gettext.h" + +/** job dependend identifiers (?) */ +#define SKILL_BASIC 0x0001 +#define SKILL_WARP 0x001b +#define SKILL_STEAL 0x0032 +#define SKILL_ENVENOM 0x0034 + +/** basic skills identifiers */ +#define BSKILL_TRADE 0x0000 +#define BSKILL_EMOTE 0x0001 +#define BSKILL_SIT 0x0002 +#define BSKILL_CREATECHAT 0x0003 +#define BSKILL_JOINPARTY 0x0004 +#define BSKILL_SHOUT 0x0005 +#define BSKILL_PK 0x0006 // ?? +#define BSKILL_SETALLIGN 0x0007 // ?? + +/** reasons why action failed */ +#define RFAIL_SKILLDEP 0x00 +#define RFAIL_INSUFSP 0x01 +#define RFAIL_INSUFHP 0x02 +#define RFAIL_NOMEMO 0x03 +#define RFAIL_SKILLDELAY 0x04 +#define RFAIL_ZENY 0x05 +#define RFAIL_WEAPON 0x06 +#define RFAIL_REDGEM 0x07 +#define RFAIL_BLUEGEM 0x08 +#define RFAIL_OVERWEIGHT 0x09 +#define RFAIL_GENERIC 0x0a + +/** should always be zero if failed */ +#define SKILL_FAILED 0x00 + +extern Net::SpecialHandler *specialHandler; + +namespace TmwAthena +{ + +SpecialHandler::SpecialHandler() +{ + static const Uint16 _messages[] = + { + SMSG_PLAYER_SKILLS, + SMSG_SKILL_FAILED, + SMSG_PLAYER_SKILL_UP, + 0 + }; + handledMessages = _messages; + specialHandler = this; +} + +void SpecialHandler::handleMessage(Net::MessageIn &msg) +{ + int skillCount; + int skillId; + + switch (msg.getId()) + { + case SMSG_PLAYER_SKILLS: + { + msg.readInt16(); // length + skillCount = (msg.getLength() - 4) / 37; + + for (int k = 0; k < skillCount; k++) + { + skillId = msg.readInt16(); + msg.readInt16(); // target type + msg.skip(2); // unused + int level = msg.readInt16(); + msg.readInt16(); // sp + msg.readInt16(); // range + msg.skip(24); // unused + int up = msg.readInt8(); + + PlayerInfo::setStatBase(skillId, level); + if (skillDialog) + skillDialog->setModifiable(skillId, up); + } + break; + } + case SMSG_PLAYER_SKILL_UP: + { + skillId = msg.readInt16(); + int level = msg.readInt16(); + msg.readInt16(); // sp + msg.readInt16(); // range + int up = msg.readInt8(); + + PlayerInfo::setStatBase(skillId, level); + if (skillDialog) + skillDialog->setModifiable(skillId, up); + } + break; + + case SMSG_SKILL_FAILED: + { + // Action failed (ex. sit because you have not reached the + // right level) + skillId = msg.readInt16(); + short bskill = msg.readInt16(); + msg.readInt16(); // btype + char success = msg.readInt8(); + char reason = msg.readInt8(); + if (success != SKILL_FAILED && bskill == BSKILL_EMOTE) + logger->log("Action: %d/%d", bskill, success); + + std::string msg; + if (success == SKILL_FAILED && skillId == SKILL_BASIC) + { + switch (bskill) + { + case BSKILL_TRADE: + msg = _("Trade failed!"); + break; + case BSKILL_EMOTE: + msg = _("Emote failed!"); + break; + case BSKILL_SIT: + msg = _("Sit failed!"); + break; + case BSKILL_CREATECHAT: + msg = _("Chat creating failed!"); + break; + case BSKILL_JOINPARTY: + msg = _("Could not join party!"); + break; + case BSKILL_SHOUT: + msg = _("Cannot shout!"); + break; + default: + logger->log("QQQ SMSG_SKILL_FAILED: bskill " + + toString(bskill)); + break; + } + + msg += " "; + + switch (reason) + { + case RFAIL_SKILLDEP: + msg += _("You have not yet reached a high enough " + "lvl!"); + break; + case RFAIL_INSUFHP: + msg += _("Insufficient HP!"); + break; + case RFAIL_INSUFSP: + msg += _("Insufficient SP!"); + break; + case RFAIL_NOMEMO: + msg += _("You have no memos!"); + break; + case RFAIL_SKILLDELAY: + msg += _("You cannot do that right now!"); + break; + case RFAIL_ZENY: + msg += _("Seems you need more money... ;-)"); + break; + case RFAIL_WEAPON: + msg += _("You cannot use this skill with that " + "kind of weapon!"); + break; + case RFAIL_REDGEM: + msg += _("You need another red gem!"); + break; + case RFAIL_BLUEGEM: + msg += _("You need another blue gem!"); + break; + case RFAIL_OVERWEIGHT: + msg += _("You're carrying to much to do this!"); + break; + default: + msg += _("Huh? What's that?"); + logger->log("QQQ SMSG_SKILL_FAILED: reason " + + toString(reason)); + break; + } + } + else + { + switch (skillId) + { + case SKILL_WARP : + msg = _("Warp failed..."); + break; + case SKILL_STEAL : + msg = _("Could not steal anything..."); + break; + case SKILL_ENVENOM : + msg = _("Poison had no effect..."); + break; + default: + logger->log("QQQ SMSG_SKILL_FAILED: skillId " + + toString(skillId)); + break; + } + } + + if (localChatTab) + localChatTab->chatLog(msg); + } + break; + default: + break; + } +} + +void SpecialHandler::use(int id _UNUSED_) +{ + // TODO +} + +void SpecialHandler::use(int id, int level, int beingId) +{ + MessageOut outMsg(CMSG_SKILL_USE_BEING); + outMsg.writeInt16(level); + outMsg.writeInt16(id); + outMsg.writeInt16(beingId); +} + +void SpecialHandler::use(int id, int level, int x, int y) +{ + MessageOut outMsg(CMSG_SKILL_USE_POSITION); + outMsg.writeInt16(level); + outMsg.writeInt16(id); + outMsg.writeInt16(x); + outMsg.writeInt16(y); +} + +void SpecialHandler::use(int id, const std::string &map) +{ + MessageOut outMsg(CMSG_SKILL_USE_MAP); + outMsg.writeInt16(id); + outMsg.writeString(map, 16); +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/specialhandler.h b/src/net/tmwa/specialhandler.h new file mode 100644 index 000000000..e4e65c607 --- /dev/null +++ b/src/net/tmwa/specialhandler.h @@ -0,0 +1,57 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_SKILLHANDLER_H +#define NET_TA_SKILLHANDLER_H + +#include "net/net.h" +#include "net/specialhandler.h" + +#include "net/tmwa/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace TmwAthena +{ + +class SpecialHandler : public MessageHandler, public Net::SpecialHandler +{ + public: + SpecialHandler(); + + void handleMessage(Net::MessageIn &msg); + + void use(int id); + + void use(int id, int level, int beingId); + + void use(int id, int level, int x, int y); + + void use(int id, const std::string &map); +}; + +} // namespace TmwAthena + +#endif // NET_TA_SKILLHANDLER_H diff --git a/src/net/tmwa/token.h b/src/net/tmwa/token.h new file mode 100644 index 000000000..3e781cd89 --- /dev/null +++ b/src/net/tmwa/token.h @@ -0,0 +1,43 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "being.h" + +#ifndef NET_TA_TOKEN_H +#define NET_TA_TOKEN_H + +struct Token +{ + int account_ID; + int session_ID1; + int session_ID2; + Gender sex; + + void clear() + { + account_ID = 0; + session_ID1 = 0; + session_ID2 = 0; + sex = GENDER_UNSPECIFIED; + } +}; + +#endif // NET_TA_TOKEN_H diff --git a/src/net/tmwa/tradehandler.cpp b/src/net/tmwa/tradehandler.cpp new file mode 100644 index 000000000..6c48d5e6c --- /dev/null +++ b/src/net/tmwa/tradehandler.cpp @@ -0,0 +1,347 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "net/tmwa/tradehandler.h" + +#include "event.h" +#include "inventory.h" +#include "item.h" +#include "localplayer.h" +#include "log.h" +#include "playerinfo.h" +#include "playerrelations.h" + +#include "gui/confirmdialog.h" +#include "gui/trade.h" + +#include "net/inventoryhandler.h" +#include "net/messagein.h" +#include "net/messageout.h" + +#include "net/tmwa/protocol.h" + +#include "utils/gettext.h" +#include "utils/stringutils.h" + +extern std::string tradePartnerName; +ConfirmDialog *confirmDlg; + +/** + * Listener for request trade dialogs + */ +namespace +{ + struct RequestTradeListener : public gcn::ActionListener + { + void action(const gcn::ActionEvent &event) + { + confirmDlg = 0; + if (event.getId() == "ignore") + player_relations.ignoreTrade(tradePartnerName); + Net::getTradeHandler()->respond(event.getId() == "yes"); + } + } listener; +} + +extern Net::TradeHandler *tradeHandler; + +namespace TmwAthena +{ + +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; + tradeHandler = this; + confirmDlg = 0; +} + + +void TradeHandler::handleMessage(Net::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. + std::string tradePartnerNameTemp = msg.readString(24); + + if (player_relations.hasPermission(tradePartnerName, + PlayerRelation::TRADE)) + { + if (PlayerInfo::isTrading() || confirmDlg) + { + Net::getTradeHandler()->respond(false); + break; + } + + tradePartnerName = tradePartnerNameTemp; + PlayerInfo::setTrading(true); + if (tradeWindow) + { + if (tradePartnerName.empty() + || tradeWindow->getAutoTradeNick() + != tradePartnerName) + { + tradeWindow->clear(); + confirmDlg = new ConfirmDialog( + _("Request for Trade"), + strprintf(_("%s wants to trade with you, do" + " you accept?"), tradePartnerName.c_str()), + true); + confirmDlg->addActionListener(&listener); + } + else + { + Net::getTradeHandler()->respond(true); + } + } + } + else + { + Net::getTradeHandler()->respond(false); + break; + } + } + break; + + case SMSG_TRADE_RESPONSE: + switch (msg.readInt8()) + { + case 0: // Too far away + SERVER_NOTICE(_("Trading isn't possible. Trade " + "partner is too far away.")) + break; + case 1: // Character doesn't exist + SERVER_NOTICE(_("Trading isn't possible. Character " + "doesn't exist.")) + break; + case 2: // Invite request check failed... + SERVER_NOTICE(_("Trade cancelled due to an unknown " + "reason.")) + break; + case 3: // Trade accepted + if (tradeWindow) + { + tradeWindow->reset(); + tradeWindow->setCaption(strprintf( + _("Trade: You and %s"), + tradePartnerName.c_str())); + tradeWindow->initTrade(tradePartnerName); + tradeWindow->setVisible(true); + } + break; + case 4: // Trade cancelled + if (player_relations.hasPermission(tradePartnerName, + PlayerRelation::SPEECH_LOG)) + { + SERVER_NOTICE(strprintf(_("Trade with %s cancelled."), + tradePartnerName.c_str())) + } + // otherwise ignore silently + + if (tradeWindow) + { + tradeWindow->setVisible(false); +// tradeWindow->clear(); + } + PlayerInfo::setTrading(false); + break; + default: // Shouldn't happen as well, but to be sure + SERVER_NOTICE(_("Unhandled trade cancel packet.")) + if (tradeWindow) + tradeWindow->clear(); + break; + } + break; + + case SMSG_TRADE_ITEM_ADD: + { + int amount = msg.readInt32(); + int 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 (tradeWindow) + { + 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() - INVENTORY_OFFSET; + Item *item = PlayerInfo::getInventory()->getItem(index); + if (!item) + { + if (tradeWindow) + tradeWindow->receivedOk(true); + return; + } + int quantity = msg.readInt16(); + + int res = msg.readInt8(); + switch (res) + { + case 0: + // Successfully added item + if (item->isEquipment() && item->isEquipped()) + Net::getInventoryHandler()->unequipItem(item); + + if (tradeWindow) + { + tradeWindow->addItem(item->getId(), true, quantity, + item->isEquipment()); + } + item->increaseQuantity(-quantity); + break; + case 1: + // Add item failed - player overweighted + SERVER_NOTICE(_("Failed adding item. Trade " + "partner is over weighted.")) + break; + case 2: + // Add item failed - player has no free slot + SERVER_NOTICE(_("Failed adding item. Trade " + "partner has no free slot.")) + break; + default: + SERVER_NOTICE(_("Failed adding item for " + "unknown reason.")) + logger->log("QQQ SMSG_TRADE_ITEM_ADD_RESPONSE: " + + toString(res)); + break; + } + } + break; + + case SMSG_TRADE_OK: + // 0 means ok from myself, 1 means ok from other; + if (tradeWindow) + tradeWindow->receivedOk(msg.readInt8() == 0); + else + msg.readInt8(); + break; + + case SMSG_TRADE_CANCEL: + SERVER_NOTICE(_("Trade canceled.")) + if (tradeWindow) + { + tradeWindow->setVisible(false); + tradeWindow->reset(); + } + PlayerInfo::setTrading(false); + break; + + case SMSG_TRADE_COMPLETE: + SERVER_NOTICE(_("Trade completed.")) + if (tradeWindow) + { + tradeWindow->setVisible(false); + tradeWindow->reset(); + } + PlayerInfo::setTrading(false); + break; + + default: + break; + } +} + +void TradeHandler::request(Being *being) +{ + if (!being) + return; + + MessageOut outMsg(CMSG_TRADE_REQUEST); + outMsg.writeInt32(being->getId()); +} + +void TradeHandler::respond(bool accept) +{ + if (!accept) + PlayerInfo::setTrading(false); + + MessageOut outMsg(CMSG_TRADE_RESPONSE); + outMsg.writeInt8(accept ? 3 : 4); +} + +void TradeHandler::addItem(Item *item, int amount) +{ + if (!item) + return; + + MessageOut outMsg(CMSG_TRADE_ITEM_ADD_REQUEST); + outMsg.writeInt16(item->getInvIndex() + INVENTORY_OFFSET); + outMsg.writeInt32(amount); +} + +void TradeHandler::removeItem(int slotNum _UNUSED_, int amount _UNUSED_) +{ + // TODO +} + +void TradeHandler::setMoney(int amount) +{ + MessageOut outMsg(CMSG_TRADE_ITEM_ADD_REQUEST); + outMsg.writeInt16(0); + outMsg.writeInt32(amount); +} + +void TradeHandler::confirm() +{ + MessageOut outMsg(CMSG_TRADE_ADD_COMPLETE); +} + +void TradeHandler::finish() +{ + MessageOut outMsg(CMSG_TRADE_OK); +} + +void TradeHandler::cancel() +{ + MessageOut outMsg(CMSG_TRADE_CANCEL_REQUEST); +} + +} // namespace TmwAthena diff --git a/src/net/tmwa/tradehandler.h b/src/net/tmwa/tradehandler.h new file mode 100644 index 000000000..39364e52d --- /dev/null +++ b/src/net/tmwa/tradehandler.h @@ -0,0 +1,65 @@ +/* + * The Mana Client + * Copyright (C) 2004-2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NET_TA_TRADEHANDLER_H +#define NET_TA_TRADEHANDLER_H + +#include "net/net.h" +#include "net/tradehandler.h" + +#include "net/tmwa/messagehandler.h" + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace TmwAthena +{ + +class TradeHandler : public MessageHandler, public Net::TradeHandler +{ + public: + TradeHandler(); + + void handleMessage(Net::MessageIn &msg); + + void request(Being *being); + + void respond(bool accept); + + void addItem(Item *item, int amount); + + void removeItem(int slotNum, int amount); + + void setMoney(int amount); + + void confirm(); + + void finish(); + + void cancel(); +}; + +} // namespace TmwAthena + +#endif // NET_TA_TRADEHANDLER_H diff --git a/src/net/tradehandler.h b/src/net/tradehandler.h new file mode 100644 index 000000000..d433cacbf --- /dev/null +++ b/src/net/tradehandler.h @@ -0,0 +1,67 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TRADEHANDLER_H +#define TRADEHANDLER_H + +#include "being.h" + +#include + +#ifdef __GNUC__ +#define _UNUSED_ __attribute__ ((unused)) +#else +#define _UNUSED_ +#endif + +namespace Net +{ +class TradeHandler +{ + public: + virtual void request(Being *being _UNUSED_) + { } +// virtual ~TradeHandler() {} + + virtual void respond(bool accept _UNUSED_) + { } + + virtual void addItem(Item *item _UNUSED_, int amount _UNUSED_) + { } + + virtual void removeItem(int slotNum _UNUSED_, int amount _UNUSED_) + { } + + virtual void setMoney(int amount _UNUSED_) + { } + + virtual void confirm() + { } + + virtual void finish() + { } + + virtual void cancel() + { } +}; +} + +#endif // TRADEHANDLER_H diff --git a/src/net/worldinfo.h b/src/net/worldinfo.h new file mode 100644 index 000000000..a92a5735e --- /dev/null +++ b/src/net/worldinfo.h @@ -0,0 +1,39 @@ +/* + * The Mana Client + * Copyright (C) 2009 The Mana World Development Team + * Copyright (C) 2009-2010 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef WORLD_INFO_H +#define WORLD_INFO_H + +#include +#include + +struct WorldInfo +{ + int address; + std::string name; + short port; + short online_users; + std::string updateHost; +}; + +typedef std::vector Worlds; + +#endif // WORLD_INFO_H -- cgit v1.2.3-70-g09d2