Example #1
0
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);
        }
    }

}
Example #2
0
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;
    };
}
Example #5
0
/// \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);
}