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
20using namespace Atlas::Objects::Operation;
21using Atlas::Objects::Root;
22using Atlas::Objects::Entity::RootEntity;
23using Atlas::Objects::Entity::Anonymous;
24using Atlas::Objects::smart_dynamic_cast;
25
26namespace Eris
27{
28
29Room::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
39Room::~Room()
40{
41 if (!m_roomId.empty()) {
42 m_lobby->getConnection().unregisterRouterForFrom(m_roomId);
43 }
44}
45
46// public command-issue wrappers
47
48void 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
69void 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
112Room* 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
136Person* Room::getPersonByUID(const std::string& uid)
137{
138 return m_lobby->getPerson(uid);
139}
140
141std::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
155Router::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
193void 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
224void 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
236void 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
250void 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
270void 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
285void 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
310void 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
const std::string & getId() const
returns the account ID if logged in
Definition: Account.h:325
bool isConnected() const
Ascertain whether or not the connection is usable for transport.
virtual void send(const Atlas::Objects::Root &obj)
Transmit an Atlas::Objects instance to the server.
Definition: Connection.cpp:160
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
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
void leave()
Definition: Room.cpp:92
Room * createRoom(const std::string &name)
Definition: Room.cpp:112
sigc::signal< void, Room *, Person *, const std::string & > Emote
Definition: Room.h:91
Room(Lobby *l, const std::string &id)
Definition: Room.cpp:29
sigc::signal< void, Room * > Entered
Definition: Room.h:84
void say(const std::string &tk)
Send a piece of text to this room.
Definition: Room.cpp:48
sigc::signal< void, Room *, Person *, const std::string & > Speech
Definition: Room.h:88
void emote(const std::string &em)
Definition: Room.cpp:69
std::vector< Person * > getPeople() const
obtain an array of pointers to everyone in this room
Definition: Room.cpp:141
sigc::signal< void, Room *, Person * > Disappearance
Similarly, emitted when the specifed person leaves the room.
Definition: Room.h:99
sigc::signal< void, Room *, Person * > Appearance
Definition: Room.h:96
Definition: Account.cpp:33
std::int64_t getNewSerialno()
operation serial number sequencing
Definition: Connection.cpp:390