#ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include using namespace Atlas::Objects::Operation; using Atlas::Objects::Root; using Atlas::Objects::Entity::RootEntity; using Atlas::Objects::Entity::Anonymous; using Atlas::Objects::smart_dynamic_cast; namespace Eris { Room::Room(Lobby *l, const std::string& id) : m_roomId(id), m_entered(false), m_lobby(l) { if (!id.empty()) m_lobby->getConnection()->registerRouterForFrom(this, id); } Room::~Room() { if (!m_roomId.empty()) m_lobby->getConnection()->unregisterRouterForFrom(this, m_roomId); } #pragma mark - // public command-issue wrappers void Room::say(const std::string &tk) { if (!m_lobby->getConnection()->isConnected()) { error() << "talking in room " << m_roomId << ", but connection is down"; return; } Anonymous speech; speech->setAttr("say", tk); speech->setAttr("loc", m_roomId); Talk t; t->setArgs1(speech); t->setTo(m_roomId); t->setFrom(m_lobby->getAccount()->getId()); t->setSerialno(getNewSerialno()); m_lobby->getConnection()->send(t); } void Room::emote(const std::string &em) { if (!m_lobby->getConnection()->isConnected()) { error() << "emoting in room " << m_roomId << ", but connection is down"; return; } Imaginary im; Anonymous emote; emote->setId("emote"); emote->setAttr("loc", m_roomId); emote->setAttr("description", em); im->setArgs1(emote); im->setTo(m_roomId); im->setFrom(m_lobby->getAccount()->getId()); im->setSerialno(getNewSerialno()); m_lobby->getConnection()->send(im); } void Room::leave() { if (!m_lobby->getConnection()->isConnected()) { error() << "leaving room " << m_roomId << ", but connection is down"; return; } Move part; part->setFrom(m_lobby->getAccount()->getId()); part->setSerialno(getNewSerialno()); Anonymous args; args->setAttr("loc", m_roomId); args->setAttr("mode", "part"); part->setArgs1(args); m_lobby->getConnection()->send(part); } Room* Room::createRoom(const std::string &name) { if (!m_lobby->getConnection()->isConnected()) { error() << "creating room in room " << m_roomId << ", but connection is down"; return NULL; } Create cr; cr->setFrom(m_lobby->getAccount()->getId()); cr->setTo(m_roomId); cr->setSerialno(getNewSerialno()); RootEntity room; room->setName(name); room->setParents(StringList(1, "room")); cr->setArgs1(room); m_lobby->getConnection()->send(cr); return NULL; } Person* Room::getPersonByUID(const std::string& uid) { return m_lobby->getPerson(uid); } std::vector Room::getPeople() const { std::vector people; for (IdPersonMap::const_iterator P=m_members.begin(); P != m_members.end(); ++P) { if (P->second) people.push_back(P->second); } return people; } #pragma mark - Router::RouterResult Room::handleOperation(const RootOperation& op) { if (op->getTo() != m_lobby->getAccount()->getId()) { error() << "Room recived op TO account " << op->getTo() << ", not the account ID"; return IGNORED; } const std::vector& args = op->getArgs(); if (op->instanceOf(APPEARANCE_NO)) { for (unsigned int A=0; A < args.size(); ++A) appearance(args[A]->getId()); return HANDLED; } if (op->instanceOf(DISAPPEARANCE_NO)) { for (unsigned int A=0; A < args.size(); ++A) disappearance(args[A]->getId()); return HANDLED; } if (op->instanceOf(SIGHT_NO)) { assert(!args.empty()); RootEntity ent = smart_dynamic_cast(args.front()); if (ent.isValid() && (ent->getId() == m_roomId)) { sight(ent); return HANDLED; } } return IGNORED; } void Room::sight(const RootEntity &room) { if (m_entered) warning() << "got SIGHT of entered room " << m_roomId; m_name = room->getName(); if (room->hasAttr("topic")) m_topic = room->getAttr("topic").asString(); m_lobby->SightPerson.connect(sigc::mem_fun(this, &Room::notifyPersonSight)); if (room->hasAttr("people")) { const Atlas::Message::ListType& people = room->getAttr("people").asList(); for (Atlas::Message::ListType::const_iterator P=people.begin(); P!=people.end(); ++P) appearance(P->asString()); } checkEntry(); if (room->hasAttr("rooms")) { const Atlas::Message::ListType& rooms = room->getAttr("rooms").asList(); for (Atlas::Message::ListType::const_iterator R=rooms.begin(); R!=rooms.end(); ++R) { m_subrooms.push_back(new Room(m_lobby, R->asString())); } } } void Room::handleSoundTalk(Person* p, const std::string& speech) { assert(p); if (m_members.count(p->getAccount()) == 0) { error() << "room " << m_roomId << " got sound(talk) from non-member account"; return; } Speech.emit(this, p, speech); } void Room::handleEmote(Person* p, const std::string& description) { assert(p); if (m_members.count(p->getAccount()) == 0) { error() << "room " << m_roomId << " got sight(imaginary) from non-member account"; return; } Emote.emit(this, p, description); } #pragma mark - // room membership updates void Room::appearance(const std::string& personId) { IdPersonMap::iterator P = m_members.find(personId); if (P != m_members.end()) { error() << "duplicate appearance of person " << personId << " in room " << m_roomId; return; } Person* person = m_lobby->getPerson(personId); if (person) { m_members[personId] = person; if (m_entered) Appearance.emit(this, person); } else { m_members[personId] = NULL; // we know the person is here, but that's all // we'll find out more when we get the SightPerson signal from Lobby } } void Room::disappearance(const std::string& personId) { IdPersonMap::iterator P = m_members.find(personId); if (P == m_members.end()) { error() << "during disappearance, person " << personId << " not found in room " << m_roomId; return; } if (P->second) // don't emit if never got sight Disappearance.emit(this, P->second); m_members.erase(P); } void Room::notifyPersonSight(Person *p) { assert(p); IdPersonMap::iterator P = m_members.find(p->getAccount()); // for the moment, all rooms get spammed with sights of people, to avoid // the need for a counting / disconnect from SightPerson scheme if (P == m_members.end()) return; if (P->second == NULL) { P->second = p; if (m_entered) Appearance.emit(this, p); else checkEntry(); } else { // fairly meaningless case, but I'm paranoid // could fire a 'changed' signal here, eg if they renamed? assert (P->second == p); } } void Room::checkEntry() { assert(m_entered == false); bool anyPending = false; for (IdPersonMap::const_iterator P = m_members.begin(); P != m_members.end(); ++P) if (P->second == NULL) anyPending = true; if (!anyPending) { Entered.emit(this); m_entered = true; } } } // of Eris namespace