예제 #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);
        }
    }

}
예제 #2
0
void Interactive::infoArrived(const Operation & op)
{
    reply_flag = true;
    if (op->getArgs().empty()) {
        return;
    }
    const Root & ent = op->getArgs().front();
    if (m_server_flag) {
        std::cout << "Server query success" << std::endl << std::flush;
        if (!ent->isDefaultName()) {
            m_serverName = ent->getName();
            std::string::size_type p = m_serverName.find(".");
            if (p != std::string::npos) {
                m_serverName = m_serverName.substr(0, p);
            }
            updatePrompt();
        }
        Element raw_attr;
        if (ent->copyAttr("server", raw_attr) == 0) {
            if (raw_attr.isString()) {
                m_systemType = raw_attr.String();
                updatePrompt();
            }
        }
        m_server_flag = false;
    } else if (m_currentTask == 0 && op->isDefaultRefno()) {
        std::cout << "Info(" << std::endl;
        output(ent);
        std::cout << ")" << std::endl << std::flush;
        // Display results of command
    }
    AtlasStreamClient::infoArrived(op);
}
예제 #3
0
void EntityImporterBase::sightArrived(const Operation & op, OpVector & res)
{
    if (op->isDefaultArgs() || op->getArgs().empty()) {
        S_LOG_FAILURE("No arg");
        return;
    }
    const Root & arg = op->getArgs().front();
    switch (m_state) {
    case ENTITY_WALKSTART:
        if (op->isDefaultRefno()) {
            break;
        }
        if (arg->isDefaultId()) {
            S_LOG_WARNING("Corrupted top level entity: no id");
            cancel();
            return;
        } else {
            getEntity(arg->getId(), res);
        }


        // Expecting sight of world root
        break;
    case ENTITY_UPDATING:
    {
        const Operation& sub_op = smart_dynamic_cast<Operation>(arg);
        if (!sub_op.isValid()) {
            break;
        }
        if (sub_op->getClassNo() != Atlas::Objects::Operation::SET_NO || sub_op->getArgs().empty() || sub_op->isDefaultSerialno()) {
            S_LOG_FAILURE("This is not our entity update response.");
            break;
        }
        walkEntities(res);
    }
        break;
    case ENTITY_CREATING:
    case ENTITY_WALKING:
        //Just ignore sights when creating; these are sights of the actual creation ops.
        break;
    default:
        S_LOG_WARNING("Unexpected state in state machine: " << m_state);
        break;
    };
}
예제 #4
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;
                    }
                }
            }
        }
    }
}
예제 #5
0
/// \brief Execute an operation sent by a connected peer
///
/// \param op The operation to be executed
/// \param res The result set of replies
void Peer::operation(const Operation &op, OpVector &res)
{
    if (!op->isDefaultRefno()) {
        replied.emit(op);
    }

    const OpNo op_no = op->getClassNo();
    switch (op_no) {
        case Atlas::Objects::Operation::INFO_NO:
        {
            // If we receive an Info op while we are not yet authenticated, it
            // can only be the result of an authentication request.
            if (m_state == PEER_AUTHENTICATING) {
                const std::vector<Root> & args = op->getArgs();
                if (args.empty()) {
                    return;
                }
                const Root & arg = args.front();
                if (!arg->hasAttrFlag(Atlas::Objects::ID_FLAG)) {
                    return;
                }
                // Response to a Login op
                m_accountId = arg->getId();
                if (!arg->getParents().empty()) {
                    m_accountType = arg->getParents().front();
                }
                m_state = PEER_AUTHENTICATED;
            } else if (m_state == PEER_AUTHENTICATED) {
                // If we received an Info op while authenticated, it is a
                // response to a teleport request.
                peerTeleportResponse(op, res);
            }
        }
        break;
        case Atlas::Objects::Operation::ERROR_NO:
        {
            m_state = PEER_FAILED;
        }
        break;
    }
}
예제 #6
0
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;
        }
    }
}
예제 #7
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);
}