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 
25 using namespace Atlas::Objects::Operation;
26 using Atlas::Objects::Root;
27 using Atlas::Objects::Entity::Anonymous;
28 using WFMath::CoordType;
29 using WFMath::TimeStamp;
30 using WFMath::numeric_constants;
31 using WFMath::TimeStamp;
32 using namespace Atlas::Message;
33 using Atlas::Objects::smart_dynamic_cast;
34 
35 namespace 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
Eris::Account::getId
const std::string & getId() const
returns the account ID if logged in
Definition: Account.h:325
Eris::Avatar::send
void send(const Atlas::Objects::Operation::RootOperation &op)
Sends an operation from this Avatar.
Definition: Avatar.cpp:426
Eris::Entity::getLocation
Entity * getLocation() const
The containing entity, or null if this is a top-level visible entity.
Definition: Entity.h:656
Eris::Entity
Entity is a concrete (instantiable) class representing one game entity.
Definition: Entity.h:55
Eris::Avatar::moveInDirection
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
Eris::Avatar::touch
void touch(Entity *, const WFMath::Point< 3 > &pos)
Touch an entity.
Definition: Avatar.cpp:90
Eris::Avatar::getIsAdmin
bool getIsAdmin() const
Gets whether the current avatar is an admin character.
Definition: Avatar.cpp:422
Eris::TransferInfo
Definition: TransferInfo.h:16
Eris::Entity::observe
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
Eris::Avatar::emote
void emote(const std::string &)
Emote something (in-game)
Definition: Avatar.cpp:148
Eris::IGRouter
Definition: IGRouter.h:13
Eris::Avatar::getWorldTime
double getWorldTime()
Definition: Avatar.cpp:316
Eris::Avatar::updateWorldTime
void updateWorldTime(double t)
Definition: Avatar.cpp:321
Eris::Avatar::say
void say(const std::string &)
Say something (in-game)
Definition: Avatar.cpp:120
Eris::Avatar::useStop
void useStop()
Stop the current task, if one is in progress.
Definition: Avatar.cpp:236
Eris::Account
Encapsulates all the state of an Atlas Account, and methods that operation on that state.
Definition: Account.h:42
Eris::Entity::getType
TypeInfo * getType() const
Gets the type of this entity.
Definition: Entity.h:650
Eris::Entity::BeingDeleted
sigc::signal< void > BeingDeleted
Definition: Entity.h:365
Eris::Entity::getId
const std::string & getId() const
Retrieve the unique entity ID.
Definition: Entity.h:635
Eris::getNewSerialno
std::int64_t getNewSerialno()
operation serial number sequencing
Definition: Connection.cpp:390
Eris
Definition: Account.cpp:33
Eris::Avatar::sayTo
void sayTo(const std::string &message, const std::vector< std::string > &entities)
Definition: Avatar.cpp:131
Eris::Avatar::getId
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
Eris::Avatar::TransferRequested
sigc::signal< void, const TransferInfo & > TransferRequested
Definition: Avatar.h:185
Eris::Avatar::setIsAdmin
void setIsAdmin(bool isAdmin)
Sets whether the current avatar is an admin character.
Definition: Avatar.cpp:418
Eris::Avatar::onAvatarEntityDeleted
void onAvatarEntityDeleted()
Called when the avatar entity is deleted.
Definition: Avatar.cpp:301
Eris::TypeInfo::refresh
void refresh()
Request update to the type info from the server.
Definition: TypeInfo.cpp:300
Eris::Avatar::moveToPoint
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
Eris::Avatar::logoutRequested
void logoutRequested()
Called when a logout of the avatar has been requested by the server.
Definition: Avatar.cpp:409
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::destroyAvatar
void destroyAvatar(const std::string &avatarId)
Destroys the avatar with the specified id, if available.
Definition: Account.cpp:422
Eris::Entity::LocationChanged
sigc::signal< void, Entity * > LocationChanged
Signal that the entity's container changed.
Definition: Entity.h:300
Eris::Account::getConnection
Connection & getConnection() const
Access the underlying Connection for this account.
Definition: Account.h:338
Eris::Avatar::GotCharacterEntity
sigc::signal< void, Entity * > GotCharacterEntity
Definition: Avatar.h:170
Eris::warning
Definition: LogStream.h:55
Eris::TypeService::setTypeProviderId
void setTypeProviderId(std::string id)
Set another provider of type data than the connection.
Definition: TypeService.cpp:213
Eris::View
Definition: View.h:40
Eris::Entity::setLocation
void setLocation(Entity *newLocation, bool removeFromOldLocation=true)
Definition: Entity.cpp:644
Eris::Avatar::CharacterEntityDeleted
sigc::signal< void > CharacterEntityDeleted
Definition: Avatar.h:175
Eris::Avatar::place
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