eris  1.4.0
A WorldForge client library.
IGRouter.cpp
1 #ifdef HAVE_CONFIG_H
2  #include "config.h"
3 #endif
4 
5 #include "IGRouter.h"
6 #include "Avatar.h"
7 #include "Connection.h"
8 #include "View.h"
9 #include "Entity.h"
10 #include "LogStream.h"
11 #include "TypeInfo.h"
12 #include "TypeBoundRedispatch.h"
13 #include "TransferInfo.h"
14 #include "TypeService.h"
15 
16 #include <Atlas/Objects/Operation.h>
17 #include <Atlas/Objects/Entity.h>
18 
19 using namespace Atlas::Objects::Operation;
20 using Atlas::Objects::Root;
21 using Atlas::Objects::Entity::RootEntity;
22 using Atlas::Objects::smart_dynamic_cast;
23 using Atlas::Message::Element;
24 
25 namespace Eris {
26 
27 IGRouter::IGRouter(Avatar& av, View& view) :
28  m_avatar(av),
29  m_view(view)
30 {
31  m_avatar.getConnection().registerRouterForTo(this, m_avatar.getEntityId());
32  m_actionType = m_avatar.getConnection().getTypeService().getTypeByName("action");
33 }
34 
35 IGRouter::~IGRouter()
36 {
37  m_avatar.getConnection().unregisterRouterForTo(this, m_avatar.getEntityId());
38 }
39 
40 Router::RouterResult IGRouter::handleOperation(const RootOperation& op)
41 {
42  if (!op->isDefaultSeconds()) {
43  // grab out world time
44  m_avatar.updateWorldTime(op->getSeconds());
45  }
46 
47  const std::vector<Root>& args = op->getArgs();
48 
49  if (op->getClassNo() == SIGHT_NO) {
50  if (args.empty()) {
51  warning() << "Avatar received sight with empty args";
52  return IGNORED;
53  }
54 
55  for (const auto& arg : args) {
56  if (arg->instanceOf(ROOT_OPERATION_NO)) {
57  handleSightOp(op, smart_dynamic_cast<RootOperation>(arg));
58  } else {
59  // initial sight of entities
60  RootEntity gent = smart_dynamic_cast<RootEntity>(arg);
61  if (gent.isValid()) {
62  // View needs a bound TypeInfo for the entity
63  if (!gent->isDefaultId() && !gent->isDefaultParent()) {
64  TypeInfo* ty = m_avatar.getConnection().getTypeService().getTypeForAtlas(gent);
65  if (!ty->isBound()) {
66  auto opCopy = op.copy();
67  opCopy->setArgs1(arg);
68  new TypeBoundRedispatch(m_avatar.getConnection(), opCopy, ty);
69  } else {
70  m_view.sight(gent);
71  }
72  }
73  }
74  }
75  }
76 
77  return HANDLED;
78 
79 
80  }
81 
82  if (op->getClassNo() == APPEARANCE_NO) {
83  for (const auto& arg : args) {
84  double stamp = -1;
85  if (!arg->isDefaultStamp()) {
86  stamp = arg->getStamp();
87  }
88 
89  if (!arg->isDefaultId()) {
90  m_view.appear(arg->getId(), stamp);
91  }
92  }
93 
94  return HANDLED;
95  }
96 
97  if (op->getClassNo() == DISAPPEARANCE_NO) {
98  for (const auto& arg : args) {
99  if (!arg->isDefaultId()) {
100  m_view.disappear(arg->getId());
101  }
102  }
103 
104  return HANDLED;
105  }
106 
107  if (op->getClassNo() == UNSEEN_NO)
108  {
109  if (args.empty()) {
110  warning() << "Avatar received unseen with empty args";
111  return IGNORED;
112  }
113  for (const auto& arg : args) {
114  if (!arg->isDefaultId()) {
115  m_view.unseen(arg->getId());
116  }
117  }
118  return HANDLED;
119  }
120 
121  // logout
122  if (op->getClassNo() == LOGOUT_NO) {
123  debug() << "Avatar received forced logout from server";
124 
125  if(args.size() >= 2) {
126  bool gotArgs = true;
127  // Teleport logout op. The second attribute is the payload for the teleport host data.
128  const Root & arg = args[1];
129  Element tp_host_attr;
130  Element tp_port_attr;
131  Element pkey_attr;
132  Element pentity_id_attr;
133  if(arg->copyAttr("teleport_host", tp_host_attr) != 0
134  || !tp_host_attr.isString()) {
135  debug() << "No teleport host specified. Doing normal logout."
136  << std::endl << std::flush;
137  gotArgs = false;
138  } else if (arg->copyAttr("teleport_port", tp_port_attr) != 0
139  || !tp_port_attr.isInt()) {
140  debug() << "No teleport port specified. Doing normal logout."
141  << std::endl << std::flush;
142  gotArgs = false;
143  } else if (arg->copyAttr("possess_key", pkey_attr) != 0
144  || !pkey_attr.isString()) {
145  debug() << "No possess key specified. Doing normal logout."
146  << std::endl << std::flush;
147  gotArgs = false;
148  } else if (arg->copyAttr("possess_entity_id", pentity_id_attr) != 0
149  || !pentity_id_attr.isString()) {
150  debug() << "No entity ID specified. Doing normal logout."
151  << std::endl << std::flush;
152  gotArgs = false;
153  }
154 
155  // Extract argument data and request transfer only if we
156  // succeed in extracting them all
157  if (gotArgs) {
158  std::string teleport_host = tp_host_attr.String();
159  int teleport_port = static_cast<int>(tp_port_attr.Int());
160  std::string possess_key = pkey_attr.String();
161  std::string possess_entity_id = pentity_id_attr.String();
162  debug() << "Server transfer data: Host: " << teleport_host
163  << ", Port: " << teleport_port << ", "
164  << "Key: " << possess_key << ", "
165  << "ID: " << possess_entity_id << std::endl << std::flush;
166  // Now do a transfer request
167  TransferInfo transfer(teleport_host, teleport_port, possess_key
168  , possess_entity_id);
169  m_avatar.logoutRequested(transfer);
170  } else {
171  m_avatar.logoutRequested();
172  }
173 
174  } else {
175  // Regular force logout op
176  m_avatar.logoutRequested();
177  }
178 
179  return HANDLED;
180  }
181 
182  return IGNORED;
183 }
184 
185 Router::RouterResult IGRouter::handleSightOp(const RootOperation& sightOp, const RootOperation& op)
186 {
187  const auto& args = op->getArgs();
188 
189  // because a SET op can potentially (legally) update multiple entities,
190  // we decode it here, not in the entity router
191  if (op->getClassNo() == SET_NO) {
192  for (const auto& arg : args) {
193  if (!arg->isDefaultId()) {
194  auto ent = m_view.getEntity(arg->getId());
195  if (!ent) {
196  if (m_view.isPending(arg->getId())) {
197  /* no-op, we'll get the state later */
198  } else {
199  m_view.sendLookAt(arg->getId());
200  }
201 
202  continue; // we don't have it, ignore
203  }
204 
205  //If we get a SET op for an entity that's not visible, it means that the entity has moved
206  //within our field of vision without sending an Appear op first. We should treat this as a
207  //regular Appear op and issue a Look op back, to get more info.
208  if (!ent->isVisible()) {
209 // float stamp = -1;
210 // if (!arg->isDefaultStamp()) {
211 // stamp = static_cast<float>(arg->getStamp());
212 // }
213 //
214 // m_view.appear(arg->getId(), stamp);
215  m_view.getEntityFromServer(arg->getId());
216  } else {
217  ent->setFromRoot(arg, false);
218  }
219  }
220  }
221  return HANDLED;
222  }
223 
224  if (!op->isDefaultParent()) {
225  // we have to handle generic 'actions' late, to avoid trapping interesting
226  // such as create or divide
227  TypeInfo* ty = m_avatar.getConnection().getTypeService().getTypeForAtlas(op);
228  if (!ty->isBound()) {
229  new TypeBoundRedispatch(m_avatar.getConnection(), sightOp, ty);
230  return HANDLED;
231  }
232 
233  //For hits we want to check the "to" field rather than the "from" field. We're more interested in
234  //the entity that was hit than the one which did the hitting.
235  //Note that we'll let the op fall through, so that we later on handle the Hit action for the "from" entity.
236  if (op->getClassNo() == HIT_NO) {
237  if (!op->isDefaultTo()) {
238  Entity* ent = m_view.getEntity(op->getTo());
239  if (ent) {
240  ent->onHit(smart_dynamic_cast<Hit>(op), *ty);
241  }
242  } else {
243  warning() << "received hit with TO unset";
244  }
245  }
246 
247 
248  if (ty->isA(m_actionType)) {
249  if (op->isDefaultFrom()) {
250  warning() << "received op " << ty->getName() << " with FROM unset";
251  return HANDLED;
252  }
253 
254  Entity* ent = m_view.getEntity(op->getFrom());
255  if (ent) {
256  ent->onAction(op, *ty);
257  }
258 
259  return HANDLED;
260  }
261  }
262 
263  return IGNORED;
264 }
265 
266 } // of namespace Eris
Eris::TypeService::getTypeForAtlas
TypeInfo * getTypeForAtlas(const Atlas::Objects::Root &obj)
Definition: TypeService.cpp:77
Eris::Entity::onHit
virtual void onHit(const Atlas::Objects::Operation::Hit &hit, const TypeInfo &typeInfo)
Definition: Entity.cpp:337
Eris::Meta
Meta encapsulates the meta-game system, including the meta-server protocol and queries.
Definition: Metaserver.h:39
Eris::Avatar::updateWorldTime
void updateWorldTime(double t)
Definition: Avatar.cpp:321
Eris::View::getEntity
ViewEntity * getEntity(const std::string &eid) const
Definition: View.cpp:45
Eris::View::sendLookAt
void sendLookAt(const std::string &eid)
Definition: View.cpp:375
Eris
Definition: Account.cpp:33
Eris::View::isPending
bool isPending(const std::string &eid) const
test if the specified entity ID is pending initial sight on the View
Definition: View.cpp:336
Eris::Avatar::logoutRequested
void logoutRequested()
Called when a logout of the avatar has been requested by the server.
Definition: Avatar.cpp:409
Eris::Entity::onAction
virtual void onAction(const Atlas::Objects::Operation::RootOperation &act, const TypeInfo &typeInfo)
Definition: Entity.cpp:332