diff options
author | Guillaume Melquiond <guillaume.melquiond@gmail.com> | 2006-12-29 13:43:24 +0000 |
---|---|---|
committer | Guillaume Melquiond <guillaume.melquiond@gmail.com> | 2006-12-29 13:43:24 +0000 |
commit | 291ad04d5b5c4ab08d85eadde116f968cd579b77 (patch) | |
tree | e4dced5715a5d9792cfdc0455a6b3ee6d3116079 /src/game-server | |
parent | 3d404e743105bb9168c89e3451cf35d7d59120b1 (diff) | |
download | manaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.tar.gz manaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.tar.bz2 manaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.tar.xz manaserv-291ad04d5b5c4ab08d85eadde116f968cd579b77.zip |
Physically split the server into one tmwserv-acount program (account +
chat + database) and multiple tmwserv-game programs (selected with
respect to the maps). Cleaned the repository by moving server-specific
source files into dedicated directories.
Diffstat (limited to 'src/game-server')
-rw-r--r-- | src/game-server/accountconnection.cpp | 85 | ||||
-rw-r--r-- | src/game-server/accountconnection.hpp | 50 | ||||
-rw-r--r-- | src/game-server/gameclient.cpp | 57 | ||||
-rw-r--r-- | src/game-server/gameclient.hpp | 70 | ||||
-rw-r--r-- | src/game-server/gamehandler.cpp | 245 | ||||
-rw-r--r-- | src/game-server/gamehandler.hpp | 66 | ||||
-rw-r--r-- | src/game-server/itemmanager.cpp | 155 | ||||
-rw-r--r-- | src/game-server/itemmanager.hpp | 90 | ||||
-rw-r--r-- | src/game-server/main-game.cpp | 360 | ||||
-rw-r--r-- | src/game-server/mapmanager.cpp | 112 | ||||
-rw-r--r-- | src/game-server/mapmanager.hpp | 83 | ||||
-rw-r--r-- | src/game-server/state.cpp | 331 | ||||
-rw-r--r-- | src/game-server/state.hpp | 77 |
13 files changed, 1781 insertions, 0 deletions
diff --git a/src/game-server/accountconnection.cpp b/src/game-server/accountconnection.cpp new file mode 100644 index 00000000..51fc13f9 --- /dev/null +++ b/src/game-server/accountconnection.cpp @@ -0,0 +1,85 @@ +/* + * The Mana World + * Copyright 2006 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 "configuration.h" +#include "defines.h" +#include "player.h" +#include "game-server/accountconnection.hpp" +#include "game-server/mapmanager.hpp" +#include "net/messagein.hpp" +#include "net/messageout.hpp" +#include "utils/logger.h" + +extern void registerGameClient(std::string const &, PlayerPtr); + +bool AccountConnection::start() +{ + if (!Connection::start(config.getValue("accountServerAddress", "localhost"), + int(config.getValue("accountServerPort", DEFAULT_SERVER_PORT)) + 1)) + { + return false; + } + LOG_INFO("Connection established to the account server.", 0); + MessageOut msg(GAMSG_REGISTER); + msg.writeString(config.getValue("gameServerAddress", "localhost")); + msg.writeShort(int(config.getValue("gameServerPort", DEFAULT_SERVER_PORT + 3))); + MapManager::Maps const &m = mapManager->getMaps(); + for (MapManager::Maps::const_iterator i = m.begin(), i_end = m.end(); i != i_end; ++i) + { + msg.writeShort(i->first); + } + send(msg); + return true; +} + +void AccountConnection::processMessage(MessageIn &msg) +{ + switch (msg.getId()) + { + case AGMSG_PLAYER_ENTER: + { + int id = msg.readLong(); + std::string name = msg.readString(); + Player *ptr = new Player(name, id); + ptr->setGender((Gender)msg.readByte()); + ptr->setHairStyle(msg.readByte()); + ptr->setHairColor(msg.readByte()); + ptr->setLevel(msg.readByte()); + ptr->setMoney(msg.readShort()); + for (int j = 0; j < NB_RSTAT; ++j) + ptr->setRawStat(j, msg.readShort()); + int x = msg.readShort(); + int y = msg.readShort(); + Point pos = { x, y }; + ptr->setPosition(pos); + ptr->setMapId(msg.readShort()); + ptr->setSpeed(150); // TODO + std::string token = msg.readString(32); + registerGameClient(token, PlayerPtr(ptr)); + } break; + + default: + LOG_WARN("Invalid message type", 0); + break; + } +} diff --git a/src/game-server/accountconnection.hpp b/src/game-server/accountconnection.hpp new file mode 100644 index 00000000..97412215 --- /dev/null +++ b/src/game-server/accountconnection.hpp @@ -0,0 +1,50 @@ +/* + * The Mana World + * Copyright 2006 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_ACCOUNTCONNECTION_H_ +#define _TMW_ACCOUNTCONNECTION_H_ + +#include "net/connection.hpp" + +/** + * A connection to the account server. + */ +class AccountConnection: public Connection +{ + public: + /** + * Initializes a connection to the account server described in the + * configuration file. Registers the maps known by MapManager. + */ + bool start(); + + protected: + /** + * Processes server messages. + */ + virtual void processMessage(MessageIn &); +}; + +extern AccountConnection *accountHandler; + +#endif diff --git a/src/game-server/gameclient.cpp b/src/game-server/gameclient.cpp new file mode 100644 index 00000000..bd5d5492 --- /dev/null +++ b/src/game-server/gameclient.cpp @@ -0,0 +1,57 @@ +/* + * The Mana World Server + * 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 <cassert> + +#include "game-server/gameclient.hpp" +#include "game-server/gamehandler.hpp" +#include "game-server/state.hpp" + +GameClient::GameClient(ENetPeer *peer): + NetComputer(peer), + mCharacterPtr(NULL) +{ +} + +GameClient::~GameClient() +{ + unsetCharacter(); +} + +void GameClient::setCharacter(PlayerPtr ch) +{ + assert(mCharacterPtr.get() == NULL); + mCharacterPtr = ch; + assert(mCharacterPtr->mClient == NULL); + mCharacterPtr->mClient = this; + gameState->addObject(ObjectPtr(mCharacterPtr)); +} + +void GameClient::unsetCharacter() +{ + if (mCharacterPtr.get() == NULL) return; + // remove being from world + gameState->removeObject(ObjectPtr(mCharacterPtr)); + mCharacterPtr->mClient = NULL; + mCharacterPtr = PlayerPtr(NULL); +} diff --git a/src/game-server/gameclient.hpp b/src/game-server/gameclient.hpp new file mode 100644 index 00000000..14f084db --- /dev/null +++ b/src/game-server/gameclient.hpp @@ -0,0 +1,70 @@ +/* + * The Mana World Server + * 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 _TMWSERV_GAMECLIENT_H_ +#define _TMWSERV_GAMECLIENT_H_ + +#include <enet/enet.h> + +#include "player.h" +#include "net/netcomputer.hpp" + +class GameHandler; + +/** + * A connected computer with an associated character. + */ +class GameClient: public NetComputer +{ + public: + /** + * Constructor. + */ + GameClient(ENetPeer *peer); + + /** + * Destructor. + */ + ~GameClient(); + + /** + * Set the selected character associated with connection. + */ + void setCharacter(PlayerPtr ch); + + /** + * Deselect the character associated with connection. + */ + void unsetCharacter(); + + /** + * Get character associated with the connection. + */ + PlayerPtr getCharacter() { return mCharacterPtr; } + + private: + /** Character associated with the conneciton. */ + PlayerPtr mCharacterPtr; +}; + +#endif diff --git a/src/game-server/gamehandler.cpp b/src/game-server/gamehandler.cpp new file mode 100644 index 00000000..b0b036e3 --- /dev/null +++ b/src/game-server/gamehandler.cpp @@ -0,0 +1,245 @@ +/* + * The Mana World Server + * 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 <cassert> +#include <map> + +#include "map.h" +#include "game-server/gameclient.hpp" +#include "game-server/gamehandler.hpp" +#include "game-server/state.hpp" +#include "net/messagein.hpp" +#include "net/messageout.hpp" +#include "net/netcomputer.hpp" +#include "utils/logger.h" + +struct GamePendingLogin +{ + PlayerPtr character; + int timeout; +}; + +typedef std::map< std::string, GamePendingLogin > GamePendingLogins; +typedef std::map< std::string, GameClient * > GamePendingClients; + +/** + * The pending logins represent clients who were given a magic token by the + * account server but who have not yet logged in to the game server. + */ +static GamePendingLogins pendingLogins; + +/** + * The pending clients represent clients who tried to login to the game server, + * but for which no magic token is available yet. This can happen when the + * communication between the account server and client went faster than the + * communication between the account server and the game server. + */ +static GamePendingClients pendingClients; + +/** + * Notification that a particular token has been given to allow a certain + * player to enter the game. + */ +void registerGameClient(std::string const &token, PlayerPtr ch) +{ + GamePendingClients::iterator i = pendingClients.find(token); + if (i != pendingClients.end()) + { + GameClient *computer = i->second; + computer->setCharacter(ch); + pendingClients.erase(i); + MessageOut result; + result.writeShort(GPMSG_CONNECT_RESPONSE); + result.writeByte(ERRMSG_OK); + computer->send(result); + } + else + { + GamePendingLogin p; + p.character = ch; + p.timeout = 300; // world ticks + pendingLogins.insert(std::make_pair(token, p)); + } +} + +bool +GameHandler::startListen(enet_uint16 port) +{ + LOG_INFO("Game handler started:", 0); + return ConnectionHandler::startListen(port); +} + +void GameHandler::removeOutdatedPending() +{ + GamePendingLogins::iterator i = pendingLogins.begin(); + GamePendingLogins::iterator next; + + while (i != pendingLogins.end()) + { + next = i; + ++next; + if (--i->second.timeout <= 0) + { + pendingLogins.erase(i); + } + i = next; + } +} + +NetComputer *GameHandler::computerConnected(ENetPeer *peer) +{ + return new GameClient(peer); +} + +void GameHandler::computerDisconnected(NetComputer *computer) +{ + GamePendingClients::iterator i; + for (i = pendingClients.begin(); i != pendingClients.end(); ++i) + { + if (i->second == computer) + { + pendingClients.erase(i); + break; + } + } + delete computer; +} + +void GameHandler::process() +{ + ConnectionHandler::process(); + removeOutdatedPending(); +} + +void GameHandler::processMessage(NetComputer *comp, MessageIn &message) +{ + GameClient &computer = *static_cast< GameClient * >(comp); + MessageOut result; + + if (computer.getCharacter().get() == NULL) { + if (message.getId() != PGMSG_CONNECT) return; + std::string magic_token = message.readString(32); + GamePendingLogins::iterator i = pendingLogins.find(magic_token); + if (i == pendingLogins.end()) + { + GamePendingClients::iterator i; + for (i = pendingClients.begin(); i != pendingClients.end(); ++i) + { + if (i->second == &computer) return; + } + pendingClients.insert(std::make_pair(magic_token, &computer)); + return; + } + computer.setCharacter(i->second.character); + pendingLogins.erase(i); + result.writeShort(GPMSG_CONNECT_RESPONSE); + result.writeByte(ERRMSG_OK); + computer.send(result); + return; + } + + switch (message.getId()) + { + case PGMSG_SAY: + { + std::string say = message.readString(); + gameState->sayAround(computer.getCharacter().get(), say); + } break; + + /* + case PGMSG_PICKUP: + { + // add item to inventory (this is too simplistic atm) + unsigned int itemId = message.readLong(); + + // remove the item from world map + + // send feedback + computer.getCharacter()->addItem(itemId); + result.writeShort(GPMSG_PICKUP_RESPONSE); + result.writeByte(ERRMSG_OK); + } break; + + case PGMSG_USE_ITEM: + { + unsigned int itemId = message.readLong(); + + result.writeShort(GPMSG_USE_RESPONSE); + + if (computer.getCharacter()->hasItem(itemId)) { + // use item + // this should execute a script which will do the appropriate action + // (the script will determine if the item is 1 use only) + result.writeByte(ERRMSG_OK); + } else { + result.writeByte(ERRMSG_FAILURE); + } + } break; + */ + + case PGMSG_WALK: + { + unsigned x = message.readShort(); + unsigned y = message.readShort(); + Point dst = {x, y}; + computer.getCharacter()->setDestination(dst); + + // no response should be required + } break; + + /* + case PGMSG_EQUIP: + { + message.readLong(); // ItemId: Not useful, the inventory knows it + char slot = message.readByte(); + + result.writeShort(GPMSG_EQUIP_RESPONSE); + result.writeByte(computer.getCharacter()->equip(slot) ? + ERRMSG_OK : ERRMSG_FAILURE); + } break; + */ + + case PGMSG_ATTACK: + { + LOG_DEBUG("Player " << computer.getCharacter()->getPublicID() + << " attacks", 0); + computer.getCharacter()->setDirection(message.readByte()); + computer.getCharacter()->setAttacking(true); + } break; + + default: + LOG_WARN("Invalid message type", 0); + result.writeShort(XXMSG_INVALID); + break; + } + + if (result.getLength() > 0) + computer.send(result); +} + +void GameHandler::sendTo(Player *beingPtr, MessageOut &msg) +{ + GameClient *client = beingPtr->getClient(); + assert(client != NULL); + client->send(msg); +} diff --git a/src/game-server/gamehandler.hpp b/src/game-server/gamehandler.hpp new file mode 100644 index 00000000..ceb37cd8 --- /dev/null +++ b/src/game-server/gamehandler.hpp @@ -0,0 +1,66 @@ +/* + * The Mana World Server + * 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_SERVER_GAMEHANDLER_ +#define _TMW_SERVER_GAMEHANDLER_ + +#include "player.h" +#include "net/connectionhandler.hpp" + +class GameClient; + +/* + * Manage connections to game server. + */ +class GameHandler: public ConnectionHandler +{ + public: + void process(); + + /** + * Start the handler + */ + bool + startListen(enet_uint16 port); + + /** + * Send message to the given player. + */ + void sendTo(Player *, MessageOut &msg); + + protected: + NetComputer *computerConnected(ENetPeer *); + void computerDisconnected(NetComputer *); + + /** + * Process messages related to core game events. + */ + void processMessage(NetComputer *computer, MessageIn &message); + + private: + void removeOutdatedPending(); +}; + +extern GameHandler *gameHandler; + +#endif diff --git a/src/game-server/itemmanager.cpp b/src/game-server/itemmanager.cpp new file mode 100644 index 00000000..3ce8350c --- /dev/null +++ b/src/game-server/itemmanager.cpp @@ -0,0 +1,155 @@ +/* + * 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 "resourcemanager.h" +#include "game-server/itemmanager.hpp" +#include "utils/logger.h" +#include "utils/xml.hpp" + +ItemManager::ItemManager(std::string const &itemReferenceFile) +{ + ResourceManager *resman = ResourceManager::getInstance(); + int size; + char *data = (char *)resman->loadFile(itemReferenceFile, size); + + if (!data) { + LOG_ERROR("Item Manager: Could not find " << itemReferenceFile << "!", 0); + free(data); + return; + } + + xmlDocPtr doc = xmlParseMemory(data, size); + free(data); + + if (!doc) + { + LOG_ERROR("Item Manager: Error while parsing item database (" + << itemReferenceFile << ")!", 0); + return; + } + + xmlNodePtr node = xmlDocGetRootElement(doc); + if (!node || !xmlStrEqual(node->name, BAD_CAST "items")) + { + LOG_ERROR("Item Manager: " << itemReferenceFile + << " is not a valid database file!", 0); + xmlFreeDoc(doc); + return; + } + + LOG_INFO("Loading item reference...", 0); + unsigned nbItems = 0; + for (node = node->xmlChildrenNode; node != NULL; node = node->next) + { + if (!xmlStrEqual(node->name, BAD_CAST "item")) { + continue; + } + + unsigned id = XML::getProperty(node, "id", 0); + + if (id == 0) + { + LOG_WARN("Item Manager: An (ignored) item has no ID in " + << itemReferenceFile << "!", 0); + continue; + } + + int itemType = XML::getProperty(node, "type", 0); + int weight = XML::getProperty(node, "weight", 0); + int value = XML::getProperty(node, "value", 0); + int maxPerSlot = XML::getProperty(node, "max_per_slot", 0); + std::string scriptName = XML::getProperty(node, "script_name", std::string()); + + Modifiers modifiers; + modifiers.element = (Element)XML::getProperty(node, "element", 0); + modifiers.lifetime = XML::getProperty(node, "lifetime", 0); + modifiers.rawStats[STAT_STRENGTH] = XML::getProperty(node, "strength", 0); + modifiers.rawStats[STAT_AGILITY] = XML::getProperty(node, "agility", 0); + modifiers.rawStats[STAT_VITALITY] = XML::getProperty(node, "vitality", 0); + modifiers.rawStats[STAT_INTELLIGENCE] = XML::getProperty(node, "intelligence", 0); + modifiers.rawStats[STAT_DEXTERITY] = XML::getProperty(node, "dexterity", 0); + modifiers.rawStats[STAT_LUCK] = XML::getProperty(node, "luck", 0); + modifiers.computedStats[STAT_HEAT] = XML::getProperty(node, "heat", 0); + modifiers.computedStats[STAT_ATTACK] = XML::getProperty(node, "attack", 0); + modifiers.computedStats[STAT_DEFENCE] = XML::getProperty(node, "defence", 0); + modifiers.computedStats[STAT_MAGIC] = XML::getProperty(node, "magic", 0); + modifiers.computedStats[STAT_ACCURACY] = XML::getProperty(node, "accuracy", 0); + modifiers.computedStats[STAT_SPEED] = XML::getProperty(node, "speed", 0); + modifiers.hp = XML::getProperty(node, "hp", 0); + modifiers.mp = XML::getProperty(node, "mp", 0); + modifiers.range = XML::getProperty(node, "range", 0); + modifiers.weaponType = (WeaponType)XML::getProperty(node, "weapon_type", 0); + modifiers.beingStateEffect = (BeingStateEffect)XML::getProperty(node, "status_effect", 0); + + ItemPtr item(new Item(modifiers, itemType, weight, + value, scriptName, maxPerSlot)); + mItemReference[id] = item; + nbItems++; + + if (maxPerSlot == 0) + { + LOG_WARN("Item Manager: Missing max per slot properties for item: " + << id << " in " << itemReferenceFile << ".", 0); + } + if (weight == 0) + { + LOG_WARN("Item Manager: Missing weight for item: " + << id << " in " << itemReferenceFile << ".", 0); + } + + LOG_INFO("Item: ID: " << id << ", itemType: " << itemType + << ", weight: " << weight << ", value: " << value << + ", scriptName: " << scriptName << ", maxPerSlot: " << maxPerSlot << ".", 3); + // Log level 5 + LOG_INFO("Modifiers:: element: " << modifiers.element << + ", lifetime: " << modifiers.lifetime + << std::endl << + ", strength: " << modifiers.rawStats[STAT_STRENGTH] << + ", agility: " << modifiers.rawStats[STAT_AGILITY] << + ", vitality: " << modifiers.rawStats[STAT_VITALITY] + << std::endl << + ", intelligence: " << modifiers.rawStats[STAT_INTELLIGENCE] << + ", dexterity: " << modifiers.rawStats[STAT_DEXTERITY] << + ", luck: " << modifiers.rawStats[STAT_LUCK] + << std::endl << + ", heat: " << modifiers.computedStats[STAT_HEAT] << + ", attack: " << modifiers.computedStats[STAT_ATTACK] << + ", defence: " << modifiers.computedStats[STAT_DEFENCE] + << std::endl << + ", magic: " << modifiers.computedStats[STAT_MAGIC] << + ", accuracy: " << modifiers.computedStats[STAT_ACCURACY] << + ", speed: " << modifiers.computedStats[STAT_SPEED] << + std::endl << + ", hp: " << modifiers.hp << + ", mp: " << modifiers.mp << + std::endl << + ", range: " << modifiers.range << + ", weapon_type: " << modifiers.weaponType << + ", status_effect: " << modifiers.beingStateEffect, 5); + } + + LOG_INFO("Loaded " << nbItems << " items from " + << itemReferenceFile << ".", 0); + + xmlFreeDoc(doc); +} diff --git a/src/game-server/itemmanager.hpp b/src/game-server/itemmanager.hpp new file mode 100644 index 00000000..a5ebc8f3 --- /dev/null +++ b/src/game-server/itemmanager.hpp @@ -0,0 +1,90 @@ +/* + * 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_ITEMMANAGER_H +#define _TMW_ITEMMANAGER_H + +#include <map> + +#include "item.h" + +/** + * The Item Manager loads the item reference database + * and also offers an API to items information, and more. + * For item objects, see the WorldItem class. + */ +class ItemManager +{ + public: + /** + * Constructor (loads item reference file) + */ + ItemManager(const std::string &itemReferenceFile); + + /** + * Gives an Item having the demanded information. + */ + ItemPtr getItem(const unsigned int itemId) + { return mItemReference[itemId]; }; + + bool use(BeingPtr beingPtr, const unsigned int itemId) + { return mItemReference[itemId].get()->use(beingPtr); }; + + /** + * Return item Type + */ + unsigned short getItemType(const unsigned int itemId) + { return mItemReference[itemId].get()->getItemType(); }; + + /** + * Return Weight of item + */ + unsigned int getWeight(const unsigned int itemId) + { return mItemReference[itemId].get()->getWeight(); }; + + /** + * Return gold value of item + */ + unsigned int getGoldValue(const unsigned int itemId) + { return mItemReference[itemId].get()->getGoldValue(); }; + + /** + * Return max item per slot + */ + unsigned short getMaxPerSlot(const unsigned int itemId) + { return mItemReference[itemId].get()->getMaxPerSlot(); }; + + /** + * Return item's modifiers + */ + Modifiers + getItemModifiers(const unsigned int itemId) + { return mItemReference[itemId].get()->getItemModifiers(); }; + + private: + std::map<unsigned int, ItemPtr> mItemReference; /**< Item reference */ +}; + +extern ItemManager *itemManager; + +#endif diff --git a/src/game-server/main-game.cpp b/src/game-server/main-game.cpp new file mode 100644 index 00000000..cfc07c8d --- /dev/null +++ b/src/game-server/main-game.cpp @@ -0,0 +1,360 @@ +/* + * The Mana World Server + * 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 <cstdlib> +#include <getopt.h> +#include <iostream> +#include <signal.h> +#include <physfs.h> +#include <enet/enet.h> + +#if (defined __USE_UNIX98 || defined __FreeBSD__) +#include "../config.h" +#elif defined WIN32 +#include "../tmwserv_private.h" +#define PACKAGE_VERSION PRODUCT_VERSION +#endif + +#include "configuration.h" +#include "resourcemanager.h" +#include "skill.h" +#include "game-server/accountconnection.hpp" +#include "game-server/gamehandler.hpp" +#include "game-server/itemmanager.hpp" +#include "game-server/mapmanager.hpp" +#include "game-server/state.hpp" +#include "net/connectionhandler.hpp" +#include "net/messageout.hpp" +#include "utils/logger.h" +#include "utils/stringfilter.h" +#include "utils/timer.h" + +// Scripting +#ifdef SCRIPT_SUPPORT + +extern "C" void Init_Tmw(); + +#if defined (SQUIRREL_SUPPORT) +std::string scriptLanguage = "squirrel"; +#elif defined (RUBY_SUPPORT) +#include <ruby.h> +int rubyStatus; +std::string scriptLanguage = "ruby"; +#elif defined (LUA_SUPPORT) +std::string scriptLanguage = "lua"; +#else +#error "Scripting enabled, but no language selected" +#endif + +#else +std::string scriptLanugage = "none"; +#endif // SCRIPT_SUPPORT + +// Default options that automake should be able to override. +#define DEFAULT_LOG_FILE "tmwserv.log" +#define DEFAULT_CONFIG_FILE "tmwserv.xml" +#define DEFAULT_ITEMSDB_FILE "items.xml" +#define DEFAULT_MAPSDB_FILE "maps.xml" + +utils::Timer worldTimer(100, false); /**< Timer for world tics set to 100 ms */ +int worldTime = 0; /**< Current world time in 100ms ticks */ +bool running = true; /**< Determines if server keeps running */ + +Skill skillTree("base"); /**< Skill tree */ + +Configuration config; /**< XML config reader */ + +utils::StringFilter *stringFilter; /**< Slang's Filter */ + +/** Item manager */ +ItemManager *itemManager; + +/** Map manager */ +MapManager *mapManager; + +/** Core game message handler */ +GameHandler *gameHandler; + +/** Account server message handler */ +AccountConnection *accountHandler; + +/** Global game state */ +State *gameState; + +/** + * Initializes the server. + */ +void initialize() +{ + + // Reset to default segmentation fault handling for debugging purposes + signal(SIGSEGV, SIG_DFL); + + // Set enet to quit on exit. + atexit(enet_deinitialize); + + /* + * If the path values aren't defined, we set the default + * depending on the platform. + */ + // The config path +#if defined CONFIG_FILE + std::string configPath = CONFIG_FILE; +#else + +#if (defined __USE_UNIX98 || defined __FreeBSD__) + std::string configPath = getenv("HOME"); + configPath += "/."; + configPath += DEFAULT_CONFIG_FILE; +#else // Win32, ... + std::string configPath = DEFAULT_CONFIG_FILE; +#endif + +#endif // defined CONFIG_FILE + + // The log path +#if defined LOG_FILE + std::string logPath = LOG_FILE; +#else + +#if (defined __USE_UNIX98 || defined __FreeBSD__) + std::string logPath = getenv("HOME"); + logPath += "/."; + logPath += DEFAULT_LOG_FILE; +#else // Win32, ... + std::string logPath = DEFAULT_LOG_FILE; +#endif + +#endif // defined LOG_FILE + + // Initialize PhysicsFS + PHYSFS_init(""); + + // Initialize the logger. + using namespace utils; + Logger::instance().setLogFile(logPath); + + // write the messages to both the screen and the log file. + Logger::instance().setTeeMode(true); + + config.init(configPath); + LOG_INFO("Using Config File: " << configPath, 0); + LOG_INFO("Using Log File: " << logPath, 0); + + // --- Initialize the managers + // Initialize the slang's and double quotes filter. + stringFilter = new StringFilter(&config); + // Initialize the map manager + mapManager = new MapManager(DEFAULT_MAPSDB_FILE); + // Initialize the item manager + itemManager = new ItemManager(DEFAULT_ITEMSDB_FILE); + + // --- Initialize the global handlers + // FIXME: Make the global handlers global vars or part of a bigger + // singleton or a local variable in the event-loop + gameHandler = new GameHandler(); + accountHandler = new AccountConnection(); + + // --- Initialize enet. + if (enet_initialize() != 0) { + LOG_FATAL("An error occurred while initializing ENet", 0); + exit(2); + } + + // --- Initialize scripting subsystem. +#ifdef RUBY_SUPPORT + LOG_INFO("Script Language: " << scriptLanguage, 0); + + // Initialize ruby + ruby_init(); + ruby_init_loadpath(); + ruby_script("tmw"); + + // Initialize bindings + Init_Tmw(); + + // Run test script + rb_load_file("scripts/init.rb"); + rubyStatus = ruby_exec(); +#else + LOG_WARN("No Scripting Language Support.", 0); +#endif +} + + +/** + * Deinitializes the server. + */ +void deinitialize() +{ + delete stringFilter; + // Write configuration file + config.write(); + + // Stop world timer + worldTimer.stop(); + + // Quit ENet + enet_deinitialize(); + +#ifdef RUBY_SUPPORT + // Finish up ruby + ruby_finalize(); + ruby_cleanup(rubyStatus); +#endif + + // Destroy message handlers + delete gameHandler; + delete accountHandler; + + // Destroy Managers + delete itemManager; + delete mapManager; + + PHYSFS_deinit(); +} + + +/** + * Show command line arguments + */ +void printHelp() +{ + std::cout << "tmwserv" << std::endl << std::endl + << "Options: " << std::endl + << " -h --help : Display this help" << std::endl + << " --verbosity <n> : Set the verbosity level" << std::endl + << " --port <n> : Set the default port to listen on" << std::endl; + exit(0); +} + +/** + * Parse the command line arguments + */ +void parseOptions(int argc, char *argv[]) +{ + const char *optstring = "h"; + + const struct option long_options[] = { + { "help", no_argument, 0, 'h' }, + { "verbosity", required_argument, 0, 'v' }, + { "port", required_argument, 0, 'p' }, + { 0 } + }; + + while (optind < argc) { + int result = getopt_long(argc, argv, optstring, long_options, NULL); + + if (result == -1) { + break; + } + + switch (result) { + default: // Unknown option + case 'h': + // Print help + printHelp(); + break; + case 'v': + // Set Verbosity to level + unsigned short verbosityLevel; + verbosityLevel = atoi(optarg); + utils::Logger::instance().setVerbosity(verbosityLevel); + LOG_INFO("Setting Log Verbosity Level to " << verbosityLevel, 0); + break; + case 'p': + // Change the port to listen on. + unsigned short portToListenOn; + portToListenOn = atoi(optarg); + config.setValue("gameServerPort", portToListenOn); + LOG_INFO("Setting Default Port to " << portToListenOn, 0); + break; + } + } +} + + +/** + * Main function, initializes and runs server. + */ +int main(int argc, char *argv[]) +{ + int elapsedWorldTicks; + + LOG_INFO("The Mana World Server v" << PACKAGE_VERSION, 0); + + // Parse Command Line Options + parseOptions(argc, argv); + + // General Initialization + initialize(); + + if (!accountHandler->start()) { + LOG_ERROR("Unable to create a connection to an account server.", 0); + return 3; + } + + if (!gameHandler->startListen(int(config.getValue("gameServerPort", DEFAULT_SERVER_PORT + 3)))) { + LOG_ERROR("Unable to create an ENet server host.", 0); + return 3; + } + + // Create state machine + gameState = new State; + + // Initialize world timer + worldTimer.start(); + + while (running) { + elapsedWorldTicks = worldTimer.poll(); + if (elapsedWorldTicks > 0) { + worldTime += elapsedWorldTicks; + + if (elapsedWorldTicks > 1) + { + LOG_WARN(elapsedWorldTicks -1 << " World Tick(s) skipped " + "because of insufficient time. please buy a faster " + "machine ;-)", 0); + }; + + // Print world time at 10 second intervals to show we're alive + if (worldTime % 100 == 0) { + LOG_INFO("World time: " << worldTime, 0); + } + + // Handle all messages that are in the message queues + accountHandler->process(); + gameHandler->process(); + // Update all active objects/beings + gameState->update(); + // Send potentially urgent outgoing messages + gameHandler->flush(); + } + worldTimer.sleep(); + } + + LOG_INFO("Received: Quit signal, closing down...", 0); + gameHandler->stopListen(); + accountHandler->stop(); + delete gameState; + deinitialize(); +} diff --git a/src/game-server/mapmanager.cpp b/src/game-server/mapmanager.cpp new file mode 100644 index 00000000..3b2caedf --- /dev/null +++ b/src/game-server/mapmanager.cpp @@ -0,0 +1,112 @@ +/* + * 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 <cassert> + +#include "map.h" +#include "mapreader.h" +#include "resourcemanager.h" +#include "game-server/mapmanager.hpp" +#include "utils/logger.h" +#include "utils/xml.hpp" + +MapManager::MapManager(std::string const &mapReferenceFile) +{ + ResourceManager *resman = ResourceManager::getInstance(); + int size; + char *data = (char *)resman->loadFile(mapReferenceFile, size); + + if (!data) { + LOG_ERROR("Map Manager: Could not find " << mapReferenceFile << "!", 0); + free(data); + return; + } + + xmlDocPtr doc = xmlParseMemory(data, size); + free(data); + + if (!doc) + { + LOG_ERROR("Map Manager: Error while parsing map database (" + << mapReferenceFile << ")!", 0); + return; + } + + xmlNodePtr node = xmlDocGetRootElement(doc); + if (!node || !xmlStrEqual(node->name, BAD_CAST "maps")) + { + LOG_ERROR("Map Manager: " << mapReferenceFile + << " is not a valid database file!", 0); + xmlFreeDoc(doc); + return; + } + + LOG_INFO("Loading map reference...", 0); + for (node = node->xmlChildrenNode; node != NULL; node = node->next) + { + if (!xmlStrEqual(node->name, BAD_CAST "map")) { + continue; + } + + unsigned id = XML::getProperty(node, "id", 0); + std::string name = XML::getProperty(node, "name", std::string()); + // TODO: load only local maps + loadMap(id, name); + } + + xmlFreeDoc(doc); +} + +MapManager::~MapManager() +{ + for (Maps::iterator i = maps.begin(), i_end = maps.end(); i != i_end; ++i) + { + delete i->second.map; + } +} + +void MapManager::loadMap(unsigned mapId, std::string const &mapFile) +{ + LoadedMap m = { mapFile, MapReader::readMap("maps/" + mapFile) }; + if (m.map == NULL) + { + LOG_ERROR("Unable to load map \"" << mapFile << "\" (id " << mapId << ")", 0); + return; + } + LOG_INFO("Loaded map \"" << mapFile << "\" (id " << mapId << ")", 0); + maps[mapId] = m; +} + +Map *MapManager::getMap(unsigned mapId) +{ + Maps::iterator i = maps.find(mapId); + assert(i != maps.end()); + return i->second.map; +} + +std::string MapManager::getMapName(unsigned mapId) +{ + Maps::iterator i = maps.find(mapId); + assert(i != maps.end()); + return i->second.fileName; +} diff --git a/src/game-server/mapmanager.hpp b/src/game-server/mapmanager.hpp new file mode 100644 index 00000000..1090a850 --- /dev/null +++ b/src/game-server/mapmanager.hpp @@ -0,0 +1,83 @@ +/* + * 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_MAPMANAGER_H +#define _TMW_MAPMANAGER_H + +#include <map> +#include <string> + +class Map; + +struct LoadedMap +{ + std::string fileName; + Map *map; +}; + +/** + * MapManager loads/unloads maps + */ +class MapManager +{ + public: + typedef std::map< unsigned, LoadedMap > Maps; + + /** + * Constructor (loads map reference file). + */ + MapManager(std::string const &); + + /** + * Returns the requested map. + */ + Map *getMap(unsigned); + + /** + * Returns the requested map name. + */ + std::string getMapName(unsigned); + + /** + * Returns all the maps. + */ + Maps const &getMaps() { return maps; } + + /** + * Destructor. + */ + ~MapManager(); + + private: + /** + * Loads the specified map. + */ + void loadMap(unsigned, std::string const &); + + // Hold all the loaded maps. + Maps maps; +}; + +extern MapManager *mapManager; + +#endif diff --git a/src/game-server/state.cpp b/src/game-server/state.cpp new file mode 100644 index 00000000..3bb2e478 --- /dev/null +++ b/src/game-server/state.cpp @@ -0,0 +1,331 @@ +/* + * The Mana World Server + * 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 <cassert> + +#include "controller.h" +#include "map.h" +#include "mapcomposite.h" +#include "point.h" +#include "game-server/gamehandler.hpp" +#include "game-server/mapmanager.hpp" +#include "game-server/state.hpp" +#include "net/messageout.hpp" +#include "utils/logger.h" + +State::State() +{ + // Create 10 maggots for testing purposes + for (int i = 0; i < 10; i++) + { + Being *being = new Controlled(OBJECT_MONSTER); + being->setSpeed(150); + being->setMapId(1); + Point pos = { 720, 900 }; + being->setPosition(pos); + addObject(ObjectPtr(being)); + } +} + +State::~State() +{ + for (std::map< unsigned, MapComposite * >::iterator i = maps.begin(), + i_end = maps.end(); i != i_end; ++i) + { + delete i->second; + } +} + +void +State::update() +{ + /* + * Update game state (update AI, etc.) + */ + for (std::map< unsigned, MapComposite * >::iterator m = maps.begin(), + m_end = maps.end(); m != m_end; ++m) + { + MapComposite *map = m->second; + + for (ObjectIterator o(map->getWholeMapIterator()); o; ++o) + { + (*o)->update(); + if ((*o)->getType() == OBJECT_PLAYER || (*o)->getType() == OBJECT_MONSTER) + { + static_cast< Being * >(*o)->clearHitsTaken(); + } + } + + for (MovingObjectIterator o(map->getWholeMapIterator()); o; ++o) + { + if ((*o)->getUpdateFlags() & ATTACK) + { + static_cast< Being * >(*o)->performAttack(m->second); + } + } + + for (MovingObjectIterator o(map->getWholeMapIterator()); o; ++o) + { + (*o)->move(); + } + + map->update(); + + /* + * Inform clients about changes in the game state + */ + for (PlayerIterator p(map->getWholeMapIterator()); p; ++p) + { + MessageOut moveMsg(GPMSG_BEINGS_MOVE); + MessageOut damageMsg(GPMSG_BEINGS_DAMAGE); + + for (MovingObjectIterator o(map->getAroundPlayerIterator(*p)); o; ++o) + { + + Point os = (*o)->getOldPosition(); + Point on = (*o)->getPosition(); + + int flags = 0; + + // Send attack messages + if ( (*o)->getUpdateFlags() & ATTACK + && (*o)->getPublicID() != (*p)->getPublicID() + && (*p)->getPosition().inRangeOf(on) + ) + { + MessageOut AttackMsg (GPMSG_BEING_ATTACK); + AttackMsg.writeShort((*o)->getPublicID()); + + LOG_DEBUG("Sending attack packet from " << (*o)->getPublicID() + << " to " << (*p)->getPublicID(), 0); + + gameHandler->sendTo(*p, AttackMsg); + } + + // Send damage messages + + if ((*o)->getType() == OBJECT_PLAYER || (*o)->getType() == OBJECT_MONSTER) + { + Being *victim = static_cast< Being * >(*o); + Hits const &hits = victim->getHitsTaken(); + for (Hits::const_iterator i = hits.begin(), + i_end = hits.end(); i != i_end; ++i) + { + damageMsg.writeShort(victim->getPublicID()); + damageMsg.writeShort((*i)); + } + } + + // Send move messages + + /* Check whether this player and this moving object were around + * the last time and whether they will be around the next time. + */ + bool wereInRange = (*p)->getOldPosition().inRangeOf(os) && + !(((*p)->getUpdateFlags() | (*o)->getUpdateFlags()) & NEW_ON_MAP); + bool willBeInRange = (*p)->getPosition().inRangeOf(on); + if (!wereInRange) + { + // o was outside p's range. + if (!willBeInRange) + { + // Nothing to report: o will not be inside p's range. + continue; + } + flags |= MOVING_DESTINATION; + + int type = (*o)->getType(); + MessageOut enterMsg(GPMSG_BEING_ENTER); + enterMsg.writeByte(type); + enterMsg.writeShort((*o)->getPublicID()); + switch (type) { + case OBJECT_PLAYER: + { + Player *q = static_cast< Player * >(*o); + enterMsg.writeString(q->getName()); + enterMsg.writeByte(q->getHairStyle()); + enterMsg.writeByte(q->getHairColor()); + enterMsg.writeByte(q->getGender()); + } break; + case OBJECT_MONSTER: + { + enterMsg.writeShort(0); // TODO: The monster ID + } break; + default: + assert(false); // TODO + } + gameHandler->sendTo(*p, enterMsg); + } + else if (!willBeInRange) + { + // o is no longer visible from p. + MessageOut leaveMsg(GPMSG_BEING_LEAVE); + leaveMsg.writeShort((*o)->getPublicID()); + gameHandler->sendTo(*p, leaveMsg); + continue; + } + else if (os.x == on.x && os.y == on.y) + { + // o does not move, nothing to report. + continue; + } + + /* At this point, either o has entered p's range, either o is + moving inside p's range. Report o's movements. */ + + Point od = (*o)->getDestination(); + if (on.x != od.x || on.y != od.y) + { + flags |= MOVING_POSITION; + if ((*o)->getUpdateFlags() & NEW_DESTINATION) + { + flags |= MOVING_DESTINATION; + } + } + else + { + // no need to synchronize on the very last step + flags |= MOVING_DESTINATION; + } + + moveMsg.writeShort((*o)->getPublicID()); + moveMsg.writeByte(flags); + if (flags & MOVING_POSITION) + { + moveMsg.writeCoordinates(on.x / 32, on.y / 32); + } + if (flags & MOVING_DESTINATION) + { + moveMsg.writeShort(od.x); + moveMsg.writeShort(od.y); + } + } + + // Don't send a packet if nothing happened in p's range. + if (moveMsg.getLength() > 2) + gameHandler->sendTo(*p, moveMsg); + + if (damageMsg.getLength() > 2) + gameHandler->sendTo(*p, damageMsg); + } + + for (ObjectIterator o(map->getWholeMapIterator()); o; ++o) + { + (*o)->clearUpdateFlags(); + } + } +} + +void +State::addObject(ObjectPtr objectPtr) +{ + unsigned mapId = objectPtr->getMapId(); + MapComposite *map = loadMap(mapId); + if (!map || !map->insert(objectPtr)) + { + // TODO: Deal with failure to place Object on the map. + return; + } + objectPtr->raiseUpdateFlags(NEW_ON_MAP); + if (objectPtr->getType() != OBJECT_PLAYER) return; + Player *playerPtr = static_cast< Player * >(objectPtr.get()); + + /* Since the player doesn't know yet where on the world he is after + * connecting to the map server, we send him an initial change map message. + */ + MessageOut mapChangeMessage(GPMSG_PLAYER_MAP_CHANGE); + mapChangeMessage.writeString(mapManager->getMapName(mapId)); + Point pos = playerPtr->getPosition(); + mapChangeMessage.writeShort(pos.x); + mapChangeMessage.writeShort(pos.y); + mapChangeMessage.writeByte(0); + gameHandler->sendTo(playerPtr, mapChangeMessage); +} + +void +State::removeObject(ObjectPtr objectPtr) +{ + unsigned mapId = objectPtr->getMapId(); + std::map< unsigned, MapComposite * >::iterator m = maps.find(mapId); + if (m == maps.end()) return; + MapComposite *map = m->second; + + int type = objectPtr->getType(); + if (type == OBJECT_MONSTER || type == OBJECT_PLAYER || type == OBJECT_NPC) + { + MovingObject *obj = static_cast< MovingObject * >(objectPtr.get()); + MessageOut msg(GPMSG_BEING_LEAVE); + msg.writeShort(obj->getPublicID()); + Point objectPos = obj->getPosition(); + + for (PlayerIterator p(map->getAroundObjectIterator(obj)); p; ++p) + { + if (*p != obj && objectPos.inRangeOf((*p)->getPosition())) + { + gameHandler->sendTo(*p, msg); + } + } + } + + map->remove(objectPtr); +} + +MapComposite *State::loadMap(unsigned mapId) +{ + std::map< unsigned, MapComposite * >::iterator m = maps.find(mapId); + if (m != maps.end()) return m->second; + Map *map = mapManager->getMap(mapId); + assert(map); + MapComposite *tmp = new MapComposite(map); + maps[mapId] = tmp; + + // will need to load extra map related resources here also + + return tmp; +} + +void State::sayAround(Object *obj, std::string text) +{ + unsigned short id = 65535; + int type = obj->getType(); + if (type == OBJECT_PLAYER || type == OBJECT_NPC || type == OBJECT_MONSTER) + { + id = static_cast< MovingObject * >(obj)->getPublicID(); + } + MessageOut msg(GPMSG_SAY); + msg.writeShort(id); + msg.writeString(text); + + std::map< unsigned, MapComposite * >::iterator m = maps.find(obj->getMapId()); + if (m == maps.end()) return; + MapComposite *map = m->second; + Point speakerPosition = obj->getPosition(); + + for (PlayerIterator i(map->getAroundObjectIterator(obj)); i; ++i) + { + if (speakerPosition.inRangeOf((*i)->getPosition())) + { + gameHandler->sendTo(*i, msg); + } + } +} diff --git a/src/game-server/state.hpp b/src/game-server/state.hpp new file mode 100644 index 00000000..36bd87d0 --- /dev/null +++ b/src/game-server/state.hpp @@ -0,0 +1,77 @@ +/* + * The Mana World Server + * 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_SERVER_STATE_ +#define _TMW_SERVER_STATE_ + +#include <map> +#include <string> + +#include "object.h" + +class MapComposite; + +/** + * State class contains all information/procedures associated with the game + * world's state. + */ +class State +{ + /** + * List of maps. + */ + std::map<unsigned int, MapComposite *> maps; + + public: + State(); + ~State(); + + /** + * Update game state (contains core server logic). + */ + void update(); + + /** + * Load map into game world. + */ + MapComposite *loadMap(unsigned mapId); + + /** + * Add object to the map. + */ + void addObject(ObjectPtr objectPtr); + + /** + * Remove an object from the map. + */ + void removeObject(ObjectPtr objectPtr); + + /** + * Say around an object. + */ + void sayAround(Object *, std::string text); +}; + +extern State *gameState; + +#endif |