void TrustedConnectionCreatorintegration::test_external_op_puppet_nonexistant() { // Dispatching a Talk external op from the creator, to the creator should // result in it being passed directly to the normal op dispatch, // shortcutting the world. m_creator->m_externalMind = new ExternalMind(*m_creator); m_creator->m_externalMind->linkUp(m_connection); Entity * other = new Entity(compose("%1", m_id_counter), m_id_counter++); other->setType(m_creatorType); m_server->m_world.addEntity(other); Atlas::Objects::Operation::Talk op; op->setFrom(m_creator->getId()); op->setTo(compose("%1", m_id_counter++)); m_connection->externalOperation(op, *m_connection); // Operation should be via world dispatch, as if it was from the Entity // we are puppeting. ASSERT_TRUE(m_Link_send_sent.isValid()); ASSERT_EQUAL(m_Link_send_sent->getParents().front(), "unseen"); ASSERT_TRUE(!m_Link_send_sent->isDefaultTo()); ASSERT_EQUAL(m_Link_send_sent->getTo(), m_creator->getId()); }
void ConnectionCharacterintegration::test_connected() { // Dispatching an external op from the character should have no effect // if the external mind is already in place. m_character->linkExternal(m_connection); ASSERT_NOT_NULL(m_character->m_externalMind); ExternalMind * em = dynamic_cast<ExternalMind*>(m_character->m_externalMind); ASSERT_NOT_NULL(em); ASSERT_TRUE(em->isLinked()); ASSERT_TRUE(em->isLinkedTo(m_connection)); Router * saved_em = m_character->m_externalMind; RootOperation op; op->setFrom(m_character->getId()); m_connection->externalOperation(op, *m_connection); ASSERT_TRUE(!m_Link_send_sent.isValid()); ASSERT_NOT_EQUAL(m_logEvent_logged, TAKE_CHAR); ASSERT_NOT_NULL(m_character->m_externalMind); ASSERT_EQUAL(m_character->m_externalMind, saved_em); em = dynamic_cast<ExternalMind*>(m_character->m_externalMind); ASSERT_NOT_NULL(em); ASSERT_TRUE(em->isLinked()); ASSERT_TRUE(em->isLinkedTo(m_connection)); }
void TrustedConnectionCreatorintegration::test_external_op_puppet() { // Dispatching a Talk external op from the creator, to the creator should // result in it being passed directly to the normal op dispatch, // shortcutting the world. m_creator->m_externalMind = new ExternalMind(*m_creator); m_creator->m_externalMind->linkUp(m_connection); Entity * other = new Entity(compose("%1", m_id_counter), m_id_counter++); other->setType(m_creatorType); m_server->m_world.addEntity(other); Atlas::Objects::Operation::Talk op; op->setFrom(m_creator->getId()); op->setTo(other->getId()); m_connection->externalOperation(op, *m_connection); // Operation should be via world dispatch, as if it was from the Entity // we are puppeting. ASSERT_TRUE(m_BaseWorld_message_called.isValid()); ASSERT_EQUAL(m_BaseWorld_message_called->getClassNo(), Atlas::Objects::Operation::TALK_NO); ASSERT_TRUE(!m_BaseWorld_message_called->isDefaultTo()); ASSERT_EQUAL(m_BaseWorld_message_called->getTo(), other->getId()); ASSERT_NOT_NULL(m_BaseWorld_message_called_from); ASSERT_EQUAL(m_BaseWorld_message_called_from, other); }
void World::RelayOperation(const Operation & op, OpVector & res) { //A Relay operation with refno sent to ourselves signals that we should prune //our registered relays in m_relays. This is a feature to allow for a timeout; if //no Relay has been received from the destination Entity after a certain period //we'll shut down the relay link. if (op->getTo() == getId() && op->getFrom() == getId() && !op->isDefaultRefno()) { auto I = m_relays.find(op->getRefno()); if (I != m_relays.end()) { //Send an empty operation to signal that the relay has expired. I->second.callback(Operation(), I->second.entityId); m_relays.erase(I); } } else { if (op->getArgs().empty()) { log(ERROR, "World::RelayOperation no args."); return; } Operation relayedOp = Atlas::Objects::smart_dynamic_cast<Operation>( op->getArgs().front()); if (!relayedOp.isValid()) { log(ERROR, "World::RelayOperation first arg is not an operation."); return; } //If a relay op has a refno, it's a response to a Relay op previously sent out to another //entity, and we should signal that we have an incoming relayed op. if (!op->isDefaultRefno()) { //Note that the relayed op should be considered untrusted in this case, as it has originated //from a random entity or its mind. auto I = m_relays.find(op->getRefno()); if (I == m_relays.end()) { log(WARNING, "World::RelayOperation could not find registrered Relay with refno."); return; } //Make sure that this op really comes from the entity the original Relay op was sent to. if (op->getFrom() != I->second.entityId) { log(WARNING, "World::RelayOperation got relay op with mismatching 'from'."); return; } //Get the relayed operation and call the callback. I->second.callback(relayedOp, I->second.entityId); m_relays.erase(I); } else { //Send it on to the basic Entity relay mechanism Entity::RelayOperation(op, res); } } }
/// \brief Handle a relay operation void Entity::RelayOperation(const Operation & op, OpVector & res) { if (op->getArgs().empty()) { log(ERROR, "Entity::RelayOperation no args."); return; } Operation relayedOp = Atlas::Objects::smart_dynamic_cast<Operation>( op->getArgs().front()); if (!relayedOp.isValid()) { log(ERROR, "Entity::RelayOperation first arg is not an operation."); return; } if (op->isDefaultSerialno()) { log(ERROR, "Entity::RelayOperation no serial number."); return; } //Add a sight of the operation Sight sight; sight->setArgs1(relayedOp); Atlas::Objects::Operation::Generic responseOp; responseOp->setType("relay", Atlas::Objects::Operation::RELAY_NO); responseOp->setArgs1(sight); responseOp->setTo(op->getFrom()); res.push_back(responseOp); //Make sure that the contained op is addressed to the entity relayedOp->setTo(getId()); operation(relayedOp, res); }
void ConnectionCharacterintegration::test_external_op() { // Dispatching a Talk external op from the character should result in // it being passed on to the world. m_character->linkExternal(m_connection); Atlas::Objects::Operation::Talk op; op->setFrom(m_character->getId()); m_connection->externalOperation(op, *m_connection); // BaseWorld::message should have been called from Enitty::sendWorld // with the Talk operation, modified to have TO set to the character. ASSERT_TRUE(m_BaseWorld_message_called.isValid()); ASSERT_EQUAL(m_BaseWorld_message_called->getClassNo(), Atlas::Objects::Operation::TALK_NO); ASSERT_TRUE(!m_BaseWorld_message_called->isDefaultTo()); ASSERT_EQUAL(m_BaseWorld_message_called->getTo(), m_character->getId()); ASSERT_NOT_NULL(m_BaseWorld_message_called_from); ASSERT_EQUAL(m_BaseWorld_message_called_from, m_character); }
/// \brief Add an operation to the ordered op queue. /// /// Any time adjustment required is made to the operation, and it /// is added to the apropriate place in the chronologically ordered /// queue. The From attribute of the operation is set to the id of /// the entity that is responsible for adding the operation to the /// queue. void OperationsDispatcher::addOperationToQueue(const Operation & op, LocatedEntity & ent) { assert(op.isValid()); assert(op->getFrom() != "cheat"); m_operation_queues_dirty = true; op->setFrom(ent.getId()); if (!op->hasAttrFlag(Atlas::Objects::Operation::FUTURE_SECONDS_FLAG)) { op->setSeconds(getTime()); m_immediateQueue.push(OpQueEntry(op, ent)); return; } double t = getTime() + (op->getFutureSeconds() * consts::time_multiplier); op->setSeconds(t); op->setFutureSeconds(0.); m_operationQueue.push(OpQueEntry(op, ent)); if (debug_flag) { std::cout << "WorldRouter::addOperationToQueue {" << std::endl; debug_dump(op, std::cout); std::cout << "}" << std::endl << std::flush; } }
void ConnectionCharacterintegration::test_connect_up() { // Dispatching an external op from the character should cause it to // get connected up with an external mind RootOperation op; op->setFrom(m_character->getId()); ASSERT_NULL(m_character->m_externalMind); m_connection->externalOperation(op, *m_connection); ASSERT_NOT_NULL(m_character->m_externalMind); ExternalMind * em = dynamic_cast<ExternalMind*>(m_character->m_externalMind); ASSERT_NOT_NULL(em); ASSERT_TRUE(em->isLinked()); ASSERT_TRUE(em->isLinkedTo(m_connection)); ASSERT_TRUE(m_Link_send_sent.isValid()); ASSERT_EQUAL(m_Link_send_sent->getClassNo(), Atlas::Objects::Operation::INFO_NO); ASSERT_EQUAL(m_logEvent_logged, TAKE_CHAR); }
void TrustedConnectionCreatorintegration::test_external_op_override() { // Dispatching a Talk external op from the creator should result in // it being passed on to the world, exactly as if this was a Character // except that we assume that Creator was set up linked. m_creator->m_externalMind = new ExternalMind(*m_creator); m_creator->m_externalMind->linkUp(m_connection); Atlas::Objects::Operation::Talk op; op->setFrom(m_creator->getId()); op->setTo(m_creator->getId()); m_connection->externalOperation(op, *m_connection); // The operation should have been passed to Entity::callOperation for // dispatch, completely unfiltered. ASSERT_TRUE(m_Entity_callOperation_called.isValid()); ASSERT_EQUAL(m_Entity_callOperation_called->getClassNo(), Atlas::Objects::Operation::TALK_NO); ASSERT_TRUE(!m_Entity_callOperation_called->isDefaultTo()); ASSERT_EQUAL(m_Entity_callOperation_called->getTo(), m_creator->getId()); }
void ConnectionCharacterintegration::test_unlinked() { // Dispatching an external op from the character if the external mind is // already in place, but is not linked to a connection should link it // back up. m_character->linkExternal(m_connection); ASSERT_NOT_NULL(m_character->m_externalMind); ExternalMind * em = dynamic_cast<ExternalMind*>(m_character->m_externalMind); ASSERT_NOT_NULL(em); ASSERT_TRUE(em->isLinked()); ASSERT_TRUE(em->isLinkedTo(m_connection)); // Remove the link from the external mind back to m_connection em->linkUp(0); ASSERT_TRUE(!em->isLinked()); Router * saved_em = m_character->m_externalMind; RootOperation op; op->setFrom(m_character->getId()); m_connection->externalOperation(op, *m_connection); ASSERT_NOT_NULL(m_character->m_externalMind); ASSERT_EQUAL(m_character->m_externalMind, saved_em); em = dynamic_cast<ExternalMind*>(m_character->m_externalMind); ASSERT_NOT_NULL(em); ASSERT_TRUE(em->isLinked()); ASSERT_TRUE(em->isLinkedTo(m_connection)); ASSERT_TRUE(m_Link_send_sent.isValid()); ASSERT_EQUAL(m_logEvent_logged, TAKE_CHAR); }
void TrustedConnectionCreatorintegration::test_external_op() { // Dispatching a Talk external op from the creator should result in // it being passed on to the world, exactly as if this was a Character // except that we assume that Creator was set up linked. m_creator->m_externalMind = new ExternalMind(*m_creator); m_creator->m_externalMind->linkUp(m_connection); Atlas::Objects::Operation::Talk op; op->setFrom(m_creator->getId()); m_connection->externalOperation(op, *m_connection); // BaseWorld::message should have been called from Enitty::sendWorld // with the Talk operation, modified to have TO set to the character. ASSERT_TRUE(m_BaseWorld_message_called.isValid()); ASSERT_EQUAL(m_BaseWorld_message_called->getClassNo(), Atlas::Objects::Operation::TALK_NO); ASSERT_TRUE(!m_BaseWorld_message_called->isDefaultTo()); ASSERT_EQUAL(m_BaseWorld_message_called->getTo(), m_creator->getId()); ASSERT_NOT_NULL(m_BaseWorld_message_called_from); ASSERT_EQUAL(m_BaseWorld_message_called_from, m_creator); }
void EntityImporterBase::infoArrived(const Operation & op, OpVector & res) { if (op->isDefaultRefno()) { return; } if (op->isDefaultArgs() || op->getArgs().empty()) { S_LOG_FAILURE("Info with no arg."); return; } const Root & arg = op->getArgs().front(); if (m_state == RULE_WALKING) { auto& current = mRuleStack.back(); if (arg->getId() != current.definition->getId()) { S_LOG_WARNING("Got info on rule " << arg->getId() << " when expecting " << current.definition->getId() << "."); return; } updateRule(arg, current.definition, res); } else if (m_state == RULE_CREATING) { mStats.rulesProcessedCount++; mStats.rulesCreateCount++; EventProgress.emit(); walkRules(res); } else if (m_state == RULE_UPDATING) { mStats.rulesProcessedCount++; mStats.rulesUpdateCount++; EventProgress.emit(); walkRules(res); } else if (m_state == ENTITY_CREATING) { if (!op.isValid()) { return; } mNewIds.insert(arg->getId()); StackEntry & current = mTreeStack.back(); current.restored_id = arg->getId(); S_LOG_VERBOSE("Created: " << arg->getParent() << "(" << arg->getId() << ")"); auto I = mCreateEntityMapping.find(op->getRefno()); if (I != mCreateEntityMapping.end()) { //Check if there's a mind that we should send further on auto mindI = mPersistedMinds.find(I->second); if (mindI != mPersistedMinds.end()) { mResolvedMindMapping.emplace_back(arg->getId(), mindI->second); } mEntityIdMap.insert(std::make_pair(I->second, arg->getId())); mCreateEntityMapping.erase(op->getRefno()); } else { S_LOG_WARNING("Got info about create for an entity which we didn't seem to have sent."); } walkEntities(res); } else if (m_state == ENTITY_WALKING) { const RootEntity& ent = smart_dynamic_cast<RootEntity>(arg); if (!ent.isValid()) { S_LOG_FAILURE("Info response is not entity."); return; } if (arg->isDefaultId()) { S_LOG_FAILURE("Corrupted info response: no id."); } const std::string & id = arg->getId(); StackEntry & current = mTreeStack.back(); const RootEntity& obj = current.obj; assert(id == obj->getId()); if (mNewIds.find(id) != mNewIds.end() || (mTreeStack.size() != 1 && ent->isDefaultLoc()) || ent->getParent() != obj->getParent()) { createEntity(obj, res); } else { Root update = obj.copy(); current.restored_id = id; S_LOG_VERBOSE("Updating: " << obj->getId() << " ," << obj->getParent()); update->removeAttrFlag(Atlas::Objects::Entity::CONTAINS_FLAG); update->removeAttrFlag(Atlas::Objects::STAMP_FLAG); Set set; set->setArgs1(update); set->setFrom(mAvatarId); set->setTo(id); set->setSerialno(newSerialNumber()); res.push_back(set); //Check if there's a mind, and if so put it in our map of resolved entity-to-mind mappings (since we know the entity id) auto mindI = mPersistedMinds.find(obj->getId()); if (mindI != mPersistedMinds.end()) { mResolvedMindMapping.emplace_back(obj->getId(), mindI->second); } ++mStats.entitiesProcessedCount; ++mStats.entitiesUpdateCount; EventProgress.emit(); m_state = ENTITY_UPDATING; } } }