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