eris  1.4.0
A WorldForge client library.
Lobby.cpp
1 #ifdef HAVE_CONFIG_H
2  #include "config.h"
3 #endif
4 
5 #include "Lobby.h"
6 #include "Connection.h"
7 #include "Log.h"
8 #include "Person.h"
9 #include "Account.h"
10 #include "Redispatch.h"
11 
12 #include <sigc++/slot.h>
13 
14 #include <Atlas/Objects/Operation.h>
15 #include <Atlas/Objects/Entity.h>
16 #include <Atlas/Objects/Anonymous.h>
17 
18 #include <algorithm>
19 #include <cassert>
20 #include <memory>
21 #include <utility>
22 
23 typedef Atlas::Objects::Entity::Account AtlasAccount;
24 using namespace Atlas::Objects::Operation;
25 using Atlas::Objects::Root;
26 using Atlas::Objects::smart_static_cast;
27 using Atlas::Objects::smart_dynamic_cast;
28 using Atlas::Objects::Entity::RootEntity;
29 using Atlas::Objects::Entity::Anonymous;
30 
31 namespace Eris {
32 
33 class OOGRouter : public Router
34 {
35 public:
36  explicit OOGRouter(Lobby* l) :
37  m_lobby(l),
38  m_anonymousLookSerialno(0)
39  {;}
40 
41  void setAnonymousLookSerialno(std::int64_t serial)
42  {
43  m_anonymousLookSerialno = serial;
44  }
45 
46 protected:
47  RouterResult handleOperation(const RootOperation& op) override
48  {
49  const std::vector<Root>& args = op->getArgs();
50 
51  if (op->instanceOf(APPEARANCE_NO)) {
52  for (const auto & arg : args) {
53  m_lobby->recvAppearance(arg);
54  }
55 
56  return HANDLED;
57  }
58 
59  if (op->instanceOf(DISAPPEARANCE_NO)) {
60  for (const auto & arg : args) {
61  m_lobby->recvDisappearance(arg);
62  }
63 
64  return HANDLED;
65  }
66 
67  // note it's important we match exactly on sight here, and not derived ops
68  // like appearance and disappearance
69  if (op->getClassNo() == Atlas::Objects::Operation::SIGHT_NO) {
70  for (const auto & arg : args) {
71  AtlasAccount acc = smart_dynamic_cast<AtlasAccount>(arg);
72  if (acc.isValid()) {
73  m_lobby->sightPerson(acc);
74  continue;
75  }
76 
77  if (op->getRefno() == m_anonymousLookSerialno) {
78  RootEntity ent = smart_dynamic_cast<RootEntity>(arg);
79  m_lobby->recvInitialSight(ent);
80  continue;
81  }
82 
83  Imaginary im = smart_dynamic_cast<Imaginary>(arg);
84  if (im.isValid()) {
85  m_lobby->recvImaginary(im);
86  continue;
87  }
88  }
89  return HANDLED;
90  } // of sight op
91 
92  Sound sound = smart_dynamic_cast<Sound>(op);
93  if (sound.isValid())
94  {
95  for (const auto & arg : args) {
96  Talk talk = smart_dynamic_cast<Talk>(arg);
97  if (talk.isValid()) {
98  m_lobby->recvTalk(talk);
99  }
100  }
101  return HANDLED;
102 
103  }
104 
105  return IGNORED;
106  }
107 
108 private:
109  TypeService& typeService()
110  {
111  // none of these can ever be nullptr, honest
112  return m_lobby->getConnection().getTypeService();
113  }
114 
115  Lobby* m_lobby;
116  std::int64_t m_anonymousLookSerialno;
117 };
118 
119 
120 Lobby::Lobby(Account& a) :
121  Room(this, std::string()),
122  m_account(a)
123 {
124  m_router = std::make_unique<OOGRouter>(this);
125 
126  if (m_account.isLoggedIn()) {
127  onLoggedIn();
128  } else {
129  m_account.LoginSuccess.connect(sigc::mem_fun(this, &Lobby::onLoggedIn));
130  }
131 
132  m_account.LogoutComplete.connect(sigc::mem_fun(this, &Lobby::onLogout));
133 }
134 
136 {
137  //We store ourself in the rooms, so lets first remove that.
138  m_rooms[getId()].release();
139 }
140 
141 void Lobby::look(const std::string &id)
142 {
143  if (!m_account.isLoggedIn()) {
144  error() << "Lobby trying look while not logged in";
145  return;
146  }
147 
148  Look look;
149  look->setFrom(m_account.getId());
150  look->setSerialno(getNewSerialno());
151 
152  if (!id.empty()) {
153  Anonymous what;
154  what->setId(id);
155  look->setArgs1(what);
156  }
157 
158  if (id.empty()) {
159  m_router->setAnonymousLookSerialno(look->getSerialno());
160  }
161 
162  getConnection().send(look);
163 }
164 
165 Room* Lobby::join(const std::string& roomId)
166 {
167  if (!m_account.isLoggedIn())
168  {
169  error() << "Lobby trying join while not logged in";
170  return nullptr;
171  }
172 
173  Anonymous what;
174  what->setAttr("loc", roomId);
175  what->setAttr("mode", "join");
176 
177  Move join;
178  join->setFrom(m_account.getId());
179  join->setSerialno(getNewSerialno());
180  join->setArgs1(what);
182 
183  auto R = m_rooms.find(roomId);
184  if (R == m_rooms.end()) {
185  m_rooms.emplace(roomId, std::make_unique<Room>(this, roomId));
186  R = m_rooms.find(roomId);
187  }
188 
189  return R->second.get();
190 }
191 
193 {
194  return m_account.getConnection();
195 }
196 
197 Person* Lobby::getPerson(const std::string &acc)
198 {
199  auto P = m_people.find(acc);
200  if (P == m_people.end())
201  {
202  look(acc);
203  // create a nullptr entry (indicates we are doing the look)
204  P = m_people.insert(P, IdPersonMap::value_type(acc, nullptr));
205  }
206 
207  return P->second.get();
208 }
209 
210 Room* Lobby::getRoom(const std::string &id)
211 {
212  auto R = m_rooms.find(id);
213  if (R == m_rooms.end()) {
214  error() << "called getRoom with unknown ID " << id;
215  return nullptr;
216  }
217 
218  return R->second.get();
219 }
220 
221 void Lobby::sightPerson(const AtlasAccount &ac)
222 {
223  if (!ac->isDefaultId()) {
224  auto P = m_people.find(ac->getId());
225  if (P == m_people.end()) {
226  error() << "got un-requested sight of person " << ac->getId();
227  return;
228  }
229 
230  if (P->second)
231  P->second->sight(ac);
232  else {
233  // install the new Person object
234  P->second = std::make_unique<Person>(*this, ac);
235  }
236 
237  // emit the signal; this lets rooms waiting on this player's info update
238  SightPerson.emit(P->second.get());
239  }
240 }
241 
242 void Lobby::recvInitialSight(const RootEntity& ent)
243 {
244  // we only hit this path when we get the anonymous LOOK response
245  // for the Lobby. We need to do the work normally done in Room's ctor
246  if (!m_roomId.empty()) {
247  return;
248  }
249 
250  m_roomId = ent->getId();
251  m_rooms[m_roomId] = std::unique_ptr<Lobby>(this); //We'll later on remove ourself from the m_rooms list in our destructor.
252  m_account.getConnection().registerRouterForFrom(this, m_roomId);
253  Room::sight(ent);
254 }
255 
259 
260 {
261 public:
262  SightPersonRedispatch(Connection& con, std::string pid, const Root& obj) :
263  Redispatch(con, obj),
264  m_person(std::move(pid))
265  {}
266 
267  void onSightPerson(Person* p)
268  {
269  if ( p->getAccount() == m_person) post();
270  }
271 private:
272  std::string m_person;
273 };
274 
275 Router::RouterResult Lobby::recvTalk(const Talk& tk)
276 {
277  if (tk->isDefaultFrom()) {
278  return IGNORED;
279  }
280  IdPersonMap::const_iterator P = m_people.find(tk->getFrom());
281  if ((P == m_people.end()) || (P->second == nullptr)) {
282  getPerson(tk->getFrom()); // force a LOOK if necessary
283  debug() << "creating sight-person-redispatch for " << tk->getFrom();
284 
285  Sight sight;
286  sight->setArgs1(tk);
287  sight->setTo(getAccount().getId());
288 
289  auto *spr = new SightPersonRedispatch(getConnection(), tk->getFrom(), sight);
290  SightPerson.connect(sigc::mem_fun(spr, &SightPersonRedispatch::onSightPerson));
291 
292  return HANDLED;
293  }
294 
295  const std::vector<Root>& args = tk->getArgs();
296  if (args.empty()) {
297  error() << "received sound(talk) with no args";
298  return HANDLED;
299  }
300 
301  for (const auto& arg : args) {
302  if (!args.front()->hasAttr("say")) {
303  error() << "received sound(talk) with bad arg";
304  continue;
305  }
306  std::string speech = arg->getAttr("say").asString();
307 
308  if (arg->hasAttr("loc")) {
309  std::string loc = arg->getAttr("loc").asString();
310  auto room = m_rooms.find(loc);
311 
312  if (room != m_rooms.end()) {
313  room->second->handleSoundTalk(P->second.get(), speech);
314  } else {
315  warning() << "lobby got sound(talk) with unknown loc: " << loc;
316  }
317  } else {
318  // no location, hence assume it's one-to-one chat
319  PrivateTalk.emit(P->second.get(), speech);
320  }
321  }
322 
323 
324 
325  return HANDLED;
326 }
327 
328 void Lobby::recvAppearance(const Atlas::Objects::Root& obj)
329 {
330  if (!obj->hasAttr("loc")) {
331  error() << "lobby got appearance arg without loc: " << obj;
332  return;
333  }
334 
335  std::string loc = obj->getAttr("loc").asString();
336  auto room = m_rooms.find(loc);
337 
338  if (room != m_rooms.end()) {
339  room->second->appearance(obj->getId());
340  } else
341  warning() << "lobby got appearance with unknown loc: " << loc;
342 }
343 
344 Router::RouterResult Lobby::recvImaginary(const Imaginary& im)
345 {
346  const auto& args = im->getArgs();
347  if (args.empty()) {
348  warning() << "received sight(imaginary) with no args: " << im;
349  return HANDLED;
350  }
351  for (const auto& arg : args) {
352  if (arg->hasAttr("description")) {
353  warning() << "received sight(imaginary) with bad args: " << im;
354  continue;
355  }
356 
357  std::string description = arg->getAttr("description").asString();
358 
359  if (im->isDefaultFrom()) {
360  continue;
361  }
362  IdPersonMap::const_iterator P = m_people.find(im->getFrom());
363  if ((P == m_people.end()) || (P->second == nullptr)) {
364  getPerson(im->getFrom()); // force a LOOK if necessary
365  debug() << "creating sight-person-redispatch for " << im->getFrom();
366 
367  Sight sight;
368  sight->setArgs1(im);
369  sight->setTo(getAccount().getId());
370 
371  auto *spr = new SightPersonRedispatch(getConnection(), im->getFrom(), sight);
372  SightPerson.connect(sigc::mem_fun(spr, &SightPersonRedispatch::onSightPerson));
373 
374  continue;
375  }
376 
377  if (arg->hasAttr("loc")) {
378  std::string loc = arg->getAttr("loc").asString();
379  auto room = m_rooms.find(loc);
380 
381  if (room != m_rooms.end()) {
382  room->second->handleEmote(P->second.get(), description);
383  } else {
384  error() << "lobby got sight(imaginary) with unknown loc: " << loc;
385  }
386  } else {
387  warning() << "received imaginary with no loc set:" << im;
388  }
389  }
390 
391 
392 
393  return HANDLED;
394 }
395 
396 
397 void Lobby::recvDisappearance(const Atlas::Objects::Root& obj)
398 {
399  if (!obj->hasAttr("loc")) {
400  error() << "lobby got disappearance arg without loc: " << obj;
401  return;
402  }
403 
404  auto locAttr = obj->getAttr("loc");
405  if (locAttr.isString()) {
406  std::string loc = locAttr.String();
407  auto room = m_rooms.find(loc);
408 
409  if (room != m_rooms.end()) {
410  room->second->disappearance(obj->getId());
411  } else {
412  error() << "lobby got disappearance with unknown loc: " << loc;
413  }
414  }
415 
416 }
417 
418 /*
419 void Lobby::processRoomCreate(const Atlas::Objects::Operation::Create &cr,
420  const Atlas::Objects::Entity::RootEntity &ent)
421 {
422  log(LOG_DEBUG, "received sight of room creation");
423 
424  PendingCreateMap::iterator P = _pendingCreate.find(cr.getSerialno());
425  if (P != _pendingCreate.end()) {
426  // it was requested locally, so we already have the Room object
427  P->second->_id = ent.getId(); // set the ID
428  P->second->setup(); // now we can call setup safely
429  P->second->sight(ent); // finally slam the data in
430  _pendingCreate.erase(P); // get rid of the request
431  }
432 
433  // find the containing room and update it's subrooms
434  // note that we may not even know about it's containing room either!
435  std::string containingRoom = ent.getAttr("loc").asString();
436  if (_roomDict.find(containingRoom) == _roomDict.end())
437  return; // we can't see it, so we don't care [we'll get the rooms anyway if we ever join the containing room]
438 
439  Room *container = _roomDict[containingRoom];
440  container->_subrooms.insert(ent.getId()); // jam it in
441 
442  StringSet strset;
443  strset.insert("rooms");
444  container->Changed.emit(strset);
445 }
446 */
447 
448 // signal handlers for various things
449 
450 void Lobby::onLoggedIn()
451 {
452  assert(m_account.isLoggedIn());
453  getConnection().registerRouterForTo(m_router.get(), m_account.getId());
454  look(""); // do initial anonymous look
455 }
456 
457 void Lobby::onLogout(bool clean)
458 {
459  getConnection().unregisterRouterForTo(m_router.get(), m_account.getId());
460 }
461 
462 } // of namespace
Eris::Person::getAccount
const std::string & getAccount() const
access the Atlas account ID for this person
Definition: Person.h:28
Eris::Account::getId
const std::string & getId() const
returns the account ID if logged in
Definition: Account.h:325
Eris::Lobby::~Lobby
~Lobby() override
Definition: Lobby.cpp:135
Eris::Lobby
Definition: Lobby.h:27
Eris::Room
Definition: Room.h:26
Eris::OOGRouter
Definition: Lobby.cpp:34
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::Person
Definition: Person.h:16
Eris::Room::getId
std::string getId() const
Definition: Room.h:74
Eris::TypeService
Definition: TypeService.h:24
Eris::Lobby::getAccount
Account & getAccount() const
Retrive the Account which this lobbby is bound to.
Definition: Lobby.h:50
Eris::Account
Encapsulates all the state of an Atlas Account, and methods that operation on that state.
Definition: Account.h:42
Eris::Router
Definition: Router.h:11
Eris::error
Definition: LogStream.h:66
Eris::Account::LogoutComplete
sigc::signal< void, bool > LogoutComplete
Emitted when a logout completes.
Definition: Account.h:207
Eris::SightPersonRedispatch
Definition: Lobby.cpp:260
Eris::Lobby::getRoom
Room * getRoom(const std::string &id)
Definition: Lobby.cpp:210
Eris::Account::isLoggedIn
bool isLoggedIn() const
Check if the account is logged in.
Definition: Account.cpp:324
Eris::getNewSerialno
std::int64_t getNewSerialno()
operation serial number sequencing
Definition: Connection.cpp:390
Eris
Definition: Account.cpp:33
Eris::Redispatch
Definition: Redispatch.h:16
Eris::Lobby::PrivateTalk
sigc::signal< void, Person *, const std::string & > PrivateTalk
Definition: Lobby.h:67
Eris::Lobby::join
Room * join(const std::string &roomID)
Definition: Lobby.cpp:165
Eris::Connection::send
virtual void send(const Atlas::Objects::Root &obj)
Transmit an Atlas::Objects instance to the server.
Definition: Connection.cpp:160
Eris::Account::getConnection
Connection & getConnection() const
Access the underlying Connection for this account.
Definition: Account.h:338
Eris::Connection
Definition: Connection.h:45
Eris::Account::LoginSuccess
sigc::signal< void > LoginSuccess
Definition: Account.h:200