1 #include <skstream/skstream.h>
2 
3 #ifdef HAVE_CONFIG_H
4     #include "config.h"
5 #endif
6 
7 #include "clientConnection.h"
8 #include "stubServer.h"
9 #include "objectSummary.h"
10 #include "testUtils.h"
11 #include "agent.h"
12 
13 #include <Eris/LogStream.h>
14 #include <Eris/Exceptions.h>
15 #include <Atlas/Objects/Encoder.h>
16 #include <Atlas/Objects/RootOperation.h>
17 #include <Atlas/Objects/Operation.h>
18 #include <Atlas/Codecs/Bach.h>
19 
20 #include <cstdio>
21 
22 #pragma warning(disable: 4068)  //unknown pragma
23 
24 using Atlas::Objects::Root;
25 using Atlas::Objects::smart_dynamic_cast;
26 using namespace Atlas::Objects::Operation;
27 using namespace Eris;
28 using Atlas::Objects::Entity::RootEntity;
29 
30 typedef Atlas::Objects::Entity::Account AtlasAccount;
31 
ClientConnection(StubServer * ss,int socket)32 ClientConnection::ClientConnection(StubServer* ss, int socket) :
33     m_stream(socket),
34     m_server(ss),
35     m_codec(NULL),
36     m_encoder(NULL)
37 {
38     m_acceptor = new Atlas::Net::StreamAccept("Eris Stub Server", m_stream);
39     m_acceptor->poll(false);
40 }
41 
~ClientConnection()42 ClientConnection::~ClientConnection()
43 {
44     for (AgentMap::iterator A=m_agents.begin(); A != m_agents.end(); ++A) {
45         delete A->second;
46     }
47 
48     delete m_encoder;
49     delete m_acceptor;
50     delete m_codec;
51 }
52 
poll()53 void ClientConnection::poll()
54 {
55     if (m_stream.eof()) {
56         fail();
57         return;
58     }
59 
60     if (m_acceptor)
61     {
62         negotiate();
63     }
64     else
65     {
66         m_codec->poll();
67 
68         while (!m_objDeque.empty())
69         {
70             RootOperation op = smart_dynamic_cast<RootOperation>(m_objDeque.front());
71             if (!op.isValid())
72                 throw InvalidOperation("ClientConnection recived something that isn't an op");
73 
74             dispatch(op);
75             m_objDeque.pop_front();
76         }
77     }
78 }
79 
isDisconnected()80 bool ClientConnection::isDisconnected()
81 {
82     return !m_stream.is_open();
83 }
84 
findAgentForEntity(const std::string & eid) const85 Agent* ClientConnection::findAgentForEntity(const std::string& eid) const
86 {
87     AgentMap::const_iterator it = m_agents.find(eid);
88     if (it != m_agents.end()) return it->second;
89     return NULL;
90 }
91 
shutdown()92 void ClientConnection::shutdown()
93 {
94     m_stream.shutdown();
95 }
96 
97 #pragma mark -
98 // basic Atlas connection / stream stuff
99 
fail()100 void ClientConnection::fail()
101 {
102     m_stream.close();
103 }
104 
negotiate()105 void ClientConnection::negotiate()
106 {
107     m_acceptor->poll();
108 
109     switch (m_acceptor->getState()) {
110     case Atlas::Net::StreamAccept::IN_PROGRESS:
111         break;
112 
113     case Atlas::Net::StreamAccept::FAILED:
114         error() << "ClientConnection got Atlas negotiation failure";
115         fail();
116         break;
117 
118     case Atlas::Net::StreamAccept::SUCCEEDED:
119         m_codec = m_acceptor->getCodec(*this);
120         m_encoder = new Atlas::Objects::ObjectsEncoder(*m_codec);
121         m_codec->streamBegin();
122 
123         delete m_acceptor;
124         m_acceptor = NULL;
125         break;
126 
127     default:
128         throw InvalidOperation("unknown state from Atlas StreamAccept in ClientConnection::negotiate");
129     }
130 }
131 
objectArrived(const Root & obj)132 void ClientConnection::objectArrived(const Root& obj)
133 {
134 /*
135     std::stringstream debugStream;
136     Atlas::Codecs::Bach debugCodec(debugStream, NULL);
137     Atlas::Objects::ObjectsEncoder debugEncoder(debugCodec);
138     debugEncoder.streamObjectsMessage(obj);
139     debugStream << std::flush;
140 
141     std::cout << "received:" << debugStream.str() << std::endl;
142   */
143     m_objDeque.push_back(obj);
144 }
145 
146 #pragma mark -
147 // dispatch / decode logic
148 
dispatch(const RootOperation & op)149 void ClientConnection::dispatch(const RootOperation& op)
150 {
151     const std::vector<Root>& args = op->getArgs();
152 
153     if (op->getFrom().empty())
154     {
155         if (m_account.empty())
156         {
157             // not logged in yet
158             if (op->getClassNo() == LOGIN_NO) {
159                 processLogin(smart_dynamic_cast<Login>(op));
160                 return;
161             }
162 
163             if (op->getClassNo() == CREATE_NO) {
164                 assert(!args.empty());
165                 processAccountCreate(smart_dynamic_cast<Create>(op));
166                 return;
167             }
168         }
169 
170         if (op->getClassNo() == GET_NO) {
171             processAnonymousGet(smart_dynamic_cast<Get>(op));
172             return;
173         }
174 
175         if (op->getClassNo() == LOGOUT_NO) {
176             return;
177         }
178 
179         throw TestFailure("got anonymous op I couldn't dispatch");
180     }
181 
182     if (op->getFrom() == m_account) {
183         dispatchOOG(op);
184         return;
185     }
186 
187     if (m_agents.count(op->getFrom())) {
188         m_agents[op->getFrom()]->processOp(op);
189         return;
190     }
191 
192 
193     if (entityIsCharacter(op->getFrom())) {
194         // IG activation
195         activateCharacter(op->getFrom(), op);
196         return;
197     }
198 
199 
200     debug() << "totally failed to handle operation " << objectSummary(op);
201 }
202 
dispatchOOG(const RootOperation & op)203 void ClientConnection::dispatchOOG(const RootOperation& op)
204 {
205     switch (op->getClassNo()) {
206     case LOOK_NO:
207         processOOGLook(smart_dynamic_cast<Look>(op));
208         return;
209 
210     case MOVE_NO:
211         return;
212 
213     case TALK_NO: {
214         Talk tk = smart_dynamic_cast<Talk>(op);
215         processTalk(tk);
216         return;
217     }
218 
219     case LOGOUT_NO: {
220         Logout logout = smart_dynamic_cast<Logout>(op);
221 
222         Info logoutInfo;
223         logoutInfo->setArgs1(logout);
224         logoutInfo->setTo(m_account);
225         logoutInfo->setRefno(logout->getSerialno());
226         send(logoutInfo);
227         return;
228     }
229 
230     case CREATE_NO: {
231         const StringList& arg0Parents(op->getArgs().front()->getParents());
232         if (arg0Parents.front() == "__fail__") {
233             sendError("bad type for char creation", op);
234             return;
235         }
236 
237         if (arg0Parents.front() == "settler") {
238             createCharacter(op);
239             return;
240         }
241     }
242 
243     default:
244         error() << "clientConnection failed to handle OOG op";
245     } // of classNo switch
246 }
247 
processLogin(const Login & login)248 void ClientConnection::processLogin(const Login& login)
249 {
250     const std::vector<Root>& args = login->getArgs();
251     if (!args.front()->hasAttr("password") || !args.front()->hasAttr("username"))
252         throw InvalidOperation("got bad LOGIN op");
253 
254     std::string username = args.front()->getAttr("username").asString();
255     if (username == "_timeout_") {
256         // deliberately send nothing
257         return;
258     }
259 
260     AccountMap::const_iterator A = m_server->findAccountByUsername(username);
261     if (A  == m_server->m_accounts.end())
262     {
263         sendError("unknown account: " + username, login);
264         return;
265     }
266 
267     if (A->second->getPassword() != args.front()->getAttr("password").asString())
268     {
269         sendError("bad password", login);
270         return;
271     }
272 
273     // update the really important member variable
274     m_account = A->second->getId();
275 
276     Info loginInfo;
277     loginInfo->setArgs1(A->second);
278     loginInfo->setTo(m_account);
279     loginInfo->setRefno(login->getSerialno());
280     send(loginInfo);
281 
282     m_server->joinRoom(m_account, "_lobby");
283     m_server->resetWorld();
284 }
285 
processAccountCreate(const Create & cr)286 void ClientConnection::processAccountCreate(const Create& cr)
287 {
288     const std::vector<Root>& args = cr->getArgs();
289     if (args.empty()) {
290         sendError("missing account in create", cr);
291         return;
292     }
293 
294     // check for duplicate username
295     AtlasAccount acc = smart_dynamic_cast<AtlasAccount>(args.front());
296     if (!acc.isValid()) {
297         sendError("malformed account in create", cr);
298         return;
299     }
300 
301     AccountMap::const_iterator A = m_server->findAccountByUsername(acc->getUsername());
302     if (A  != m_server->m_accounts.end()) {
303         sendError("duplicate account: " + acc->getUsername(), cr);
304         return;
305     }
306 
307     m_account = std::string("_") + acc->getUsername() + "_123";
308     acc->setId(m_account);
309     m_server->m_accounts[m_account] = acc;
310 
311     Info createInfo;
312     createInfo->setArgs1(acc);
313     createInfo->setTo(m_account);
314     createInfo->setRefno(cr->getSerialno());
315     send(createInfo);
316 
317     m_server->joinRoom(m_account, "_lobby");
318     m_server->resetWorld();
319 }
320 
processOOGLook(const Look & lk)321 void ClientConnection::processOOGLook(const Look& lk)
322 {
323     const std::vector<Root>& args = lk->getArgs();
324     std::string lookTarget;
325 
326     if (args.empty()) {
327         lookTarget = "_lobby";
328     } else {
329         lookTarget = args.front()->getId();
330     }
331 
332     RootEntity thing;
333     if (m_server->m_accounts.count(lookTarget))
334     {
335         thing = m_server->m_accounts[lookTarget];
336         if (lookTarget != lk->getFrom())
337         {
338             // prune
339             thing->removeAttr("characters");
340             thing->removeAttr("password");
341         }
342     }
343     else if (m_server->m_world.count(lookTarget))
344     {
345         // ensure it's owned by the account, i.e in characters
346         if (!entityIsCharacter(lookTarget))
347         {
348             sendError("not allowed to look at that entity", lk);
349             return;
350         }
351 
352         thing = m_server->m_world[lookTarget];
353     }
354     else if (m_server->m_rooms.count(lookTarget))
355     {
356         // should check room view permissions?
357         thing = m_server->m_rooms[lookTarget];
358     } else {
359         // didn't find any entity with the id
360         sendError("processed OOG look for unknown entity " + lookTarget, lk);
361         return;
362     }
363 
364     Sight st;
365     st->setArgs1(thing);
366     st->setFrom(lookTarget);
367     st->setTo(lk->getFrom());
368     st->setRefno(lk->getSerialno());
369     send(st);
370 }
371 
processTalk(const Atlas::Objects::Operation::Talk & tk)372 void ClientConnection::processTalk(const Atlas::Objects::Operation::Talk& tk)
373 {
374     if (m_server->m_rooms.count(tk->getTo()))
375         return m_server->talkInRoom(tk, tk->getTo());
376 
377     if (m_server->m_accounts.count(tk->getTo())) {
378         ClientConnection* cc = m_server->getConnectionForAccount(tk->getTo());
379         if (!cc) {
380             sendError("oog chat: account is offline", tk);
381             return;
382         }
383 
384         Sound snd;
385         snd->setFrom(m_account);
386         snd->setArgs1(tk);
387         snd->setTo(cc->m_account);
388         cc->send(snd);
389         return;
390     }
391 
392     if (tk->getTo().empty()) {
393         // lobby chat
394         return m_server->talkInRoom(tk, "_lobby");
395     }
396 
397     sendError("bad TO for OOG TALK op: " + tk->getTo(), tk);
398 }
399 
processAnonymousGet(const Get & get)400 void ClientConnection::processAnonymousGet(const Get& get)
401 {
402     const std::vector<Root>& args = get->getArgs();
403     if (args.empty())
404     {
405         Info serverInfo;
406         RootEntity svObj;
407 
408         Atlas::Message::ListType prs;
409         prs.push_back("server");
410         svObj->setParentsAsList(prs);
411 
412         svObj->setName("Bob's StubServer");
413         svObj->setAttr("server", "stubserver");
414         svObj->setAttr("ruleset", "stub-world");
415         svObj->setAttr("uptime", 666.0);
416         svObj->setAttr("clients", 42);
417 
418         serverInfo->setArgs1(svObj);
419         send(serverInfo);
420     } else {
421         std::string typeName = args.front()->getId();
422 
423         if (m_server->m_types.count(typeName))
424         {
425             Info typeInfo;
426             typeInfo->setArgs1(m_server->m_types[typeName]);
427             typeInfo->setRefno(get->getSerialno());
428             send(typeInfo);
429         } else
430             sendError("unknown type " + typeName, get);
431     }
432 }
433 
activateCharacter(const std::string & charId,const RootOperation & op)434 void ClientConnection::activateCharacter(const std::string& charId, const RootOperation& op)
435 {
436     // special magic testing IDs
437     if (charId == "_fail_") {
438         sendError("deliberate", op);
439         return;
440     }
441 
442     assert(entityIsCharacter(charId));
443     //debug() << "activation, inbound op's serial is " << op->getSerialno();
444 
445     if (m_agents.count(charId)) {
446         sendError("duplicate character action", op);
447         return;
448     }
449 
450     Agent* ag = new Agent(this, charId);
451     m_agents[charId] = ag;
452 
453     Info info;
454     info->setArgs1(m_server->m_world[charId]);
455     info->setFrom(charId);
456     info->setTo(m_account); // I *think* this is right
457     info->setRefno(op->getSerialno());
458 
459     send(info);
460     ag->processOp(op); // process as normal
461 }
462 
createCharacter(const RootOperation & op)463 void ClientConnection::createCharacter(const RootOperation& op)
464 {
465     static unsigned int charCounter = 0;
466     char charId[64];
467     ::snprintf(charId, 64, "_customChar_%d", ++charCounter);
468 
469     RootEntity ent = smart_dynamic_cast<RootEntity>(op->getArgs().front());
470 
471     ent->setId(charId);
472     ent->setLoc("_world");
473     m_server->m_world[charId] = ent;
474 
475     StringList children(m_server->m_world["_world"]->getContains());
476     children.push_back(charId);
477     m_server->m_world["_world"]->setContains(children);
478 
479     Agent* ag = new Agent(this, charId);
480     ag->setEntityVisible(charId, true);
481 
482     m_agents[charId] = ag;
483 
484     Info info;
485     info->setArgs1(m_server->m_world[charId]);
486     info->setFrom(charId);
487     info->setTo(m_account); // I *think* this is right
488     info->setRefno(op->getSerialno());
489 
490     send(info);
491 }
492 
493 #pragma mark -
494 
sendError(const std::string & msg,const RootOperation & op)495 void ClientConnection::sendError(const std::string& msg, const RootOperation& op)
496 {
497     Error errOp;
498 
499     errOp->setRefno(op->getSerialno());
500     errOp->setTo(op->getFrom());
501 
502     Root arg0;
503     arg0->setAttr("message", msg);
504 
505     std::vector<Root>& args = errOp->modifyArgs();
506     args.push_back(arg0);
507     args.push_back(op);
508 
509     send(errOp);
510 }
511 
send(const Root & obj)512 void ClientConnection::send(const Root& obj)
513 {
514     m_encoder->streamObjectsMessage(obj);
515     m_stream << std::flush;
516 }
517 
entityIsCharacter(const std::string & id)518 bool ClientConnection::entityIsCharacter(const std::string& id)
519 {
520     assert(!m_account.empty());
521     AtlasAccount p = m_server->m_accounts[m_account];
522     StringSet characters(p->getCharacters().begin(),  p->getCharacters().end());
523 
524     return characters.count(id);
525 }
526 
operator <<(std::ostream & io,const objectSummary & summary)527 std::ostream& operator<<(std::ostream& io, const objectSummary& summary)
528 {
529     const StringList& parents = summary.m_obj->getParents();
530     if (parents.size() == 0)
531     {
532         io << "un-typed object";
533     } else {
534         io << parents.front();
535     }
536 
537     return io;
538 }
539