eris  1.4.0
A WorldForge client library.
Room.cpp
1 #ifdef HAVE_CONFIG_H
2  #include "config.h"
3 #endif
4 
5 #include "Room.h"
6 #include "Lobby.h"
7 #include "Connection.h"
8 #include "Person.h"
9 #include "Log.h"
10 #include "Exceptions.h"
11 #include "Account.h"
12 
13 #include <sigc++/slot.h>
14 
15 #include <Atlas/Objects/Operation.h>
16 #include <Atlas/Objects/Anonymous.h>
17 
18 #include <cassert>
19 
20 using namespace Atlas::Objects::Operation;
21 using Atlas::Objects::Root;
22 using Atlas::Objects::Entity::RootEntity;
23 using Atlas::Objects::Entity::Anonymous;
24 using Atlas::Objects::smart_dynamic_cast;
25 
26 namespace Eris
27 {
28 
29 Room::Room(Lobby *l, const std::string& id) :
30  m_roomId(id),
31  m_entered(false),
32  m_lobby(l)
33 {
34  if (!id.empty()) {
35  m_lobby->getConnection().registerRouterForFrom(this, id);
36  }
37 }
38 
39 Room::~Room()
40 {
41  if (!m_roomId.empty()) {
42  m_lobby->getConnection().unregisterRouterForFrom(m_roomId);
43  }
44 }
45 
46 // public command-issue wrappers
47 
48 void Room::say(const std::string &tk)
49 {
50  if (!m_lobby->getConnection().isConnected())
51  {
52  error() << "talking in room " << m_roomId << ", but connection is down";
53  return;
54  }
55 
56  Anonymous speech;
57  speech->setAttr("say", tk);
58  speech->setAttr("loc", m_roomId);
59 
60  Talk t;
61  t->setArgs1(speech);
62  t->setTo(m_roomId);
63  t->setFrom(m_lobby->getAccount().getId());
64  t->setSerialno(getNewSerialno());
65 
66  m_lobby->getConnection().send(t);
67 }
68 
69 void Room::emote(const std::string &em)
70 {
71  if (!m_lobby->getConnection().isConnected())
72  {
73  error() << "emoting in room " << m_roomId << ", but connection is down";
74  return;
75  }
76 
77  Imaginary im;
78 
79  Anonymous emote;
80  emote->setId("emote");
81  emote->setAttr("loc", m_roomId);
82  emote->setAttr("description", em);
83 
84  im->setArgs1(emote);
85  im->setTo(m_roomId);
86  im->setFrom(m_lobby->getAccount().getId());
87  im->setSerialno(getNewSerialno());
88 
89  m_lobby->getConnection().send(im);
90 }
91 
93 {
94  if (!m_lobby->getConnection().isConnected())
95  {
96  error() << "leaving room " << m_roomId << ", but connection is down";
97  return;
98  }
99 
100  Move part;
101  part->setFrom(m_lobby->getAccount().getId());
102  part->setSerialno(getNewSerialno());
103 
104  Anonymous args;
105  args->setAttr("loc", m_roomId);
106  args->setAttr("mode", "part");
107  part->setArgs1(args);
108 
109  m_lobby->getConnection().send(part);
110 }
111 
112 Room* Room::createRoom(const std::string &name)
113 {
114  if (!m_lobby->getConnection().isConnected())
115  {
116  error() << "creating room in room " << m_roomId << ", but connection is down";
117  return nullptr;
118  }
119 
120 
121  Create cr;
122  cr->setFrom(m_lobby->getAccount().getId());
123  cr->setTo(m_roomId);
124  cr->setSerialno(getNewSerialno());
125 
126  RootEntity room;
127  room->setName(name);
128  room->setParent("room");
129 
130  cr->setArgs1(room);
131  m_lobby->getConnection().send(cr);
132 
133  return nullptr;
134 }
135 
136 Person* Room::getPersonByUID(const std::string& uid)
137 {
138  return m_lobby->getPerson(uid);
139 }
140 
141 std::vector<Person*> Room::getPeople() const
142 {
143  std::vector<Person*> people;
144 
145  for (const auto & member : m_members)
146  {
147  if (member.second) {
148  people.push_back(member.second);
149  }
150  }
151 
152  return people;
153 }
154 
155 Router::RouterResult Room::handleOperation(const RootOperation& op)
156 {
157  if (op->getTo() != m_lobby->getAccount().getId()) {
158  error() << "Room received op TO account " << op->getTo() << ", not the account ID";
159  return IGNORED;
160  }
161 
162  const std::vector<Root>& args = op->getArgs();
163 
164  if (op->instanceOf(APPEARANCE_NO)) {
165  for (const auto & arg : args) {
166  appearance(arg->getId());
167  }
168 
169  return HANDLED;
170  }
171 
172  if (op->instanceOf(DISAPPEARANCE_NO)) {
173  for (const auto & arg : args) {
174  disappearance(arg->getId());
175  }
176 
177  return HANDLED;
178  }
179 
180  if (op->instanceOf(SIGHT_NO)) {
181  assert(!args.empty());
182  RootEntity ent = smart_dynamic_cast<RootEntity>(args.front());
183 
184  if (ent.isValid() && (ent->getId() == m_roomId)) {
185  sight(ent);
186  return HANDLED;
187  }
188  }
189 
190  return IGNORED;
191 }
192 
193 void Room::sight(const RootEntity &room)
194 {
195  if (m_entered)
196  warning() << "got SIGHT of entered room " << m_roomId;
197 
198  m_name = room->getName();
199  if (room->hasAttr("topic"))
200  m_topic = room->getAttr("topic").asString();
201 
202  m_lobby->SightPerson.connect(sigc::mem_fun(this, &Room::notifyPersonSight));
203 
204  if (room->hasAttr("people"))
205  {
206  const Atlas::Message::ListType& people = room->getAttr("people").asList();
207  for (const auto & person : people) {
208  appearance(person.asString());
209  }
210  }
211 
212  checkEntry();
213 
214  if (room->hasAttr("rooms"))
215  {
216  const Atlas::Message::ListType& rooms = room->getAttr("rooms").asList();
217  for (const auto & item : rooms)
218  {
219  m_subrooms.push_back(new Room(m_lobby, item.asString()));
220  }
221  }
222 }
223 
224 void Room::handleSoundTalk(Person* p, const std::string& speech)
225 {
226  assert(p);
227 
228  if (m_members.count(p->getAccount()) == 0) {
229  error() << "room " << m_roomId << " got sound(talk) from non-member account";
230  return;
231  }
232 
233  Speech.emit(this, p, speech);
234 }
235 
236 void Room::handleEmote(Person* p, const std::string& description)
237 {
238  assert(p);
239 
240  if (m_members.count(p->getAccount()) == 0) {
241  error() << "room " << m_roomId << " got sight(imaginary) from non-member account";
242  return;
243  }
244 
245  Emote.emit(this, p, description);
246 }
247 
248 // room membership updates
249 
250 void Room::appearance(const std::string& personId)
251 {
252  auto P = m_members.find(personId);
253  if (P != m_members.end()) {
254  error() << "duplicate appearance of person " << personId << " in room " << m_roomId;
255  return;
256  }
257 
258  Person* person = m_lobby->getPerson(personId);
259  if (person)
260  {
261  m_members[personId] = person;
262  if (m_entered)
263  Appearance.emit(this, person);
264  } else {
265  m_members[personId] = nullptr; // we know the person is here, but that's all
266  // we'll find out more when we get the SightPerson signal from Lobby
267  }
268 }
269 
270 void Room::disappearance(const std::string& personId)
271 {
272  auto P = m_members.find(personId);
273  if (P == m_members.end())
274  {
275  error() << "during disappearance, person " << personId << " not found in room " << m_roomId;
276  return;
277  }
278 
279  if (P->second) // don't emit if never got sight
280  Disappearance.emit(this, P->second);
281 
282  m_members.erase(P);
283 }
284 
285 void Room::notifyPersonSight(Person *p)
286 {
287  assert(p);
288  auto P = m_members.find(p->getAccount());
289  // for the moment, all rooms get spammed with sights of people, to avoid
290  // the need for a counting / disconnect from SightPerson scheme
291  if (P == m_members.end()) {
292  return;
293  }
294 
295  if (P->second == nullptr) {
296  m_members[p->getAccount()] = p;
297 
298  if (m_entered) {
299  Appearance.emit(this, p);
300  } else {
301  checkEntry();
302  }
303  } else {
304  // fairly meaningless case, but I'm paranoid
305  // could fire a 'changed' signal here, eg if they renamed?
306  assert (P->second == p);
307  }
308 }
309 
310 void Room::checkEntry()
311 {
312  assert(!m_entered);
313 
314  bool anyPending = false;
315  for (auto& entry : m_members) {
316  if (entry.second == nullptr) {
317  anyPending = true;
318  }
319  }
320 
321  if (!anyPending)
322  {
323  Entered.emit(this);
324  m_entered = true;
325  }
326 }
327 
328 } // of Eris namespace
Eris::Room::Appearance
sigc::signal< void, Room *, Person * > Appearance
Definition: Room.h:96
Eris::Account::getId
const std::string & getId() const
returns the account ID if logged in
Definition: Account.h:325
Eris::Lobby
Definition: Lobby.h:26
Eris::Room
Definition: Room.h:25
Eris::Room::Emote
sigc::signal< void, Room *, Person *, const std::string & > Emote
Definition: Room.h:91
Eris::Lobby::SightPerson
sigc::signal< void, Person * > SightPerson
Emitted when sight of a person is received.
Definition: Lobby.h:60
Eris::Lobby::getPerson
Person * getPerson(const std::string &acc)
obtain a person's info, given their account ID; may return nullptr
Definition: Lobby.cpp:197
Eris::Lobby::getConnection
Connection & getConnection() const
Helper method to access the underlying Connection from the Account.
Definition: Lobby.cpp:192
Eris::Room::Speech
sigc::signal< void, Room *, Person *, const std::string & > Speech
Definition: Room.h:88
Eris::Room::leave
void leave()
Definition: Room.cpp:92
Eris::Person
Definition: Person.h:15
Eris::Lobby::getAccount
Account & getAccount() const
Retrive the Account which this lobbby is bound to.
Definition: Lobby.h:50
Eris::Room::createRoom
Room * createRoom(const std::string &name)
Definition: Room.cpp:112
Eris::error
Definition: LogStream.h:65
Eris::BaseConnection::isConnected
bool isConnected() const
Ascertain whether or not the connection is usable for transport.
Definition: BaseConnection.h:72
Eris::getNewSerialno
std::int64_t getNewSerialno()
operation serial number sequencing
Definition: Connection.cpp:390
Eris::Room::say
void say(const std::string &tk)
Send a piece of text to this room.
Definition: Room.cpp:48
Eris
Definition: Account.cpp:33
Eris::Room::Room
Room(Lobby *l, const std::string &id)
Definition: Room.cpp:29
Eris::Room::Entered
sigc::signal< void, Room * > Entered
Definition: Room.h:84
Eris::Connection::send
virtual void send(const Atlas::Objects::Root &obj)
Transmit an Atlas::Objects instance to the server.
Definition: Connection.cpp:160
Eris::Room::emote
void emote(const std::string &em)
Definition: Room.cpp:69
Eris::Room::Disappearance
sigc::signal< void, Room *, Person * > Disappearance
Similarly, emitted when the specifed person leaves the room.
Definition: Room.h:99
Eris::Room::getPeople
std::vector< Person * > getPeople() const
obtain an array of pointers to everyone in this room
Definition: Room.cpp:141