eris 1.4.0
A WorldForge client library.
Avatar.cpp
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include "Avatar.h"
6#include "Entity.h"
7#include "Connection.h"
8#include "Log.h"
9#include "View.h"
10#include "IGRouter.h"
11#include "Account.h"
12#include "Exceptions.h"
13#include "TypeService.h"
14#include "Response.h"
15#include "EventService.h"
16#include "TypeInfo.h"
17
18#include <wfmath/atlasconv.h>
19#include <sigc++/slot.h>
20
21#include <Atlas/Objects/Operation.h>
22#include <Atlas/Objects/Entity.h>
23#include <Atlas/Objects/Anonymous.h>
24
25using namespace Atlas::Objects::Operation;
26using Atlas::Objects::Root;
27using Atlas::Objects::Entity::Anonymous;
28using WFMath::CoordType;
29using WFMath::TimeStamp;
30using WFMath::numeric_constants;
31using WFMath::TimeStamp;
32using namespace Atlas::Message;
33using Atlas::Objects::smart_dynamic_cast;
34
35namespace Eris {
36
37 Avatar::Avatar(Account &pl, std::string mindId, std::string entityId) :
38 m_account(pl),
39 m_mindId(std::move(mindId)),
40 m_entityId(std::move(entityId)),
41 m_entity(nullptr),
42 m_stampAtLastOp(TimeStamp::now()),
43 m_lastOpTime(0.0),
44 m_view(new View(*this)),
45 m_router(new IGRouter(*this, *m_view)),
46 m_isAdmin(false),
47 m_logoutTimer(nullptr) {
48 m_account.getConnection().getTypeService().setTypeProviderId(m_mindId);
49 m_entityAppearanceCon= m_view->notifyWhenEntitySeen(m_entityId, sigc::mem_fun(this, &Avatar::onEntityAppear));
50
51 //Start by requesting general entity data from the server.
52 m_view->getEntityFromServer("");
53 //And then our specific entity.
54 m_view->getEntityFromServer(m_entityId);
55 }
56
57 Avatar::~Avatar() {
58 m_entityParentDeletedConnection.disconnect();
59 m_avatarEntityDeletedConnection.disconnect();
60 m_account.getConnection().getTypeService().setTypeProviderId("");
61 for (auto &entry : m_activeContainers) {
62 if (entry.second) {
63 auto entityRef = *entry.second;
64 if (entityRef) {
65 ContainerClosed(*entityRef);
66 }
67 }
68 }
69 }
70
71 void Avatar::deactivate() {
72 //Send a Logout op from the Account, with the avatar mind as entity reference.
73 Logout l;
74 Anonymous arg;
75 arg->setId(m_mindId);
76 l->setArgs1(arg);
77 l->setSerialno(getNewSerialno());
78 l->setFrom(m_account.getId());
79
80 getConnection().getResponder().await(l->getSerialno(), this, &Avatar::logoutResponse);
81 getConnection().send(l);
82 m_logoutTimer = std::make_unique<TimedEvent>(getConnection().getEventService(), std::chrono::seconds(5),
83 [&]() {
84 warning()
85 << "Did not receive logout response after five seconds; forcing Avatar logout.";
86 m_account.destroyAvatar(getId());
87 });
88 }
89
90 void Avatar::touch(Entity *e, const WFMath::Point<3> &pos) {
91 Touch touchOp;
92 touchOp->setFrom(m_mindId);
93
94 Anonymous what;
95 what->setId(e->getId());
96 if (pos.isValid()) {
97 what->setPosAsList(Atlas::Message::Element(pos.toAtlas()).asList());
98 }
99 touchOp->setArgs1(what);
100
101 getConnection().send(touchOp);
102 }
103
104 void Avatar::wield(Eris::Entity *entity, std::string attachPoint) const {
105
106
107 Atlas::Objects::Entity::Anonymous arguments;
108 if (entity) {
109 arguments->setId(entity->getId());
110 }
111 arguments->setAttr("attachment", std::move(attachPoint));
112 Atlas::Objects::Operation::Wield wield;
113 wield->setFrom(getId());
114 wield->setArgs1(arguments);
115
116 getConnection().send(wield);
117
118 }
119
120 void Avatar::say(const std::string &msg) {
121 Talk t;
122
123 Anonymous what;
124 what->setAttr("say", msg);
125 t->setArgs1(what);
126 t->setFrom(m_mindId);
127
128 getConnection().send(t);
129 }
130
131 void Avatar::sayTo(const std::string &message, const std::vector<std::string> &entities) {
132 Talk t;
133
134 Anonymous what;
135 what->setAttr("say", message);
136 Atlas::Message::ListType addressList;
137 for (const auto &entity : entities) {
138 addressList.emplace_back(entity);
139 }
140 what->setAttr("address", addressList);
141 t->setArgs1(what);
142 t->setFrom(m_mindId);
143
144 getConnection().send(t);
145 }
146
147
148 void Avatar::emote(const std::string &em) {
149 Imaginary im;
150
151 Anonymous emote;
152 emote->setId("emote");
153 emote->setAttr("description", em);
154
155 im->setArgs1(emote);
156 im->setFrom(m_mindId);
157 im->setSerialno(getNewSerialno());
158
159 getConnection().send(im);
160 }
161
162 void Avatar::moveToPoint(const WFMath::Point<3> &pos, const WFMath::Quaternion &orient) {
163 Anonymous what;
164 what->setLoc(m_entity->getLocation()->getId());
165 what->setId(m_entityId);
166 if (pos.isValid()) {
167 what->setAttr("pos", pos.toAtlas());
168 }
169 if (orient.isValid()) {
170 what->setAttr("orientation", orient.toAtlas());
171 }
172
173 Move moveOp;
174 moveOp->setFrom(m_mindId);
175 moveOp->setArgs1(what);
176
177 getConnection().send(moveOp);
178 }
179
180
181 void Avatar::moveInDirection(const WFMath::Vector<3> &vel, const WFMath::Quaternion &orient) {
182 Anonymous arg;
183 if (vel.isValid()) {
184 arg->setAttr("_propel", vel.toAtlas());
185 }
186 if (orient.isValid()) {
187 arg->setAttr("_direction", orient.toAtlas());
188 }
189 arg->setId(m_entityId);
190
191 Set setOp;
192 setOp->setFrom(m_mindId);
193 setOp->setArgs1(arg);
194
195 getConnection().send(setOp);
196 }
197
198 void Avatar::place(const Entity *entity,
199 const Entity *container,
200 const WFMath::Point<3> &pos,
201 const WFMath::Quaternion &orientation,
202 boost::optional<float> offset,
203 int amount) {
204 Anonymous what;
205 what->setLoc(container->getId());
206 if (pos.isValid()) {
207 what->setPosAsList(Atlas::Message::Element(pos.toAtlas()).asList());
208 }
209 if (orientation.isValid()) {
210 what->setAttr("orientation", orientation.toAtlas());
211 }
212 if (offset) {
213 what->setAttr("planted-offset", offset.get());
214 }
215 if (amount != 1) {
216 what->setAttr("amount", amount);
217 }
218
219 what->setId(entity->getId());
220
221 Move moveOp;
222 moveOp->setFrom(m_mindId);
223 moveOp->setArgs1(what);
224
225 //if the avatar is an admin, we will set the TO property
226 //this will bypass all of the server's filtering, allowing us to place any
227 //entity, unrelated to if it's too heavy or belong to someone else
228 if (getIsAdmin()) {
229 moveOp->setTo(entity->getId());
230 }
231
232 getConnection().send(moveOp);
233
234 }
235
237 Use use;
238 use->setFrom(m_mindId);
239 getConnection().send(use);
240 }
241
242 void Avatar::onEntityAppear(Entity *ent) {
243 if (ent->getId() == m_entityId) {
244 assert(m_entity == nullptr);
245 m_entity = ent;
246
247 //Since the avatar entity is special we need to protect it from being deleted.
248 //Normally when the parent entity is deleted, all child entities will be deleted (in cascading order).
249 //However, for the avatar entity we'll listen for when our parent is deleted and detach ourselves from it.
250 //This works by relying on the server later on sending information about the avatar entity's new
251 //surroundings.
252 auto entityParentDeletedFn = [this, ent](){
253 //remove ourselves from the parent before it's deleted
254 ent->setLocation(nullptr);
255 m_entityParentDeletedConnection.disconnect();
256 };
257
258 ent->LocationChanged.connect([this, ent, entityParentDeletedFn](Entity* oldParent){
259 m_entityParentDeletedConnection.disconnect();
260 if (ent->getLocation()) {
261 m_entityParentDeletedConnection = ent->getLocation()->BeingDeleted.connect(entityParentDeletedFn);
262 }
263 });
264 if (ent->getLocation()) {
265 m_entityParentDeletedConnection = ent->getLocation()->BeingDeleted.connect(entityParentDeletedFn);
266 }
267
268 m_avatarEntityDeletedConnection = ent->BeingDeleted.connect(
269 sigc::mem_fun(this, &Avatar::onAvatarEntityDeleted));
270
271 //Check if we're admin before we announce ourselves to the world.
272 ent->observe("is_admin",
273 [this](const Atlas::Message::Element &elem) {
274 if (elem.isInt() && elem.asInt() != 0) {
275 setIsAdmin(true);
276 } else {
277 setIsAdmin(false);
278 }
279 },
280 true);
281
282 GotCharacterEntity.emit(ent);
283 m_entityAppearanceCon.disconnect(); // stop listening to View::Appearance
284
285 //Refresh type info, since we now have the possibility to get protected attributes.
286 auto parentType = ent->getType();
287 while (parentType) {
288 parentType->refresh();
289 parentType = parentType->getParent();
290 }
291
292 //The container system relies on the "_containers_active" property to define what containers the
293 //avatar entity currenty is interacting with.
294 ent->observe("_containers_active",
295 [this](const Atlas::Message::Element &elem) { containerActiveChanged(elem); },
296 true);
297
298 }
299 }
300
303 m_entity = nullptr;
304 //When the avatar entity is destroyed we should also deactivate the character.
305 deactivate();
306 }
307
308 void Avatar::onTransferRequested(const TransferInfo &transfer) {
309 TransferRequested.emit(transfer);
310 }
311
312 Connection &Avatar::getConnection() const {
313 return m_account.getConnection();
314 }
315
317 WFMath::TimeDiff deltaT = TimeStamp::now() - m_stampAtLastOp;
318 return m_lastOpTime + ((double)deltaT.milliseconds() / 1000.0);
319 }
320
321 void Avatar::updateWorldTime(double seconds) {
322 m_stampAtLastOp = TimeStamp::now();
323 m_lastOpTime = seconds;
324 }
325
326 void Avatar::logoutResponse(const RootOperation &op) {
327 if (!op->instanceOf(INFO_NO)) {
328 warning() << "received an avatar logout response that is not an INFO";
329 return;
330 }
331
332 const std::vector<Root> &args(op->getArgs());
333
334 if (args.empty() || (args.front()->getClassNo() != LOGOUT_NO)) {
335 warning() << "argument of avatar logout INFO is not a logout op";
336 return;
337 }
338
339 RootOperation logout = smart_dynamic_cast<RootOperation>(args.front());
340 const std::vector<Root> &args2(logout->getArgs());
341 if (args2.empty()) {
342 warning() << "argument of avatar INFO(LOGOUT) is empty";
343 return;
344 }
345
346 std::string charId = args2.front()->getId();
347 debug() << "got logout for character " << charId;
348 if (charId != m_mindId) {
349 error() << "got logout for character " << charId
350 << " that is not this avatar " << m_mindId;
351 return;
352 }
353
354 m_account.destroyAvatar(getId());
355 }
356
357 void Avatar::containerActiveChanged(const Atlas::Message::Element &element) {
358 std::set<std::string> entityIdSet;
359 if (element.isList()) {
360 auto &entityList = element.List();
361 for (auto &entry: entityList) {
362 if (entry.isString()) {
363 entityIdSet.insert(entry.String());
364 }
365 }
366 }
367 for (auto I = m_activeContainers.begin(); I != m_activeContainers.end();) {
368 auto &entry = *I;
369 if (entityIdSet.find(entry.first) == entityIdSet.end()) {
370 if (I->second) {
371 auto &entityRef = *I->second;
372 if (entityRef) {
373 ContainerClosed(*entityRef);
374 }
375 }
376 I = m_activeContainers.erase(I);
377 } else {
378 entityIdSet.erase(I->first);
379 ++I;
380 }
381 }
382
383 for (auto &id : entityIdSet) {
384 auto ref = std::make_unique<EntityRef>(*m_view, id);
385 auto refInstance = ref.get();
386 if (*refInstance) {
387 ContainerOpened(**refInstance);
388 ref->Changed.connect([this](Entity *newEntity, Entity *oldEntity) {
389 if (!newEntity) {
390 //Guaranteed to be an instance.
391 ContainerClosed(*oldEntity);
392 }
393 });
394 } else {
395 ref->Changed.connect([this](Entity *newEntity, Entity *oldEntity) {
396 if (newEntity) {
397 ContainerOpened(*newEntity);
398 } else {
399 //Guaranteed to be an instance.
400 ContainerClosed(*oldEntity);
401 }
402 });
403 }
404 m_activeContainers.emplace(id, std::move(ref));
405 }
406 }
407
408
410 m_account.destroyAvatar(getId());
411 }
412
413 void Avatar::logoutRequested(const TransferInfo &transferInfo) {
414 onTransferRequested(transferInfo);
415 m_account.destroyAvatar(getId());
416 }
417
418 void Avatar::setIsAdmin(bool isAdmin) {
419 m_isAdmin = isAdmin;
420 }
421
422 bool Avatar::getIsAdmin() const {
423 return m_isAdmin;
424 }
425
426 void Avatar::send(const Atlas::Objects::Operation::RootOperation &op) {
427 op->setFrom(m_mindId);
428 m_account.getConnection().send(op);
429 }
430
431
432} // of namespace Eris
Encapsulates all the state of an Atlas Account, and methods that operation on that state.
Definition: Account.h:42
void destroyAvatar(const std::string &avatarId)
Destroys the avatar with the specified id, if available.
Definition: Account.cpp:422
const std::string & getId() const
returns the account ID if logged in
Definition: Account.h:325
Connection & getConnection() const
Access the underlying Connection for this account.
Definition: Account.h:338
void logoutRequested()
Called when a logout of the avatar has been requested by the server.
Definition: Avatar.cpp:409
bool getIsAdmin() const
Gets whether the current avatar is an admin character.
Definition: Avatar.cpp:422
void updateWorldTime(double t)
Definition: Avatar.cpp:321
double getWorldTime()
Definition: Avatar.cpp:316
Avatar(Account &pl, std::string mindId, std::string entityId)
Definition: Avatar.cpp:37
void moveToPoint(const WFMath::Point< 3 > &, const WFMath::Quaternion &orient)
Have the character move towards a position. Any non-valid data will not be sent.
Definition: Avatar.cpp:162
void setIsAdmin(bool isAdmin)
Sets whether the current avatar is an admin character.
Definition: Avatar.cpp:418
void onAvatarEntityDeleted()
Called when the avatar entity is deleted.
Definition: Avatar.cpp:301
void sayTo(const std::string &message, const std::vector< std::string > &entities)
Definition: Avatar.cpp:131
sigc::signal< void > CharacterEntityDeleted
Definition: Avatar.h:175
sigc::signal< void, const TransferInfo & > TransferRequested
Definition: Avatar.h:185
void place(const Entity *entity, const Entity *container, const WFMath::Point< 3 > &pos=WFMath::Point< 3 >(), const WFMath::Quaternion &orientation=WFMath::Quaternion(), boost::optional< float > offset=boost::none, int amount=1)
Place an entity inside another one.
Definition: Avatar.cpp:198
void moveInDirection(const WFMath::Vector< 3 > &, const WFMath::Quaternion &)
Set the character's velocity and orientation. Any non-valid data will not be sent.
Definition: Avatar.cpp:181
void useStop()
Stop the current task, if one is in progress.
Definition: Avatar.cpp:236
void send(const Atlas::Objects::Operation::RootOperation &op)
Sends an operation from this Avatar.
Definition: Avatar.cpp:426
void touch(Entity *, const WFMath::Point< 3 > &pos)
Touch an entity.
Definition: Avatar.cpp:90
void say(const std::string &)
Say something (in-game)
Definition: Avatar.cpp:120
sigc::signal< void, Entity * > GotCharacterEntity
Definition: Avatar.h:170
const std::string & getId() const
Get the Mind id of this Avatar. All interaction with the entity goes through the Mind.
Definition: Avatar.h:250
void emote(const std::string &)
Emote something (in-game)
Definition: Avatar.cpp:148
virtual void send(const Atlas::Objects::Root &obj)
Transmit an Atlas::Objects instance to the server.
Definition: Connection.cpp:160
Entity is a concrete (instantiable) class representing one game entity.
Definition: Entity.h:56
TypeInfo * getType() const
Gets the type of this entity.
Definition: Entity.h:650
sigc::signal< void, Entity * > LocationChanged
Signal that the entity's container changed.
Definition: Entity.h:300
void setLocation(Entity *newLocation, bool removeFromOldLocation=true)
Definition: Entity.cpp:644
const std::string & getId() const
Retrieve the unique entity ID.
Definition: Entity.h:635
sigc::signal< void > BeingDeleted
Definition: Entity.h:365
Entity * getLocation() const
The containing entity, or null if this is a top-level visible entity.
Definition: Entity.h:656
sigc::connection observe(const std::string &propertyName, const PropertyChangedSlot &aslot, bool evaluateNow)
Setup an observer so that the specified slot is fired when the named property's value changes.
Definition: Entity.cpp:196
void refresh()
Request update to the type info from the server.
Definition: TypeInfo.cpp:300
void setTypeProviderId(std::string id)
Set another provider of type data than the connection.
Definition: Account.cpp:33
std::int64_t getNewSerialno()
operation serial number sequencing
Definition: Connection.cpp:390