diff options
Diffstat (limited to 'src/net')
34 files changed, 2986 insertions, 438 deletions
diff --git a/src/net/beinghandler.cpp b/src/net/beinghandler.cpp new file mode 100644 index 00000000..9578f5c1 --- /dev/null +++ b/src/net/beinghandler.cpp @@ -0,0 +1,356 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "beinghandler.h" + +#include <SDL_types.h> + +#include "messagein.h" +#include "protocol.h" + +#include "../being.h" +#include "../beingmanager.h" +#include "../game.h" +#include "../localplayer.h" +#include "../log.h" +#include "../main.h" +#include "../sound.h" + +const int EMOTION_TIME = 150; /**< Duration of emotion icon */ + +BeingHandler::BeingHandler() +{ + static const Uint16 _messages[] = { + SMSG_BEING_VISIBLE, + SMSG_BEING_MOVE, + SMSG_BEING_REMOVE, + SMSG_BEING_ACTION, + SMSG_BEING_LEVELUP, + SMSG_BEING_EMOTION, + SMSG_BEING_CHANGE_LOOKS, + SMSG_BEING_NAME_RESPONSE, + SMSG_PLAYER_UPDATE_1, + SMSG_PLAYER_UPDATE_2, + SMSG_PLAYER_MOVE, + 0x0119, + 0 + }; + handledMessages = _messages; +} + +void BeingHandler::handleMessage(MessageIn *msg) +{ + Uint32 id; + Uint16 job, speed; + Sint16 param1; + Sint8 type; + Being *srcBeing, *dstBeing; + + switch (msg->getId()) + { + case SMSG_BEING_VISIBLE: + case SMSG_BEING_MOVE: + // Information about a being in range + id = msg->readInt32(); + speed = msg->readInt16(); + msg->readInt16(); // unknown + msg->readInt16(); // unknown + msg->readInt16(); // option + job = msg->readInt16(); // class + + dstBeing = beingManager->findBeing(id); + + if (dstBeing == NULL) + { + // Being with id >= 110000000 and job 0 are better + // known as ghosts, so don't create those. + if (job == 0 && id >= 110000000) + { + break; + } + + dstBeing = beingManager->createBeing(id, job); + } + else if (msg->getId() == 0x0078) + { + dstBeing->clearPath(); + dstBeing->mFrame = 0; + dstBeing->walk_time = tick_time; + dstBeing->action = Being::STAND; + } + + // Prevent division by 0 when calculating frame + if (speed == 0) { speed = 150; } + + dstBeing->setWalkSpeed(speed); + dstBeing->job = job; + dstBeing->setHairStyle(msg->readInt16()); + dstBeing->setWeapon(msg->readInt16()); + msg->readInt16(); // head option bottom + + if (msg->getId() == SMSG_BEING_MOVE) + { + msg->readInt32(); // server tick + } + + msg->readInt16(); // shield + msg->readInt16(); // head option top + msg->readInt16(); // head option mid + dstBeing->setHairColor(msg->readInt16()); + msg->readInt16(); // unknown + msg->readInt16(); // head dir + msg->readInt16(); // guild + msg->readInt16(); // unknown + msg->readInt16(); // unknown + msg->readInt16(); // manner + msg->readInt16(); // karma + msg->readInt8(); // unknown + msg->readInt8(); // sex + + if (msg->getId() == SMSG_BEING_MOVE) + { + Uint16 srcX, srcY, dstX, dstY; + msg->readCoordinatePair(srcX, srcY, dstX, dstY); + dstBeing->action = Being::STAND; + dstBeing->x = srcX; + dstBeing->y = srcY; + dstBeing->setDestination(dstX, dstY); + } + else + { + msg->readCoordinates(dstBeing->x, dstBeing->y, + dstBeing->direction); + } + + msg->readInt8(); // unknown + msg->readInt8(); // unknown + msg->readInt8(); // unknown / sit + break; + + case SMSG_BEING_REMOVE: + // A being should be removed or has died + dstBeing = beingManager->findBeing(msg->readInt32()); + + if (!dstBeing) + break; + + if (msg->readInt8() == 1) + { + // Death + switch (dstBeing->getType()) + { + case Being::MONSTER: + dstBeing->action = Being::MONSTER_DEAD; + dstBeing->mFrame = 0; + dstBeing->walk_time = tick_time; + break; + + default: + dstBeing->action = Being::DEAD; + break; + } + } + else + { + beingManager->destroyBeing(dstBeing); + } + + if (dstBeing == player_node->getTarget()) + { + player_node->stopAttack(); + } + break; + + case SMSG_BEING_ACTION: + srcBeing = beingManager->findBeing(msg->readInt32()); + dstBeing = beingManager->findBeing(msg->readInt32()); + msg->readInt32(); // server tick + msg->readInt32(); // src speed + msg->readInt32(); // dst speed + param1 = msg->readInt16(); + msg->readInt16(); // param 2 + type = msg->readInt8(); + msg->readInt16(); // param 3 + + switch (type) + { + case 0: // Damage + if (dstBeing == NULL) break; + + dstBeing->setDamage(param1, SPEECH_TIME); + + if (srcBeing != NULL && + srcBeing != player_node) + { + // buggy + srcBeing->action = Being::ATTACK; + srcBeing->mFrame = 0; + srcBeing->walk_time = tick_time; + } + break; + + case 2: // Sit + if (srcBeing == NULL) break; + srcBeing->mFrame = 0; + srcBeing->action = Being::SIT; + break; + + case 3: // Stand up + if (srcBeing == NULL) break; + srcBeing->mFrame = 0; + srcBeing->action = Being::STAND; + break; + } + break; + + case SMSG_BEING_LEVELUP: + if ((Uint32)msg->readInt32() == player_node->getId()) { + logger->log("Level up"); + sound.playSfx("sfx/levelup.ogg"); + } else { + logger->log("Someone else went level up"); + } + msg->readInt32(); // type + break; + + case SMSG_BEING_EMOTION: + if (!(dstBeing = beingManager->findBeing(msg->readInt32()))) + { + break; + } + + dstBeing->emotion = msg->readInt8(); + dstBeing->emotion_time = EMOTION_TIME; + break; + + case SMSG_BEING_CHANGE_LOOKS: + if (!(dstBeing = beingManager->findBeing(msg->readInt32()))) + { + break; + } + + switch (msg->readInt8()) { + case 1: + dstBeing->setHairStyle(msg->readInt8()); + break; + case 2: + dstBeing->setWeapon(msg->readInt8()); + break; + case 6: + dstBeing->setHairColor(msg->readInt8()); + break; + default: + msg->readInt8(); // unsupported + break; + } + break; + + case SMSG_BEING_NAME_RESPONSE: + if ((dstBeing = beingManager->findBeing(msg->readInt32()))) + { + dstBeing->setName(msg->readString(24)); + } + break; + + case SMSG_PLAYER_UPDATE_1: + case SMSG_PLAYER_UPDATE_2: + case SMSG_PLAYER_MOVE: + // An update about a player, potentially including movement. + id = msg->readInt32(); + speed = msg->readInt16(); + msg->readInt16(); // option 1 + msg->readInt16(); // option 2 + msg->readInt16(); // option + job = msg->readInt16(); + + dstBeing = beingManager->findBeing(id); + + if (dstBeing == NULL) + { + dstBeing = beingManager->createBeing(id, job); + } + + dstBeing->setWalkSpeed(speed); + dstBeing->job = job; + dstBeing->setHairStyle(msg->readInt16()); + dstBeing->setWeaponById(msg->readInt16()); // item id 1 + msg->readInt16(); // item id 2 + msg->readInt16(); // head option bottom + + if (msg->getId() == SMSG_PLAYER_MOVE) + { + msg->readInt32(); // server tick + } + + msg->readInt16(); // head option top + msg->readInt16(); // head option mid + dstBeing->setHairColor(msg->readInt16()); + msg->readInt16(); // unknown + msg->readInt16(); // head dir + msg->readInt32(); // guild + msg->readInt32(); // emblem + msg->readInt16(); // manner + msg->readInt8(); // karma + msg->readInt8(); // sex + + if (msg->getId() == SMSG_PLAYER_MOVE) + { + Uint16 srcX, srcY, dstX, dstY; + msg->readCoordinatePair(srcX, srcY, dstX, dstY); + dstBeing->x = srcX; + dstBeing->y = srcY; + dstBeing->setDestination(dstX, dstY); + } + else + { + msg->readCoordinates(dstBeing->x, dstBeing->y, + dstBeing->direction); + } + + msg->readInt8(); // unknown + msg->readInt8(); // unknown + + if (msg->getId() == SMSG_PLAYER_UPDATE_1) + { + if (msg->readInt8() == 2) + { + dstBeing->action = Being::SIT; + } + } + else if (msg->getId() == SMSG_PLAYER_MOVE) + { + msg->readInt8(); // unknown + } + + msg->readInt8(); // Lv + msg->readInt8(); // unknown + + dstBeing->walk_time = tick_time; + dstBeing->mFrame = 0; + break; + + case 0x0119: + // Change in players look + break; + } +} diff --git a/src/net/beinghandler.h b/src/net/beinghandler.h new file mode 100644 index 00000000..03012f39 --- /dev/null +++ b/src/net/beinghandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_BEINGHANDLER_H +#define _TMW_NET_BEINGHANDLER_H + +#include "messagehandler.h" + +class BeingHandler : public MessageHandler +{ + public: + BeingHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/buysellhandler.cpp b/src/net/buysellhandler.cpp new file mode 100644 index 00000000..501762ad --- /dev/null +++ b/src/net/buysellhandler.cpp @@ -0,0 +1,129 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "buysellhandler.h" + +#include <SDL_types.h> + +#include "messagein.h" +#include "protocol.h" + +#include "../beingmanager.h" +#include "../item.h" +#include "../localplayer.h" +#include "../npc.h" + +#include "../gui/buy.h" +#include "../gui/chat.h" +#include "../gui/sell.h" + +extern BuyDialog *buyDialog; +extern SellDialog *sellDialog; +extern Window *buySellDialog; + +BuySellHandler::BuySellHandler() +{ + static const Uint16 _messages[] = { + SMSG_NPC_BUY_SELL_CHOICE, + SMSG_NPC_BUY, + SMSG_NPC_SELL, + SMSG_NPC_BUY_RESPONSE, + SMSG_NPC_SELL_RESPONSE, + 0 + }; + handledMessages = _messages; +} + +void BuySellHandler::handleMessage(MessageIn *msg) +{ + int n_items; + switch (msg->getId()) + { + case SMSG_NPC_BUY_SELL_CHOICE: + buyDialog->setVisible(false); + buyDialog->reset(); + sellDialog->setVisible(false); + sellDialog->reset(); + buySellDialog->setVisible(true); + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(msg->readInt32())); + break; + + case SMSG_NPC_BUY: + msg->readInt16(); // length + n_items = (msg->getLength() - 4) / 11; + buyDialog->reset(); + buyDialog->setMoney(player_node->gp); + buyDialog->setVisible(true); + + for (int k = 0; k < n_items; k++) + { + Sint32 value = msg->readInt32(); + msg->readInt32(); // DCvalue + msg->readInt8(); // type + Sint16 itemId = msg->readInt16(); + buyDialog->addItem(itemId, value); + } + break; + + case SMSG_NPC_SELL: + msg->readInt16(); // length + n_items = (msg->getLength() - 4) / 10; + if (n_items > 0) { + sellDialog->reset(); + sellDialog->setVisible(true); + + for (int k = 0; k < n_items; k++) + { + Sint16 index = msg->readInt16(); + Sint32 value = msg->readInt32(); + msg->readInt32(); // OCvalue + + Item *item = player_node->getInvItem(index); + if (item && !(item->isEquipped())) { + sellDialog->addItem(item, value); + } + } + } + else { + chatWindow->chatLog("Nothing to sell", BY_SERVER); + current_npc = 0; + } + break; + + case SMSG_NPC_BUY_RESPONSE: + if (msg->readInt8() == 0) { + chatWindow->chatLog("Thanks for buying", BY_SERVER); + } else { + chatWindow->chatLog("Unable to buy", BY_SERVER); + } + break; + + case SMSG_NPC_SELL_RESPONSE: + if (msg->readInt8() == 0) { + chatWindow->chatLog("Thanks for selling", BY_SERVER); + } else { + chatWindow->chatLog("Unable to sell", BY_SERVER); + } + break; + } +} diff --git a/src/net/buysellhandler.h b/src/net/buysellhandler.h new file mode 100644 index 00000000..673aaac1 --- /dev/null +++ b/src/net/buysellhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_BUYSELLHANDLER_H +#define _TMW_NET_BUYSELLHANDLER_H + +#include "messagehandler.h" + +class BuySellHandler : public MessageHandler +{ + public: + BuySellHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/charserverhandler.cpp b/src/net/charserverhandler.cpp new file mode 100644 index 00000000..a60e39b0 --- /dev/null +++ b/src/net/charserverhandler.cpp @@ -0,0 +1,210 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "charserverhandler.h" + +#include "messagein.h" +#include "network.h" +#include "protocol.h" + +#include "../game.h" +#include "../localplayer.h" +#include "../log.h" +#include "../main.h" + +#include "../gui/ok_dialog.h" + +CharServerHandler::CharServerHandler() +{ + static const Uint16 _messages[] = { + 0x006b, + 0x006c, + 0x006d, + 0x006e, + 0x006f, + 0x0070, + 0x0071, + 0x0081, + 0 + }; + handledMessages = _messages; +} + +void CharServerHandler::handleMessage(MessageIn *msg) +{ + int slot; + LocalPlayer *tempPlayer; + + logger->log("CharServerHandler: Packet ID: %x, Length: %d", + msg->getId(), msg->getLength()); + switch (msg->getId()) + { + case 0x006b: + // Skip length word and an additional mysterious 20 bytes + msg->skip(2 + 20); + + // Derive number of characters from message length + n_character = (msg->getLength() - 24) / 106; + + for (int i = 0; i < n_character; i++) + { + tempPlayer = readPlayerData(msg, slot); + mCharInfo->select(slot); + mCharInfo->setEntry(tempPlayer); + logger->log("CharServer: Player: %s (%d)", + tempPlayer->getName().c_str(), slot); + } + + state = CHAR_SELECT_STATE; + break; + + case 0x006c: + switch (msg->readInt8()) { + case 0: + errorMessage = "Access denied"; + break; + case 1: + errorMessage = "Cannot use this ID"; + break; + default: + errorMessage = "Unknown failure to select character"; + break; + } + mCharInfo->unlock(); + break; + + case 0x006d: + tempPlayer = readPlayerData(msg, slot); + mCharInfo->unlock(); + mCharInfo->select(slot); + mCharInfo->setEntry(tempPlayer); + n_character++; + break; + + case 0x006e: + new OkDialog(NULL, "Error", "Failed to create character"); + break; + + case 0x006f: + delete mCharInfo->getEntry(); + mCharInfo->setEntry(0); + mCharInfo->unlock(); + n_character--; + new OkDialog(NULL, "Info", "Player deleted"); + break; + + case 0x0070: + mCharInfo->unlock(); + new OkDialog(NULL, "Error", "Failed to delete character."); + break; + + case 0x0071: + char_ID = msg->readInt32(); + map_path = msg->readString(16); + map_address = msg->readInt32(); + map_port = msg->readInt16(); + player_node = mCharInfo->getEntry(); + mCharInfo->unlock(); + mCharInfo->select(0); + // Clear unselected players infos + do + { + LocalPlayer *tmp = mCharInfo->getEntry(); + if (tmp != player_node) + delete tmp; + mCharInfo->next(); + } while (mCharInfo->getPos()); + + state = CONNECTING_STATE; + + logger->log("CharSelect: Map: %s", map_path.c_str()); + logger->log("CharSelect: Server: %s:%i", iptostring(map_address), + map_port); + break; + + case 0x0081: + switch (msg->readInt8()) { + case 1: + errorMessage = "Map server offline"; + break; + case 3: + errorMessage = "Speed hack detected"; + break; + case 8: + errorMessage = "Duplicated login"; + break; + default: + errorMessage = "Unkown error with 0x0081"; + break; + } + mCharInfo->unlock(); + state = ERROR_STATE; + break; + } +} + +LocalPlayer* CharServerHandler::readPlayerData(MessageIn *msg, int &slot) +{ + LocalPlayer *tempPlayer = new LocalPlayer(account_ID, 0, NULL); + + tempPlayer->mLoginId = msg->readInt32(); + tempPlayer->totalWeight = 0; + tempPlayer->maxWeight = 0; + tempPlayer->lastAttackTime = 0; + tempPlayer->xp = msg->readInt32(); + tempPlayer->gp = msg->readInt32(); + tempPlayer->jobXp = msg->readInt32(); + tempPlayer->jobLvl = msg->readInt32(); + msg->skip(8); // unknown + msg->readInt32(); // option + msg->readInt32(); // karma + msg->readInt32(); // manner + msg->skip(2); // unknown + tempPlayer->hp = msg->readInt16(); + tempPlayer->maxHp = msg->readInt16(); + tempPlayer->mp = msg->readInt16(); + tempPlayer->maxMp = msg->readInt16(); + msg->readInt16(); // speed + msg->readInt16(); // class + tempPlayer->setHairStyle(msg->readInt16()); + Uint16 weapon = msg->readInt16(); + if (weapon == 11) + weapon = 2; + tempPlayer->setWeapon(weapon); + tempPlayer->lvl = msg->readInt16(); + msg->readInt16(); // skill point + msg->readInt16(); // head bottom + msg->readInt16(); // shield + msg->readInt16(); // head option top + msg->readInt16(); // head option mid + tempPlayer->setHairColor(msg->readInt16()); + msg->readInt16(); // unknown + tempPlayer->setName(msg->readString(24)); + for (int i = 0; i < 6; i++) { + tempPlayer->ATTR[i] = msg->readInt8(); + } + slot = msg->readInt8(); // character slot + msg->readInt8(); // unknown + + return tempPlayer; +} diff --git a/src/net/charserverhandler.h b/src/net/charserverhandler.h new file mode 100644 index 00000000..21178377 --- /dev/null +++ b/src/net/charserverhandler.h @@ -0,0 +1,48 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_CHARSERVERHANDLER_H +#define _TMW_NET_CHARSERVERHANDLER_H + +#include "messagehandler.h" + +#include "../lockedarray.h" + +class LocalPlayer; + +class CharServerHandler : public MessageHandler +{ + public: + CharServerHandler(); + + void handleMessage(MessageIn *msg); + + void setCharInfo(LockedArray<LocalPlayer*> *charInfo) { mCharInfo = charInfo; }; + + protected: + LockedArray<LocalPlayer*> *mCharInfo; + + LocalPlayer* readPlayerData(MessageIn *msg, int &slot); +}; + +#endif diff --git a/src/net/chathandler.cpp b/src/net/chathandler.cpp new file mode 100644 index 00000000..97e8186d --- /dev/null +++ b/src/net/chathandler.cpp @@ -0,0 +1,118 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "chathandler.h" + +#include <SDL_types.h> +#include <string> +#include <sstream> + +#include "messagein.h" +#include "protocol.h" + +#include "../being.h" +#include "../beingmanager.h" +#include "../game.h" + +#include "../gui/chat.h" + +extern Being *player_node; + +ChatHandler::ChatHandler() +{ + static const Uint16 _messages[] = { + SMSG_BEING_CHAT, + SMSG_PLAYER_CHAT, + SMSG_GM_CHAT, + SMSG_WHO_ANSWER, + 0x10c, // MVP + 0 + }; + handledMessages = _messages; +} + +void ChatHandler::handleMessage(MessageIn *msg) +{ + Being *being; + std::string chatMsg; + std::stringstream ss; + Sint16 chatMsgLength; + + switch (msg->getId()) + { + // Received speech from being + case SMSG_BEING_CHAT: + chatMsgLength = msg->readInt16() - 8; + being = beingManager->findBeing(msg->readInt32()); + + if (!being || chatMsgLength <= 0) + { + break; + } + + chatMsg = msg->readString(chatMsgLength); + chatWindow->chatLog(chatMsg, BY_OTHER); + chatMsg.erase(0, chatMsg.find(" : ", 0) + 3); + being->setSpeech(chatMsg, SPEECH_TIME); + break; + + case SMSG_PLAYER_CHAT: + case SMSG_GM_CHAT: + chatMsgLength = msg->readInt16() - 4; + + if (chatMsgLength <= 0) + { + break; + } + + chatMsg = msg->readString(chatMsgLength); + + if (msg->getId() == SMSG_PLAYER_CHAT) + { + chatWindow->chatLog(chatMsg, BY_PLAYER); + + std::string::size_type pos = chatMsg.find(" : ", 0); + if (pos != std::string::npos) + { + chatMsg.erase(0, pos + 3); + } + player_node->setSpeech(chatMsg, SPEECH_TIME); + } + else + { + chatWindow->chatLog(chatMsg, BY_GM); + } + break; + + case SMSG_WHO_ANSWER: + ss << "Online users: " << msg->readInt32(); + chatWindow->chatLog(ss.str(), BY_SERVER); + break; + + case 0x010c: + // Display MVP player + msg->readInt32(); // id + chatWindow->chatLog("MVP player", BY_SERVER); + break; + } +} diff --git a/src/net/chathandler.h b/src/net/chathandler.h new file mode 100644 index 00000000..eed19206 --- /dev/null +++ b/src/net/chathandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_CHATHANDLER_H +#define _TMW_NET_CHATHANDLER_H + +#include "messagehandler.h" + +class ChatHandler : public MessageHandler +{ + public: + ChatHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/equipmenthandler.cpp b/src/net/equipmenthandler.cpp new file mode 100644 index 00000000..437b5f3e --- /dev/null +++ b/src/net/equipmenthandler.cpp @@ -0,0 +1,210 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "equipmenthandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../being.h" +#include "../beingmanager.h" +#include "../equipment.h" +#include "../item.h" +#include "../localplayer.h" +#include "../log.h" + +#include "../gui/chat.h" + +EquipmentHandler::EquipmentHandler() +{ + static const Uint16 _messages[] = { + SMSG_PLAYER_EQUIPMENT, + SMSG_PLAYER_EQUIP, + 0x01d7, + SMSG_PLAYER_UNEQUIP, + SMSG_PLAYER_ARROW_EQUIP, + 0 + }; + handledMessages = _messages; +} + +void EquipmentHandler::handleMessage(MessageIn *msg) +{ + Sint32 itemCount; + Sint16 index, equipPoint, itemId; + Sint8 type; + int mask, position; + Being *being; + Item *item; + + switch (msg->getId()) + { + case SMSG_PLAYER_EQUIPMENT: + msg->readInt16(); // length + itemCount = (msg->getLength() - 4) / 20; + + for (int loop = 0; loop < itemCount; loop++) + { + index = msg->readInt16(); + itemId = msg->readInt16(); + msg->readInt8(); // type + msg->readInt8(); // identify flag + msg->readInt16(); // equip type + equipPoint = msg->readInt16(); + msg->readInt8(); // attribute + msg->readInt8(); // refine + msg->skip(8); // card + + player_node->addInvItem(index, itemId, 1, true); + + if (equipPoint) + { + mask = 1; + position = 0; + while (!(equipPoint & mask)) + { + mask <<= 1; + position++; + } + item = player_node->getInvItem(index); + player_node->mEquipment->setEquipment(position - 1, item); + } + } + break; + + case SMSG_PLAYER_EQUIP: + index = msg->readInt16(); + equipPoint = msg->readInt16(); + type = msg->readInt8(); + + logger->log("Equipping: %i %i %i", index, equipPoint, type); + + if (!type) { + chatWindow->chatLog("Unable to equip.", BY_SERVER); + break; + } + + if (!equipPoint) { + // No point given, no point in searching + break; + } + + // Unequip any existing equipped item in this position + mask = 1; + position = 0; + while (!(equipPoint & mask)) { + mask <<= 1; + position++; + } + logger->log("Position %i", position - 1); + item = player_node->mEquipment->getEquipment(position - 1); + if (item) { + item->setEquipped(false); + } + + item = player_node->getInvItem(index); + player_node->mEquipment->setEquipment(position - 1, item); + player_node->setWeaponById(item->getId()); + break; + + case 0x01d7: + // Equipment related + being = beingManager->findBeing(msg->readInt32()); + msg->readInt8(); // equip point + itemId = msg->readInt16(); + msg->readInt16(); // item id 2 + + if (!being) + break; + + being->setWeaponById(itemId); + break; + + case SMSG_PLAYER_UNEQUIP: + index = msg->readInt16(); + equipPoint = msg->readInt16(); + type = msg->readInt8(); + + if (!type) { + chatWindow->chatLog("Unable to unequip.", BY_SERVER); + break; + } + + if (!equipPoint) { + // No point given, no point in searching + break; + } + + mask = 1; + position = 0; + while (!(equipPoint & mask)) { + mask <<= 1; + position++; + } + + item = player_node->getInvItem(index); + + if (!item) + break; + + item->setEquipped(false); + + switch (item->getId()) { + case 529: + case 1199: + player_node->mEquipment->setArrows(NULL); + break; + case 521: + case 522: + case 530: + case 536: + case 1200: + case 1201: + player_node->setWeapon(0); + // TODO: Why this break? Shouldn't a weapon be + // unequipped in inventory too? + break; + default: + player_node->mEquipment->removeEquipment(position - 1); + break; + } + logger->log("Unequipping: %i %i(%i) %i", + index, equipPoint, type, position - 1); + break; + + case SMSG_PLAYER_ARROW_EQUIP: + itemId = msg->readInt16(); + + if (itemId <= 1) + break; + + item = player_node->getInvItem(itemId); + if (!item) + break; + + item->setEquipped(true); + player_node->mEquipment->setArrows(item); + logger->log("Arrows equipped: %i", itemId); + break; + } +} diff --git a/src/net/equipmenthandler.h b/src/net/equipmenthandler.h new file mode 100644 index 00000000..656f7a73 --- /dev/null +++ b/src/net/equipmenthandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_EQUIPMENTHANDLER_H +#define _TMW_NET_EQUIPMENTHANDLER_H + +#include "messagehandler.h" + +class EquipmentHandler : public MessageHandler +{ + public: + EquipmentHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/inventoryhandler.cpp b/src/net/inventoryhandler.cpp new file mode 100644 index 00000000..3742d327 --- /dev/null +++ b/src/net/inventoryhandler.cpp @@ -0,0 +1,128 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "inventoryhandler.h" + +#include <SDL_types.h> + +#include "messagein.h" +#include "protocol.h" + +#include "../item.h" +#include "../localplayer.h" + +#include "../gui/chat.h" + +InventoryHandler::InventoryHandler() +{ + static const Uint16 _messages[] = { + SMSG_PLAYER_INVENTORY, + SMSG_PLAYER_INVENTORY_ADD, + SMSG_PLAYER_INVENTORY_REMOVE, + SMSG_PLAYER_INVENTORY_USE, + SMSG_ITEM_USE_RESPONSE, + 0 + }; + handledMessages = _messages; +} + +void InventoryHandler::handleMessage(MessageIn *msg) +{ + Sint32 number; + Sint16 index, amount, itemId, equipType; + + switch (msg->getId()) + { + case SMSG_PLAYER_INVENTORY: + // Only called on map load / warp. First reset all items + // to not load them twice on map change. + player_node->clearInventory(); + msg->readInt16(); // length + number = (msg->getLength() - 4) / 18; + + for (int loop = 0; loop < number; loop++) + { + index = msg->readInt16(); + itemId = msg->readInt16(); + msg->readInt8(); // type + msg->readInt8(); // identify flag + amount = msg->readInt16(); + msg->skip(2); // unknown + msg->skip(8); // card (4 shorts) + + player_node->addInvItem(index, itemId, amount, false); + + // Trick because arrows are not considered equipment + if (itemId == 1199 || itemId == 529) + { + player_node->getInvItem(index)->setEquipment(true); + } + } + break; + + case SMSG_PLAYER_INVENTORY_ADD: + index = msg->readInt16(); + amount = msg->readInt16(); + itemId = msg->readInt16(); + msg->readInt8(); // identify flag + msg->readInt8(); // attribute + msg->readInt8(); // refine + msg->skip(8); // card + equipType = msg->readInt16(); + msg->readInt8(); // type + + if (msg->readInt8()> 0) { + chatWindow->chatLog("Unable to pick up item", BY_SERVER); + } else { + player_node->addInvItem(index, itemId, amount, equipType != 0); + } + break; + + case SMSG_PLAYER_INVENTORY_REMOVE: + index = msg->readInt16(); + amount = msg->readInt16(); + player_node->getInvItem(index)->increaseQuantity(-amount); + break; + + case SMSG_PLAYER_INVENTORY_USE: + index = msg->readInt16(); + msg->readInt16(); // item id + msg->readInt32(); // id + amount = msg->readInt16(); + msg->readInt8(); // type + + player_node->getInvItem(index)->setQuantity(amount); + break; + + case SMSG_ITEM_USE_RESPONSE: + index = msg->readInt16(); + amount = msg->readInt16(); + + if (msg->readInt8() == 0) { + chatWindow->chatLog("Failed to use item", BY_SERVER); + } else { + player_node->getInvItem(index)->setQuantity(amount); + } + break; + } +} diff --git a/src/net/inventoryhandler.h b/src/net/inventoryhandler.h new file mode 100644 index 00000000..aedbc3a1 --- /dev/null +++ b/src/net/inventoryhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_INVENTORYHANDLER_H +#define _TMW_NET_INVENTORYHANDLER_H + +#include "messagehandler.h" + +class InventoryHandler : public MessageHandler +{ + public: + InventoryHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/itemhandler.cpp b/src/net/itemhandler.cpp new file mode 100644 index 00000000..d2a938fd --- /dev/null +++ b/src/net/itemhandler.cpp @@ -0,0 +1,67 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "itemhandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../engine.h" +#include "../floor_item.h" + +ItemHandler::ItemHandler() +{ + static const Uint16 _messages[] = { + SMSG_ITEM_VISIBLE, + SMSG_ITEM_DROPPED, + SMSG_ITEM_REMOVE, + 0 + }; + handledMessages = _messages; +} + +void ItemHandler::handleMessage(MessageIn *msg) +{ + Uint32 id; + Uint16 x, y; + Sint16 itemId; + + switch (msg->getId()) + { + case SMSG_ITEM_VISIBLE: + case SMSG_ITEM_DROPPED: + id = msg->readInt32(); + itemId = msg->readInt16(); + msg->readInt8(); // identify flag + x = msg->readInt16(); + y = msg->readInt16(); + msg->skip(4); // amount,subX,subY / subX,subY,amount + + add_floor_item(new FloorItem(id, itemId, x, y, engine->getCurrentMap())); + break; + + case SMSG_ITEM_REMOVE: + remove_floor_item(msg->readInt32()); + break; + } +} diff --git a/src/net/itemhandler.h b/src/net/itemhandler.h new file mode 100644 index 00000000..b2104722 --- /dev/null +++ b/src/net/itemhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_ITEMHANDLER_H +#define _TMW_NET_ITEMHANDLER_H + +#include "messagehandler.h" + +class ItemHandler : public MessageHandler +{ + public: + ItemHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/loginhandler.cpp b/src/net/loginhandler.cpp new file mode 100644 index 00000000..fd525d99 --- /dev/null +++ b/src/net/loginhandler.cpp @@ -0,0 +1,114 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "loginhandler.h" + +#include "messagein.h" +#include "network.h" +#include "protocol.h" + +#include "../log.h" +#include "../main.h" +#include "../serverinfo.h" + +extern SERVER_INFO **server_info; + +LoginHandler::LoginHandler() +{ + static const Uint16 _messages[] = { + 0x0069, + 0x006a, + 0 + }; + handledMessages = _messages; +} + +void LoginHandler::handleMessage(MessageIn *msg) +{ + switch (msg->getId()) + { + case 0x0069: + // Skip the length word + msg->skip(2); + + n_server = (msg->getLength() - 47) / 32; + server_info = (SERVER_INFO**)malloc(sizeof(SERVER_INFO*) * n_server); + + session_ID1 = msg->readInt32(); + account_ID = msg->readInt32(); + session_ID2 = msg->readInt32(); + msg->skip(30); // unknown + sex = msg->readInt8(); + + for (int i = 0; i < n_server; i++) + { + server_info[i] = new SERVER_INFO; + + server_info[i]->address = msg->readInt32(); + server_info[i]->port = msg->readInt16(); + server_info[i]->name = msg->readString(20); + server_info[i]->online_users = msg->readInt32(); + msg->skip(2); // unknown + + logger->log("Network: Server: %s (%s:%d)", + server_info[i]->name.c_str(), + iptostring(server_info[i]->address), + server_info[i]->port); + } + state = CHAR_SERVER_STATE; + break; + + case 0x006a: + int loginError = msg->readInt8(); + logger->log("Login::error code: %i", loginError); + + switch (loginError) { + 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 blocked by the GM Team"; + break; + case 6: + errorMessage = "You have been banned for 5 minutes"; + break; + case 9: + errorMessage = "This account is already logged in"; + break; + default: + errorMessage = "Unknown error"; + break; + } + state = ERROR_STATE; + break; + } +} diff --git a/src/net/loginhandler.h b/src/net/loginhandler.h new file mode 100644 index 00000000..99ade7f1 --- /dev/null +++ b/src/net/loginhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_LOGINHANDLER_H +#define _TMW_NET_LOGINHANDLER_H + +#include "messagehandler.h" + +class LoginHandler : public MessageHandler +{ + public: + LoginHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/maploginhandler.cpp b/src/net/maploginhandler.cpp new file mode 100644 index 00000000..27a7b4c6 --- /dev/null +++ b/src/net/maploginhandler.cpp @@ -0,0 +1,63 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "maploginhandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../localplayer.h" +#include "../log.h" +#include "../main.h" + +MapLoginHandler::MapLoginHandler() +{ + static const Uint16 _messages[] = { + SMSG_LOGIN_SUCCESS, + 0x0081, + 0 + }; + handledMessages = _messages; +} + +void MapLoginHandler::handleMessage(MessageIn *msg) +{ + unsigned char direction; + + switch (msg->getId()) + { + case SMSG_LOGIN_SUCCESS: + msg->readInt32(); // server tick + msg->readCoordinates(player_node->x, player_node->y, direction); + msg->skip(2); // unknown + logger->log("Protocol: Player start position: (%d, %d), Direction: %d", + player_node->x, player_node->y, direction); + state = GAME_STATE; + break; + + case 0x0081: + logger->log("Warning: Map server D/C"); + state = ERROR_STATE; + break; + } +} diff --git a/src/net/maploginhandler.h b/src/net/maploginhandler.h new file mode 100644 index 00000000..fe597549 --- /dev/null +++ b/src/net/maploginhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_MAPLOGINHANDLER_H +#define _TMW_NET_MAPLOGINHANDLER_H + +#include "messagehandler.h" + +class MapLoginHandler : public MessageHandler +{ + public: + MapLoginHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/messagehandler.cpp b/src/net/messagehandler.cpp new file mode 100644 index 00000000..849b6716 --- /dev/null +++ b/src/net/messagehandler.cpp @@ -0,0 +1,45 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "messagehandler.h" + +#include <cassert> + +#include "network.h" + +MessageHandler::MessageHandler(): + mNetwork(0) +{ +} + +MessageHandler::~MessageHandler() +{ + if (mNetwork) + mNetwork->unregisterHandler(this); +} + +void MessageHandler::setNetwork(Network *network) +{ + assert(!(network && mNetwork)); + mNetwork = network; +} diff --git a/src/net/messagehandler.h b/src/net/messagehandler.h new file mode 100644 index 00000000..c09037f6 --- /dev/null +++ b/src/net/messagehandler.h @@ -0,0 +1,48 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_MESSAGEHANDLER_H +#define _TMW_NET_MESSAGEHANDLER_H + +#include <SDL_types.h> + +class MessageIn; +class Network; + +class MessageHandler +{ + public: + const Uint16 *handledMessages; + + MessageHandler(); + virtual ~MessageHandler(); + + virtual void handleMessage(MessageIn *msg) =0; + + void setNetwork(Network *network); + + protected: + Network *mNetwork; +}; + +#endif diff --git a/src/net/messageout.cpp b/src/net/messageout.cpp index 9b27a6a5..f6ed5de6 100644 --- a/src/net/messageout.cpp +++ b/src/net/messageout.cpp @@ -30,63 +30,42 @@ #include "network.h" #include "packet.h" -MessageOut::MessageOut(): - mPacket(0), +MessageOut::MessageOut(Network *network): + mNetwork(network), mData(0), mDataSize(0), mPos(0) { - mData = out + out_size; -} - -MessageOut::~MessageOut() -{ - if (mPacket) { - delete mPacket; - } - - // Don't free this data for now, see above. - //if (mData) { - // free(mData); - //} -} - -void MessageOut::expand(size_t bytes) -{ - /*mData = (char*)realloc(mData, bytes); - mDataSize = bytes;*/ + mData = mNetwork->mOutBuffer + mNetwork->mOutSize; } void MessageOut::writeInt8(Sint8 value) { - expand(mPos + sizeof(Sint8)); mData[mPos] = value; mPos += sizeof(Sint8); - out_size += sizeof(Sint8); + mNetwork->mOutSize+= sizeof(Sint8); } void MessageOut::writeInt16(Sint16 value) { - expand(mPos + sizeof(Sint16)); #if SDL_BYTEORDER == SDL_BIG_ENDIAN (*(Sint16 *)(mData + mPos)) = SDL_Swap16(value); #else (*(Sint16 *)(mData + mPos)) = value; #endif mPos += sizeof(Sint16); - out_size += sizeof(Sint16); + mNetwork->mOutSize += sizeof(Sint16); } void MessageOut::writeInt32(Sint32 value) { - expand(mPos + sizeof(Sint32)); #if SDL_BYTEORDER == SDL_BIG_ENDIAN (*(Sint32 *)(mData + mPos)) = SDL_Swap32(value); #else (*(Sint32 *)(mData + mPos)) = value; #endif mPos += sizeof(Sint32); - out_size += sizeof(Sint32); + mNetwork->mOutSize += sizeof(Sint32); } void MessageOut::writeString(const std::string &string, int length) @@ -97,39 +76,27 @@ void MessageOut::writeString(const std::string &string, int length) { // Write the length at the start if not fixed writeInt16(string.length()); - expand(mPos + string.length()); } else { // Make sure the length of the string is no longer than specified toWrite = string.substr(0, length); - expand(mPos + length); } // Write the actual string memcpy(&mData[mPos], (void*)toWrite.c_str(), toWrite.length()); mPos += toWrite.length(); - out_size += toWrite.length(); + mNetwork->mOutSize += toWrite.length(); // Pad remaining space with zeros if (length > (int)toWrite.length()) { memset(&mData[mPos], '\0', length - toWrite.length()); mPos += length - toWrite.length(); - out_size += length - toWrite.length(); + mNetwork->mOutSize += length - toWrite.length(); } } -const Packet *MessageOut::getPacket() -{ - if (!mPacket) - { - mPacket = new Packet(mData, mDataSize); - } - - return mPacket; -} - MessageOut& operator<<(MessageOut &msg, const Sint8 &rhs) { msg.writeInt8(rhs); diff --git a/src/net/messageout.h b/src/net/messageout.h index b2ee506e..f6468adb 100644 --- a/src/net/messageout.h +++ b/src/net/messageout.h @@ -27,7 +27,7 @@ #include <iosfwd> #include <SDL_types.h> -class Packet; +class Network; /** * Used for building an outgoing message. @@ -42,12 +42,7 @@ class MessageOut /** * Constructor. */ - MessageOut(); - - /** - * Destructor. - */ - ~MessageOut(); + MessageOut(Network *network); void writeInt8(Sint8 value); /**< Writes a byte. */ void writeInt16(Sint16 value); /**< Writes a short. */ @@ -59,24 +54,9 @@ class MessageOut */ void writeString(const std::string &string, int length = -1); - /** - * Returns an instance of Packet derived from the written data. Use for - * sending the packet. No more writing to the packet may be done after - * a call to this method. - */ - const Packet *getPacket(); - private: - /** - * 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); + Network *mNetwork; - Packet *mPacket; /**< Created packet. */ char *mData; /**< Data building up. */ unsigned int mDataSize; /**< Size of data. */ unsigned int mPos; /**< Position in the data. */ diff --git a/src/net/network.cpp b/src/net/network.cpp index fbcf199b..3b0652e2 100644 --- a/src/net/network.cpp +++ b/src/net/network.cpp @@ -23,15 +23,10 @@ #include "network.h" -#include <cassert> -#include <sstream> -#include <SDL_net.h> -#include <SDL_thread.h> - +#include "messagehandler.h" #include "messagein.h" #include "../log.h" -#include "../main.h" /** Warning: buffers and other variables are shared, so there can be only one connection active at a time */ @@ -81,309 +76,379 @@ short packet_lengths[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -unsigned int buffer_size = 65536; -char *in = NULL; -char *out = NULL; -unsigned int in_size = 0; -unsigned int out_size = 0; -int connectionOpen = NET_IDLE; +const unsigned int BUFFER_SIZE = 65536; -TCPsocket sock; -SDLNet_SocketSet set; -SDL_Thread *mThread = NULL; -SDL_mutex *mMutex = NULL; -IPaddress *ip = NULL; +int networkThread(void *data) +{ + Network *network = static_cast<Network*>(data); -char *iptostring(int address) + if (!network->realConnect()) + return -1; + + network->receive(); + + return 0; +} + +Network::Network(): + mAddress(0), mPort(0), + mInBuffer(new char[BUFFER_SIZE]), + mOutBuffer(new char[BUFFER_SIZE]), + mInSize(0), mOutSize(0), + mToSkip(0), + mState(IDLE), + mWorkerThread(0) { - static char asciiIP[16]; + mMutex = SDL_CreateMutex(); +} - sprintf(asciiIP, "%i.%i.%i.%i", - (unsigned char)(address), - (unsigned char)(address >> 8), - (unsigned char)(address >> 16), - (unsigned char)(address >> 24)); +Network::~Network() +{ + clearHandlers(); - return asciiIP; + if (mAddress) + free(mAddress); + + if (mState != IDLE && mState != ERROR) + disconnect(); + + SDL_DestroyMutex(mMutex); + + delete mInBuffer; + delete mOutBuffer; } -int connectionThread(void *ptr) +bool Network::connect(const char *address, short port) { - // Create the socket for the current session - sock = SDLNet_TCP_Open((IPaddress *)ptr); - if (!sock) + if (mState != IDLE && mState != ERROR) { - logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; - return NET_ERROR; + logger->log("Tried to connect an already connected socket!"); + return false; } - // Create a socket set to listen to socket - set = SDLNet_AllocSocketSet(1); - if (!set) + if (!address) { - logger->log("Error in SDLNet_AllocSocketSet(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; - return NET_ERROR; + logger->log("Empty address given to Network::connect()!"); + mState = ERROR; + return false; } - // Add the socket to the set - int ret = SDLNet_TCP_AddSocket(set, sock); - if (ret == -1) + if (mAddress) + free(mAddress); + + mAddress = strdup(address); + mPort = port; + + // Reset to sane values + mOutSize = 0; + mInSize = 0; + mToSkip = 0; + + mState = CONNECTING; + mWorkerThread = SDL_CreateThread(networkThread, this); + if (!mWorkerThread) { - logger->log("Error in SDLNet_AddSocket(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; - return NET_ERROR; + logger->log("Unable to create network worker thread"); + mState = ERROR; + return false; } - // Init buffers - in = (char*)malloc(buffer_size); - out = (char*)malloc(buffer_size); - memset(in, '\0', buffer_size); - memset(out, '\0', buffer_size); - in_size = 0; - out_size = 0; - - SDL_mutexP(mMutex); - logger->log("Network::Started session with %s:%i", - iptostring(((IPaddress *)ptr)->host), - ((IPaddress *)ptr)->port); - connectionOpen = NET_CONNECTED; - SDL_mutexV(mMutex); - return NET_CONNECTED; + return true; } -void openConnection(const char* address, short port) +void Network::disconnect() { - //assert(connectionOpen <= NET_IDLE); + if (mState != CONNECTED && mState != CONNECTING) + return; - // Initialize SDL_net - if (SDLNet_Init() == -1) + mState = IDLE; + + if (mWorkerThread) { - logger->log("Error in SDLNet_Init(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; + SDL_WaitThread(mWorkerThread, NULL); + mWorkerThread = NULL; } + SDLNet_TCP_Close(mSocket); +} - ip = new IPaddress(); +void Network::registerHandler(MessageHandler *handler) +{ + const Uint16 *i = handler->handledMessages; - // Resolve host name - if (SDLNet_ResolveHost(ip, address, port) == -1) + while(*i) { - logger->log("Error in SDLNet_ResolveHost(): %s", SDLNet_GetError()); - connectionOpen = NET_ERROR; + mMessageHandlers[*i] = handler; + i++; } - connectionOpen = NET_CONNECTING; - // Create the synchronization lock - mMutex = SDL_CreateMutex(); - // Create the connection thread - mThread = SDL_CreateThread(connectionThread, ip); - if (mThread == NULL) { - logger->log("Unable to create connection thread"); - connectionOpen = NET_ERROR; - } + handler->setNetwork(this); } -int pollConnection() +void Network::unregisterHandler(MessageHandler *handler) { - if (mMutex) - { - SDL_mutexP(mMutex); - } + const Uint16 *i = handler->handledMessages; - switch (connectionOpen) + while(*i) { - case NET_IDLE: - case NET_CONNECTING: - break; - case NET_CONNECTED: - case NET_ERROR: - SDL_WaitThread(mThread, NULL); - mThread = NULL; - SDL_DestroyMutex(mMutex); - mMutex = NULL; - break; + std::map<Uint16, MessageHandler*>::iterator iter; + iter = mMessageHandlers.find(*i); + if (iter != mMessageHandlers.end()) + { + mMessageHandlers.erase(iter); + } + i++; } - if (mMutex) + handler->setNetwork(0); +} + +void Network::clearHandlers() +{ + std::map<Uint16, MessageHandler*>::iterator i; + for (i = mMessageHandlers.begin(); i != mMessageHandlers.end(); i++) { - SDL_mutexV(mMutex); + i->second->setNetwork(0); } - return connectionOpen; + mMessageHandlers.clear(); +} + +void Network::dispatchMessages() +{ + if (!messageReady()) + return; + + MessageIn msg = getNextMessage(); + + std::map<Uint16, MessageHandler*>::iterator iter; + iter = mMessageHandlers.find(msg.getId()); + + if (iter != mMessageHandlers.end()) + iter->second->handleMessage(&msg); + else + logger->log("Unhandled packet: %x", msg.getId()); + + skip(msg.getLength()); } -void closeConnection() +void Network::flush() { - //assert(connectionOpen > ); + if (!mOutSize || mState != CONNECTED) + return; + + int ret; - if (connectionOpen == NET_ERROR)return; - if (mThread) + SDL_mutexP(mMutex); + ret = SDLNet_TCP_Send(mSocket, mOutBuffer, mOutSize); + if (ret < (int)mOutSize) { - SDL_WaitThread(mThread, NULL); - mThread = NULL; + logger->log("Error in SDLNet_TCP_Send(): %s", SDLNet_GetError()); + mState = ERROR; } + mOutSize = 0; + SDL_mutexV(mMutex); +} - if (mMutex) +void Network::skip(int len) +{ + SDL_mutexP(mMutex); + mToSkip += len; + if (!mInSize) { - SDL_DestroyMutex(mMutex); - mMutex = NULL; + SDL_mutexV(mMutex); + return; } - if (ip) + if (mInSize >= mToSkip) { - delete ip; - ip = NULL; + mInSize -= mToSkip; + memmove(mInBuffer, mInBuffer + mToSkip, mInSize); + mToSkip = 0; } - - // Remove the socket from the socket set - int ret = SDLNet_TCP_DelSocket(set, sock); - if (ret == -1) + else { - logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError()); + mToSkip -= mInSize; + mInSize = 0; } + SDL_mutexV(mMutex); +} - // Close the TCP connection - SDLNet_TCP_Close(sock); - - // Free the socket set - SDLNet_FreeSocketSet(set); - set = NULL; +bool Network::messageReady() +{ + int len = -1; - // Clear buffers - if (in != NULL) + SDL_mutexP(mMutex); + if (mInSize >= 2) { - free(in); - in = NULL; + len = packet_lengths[readWord(0)]; + + if (len == -1 && mInSize > 4) + len = readWord(2); + } - if (out != NULL) + bool ret = (mInSize >= static_cast<unsigned int>(len)); + SDL_mutexV(mMutex); + + return ret; +} + +MessageIn Network::getNextMessage() +{ + while (!messageReady()) { - free(out); - out = NULL; + if (mState == ERROR) + break; } - in_size = 0; - out_size = 0; + SDL_mutexP(mMutex); + int msgId = readWord(0); + int len = packet_lengths[msgId]; - // Shutdown the network API - SDLNet_Quit(); + if (len == -1) + len = readWord(2); - logger->log("Network::Closed session"); - connectionOpen = NET_IDLE; +#ifdef DEBUG + printf("Received packet 0x%x of length %d\n", msgId, length); +#endif + + MessageIn msg(mInBuffer, len); + SDL_mutexV(mMutex); + + return msg; } -void flush() +bool Network::realConnect() { - // Send all available data, waits if not all data can be sent immediately - if (out_size > 0) + IPaddress ipAddress; + + if (SDLNet_ResolveHost(&ipAddress, mAddress, mPort) == -1) { - int ret = SDLNet_TCP_Send(sock, (char*)out, out_size); - if (ret < (int)out_size) - { - logger->log("Error in SDLNet_TCP_Send(): %s", SDLNet_GetError()); - errorMessage = "You got disconnected from server"; - state = ERROR_STATE; - return; - } - out_size -= ret; + logger->log("Error in SDLNet_ResolveHost(): %s", SDLNet_GetError()); + mState = ERROR; + return false; } - int numReady = SDLNet_CheckSockets(set, 0); - if (numReady == -1) + mState = CONNECTING; + + mSocket = SDLNet_TCP_Open(&ipAddress); + if (!mSocket) { - logger->log("Error: SDLNet_CheckSockets"); - return; + logger->log("Error in SDLNet_TCP_Open(): %s", SDLNet_GetError()); + mState = ERROR; + return false; } - else if (numReady == 0) // any socket ready + + 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))) { + logger->log("Error in SDLNet_AllocSocketSet(): %s", SDLNet_GetError()); + mState = ERROR; return; } - else if (numReady == 1) // one socket is ready - { - // Receive data from the socket - int ret = SDLNet_TCP_Recv(sock, in + in_size, buffer_size - in_size); - if (ret <= 0) - { - logger->log("Error in SDLNet_TCP_Recv(): %s", SDLNet_GetError()); - errorMessage = "You got disconnected from server"; - state = ERROR_STATE; - return; - } - else { - in_size += ret; - } - } - else // more than one socket is ready.. this should not happen since we only listen once socket. + + if (SDLNet_TCP_AddSocket(set, mSocket) == -1) { - logger->log("Error in SDLNet_TCP_Recv(), %d sockets are ready : %s", numReady, SDLNet_GetError()); - errorMessage = "You got disconnected from server"; - state = ERROR_STATE; - return; + logger->log("Error in SDLNet_AddSocket(): %s", SDLNet_GetError()); + mState = ERROR; } -} -unsigned short readWord(int pos) -{ -#if SDL_BYTEORDER == SDL_BIG_ENDIAN - return SDL_Swap16((*(unsigned short*)(in+(pos)))); -#else - return (*(unsigned short *)(in+(pos))); -#endif -} - -bool packetReady() -{ - bool ret = false; - if (in_size >= 2) + while (mState == CONNECTED) { - int length = packet_lengths[readWord(0)]; - if (length == -1) + // 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) { - if (in_size >= 4) - { - length = readWord(2); - if (in_size >= (unsigned int)length) + case -1: + logger->log("Error: SDLNet_CheckSockets"); + // FALLTHROUGH + case 0: + break; + + case 1: + // Receive data from the socket + SDL_mutexP(mMutex); + ret = SDLNet_TCP_Recv(mSocket, mInBuffer + mInSize, BUFFER_SIZE - mInSize); + + if (!ret) { - ret = true; + // We got disconnected + mState = IDLE; + logger->log("Disconnected."); } - } - } - else if (in_size >= (unsigned int)length) - { - ret = true; + else if (ret < 0) + { + logger->log("Error in SDLNet_TCP_Recv(): %s", SDLNet_GetError()); + mState = ERROR; + } + 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. + logger->log("Error in SDLNet_TCP_Recv(), %d sockets are ready : %s", numReady, SDLNet_GetError()); + mState = ERROR; + break; } } - return ret; -} - -MessageIn -get_next_message() -{ - // At least 2 bytes should be received for the message ID - while (in_size < 2 && state != ERROR_STATE) flush(); - - int length = packet_lengths[readWord(0)]; - if (length == -1) + if (SDLNet_TCP_DelSocket(set, mSocket) == -1) { - // Another 2 bytes should be received for the length - while (in_size < 4) flush(); - length = readWord(2); + logger->log("Error in SDLNet_DelSocket(): %s", SDLNet_GetError()); } -#ifdef DEBUG - printf("Received packet 0x%x of length %d\n", readWord(0), length); -#endif + SDLNet_FreeSocketSet(set); +} + +char *iptostring(int address) +{ + static char asciiIP[16]; - // Make sure the whole packet is received - while (in_size < static_cast<unsigned int>(length) && state != ERROR_STATE) flush(); + sprintf(asciiIP, "%i.%i.%i.%i", + (unsigned char)(address), + (unsigned char)(address >> 8), + (unsigned char)(address >> 16), + (unsigned char)(address >> 24)); - return MessageIn(in, length); + return asciiIP; } -void skip(int len) +Uint16 Network::readWord(int pos) { - memcpy(in, in + len, in_size - len); - in_size -= len; +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + return SDL_Swap16((*(Uint16*)(mInBuffer+(pos)))); +#else + return (*(Uint16*)(mInBuffer+(pos))); +#endif } diff --git a/src/net/network.h b/src/net/network.h index db95d7c3..75bde584 100644 --- a/src/net/network.h +++ b/src/net/network.h @@ -24,41 +24,77 @@ #ifndef _TMW_NETWORK_ #define _TMW_NETWORK_ -#define NET_ERROR -1 -#define NET_CONNECTED 0 -#define NET_IDLE 1 -#define NET_CONNECTING 2 -#define NET_DATA 3 +#include <map> +#include <SDL_net.h> +#include <SDL_thread.h> +class MessageHandler; class MessageIn; -/** Convert an address from int format to string */ -char *iptostring(int address); +class Network; -/** Open a session with a server */ -void openConnection(const char* address, short port); +class Network +{ + public: + friend int networkThread(void *data); + friend class MessageOut; -/** Returns the status of the current connection attempt. */ -int pollConnection(); + Network(); + ~Network(); -/** Close a session */ -void closeConnection(); + bool connect(const char *address, short port); + void disconnect(); -/** Send and receive data waiting in the buffers */ -void flush(); + void registerHandler(MessageHandler *handler); + void unregisterHandler(MessageHandler *handler); + void clearHandlers(); -/** Check if a packet is complete */ -bool packetReady(); + int getState() const { return mState; } + bool isConnected() const { return mState == CONNECTED; } -/** - * Returns the next arriving message, waiting for it if necessary. - */ -MessageIn get_next_message(); -extern char *out; + int getInSize() const { return mInSize; } + + void skip(int len); + + bool messageReady(); + MessageIn getNextMessage(); + + void dispatchMessages(); + void flush(); + + enum { + IDLE, + CONNECTED, + CONNECTING, + DATA, + ERROR + }; + + protected: + Uint16 readWord(int pos); + + TCPsocket mSocket; -void skip(int len); + char *mAddress; + short mPort; -extern unsigned int in_size; /**< Amount of data in input buffer. */ -extern unsigned int out_size; /**< Amount of data in output buffer. */ + char *mInBuffer, *mOutBuffer; + unsigned int mInSize, mOutSize; + + unsigned int mToSkip; + + int mState; + + SDL_Thread *mWorkerThread; + SDL_mutex *mMutex; + + std::map<Uint16, MessageHandler*> mMessageHandlers; + + bool realConnect(); + void receive(); +}; + +/** Convert an address from int format to string */ +char *iptostring(int address); #endif diff --git a/src/net/npchandler.cpp b/src/net/npchandler.cpp new file mode 100644 index 00000000..a803710e --- /dev/null +++ b/src/net/npchandler.cpp @@ -0,0 +1,74 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "npchandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../beingmanager.h" +#include "../npc.h" + +#include "../gui/npclistdialog.h" +#include "../gui/npc_text.h" + +extern NpcListDialog *npcListDialog; +extern NpcTextDialog *npcTextDialog; + +NPCHandler::NPCHandler() +{ + static const Uint16 _messages[] = { + SMSG_NPC_CHOICE, + SMSG_NPC_MESSAGE, + SMSG_NPC_NEXT, + SMSG_NPC_CLOSE, + 0 + }; + handledMessages = _messages; +} + +void NPCHandler::handleMessage(MessageIn *msg) +{ + switch (msg->getId()) + { + case SMSG_NPC_CHOICE: + msg->readInt16(); // length + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(msg->readInt32())); + npcListDialog->parseItems(msg->readString(msg->getLength() - 8)); + npcListDialog->setVisible(true); + break; + + case SMSG_NPC_MESSAGE: + msg->readInt16(); // length + current_npc = dynamic_cast<NPC*>(beingManager->findBeing(msg->readInt32())); + npcTextDialog->addText(msg->readString(msg->getLength() - 8)); + npcListDialog->setVisible(false); + npcTextDialog->setVisible(true); + break; + + case SMSG_NPC_NEXT: + case SMSG_NPC_CLOSE: + // Next/Close button in NPC dialog, currently unused + break; + } +} diff --git a/src/net/npchandler.h b/src/net/npchandler.h new file mode 100644 index 00000000..903ecd10 --- /dev/null +++ b/src/net/npchandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_NPCHANDLER_H +#define _TMW_NET_NPCHANDLER_H + +#include "messagehandler.h" + +class NPCHandler : public MessageHandler +{ + public: + NPCHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/playerhandler.cpp b/src/net/playerhandler.cpp new file mode 100644 index 00000000..1a255e0b --- /dev/null +++ b/src/net/playerhandler.cpp @@ -0,0 +1,301 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "playerhandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../engine.h" +#include "../localplayer.h" +#include "../log.h" +#include "../npc.h" + +#include "../gui/chat.h" +#include "../gui/ok_dialog.h" +#include "../gui/skill.h" + +// TODO Move somewhere else +OkDialog *weightNotice = NULL; +OkDialog *deathNotice = NULL; + +/** + * Listener used for handling the overweigth message. + */ +// TODO Move somewhere else +class WeightNoticeListener : public gcn::ActionListener +{ + public: + void action(const std::string &eventId) + { + weightNotice = NULL; + } +} weightNoticeListener; + + +/** + * Listener used for handling death message. + */ +// TODO Move somewhere else +class DeathNoticeListener : public gcn::ActionListener { + public: + void action(const std::string &eventId) { + player_node->revive(); + deathNotice = NULL; + } +} deathNoticeListener; + +PlayerHandler::PlayerHandler() +{ + static const Uint16 _messages[] = { + SMSG_WALK_RESPONSE, + SMSG_PLAYER_WARP, + SMSG_PLAYER_STAT_UPDATE_1, + SMSG_PLAYER_STAT_UPDATE_2, + SMSG_PLAYER_STAT_UPDATE_3, + SMSG_PLAYER_STAT_UPDATE_4, + SMSG_PLAYER_STAT_UPDATE_5, + SMSG_PLAYER_STAT_UPDATE_6, + SMSG_PLAYER_ARROW_MESSAGE, + 0 + }; + handledMessages = _messages; +} + +void PlayerHandler::handleMessage(MessageIn *msg) +{ + switch (msg->getId()) + { + case SMSG_WALK_RESPONSE: + // It is assumed by the client any request to walk actually + // succeeds on the server. The plan is to have a correction + // message when the server senses the client has the wrong + // idea. + break; + + case SMSG_PLAYER_WARP: + { + std::string mapPath = msg->readString(16); + Uint16 x = msg->readInt16(); + Uint16 y = msg->readInt16(); + + logger->log("Warping to %s (%d, %d)", mapPath.c_str(), x, y); + + // Switch the actual map, deleting the previous one + engine->changeMap(mapPath); + + current_npc = 0; + + player_node->action = Being::STAND; + player_node->stopAttack(); + player_node->mFrame = 0; + player_node->x = x; + player_node->y = y; + } + break; + + case SMSG_PLAYER_STAT_UPDATE_1: + { + Sint16 type = msg->readInt16(); + Uint32 value = msg->readInt32(); + + switch (type) + { + //case 0x0000: + // player_node->setWalkSpeed(msg->readInt32()); + // break; + case 0x0005: player_node->hp = value; break; + case 0x0006: player_node->maxHp = value; break; + case 0x0007: player_node->mp = value; break; + case 0x0008: player_node->maxMp = value; break; + case 0x000b: player_node->lvl = value; break; + case 0x000c: + player_node->skillPoint = value; + skillDialog->update(); + break; + case 0x0018: + if (value >= player_node->maxWeight / 2 && + player_node->totalWeight < + player_node->maxWeight / 2) + { + weightNotice = new OkDialog("Message", + "You are carrying more then half your " + "weight. You are unable to regain " + "health.", + &weightNoticeListener); + } + player_node->totalWeight = value; + break; + case 0x0019: player_node->maxWeight = value; break; + case 0x0037: player_node->jobLvl = value; break; + case 0x0009: + player_node->statsPointsToAttribute = value; + break; + case 0x0029: player_node->ATK = value; break; + case 0x002b: player_node->MATK = value; break; + case 0x002d: player_node->DEF = value; break; + case 0x002f: player_node->MDEF = value; break; + case 0x0031: player_node->HIT = value; break; + case 0x0032: player_node->FLEE = value; break; + case 0x0035: player_node->aspd = value; break; + } + + if (player_node->hp == 0 && deathNotice == NULL) + { + deathNotice = new OkDialog("Message", + "You're now dead, press ok to restart", + &deathNoticeListener); + player_node->action = Being::DEAD; + } + } + break; + + case SMSG_PLAYER_STAT_UPDATE_2: + switch (msg->readInt16()) { + case 0x0001: + player_node->xp = msg->readInt32(); + break; + case 0x0002: + player_node->jobXp = msg->readInt32(); + break; + case 0x0014: + player_node->gp = msg->readInt32(); + break; + case 0x0016: + player_node->xpForNextLevel = msg->readInt32(); + break; + case 0x0017: + player_node->jobXpForNextLevel = msg->readInt32(); + break; + } + break; + + case SMSG_PLAYER_STAT_UPDATE_3: + { + Sint32 type = msg->readInt32(); + Sint32 base = msg->readInt32(); + Sint32 bonus = msg->readInt32(); + Sint32 total = base + bonus; + + switch (type) { + case 0x000d: player_node->ATTR[LocalPlayer::STR] = total; break; + case 0x000e: player_node->ATTR[LocalPlayer::AGI] = total; break; + case 0x000f: player_node->ATTR[LocalPlayer::VIT] = total; break; + case 0x0010: player_node->ATTR[LocalPlayer::INT] = total; break; + case 0x0011: player_node->ATTR[LocalPlayer::DEX] = total; break; + case 0x0012: player_node->ATTR[LocalPlayer::LUK] = total; break; + } + } + break; + + case SMSG_PLAYER_STAT_UPDATE_4: + { + Sint16 type = msg->readInt16(); + Sint8 fail = msg->readInt8(); + Sint8 value = msg->readInt8(); + + if (fail == 1) + { + switch (type) { + case 0x000d: player_node->ATTR[LocalPlayer::STR] = value; break; + case 0x000e: player_node->ATTR[LocalPlayer::AGI] = value; break; + case 0x000f: player_node->ATTR[LocalPlayer::VIT] = value; break; + case 0x0010: player_node->ATTR[LocalPlayer::INT] = value; break; + case 0x0011: player_node->ATTR[LocalPlayer::DEX] = value; break; + case 0x0012: player_node->ATTR[LocalPlayer::LUK] = value; break; + } + } + } + break; + + // Updates stats and status points + case SMSG_PLAYER_STAT_UPDATE_5: + player_node->statsPointsToAttribute = msg->readInt16(); + player_node->ATTR[LocalPlayer::STR] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::STR] = msg->readInt8(); + player_node->ATTR[LocalPlayer::AGI] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::AGI] = msg->readInt8(); + player_node->ATTR[LocalPlayer::VIT] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::VIT] = msg->readInt8(); + player_node->ATTR[LocalPlayer::INT] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::INT] = msg->readInt8(); + player_node->ATTR[LocalPlayer::DEX] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::DEX] = msg->readInt8(); + player_node->ATTR[LocalPlayer::LUK] = msg->readInt8(); + player_node->ATTR_UP[LocalPlayer::LUK] = msg->readInt8(); + player_node->ATK = msg->readInt16(); // ATK + player_node->ATK_BONUS = msg->readInt16(); // ATK bonus + player_node->MATK = msg->readInt16(); // MATK max + player_node->MATK_BONUS = msg->readInt16(); // MATK min + player_node->DEF = msg->readInt16(); // DEF + player_node->DEF_BONUS = msg->readInt16(); // DEF bonus + player_node->MDEF = msg->readInt16(); // MDEF + player_node->MDEF_BONUS = msg->readInt16(); // MDEF bonus + player_node->HIT = msg->readInt16(); // HIT + player_node->FLEE = msg->readInt16(); // FLEE + player_node->FLEE_BONUS = msg->readInt16(); // FLEE bonus + msg->readInt16(); // critical + msg->readInt16(); // unknown + break; + + case SMSG_PLAYER_STAT_UPDATE_6: + switch (msg->readInt16()) { + case 0x0020: player_node->ATTR_UP[LocalPlayer::STR] = msg->readInt8(); break; + case 0x0021: player_node->ATTR_UP[LocalPlayer::AGI] = msg->readInt8(); break; + case 0x0022: player_node->ATTR_UP[LocalPlayer::VIT] = msg->readInt8(); break; + case 0x0023: player_node->ATTR_UP[LocalPlayer::INT] = msg->readInt8(); break; + case 0x0024: player_node->ATTR_UP[LocalPlayer::DEX] = msg->readInt8(); break; + case 0x0025: player_node->ATTR_UP[LocalPlayer::LUK] = msg->readInt8(); break; + } + break; + + case SMSG_PLAYER_ARROW_MESSAGE: + { + Sint16 type = msg->readInt16(); + + switch (type) { + case 0: + chatWindow->chatLog("Equip arrows first", + BY_SERVER); + break; + default: + logger->log("0x013b: Unhandled message %i", type); + break; + } + } + break; + + //Stop walking + //case 0x0088: // Disabled because giving some problems + //if (being = beingManager->findBeing(readInt32(2))) { + // if (being->getId() != player_node->getId()) { + // being->action = STAND; + // being->mFrame = 0; + // set_coordinates(being->coordinates, + // readWord(6), readWord(8), + // get_direction(being->coordinates)); + // } + //} + //break; + } +} diff --git a/src/net/playerhandler.h b/src/net/playerhandler.h new file mode 100644 index 00000000..b28a23f5 --- /dev/null +++ b/src/net/playerhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_PLAYERHANDLER_H +#define _TMW_NET_PLAYERHANDLER_H + +#include "messagehandler.h" + +class PlayerHandler : public MessageHandler +{ + public: + PlayerHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/protocol.cpp b/src/net/protocol.cpp index 28663827..5de1b0cf 100644 --- a/src/net/protocol.cpp +++ b/src/net/protocol.cpp @@ -23,14 +23,6 @@ #include "protocol.h" -#include "messageout.h" - -#include "../being.h" -#include "../game.h" -#include "../main.h" -#include "../playerinfo.h" -#include "../sound.h" - #define LOBYTE(w) ((unsigned char)(w)) #define HIBYTE(w) ((unsigned char)(((unsigned short)(w)) >> 8)) @@ -64,101 +56,3 @@ void set_coordinates(char *data, data[2] = LOBYTE(temp); data[2] |= direction; } - -void walk(unsigned short x, unsigned short y, unsigned char direction) -{ - char temp[3]; - MessageOut outMsg; - set_coordinates(temp, x, y, direction); - outMsg.writeInt16(0x0085); - outMsg.writeString(temp, 3); -} - -void action(char type, int id) -{ - MessageOut outMsg; - outMsg.writeInt16(0x0089); - outMsg.writeInt32(id); - outMsg.writeInt8(type); -} - -void talk(Being *being) -{ - MessageOut outMsg; - outMsg.writeInt16(CMSG_NPC_TALK); - outMsg.writeInt32(being->getId()); - outMsg.writeInt8(0); -} - -void pickUp(Uint32 floorItemId) -{ - MessageOut outMsg; - outMsg.writeInt16(CMSG_ITEM_PICKUP); - outMsg.writeInt32(floorItemId); -} - -Being* attack(unsigned short x, unsigned short y, unsigned char direction) -{ - Being *target = NULL; - - switch (direction) - { - case Being::SOUTH: - target = findNode(x, y + 1, Being::MONSTER); - if (!target) target = findNode(x, y + 1, Being::PLAYER); - break; - - case Being::WEST: - target = findNode(x - 1, y, Being::MONSTER); - if (!target) target = findNode(x - 1, y, Being::PLAYER); - break; - - case Being::NORTH: - target = findNode(x, y - 1, Being::MONSTER); - if (!target) target = findNode(x, y - 1, Being::PLAYER); - break; - - case Being::EAST: - target = findNode(x + 1, y, Being::MONSTER); - if (!target) target = findNode(x + 1, y, Being::PLAYER); - break; - } - - if (target) { - attack(target); - } - - return target; -} - -void attack(Being *target) -{ - int dist_x = target->x - player_node->x; - int dist_y = target->y - player_node->y; - - if (abs(dist_y) >= abs(dist_x)) - { - if (dist_y > 0) - player_node->direction = Being::SOUTH; - else - player_node->direction = Being::NORTH; - } - else - { - if (dist_x > 0) - player_node->direction = Being::EAST; - else - player_node->direction = Being::WEST; - } - - // Implement charging attacks here - player_info->lastAttackTime = 0; - - player_node->action = Being::ATTACK; - action(0, target->getId()); - player_node->walk_time = tick_time; - if (player_node->getWeapon() == 2) - sound.playSfx("sfx/bow_shoot_1.ogg"); - else - sound.playSfx("sfx/fist-swish.ogg"); -} diff --git a/src/net/protocol.h b/src/net/protocol.h index e7d6f286..716aa0f9 100644 --- a/src/net/protocol.h +++ b/src/net/protocol.h @@ -24,10 +24,6 @@ #ifndef _TMW_PROTOCOL_ #define _TMW_PROTOCOL_ -#include <SDL_types.h> - -class Being; - // Packets from server to client #define SMSG_LOGIN_SUCCESS 0x0073 /**< Contains starting location */ #define SMSG_PLAYER_UPDATE_1 0x01d8 @@ -108,7 +104,6 @@ class Being; #define CMSG_PLAYER_EQUIP 0x00a9 #define CMSG_PLAYER_UNEQUIP 0x00ab - /** Decodes src direction */ unsigned char get_src_direction(char data); @@ -118,22 +113,4 @@ unsigned char get_dest_direction(char data); /** Encodes coords and direction in 3 bytes data */ void set_coordinates(char *data, unsigned short x, unsigned short y, unsigned char direction); -/** Requests to walk */ -void walk(unsigned short x, unsigned short y, unsigned char direction); - -/** Request to attack */ -Being* attack(unsigned short x, unsigned short y, unsigned char direction); - -/** Request to attack */ -void attack(Being *target); - -/** Request action */ -void action(char type, int id); - -/** Talk to a being */ -void talk(Being *being); - -/** Pick up an item */ -void pickUp(Uint32 floorItemId); - #endif diff --git a/src/net/skillhandler.cpp b/src/net/skillhandler.cpp new file mode 100644 index 00000000..e9dc9c19 --- /dev/null +++ b/src/net/skillhandler.cpp @@ -0,0 +1,93 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "skillhandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../gui/chat.h" +#include "../gui/skill.h" + +SkillHandler::SkillHandler() +{ + static const Uint16 _messages[] = { + SMSG_PLAYER_SKILLS, + SMSG_SKILL_FAILED, + 0 + }; + handledMessages = _messages; +} + +void SkillHandler::handleMessage(MessageIn *msg) +{ + int skillCount; + + switch (msg->getId()) + { + case SMSG_PLAYER_SKILLS: + msg->readInt16(); // length + skillCount = (msg->getLength() - 4) / 37; + skillDialog->cleanList(); + + for (int k = 0; k < skillCount; k++) + { + Sint16 skillId = msg->readInt16(); + msg->readInt16(); // target type + msg->readInt16(); // unknown + Sint16 level = msg->readInt16(); + Sint16 sp = msg->readInt16(); + msg->readInt16(); // range + std::string skillName = msg->readString(24); + Sint8 up = msg->readInt8(); + + if (level != 0 || up != 0) + { + if (skillDialog->hasSkill(skillId)) { + skillDialog->setSkill(skillId, level, sp); + } + else { + skillDialog->addSkill(skillId, level, sp); + } + } + } + break; + + case SMSG_SKILL_FAILED: + // Action failed (ex. sit because you have not reached the + // right level) + CHATSKILL action; + action.skill = msg->readInt16(); + action.bskill = msg->readInt16(); + action.unused = msg->readInt16(); // unknown + action.success = msg->readInt8(); + action.reason = msg->readInt8(); + if (action.success != SKILL_FAILED && + action.bskill == BSKILL_EMOTE) + { + printf("Action: %d/%d", action.bskill, action.success); + } + chatWindow->chatLog(action); + break; + } +} diff --git a/src/net/skillhandler.h b/src/net/skillhandler.h new file mode 100644 index 00000000..820a7b6a --- /dev/null +++ b/src/net/skillhandler.h @@ -0,0 +1,37 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_SKILLHANDLER_H +#define _TMW_NET_SKILLHANDLER_H + +#include "messagehandler.h" + +class SkillHandler : public MessageHandler +{ + public: + SkillHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif diff --git a/src/net/tradehandler.cpp b/src/net/tradehandler.cpp new file mode 100644 index 00000000..6ed3bab2 --- /dev/null +++ b/src/net/tradehandler.cpp @@ -0,0 +1,179 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#include "tradehandler.h" + +#include "messagein.h" +#include "protocol.h" + +#include "../item.h" +#include "../localplayer.h" + +#include "../gui/chat.h" +#include "../gui/requesttrade.h" +#include "../gui/trade.h" + +std::string tradePartnerName; + +TradeHandler::TradeHandler() +{ + static const Uint16 _messages[] = { + SMSG_TRADE_REQUEST, + SMSG_TRADE_RESPONSE, + SMSG_TRADE_ITEM_ADD, + SMSG_TRADE_ITEM_ADD_RESPONSE, + SMSG_TRADE_OK, + SMSG_TRADE_CANCEL, + SMSG_TRADE_COMPLETE, + 0 + }; + handledMessages = _messages; +} + +void TradeHandler::handleMessage(MessageIn *msg) +{ + switch (msg->getId()) + { + case SMSG_TRADE_REQUEST: + // If a trade window or request window is already open, send a + // trade cancel to any other trade request. + // + // Note that it would be nice if the server would prevent this + // situation, and that the requesting player would get a + // special message about the player being occupied. + if (!player_node->tradeRequestOk()) + { + player_node->tradeReply(false); + break; + } + + player_node->setTrading(true); + tradePartnerName = msg->readString(24); + new RequestTradeDialog(tradePartnerName); + break; + + case SMSG_TRADE_RESPONSE: + switch (msg->readInt8()) + { + case 0: // Too far away + chatWindow->chatLog("Trading isn't possible. " + "Trade partner is too far away.", + BY_SERVER); + break; + case 1: // Character doesn't exist + chatWindow->chatLog("Trading isn't possible. " + "Character doesn't exist.", + BY_SERVER); + break; + case 2: // Invite request check failed... + chatWindow->chatLog("Trade cancelled due to an " + "unknown reason.", BY_SERVER); + break; + case 3: // Trade accepted + tradeWindow->reset(); + tradeWindow->setCaption( + "Trade: You and " + tradePartnerName); + tradeWindow->setVisible(true); + break; + case 4: // Trade cancelled + chatWindow->chatLog("Trade cancelled.", BY_SERVER); + tradeWindow->setVisible(false); + player_node->setTrading(false); + break; + default: // Shouldn't happen as well, but to be sure + chatWindow->chatLog("Unhandled trade cancel packet", + BY_SERVER); + break; + } + break; + + case SMSG_TRADE_ITEM_ADD: + { + Sint32 amount = msg->readInt32(); + Sint16 type = msg->readInt16(); + msg->readInt8(); // identified flag + msg->readInt8(); // attribute + msg->readInt8(); // refine + msg->skip(8); // card (4 shorts) + + // TODO: handle also identified, etc + if (type == 0) { + tradeWindow->addMoney(amount); + } else { + tradeWindow->addItem(type, false, amount, false); + } + } + break; + + case SMSG_TRADE_ITEM_ADD_RESPONSE: + // Trade: New Item add response (was 0x00ea, now 01b1) + { + Item *item = player_node->getInvItem(msg->readInt16()); + Sint16 quantity = msg->readInt16(); + + switch (msg->readInt8()) + { + case 0: + // Successfully added item + if (item->isEquipment() && item->isEquipped()) + { + player_node->unequipItem(item); + } + tradeWindow->addItem(item->getId(), true, quantity, + item->isEquipment()); + item->increaseQuantity(-quantity); + break; + case 1: + // Add item failed - player overweighted + chatWindow->chatLog("Failed adding item. Trade " + "partner is over weighted.", + BY_SERVER); + break; + default: + chatWindow->chatLog("Failed adding item for " + "unknown reason.", BY_SERVER); + break; + } + } + break; + + case SMSG_TRADE_OK: + // 0 means ok from myself, 1 means ok from other; + tradeWindow->receivedOk(msg->readInt8() == 0); + break; + + case SMSG_TRADE_CANCEL: + chatWindow->chatLog("Trade canceled.", BY_SERVER); + tradeWindow->setVisible(false); + tradeWindow->reset(); + player_node->setTrading(false); + break; + + case SMSG_TRADE_COMPLETE: + chatWindow->chatLog("Trade completed.", BY_SERVER); + tradeWindow->setVisible(false); + tradeWindow->reset(); + player_node->setTrading(false); + break; + } +} diff --git a/src/net/tradehandler.h b/src/net/tradehandler.h new file mode 100644 index 00000000..a1971004 --- /dev/null +++ b/src/net/tradehandler.h @@ -0,0 +1,39 @@ +/* + * The Mana World + * Copyright 2004 The Mana World Development Team + * + * This file is part of The Mana World. + * + * The Mana World is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana World is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana World; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * $Id$ + */ + +#ifndef _TMW_NET_TRADEHANDLER_H +#define _TMW_NET_TRADEHANDLER_H + +#include "messagehandler.h" + +class Network; + +class TradeHandler : public MessageHandler +{ + public: + TradeHandler(); + + void handleMessage(MessageIn *msg); +}; + +#endif |