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); } } }
void RuleTraversalTask::operation(const Operation & op, OpVector & res) { if (!op->isDefaultRefno() && op->getRefno() == mSerial) { if (op->getClassNo() == Atlas::Objects::Operation::INFO_NO) { if (!op->getArgs().empty()) { const auto& arg = op->getArgs().front(); if (arg.isValid()) { bool result = mVisitor(arg); if (result) { Element childrenElement; if (arg->copyAttr("children", childrenElement) == 0 && childrenElement.isList() && !childrenElement.List().empty()) { const ListType& childrenList = childrenElement.asList(); mStack.emplace_back(); for (auto& childElement : childrenList) { if (childElement.isString()) { mStack.back().children.push_back(childElement.String()); } } mStack.back().currentChildIterator = mStack.back().children.begin(); getRule(*mStack.back().currentChildIterator, res); return; } else { while (!mStack.empty()) { StackEntry& stackEntry = mStack.back(); stackEntry.currentChildIterator++; if (stackEntry.currentChildIterator == stackEntry.children.end()) { mStack.pop_back(); } else { getRule(*stackEntry.currentChildIterator, res); return; } } m_complete = true; } } else { m_complete = true; } } } } } }
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; } } }
void EntityImporterBase::errorArrived(const Operation & op, OpVector & res) { std::string errorMessage; if (!op->getArgs().empty()) { auto arg = op->getArgs().front(); if (arg->hasAttr("message")) { const Atlas::Message::Element messageElem = arg->getAttr("message"); if (messageElem.isString()) { errorMessage = messageElem.asString(); } } } switch (m_state) { case RULE_WALKING: { //An error here just means that the rule we asked for didn't exist on the server, and we need //to create it. This is an expected result. auto& current = mRuleStack.back(); auto definition = current.definition; assert(definition.isValid()); createRule(definition, res); } break; case RULE_CREATING: { mStats.rulesProcessedCount++; mStats.rulesCreateErrorCount++; //An error here means that something went wrong when trying to create a rule. This is wrong. //It probably means that there's something wrong with the data we're sending. auto& current = mRuleStack.back(); std::string ruleId = current.definition->getId(); S_LOG_FAILURE("Could not create rule with id '" << ruleId << "', continuing with next. Server message: " << errorMessage); EventProgress.emit(); walkRules(res); } break; case RULE_UPDATING: { mStats.rulesProcessedCount++; //An error here means that something went wrong when trying to update a rule. This is wrong. //It probably means that there's something wrong with the data we're sending. auto& current = mRuleStack.back(); std::string ruleId = current.definition->getId(); S_LOG_FAILURE("Could not update rule with id '" << ruleId << "', continuing with next. Server message: " << errorMessage); mStats.rulesCreateErrorCount++; EventProgress.emit(); walkRules(res); } break; case ENTITY_WALKING: { //An error here just means that the entity we asked for didn't exist on the server, and we need //to create it. This is an expected result. assert(!mTreeStack.empty()); StackEntry & current = mTreeStack.back(); const RootEntity& obj = current.obj; assert(obj.isValid()); createEntity(obj, res); } break; case ENTITY_CREATING: { //An error here means that something went wrong when trying to create an entity. This is wrong. //It probably means that there's something wrong with the data we're sending. Either the //persisted data is corrupt, or there have been changes on the server (for example entity types //renamed or removed). std::string entityType = "unknown"; auto I = mCreateEntityMapping.find(op->getRefno()); if (I != mCreateEntityMapping.end()) { auto J = mPersistedEntities.find(I->second); if (J != mPersistedEntities.end()) { auto& entity = J->second; entityType = entity->getParent(); } } S_LOG_FAILURE("Could not create entity of type '" << entityType << "', continuing with next. Server message: " << errorMessage); mStats.entitiesCreateErrorCount++; EventProgress.emit(); walkEntities(res); } break; default: S_LOG_FAILURE("Unexpected state in state machine: " << m_state << ". Server message: " << errorMessage); break; }; }
/// \brief Handle an Info op response sent as reply to a teleport request /// /// @param op The Info op sent back as reply to a teleport request /// @param res The result set of replies void Peer::peerTeleportResponse(const Operation &op, OpVector &res) { log(INFO, "Got a peer teleport response"); // Response to a Create op const std::vector<Root> & args = op->getArgs(); if (args.size() < 1) { log(ERROR, "Malformed args in Info op"); return; } const Root & arg = args.front(); if (op->isDefaultRefno()) { log(ERROR, "Response to teleport has no refno"); return; } long iid = op->getRefno(); CommPeer *peer = dynamic_cast<CommPeer*>(&m_commClient); if(peer == 0) { log(ERROR, "Unable to get CommPeer object"); return; } TeleportMap::iterator I = m_teleports.find(iid); if (I == m_teleports.end()) { log(ERROR, "Info op for unknown create"); return; } TeleportState *s = I->second; assert (s != NULL); s->setCreated(); log(INFO, String::compose("Entity with ID %1 replicated on peer", iid)); // This is the sender entity. This is retreived again rather than // relying on a pointer (in the TeleportState object perhaps) as the // entity might have been deleted in the time between sending and response Entity * entity = BaseWorld::instance().getEntity(iid); if (entity == 0) { log(ERROR, String::compose("No entity found with ID: %1", iid)); // Clean up the teleport state object m_teleports.erase(I); return; } // If entity has a mind, add extra information in the Logout op if (s->isMind()) { Character * chr = dynamic_cast<Character *>(entity); if (!chr) { log(ERROR, "Entity is not a character"); return; } if (chr->m_externalMind == 0) { log(ERROR, "No external mind (though teleport state claims it)"); return; } ExternalMind * mind = dynamic_cast<ExternalMind*>(chr->m_externalMind); if (mind == 0 || !mind->isConnected()) { log(ERROR, "Mind is NULL or not connected"); return; } std::vector<Root> logout_args; Anonymous op_arg; op_arg->setId(entity->getId()); logout_args.push_back(op_arg); Anonymous ip_arg; ip_arg->setAttr("teleport_host", peer->getHost()); ip_arg->setAttr("teleport_port", peer->getPort()); ip_arg->setAttr("possess_key", s->getPossessKey()); ip_arg->setAttr("possess_entity_id", arg->getId()); logout_args.push_back(ip_arg); Logout logoutOp; logoutOp->setArgs(logout_args); logoutOp->setTo(entity->getId()); OpVector temp; mind->operation(logoutOp, temp); log(INFO, "Sent random key to connected mind"); } // FIXME Remove from the world cleanly, not delete. // Delete the entity from the current world Delete delOp; Anonymous del_arg; del_arg->setId(entity->getId()); delOp->setArgs1(del_arg); delOp->setTo(entity->getId()); entity->sendWorld(delOp); log(INFO, "Deleted entity from current server"); logEvent(EXPORT_ENT, String::compose("%1 - %2 Exported entity", getId(), entity->getId())); // Clean up the teleport state object m_teleports.erase(I); }