summaryrefslogtreecommitdiff
path: root/src/game-server/state.cpp
diff options
context:
space:
mode:
authorGuillaume Melquiond <guillaume.melquiond@gmail.com>2006-12-29 13:43:24 +0000
committerGuillaume Melquiond <guillaume.melquiond@gmail.com>2006-12-29 13:43:24 +0000
commit291ad04d5b5c4ab08d85eadde116f968cd579b77 (patch)
treee4dced5715a5d9792cfdc0455a6b3ee6d3116079 /src/game-server/state.cpp
parent3d404e743105bb9168c89e3451cf35d7d59120b1 (diff)
downloadmanaserv-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/state.cpp')
-rw-r--r--src/game-server/state.cpp331
1 files changed, 331 insertions, 0 deletions
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);
+ }
+ }
+}