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
23typedef Atlas::Objects::Entity::Account AtlasAccount;
24using namespace Atlas::Objects::Operation;
25using Atlas::Objects::Root;
26using Atlas::Objects::smart_static_cast;
27using Atlas::Objects::smart_dynamic_cast;
28using Atlas::Objects::Entity::RootEntity;
29using Atlas::Objects::Entity::Anonymous;
30
31namespace Eris {
32
33class OOGRouter : public Router
34{
35public:
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
46protected:
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
108private:
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
120Lobby::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
141void 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
165Room* 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
197Person* 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
210Room* 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
221void 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
242void 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{
261public:
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 }
271private:
272 std::string m_person;
273};
274
275Router::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
328void 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
344Router::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
397void 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/*
419void 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
450void 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
457void Lobby::onLogout(bool clean)
458{
459 getConnection().unregisterRouterForTo(m_router.get(), m_account.getId());
460}
461
462} // of namespace
Encapsulates all the state of an Atlas Account, and methods that operation on that state.
Definition: Account.h:42
const std::string & getId() const
returns the account ID if logged in
Definition: Account.h:325
sigc::signal< void > LoginSuccess
Definition: Account.h:200
Connection & getConnection() const
Access the underlying Connection for this account.
Definition: Account.h:338
sigc::signal< void, bool > LogoutComplete
Emitted when a logout completes.
Definition: Account.h:207
bool isLoggedIn() const
Check if the account is logged in.
Definition: Account.cpp:324
virtual void send(const Atlas::Objects::Root &obj)
Transmit an Atlas::Objects instance to the server.
Definition: Connection.cpp:160
Room * join(const std::string &roomID)
Definition: Lobby.cpp:165
Room * getRoom(const std::string &id)
Definition: Lobby.cpp:210
Person * getPerson(const std::string &acc)
obtain a person's info, given their account ID; may return nullptr
Definition: Lobby.cpp:197
Connection & getConnection() const
Helper method to access the underlying Connection from the Account.
Definition: Lobby.cpp:192
sigc::signal< void, Person *, const std::string & > PrivateTalk
Definition: Lobby.h:67
Account & getAccount() const
Retrive the Account which this lobbby is bound to.
Definition: Lobby.h:50
sigc::signal< void, Person * > SightPerson
Emitted when sight of a person is received.
Definition: Lobby.h:60
~Lobby() override
Definition: Lobby.cpp:135
const std::string & getAccount() const
access the Atlas account ID for this person
Definition: Person.h:28
std::string getId() const
Definition: Room.h:74
Definition: Account.cpp:33
std::int64_t getNewSerialno()
operation serial number sequencing
Definition: Connection.cpp:390