void Interactive::disappearanceArrived(const Operation & op) { if (m_accountId.empty()) { return; } if (m_accountId != op->getTo()) { // This is an IG op we are monitoring return; } if (op->getArgs().empty()) { return; } RootEntity ent = smart_dynamic_cast<RootEntity>(op->getArgs().front()); if (!ent.isValid()) { std::cerr << "Got Disappearance of non-entity" << std::endl << std::flush; return; } if (!ent->hasAttrFlag(Atlas::Objects::ID_FLAG)) { std::cerr << "Got Disappearance of non-string ID" << std::endl << std::flush; return; } const std::string & id = ent->getId(); std::cout << "Disappearance(id: " << id << ")"; if (!ent->hasAttrFlag(Atlas::Objects::Entity::LOC_FLAG)) { std::cout << std::endl << std::flush; return; } const std::string & loc = ent->getLoc(); std::cout << " in " << loc << std::endl; if (loc == "lobby") { std::cout << id << " has logged out." << std::endl; } std::cout << std::flush; }
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 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); }
/// \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); }
HandlerResult SpawnerProperty::tick_handler(LocatedEntity * e, const Operation & op, OpVector & res) { if (!op->getArgs().empty()) { auto& arg = op->getArgs().front(); if (arg->getName() == "spawner") { //This is our tick handleTick(e, op, res); return OPERATION_BLOCKED; } } return OPERATION_IGNORED; }
void Juncture::customConnectOperation(const Operation & op, OpVector & res) { log(INFO, "Juncture got connect"); if (m_peer != 0) { error(op, "Juncture already connected", res, getId()); return; } assert(m_socket == 0); const std::vector<Root> & args = op->getArgs(); if (args.empty()) { error(op, "No argument to connect op", res, getId()); return; } const Root & arg = args.front(); Element hostname_attr; if (arg->copyAttr("hostname", hostname_attr) != 0 || !hostname_attr.isString()) { error(op, "Argument to connect op has no hostname", res, getId()); return; } const std::string & hostname = hostname_attr.String(); Element port_attr; if (arg->copyAttr("port", port_attr) != 0 || !port_attr.isInt()) { error(op, "Argument to connect op has no port", res, getId()); return; } int port = port_attr.Int(); debug(std::cout << "Connecting to " << hostname << std::endl << std::flush;);
void World::DeleteOperation(const Operation & op, OpVector & res) { //A delete operation with an argument sent to the world indicates that an //entity should be deleted forcefully (whereas a Delete operation sent to //an entity directly, which is the norm, always can be overridden by the entity). auto& args = op->getArgs(); if (!args.empty()) { auto arg = args.front(); if (!arg->isDefaultId()) { auto entity = BaseWorld::instance().getEntity(arg->getId()); if (entity) { if (entity == this) { Atlas::Message::Element force; if (arg->copyAttr("force", force) == 0 && force.isInt() && force.asInt() == 1) { clearWorld(res); } else { log(ERROR, "World::DeleteOperation cannot delete world unless 'force' flag is set."); } } else { BaseWorld::instance().delEntity(entity.get()); } } else { log(NOTICE, String::compose("Tried to delete non existent entity with id %1", arg->getId())); } } else { log(ERROR, "World::DeleteOperation got delete op with arg but no id."); } } else { assert(m_location.m_parent == nullptr); // Deleting has no effect. } }
void Admin::LogoutOperation(const Operation & op, OpVector & res) { const std::vector<Root> & args = op->getArgs(); if (args.empty()) { Account::LogoutOperation(op, res); return; } const Root & arg = args.front(); if (!arg->hasAttrFlag(Atlas::Objects::ID_FLAG)) { error(op, "No account id given on logout op", res, getId()); return; } const std::string & account_id = arg->getId(); if (account_id == getId()) { Account::LogoutOperation(op, res); return; } if (m_connection == NULL) { error(op,"Disconnected admin account handling explicit logout",res, getId()); return; } Router * account = m_connection->m_server.getObject(account_id); if (!account) { error(op, "Logout failed", res, getId()); return; } account->operation(op, res); }
HandlerResult DomainProperty::tick_handler(LocatedEntity * entity, const Operation & op, OpVector & res) { if (!op->getArgs().empty() && op->getArgs().front()->getName() == "domain") { Domain* domain = sInstanceState.getState(entity); if (domain) { float timeNow = op->getSeconds(); float timeForNextTick = domain->tick(timeNow, res); if (timeForNextTick > 0) { scheduleTick(*entity, timeForNextTick); } } return OPERATION_BLOCKED; } return OPERATION_IGNORED; }
void WaitForDeletionTask::operation(const Operation & op, OpVector & res) { if (op->getClassNo() == Atlas::Objects::Operation::SIGHT_NO) { if (!op->getArgs().empty()) { auto& innerOp = op->getArgs().front(); if (innerOp->getClassNo() == Atlas::Objects::Operation::DELETE_NO) { auto deleteOp = Atlas::Objects::smart_dynamic_cast<Operation>(innerOp); if (!deleteOp->getArgs().empty()) { auto args = deleteOp->getArgs().front(); if (args->getId() == m_entityId) { m_complete = true; } } } } } }
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; }; }
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; } } } } } }
HandlerResult BurnSpeedProperty::burn_handler(LocatedEntity * e, const Operation & op, OpVector & res) { if (op->getArgs().empty()) { e->error(op, "Fire op has no argument", res, e->getId()); return OPERATION_IGNORED; } const double & burn_speed = data(); const Root & fire_ent = op->getArgs().front(); double consumed = burn_speed * fire_ent->getAttr("status").asNum(); const std::string & to = fire_ent->getId(); Anonymous nour_ent; nour_ent->setId(to); nour_ent->setAttr("mass", consumed); StatusProperty * status_prop = e->requirePropertyClass<StatusProperty>("status", 1.f); assert(status_prop != 0); status_prop->setFlags(flag_unsent); double & status = status_prop->data(); Element mass_attr; if (e->getAttrType("mass", mass_attr, Element::TYPE_FLOAT) != 0) { mass_attr = 1.f; } status -= (consumed / mass_attr.Float()); status_prop->apply(e); Update update; update->setTo(e->getId()); res.push_back(update); Nourish n; n->setTo(to); n->setArgs1(nour_ent); res.push_back(n); return OPERATION_IGNORED; }
void Admin::SetOperation(const Operation & op, OpVector & res) { const std::vector<Root> & args = op->getArgs(); if (args.empty()) { error(op, "Set has no args.", res, getId()); return; } const Root & arg = args.front(); if (!arg->hasAttrFlag(Atlas::Objects::OBJTYPE_FLAG)) { error(op, "Set arg has no objtype.", res, getId()); return; } const std::string & objtype = arg->getObjtype(); if (!arg->hasAttrFlag(Atlas::Objects::ID_FLAG)) { error(op, "Set arg has no id.", res, getId()); return; } const std::string & id = arg->getId(); if (objtype == "object" || objtype == "obj") { long intId = integerId(id); if (intId == getIntId()) { setAttribute(arg); } else { if (m_charactersDict.find(intId) != m_charactersDict.end()) { Account::SetOperation(op, res); return; } log(WARNING, "Unable to set attributes of non-character yet"); } // Manipulate attributes of existing objects. } else if (objtype == "class" || objtype == "op_definition") { if (Inheritance::instance().hasClass(id)) { if (Ruleset::instance()->modifyRule(id, arg) == 0) { Info info; info->setTo(getId()); info->setArgs1(arg); res.push_back(info); } else { error(op, "Updating type failed", res, getId()); } return; } error(op, "Client attempting to use obsolete Set to install new type", res, getId()); return; } else { error(op, "Unknow object type set", res, getId()); return; } }
/// \brief Process a Monitor operation /// /// @param op The operation to be processed. /// @param res The result of the operation is returned here. void Admin::customMonitorOperation(const Operation & op, OpVector & res) { if (!op->getArgs().empty()) { if (m_connection != 0) { if (!m_monitorConnection.connected()) { m_monitorConnection = m_connection->m_server.m_world.Dispatching.connect(sigc::mem_fun(this, &Admin::opDispatched)); } } } else { if (m_monitorConnection.connected()) { m_monitorConnection.disconnect(); } } }
void Juncture::LoginOperation(const Operation & op, OpVector & res) { log(INFO, "Juncture got login"); const std::vector<Root> & args = op->getArgs(); if (args.empty()) { error(op, "No argument to connect op", res, getId()); return; } const Root & arg = args.front(); Element username_attr; if (arg->copyAttr("username", username_attr) != 0 || !username_attr.isString()) { error(op, "Argument to connect op has no username", res, getId()); return; } const std::string & username = username_attr.String(); Element password_attr; if (arg->copyAttr("password", password_attr) != 0 || !password_attr.isString()) { error(op, "Argument to connect op has no password", res, getId()); return; } const std::string & password = password_attr.String(); if (m_peer == 0) { error(op, "Juncture not connected", res, getId()); return; } assert(m_socket == 0); if (m_peer->getAuthState() != PEER_INIT) { error(op, "Juncture not ready", res, getId()); return; } Anonymous account; account->setAttr("username", username); account->setAttr("password", password); Login l; l->setArgs1(account); if (!op->isDefaultSerialno()) { l->setSerialno(op->getSerialno()); } // Send the login op m_peer->send(l); m_peer->setAuthState(PEER_AUTHENTICATING); }
void Interactive::soundArrived(const Operation & op) { if (m_accountId.empty()) { return; } if (m_accountId != op->getTo()) { // This is an IG op we are monitoring return; } reply_flag = true; if (op->getArgs().empty()) { std::cout << "Sound op has no args" << std::endl << std::flush; return; } Operation sub_op = smart_dynamic_cast<Operation>(op->getArgs().front()); if (!sub_op.isValid()) { return; } if (sub_op->isDefaultFrom()) { std::cout << "Sound arg has no from" << std::endl << std::flush; return; } const std::string & from = sub_op->getFrom(); if (sub_op->getArgs().empty()) { std::cout << "Sound arg has no args" << std::endl << std::flush; return; } const Root & arg = sub_op->getArgs().front(); Element say; if (arg->copyAttr("say", say) != 0 || !say.isString()) { std::cout << "Sound arg arg has no say" << std::endl << std::flush; return; } std::cout << "[" << from << "] " << say.String() << std::endl << std::flush; }
void Interactive::errorArrived(const Operation & op) { reply_flag = true; error_flag = true; if (m_currentTask != 0) { return; } std::cout << "Error("; const std::vector<Root> & args = op->getArgs(); const Root & arg = args.front(); Element message_attr; if (arg->copyAttr("message", message_attr) == 0 && message_attr.isString()) { std::cout << message_attr.asString(); } std::cout << ")" << std::endl << std::flush; }
void ServerAccount::createObject(const std::string & type_str, const Root & arg, const Operation & op, OpVector & res) { // Format of the Create ops that are received by this function should // have the entity to be created as the first argument. If the entity // being created is a character associated with an account, an additional // argument should specify the possess key that will be used by the client // to claim ownership of the entity being created. if (arg->getObjtype() != "obj") { // Return error to peer error(op, "Only creation of entities by peer server is permitted", res, getId()); return; } RootEntity ent = smart_dynamic_cast<RootEntity>(arg); if(!ent.isValid()) { log(ERROR, "Character creation arg is malformed"); return; } // If we have a possess key (entity has a mind) TeleportAuthenticator * tele_auth = 0; std::string possess_key; const std::vector<Root> & args = op->getArgs(); if (args.size() == 2) { const Root & arg2 = args.back(); Element key; if(arg2->copyAttr("possess_key", key) == 0 && key.isString()) { possess_key = key.String(); tele_auth = TeleportAuthenticator::instance(); } else { log(ERROR, "Entity has mind but no possess key found"); return; } } debug( std::cout << "ServerAccount creating a " << type_str << " object" << std::endl << std::flush; );
/// \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; } }
void TasksProperty::TickOperation(LocatedEntity * owner, const Operation & op, OpVector & res) { if (m_task == 0) { return; } const std::vector<Root> & args = op->getArgs(); if (args.empty()) { return; } const Root & arg = args.front(); Element serialno; if (arg->copyAttr(SERIALNO, serialno) == 0 && (serialno.isInt())) { if (serialno.asInt() != m_task->serialno()) { debug(std::cout << "Old tick" << std::endl << std::flush;); return; }
HandlerResult TerrainModProperty::move_handler(LocatedEntity * e, const Operation & op, OpVector & res) { // FIXME Force instantiation of a class property? // Check the validity of the operation. const std::vector<Root> & args = op->getArgs(); if (args.empty()) { return OPERATION_IGNORED; } RootEntity ent = Atlas::Objects::smart_dynamic_cast<RootEntity>(args.front()); if (!ent.isValid()) { return OPERATION_IGNORED; } if (e->getId() != ent->getId()) { return OPERATION_IGNORED; } // Update the modifier move(e); return OPERATION_IGNORED; }
void Flusher::operation(const Operation & op, OpVector & res) { shared_ptr<ObjectContext> flush_context = m_context.lock(); if (!flush_context) { m_complete = true; return; } if (op->getClassNo() == Atlas::Objects::Operation::SIGHT_NO) { // We have a sight op, check if its the sight of an entity we // want to delete. const std::vector<Root> & args = op->getArgs(); if (args.empty()) { std::cerr << "Got empty sight" << std::endl << std::flush; return; } const Root & arg = args.front(); assert(arg.isValid()); RootEntity sight_ent = smart_dynamic_cast<RootEntity>(arg); if (!sight_ent.isValid()) { return; } if (!sight_ent->hasAttrFlag(Atlas::Objects::ID_FLAG)) { std::cerr << "Got sight no ID" << std::endl << std::flush; return; } if (!sight_ent->hasAttrFlag(Atlas::Objects::PARENTS_FLAG)) { std::cerr << "Got sight no PARENTS" << std::endl << std::flush; return; } if (sight_ent->getParents().empty() || sight_ent->getParents().front() != type) { return; } const std::string & id = sight_ent->getId(); std::cout << "Deleting: " << type << "(" << id << ")" << std::endl << std::flush; // Send a delete to the entity we have seen. Delete d; Anonymous dmap; dmap->setId(id); d->setArgs1(dmap); flush_context->setFromContext(d); d->setTo(id); res.push_back(d); // Send a tick for a short time in the future so that // we can look again once this entity is definitly gone. Tick t; Anonymous tick_arg; tick_arg->setName("flusher"); flush_context->setFromContext(t); t->setTo(t->getFrom()); t->setFutureSeconds(0.1); t->setArgs1(tick_arg); res.push_back(t); } else if (op->getParents().front() == "tick") { // We have a tick op, check if its the one we sent ourselves // to schedule the next look. if (op->getArgs().empty() || op->getArgs().front()->getName() != "flusher") { std::cout << "Not for us" << std::endl << std::flush; return; } // Send another look by type. Look l; Anonymous lmap; lmap->setParents(std::list<std::string>(1, type)); l->setArgs1(lmap); flush_context->setFromContext(l); res.push_back(l); } else if (op->getParents().front() == "unseen") { // We have an unseen op, which signals our last look returned // no results. m_complete = true; } }
HandlerResult UsagesProperty::use_handler(LocatedEntity* e, const Operation& op, OpVector& res) { auto actor = BaseWorld::instance().getEntity(op->getFrom()); if (!actor) { e->error(op, "Could not find 'from' entity.", res, e->getId()); return OPERATION_IGNORED; } if (op->isDefaultFrom()) { actor->error(op, "Top op has no 'from' attribute.", res, actor->getId()); return OPERATION_IGNORED; } if (!op->getArgs().empty()) { auto& arg = op->getArgs().front(); auto argOp = smart_dynamic_cast<Atlas::Objects::Operation::RootOperation>(arg); if (!argOp) { actor->error(op, "First arg wasn't an operation.", res, actor->getId()); return OPERATION_IGNORED; } if (!argOp->hasAttrFlag(Atlas::Objects::PARENT_FLAG)) { actor->error(op, "Use arg op has malformed parent", res, actor->getId()); return OPERATION_IGNORED; } auto op_type = argOp->getParent(); debug_print("Got op type " << op_type << " from arg"); auto obj = Atlas::Objects::Factories::instance()->createObject(op_type); if (!obj.isValid()) { log(ERROR, String::compose("Character::UseOperation Unknown op type " "\"%1\".", op_type)); return OPERATION_IGNORED; } auto rop = smart_dynamic_cast<Operation>(obj); if (!rop.isValid()) { log(ERROR, String::compose("Character::UseOperation Op type " "\"%1\" but it is not an operation type. ", op_type)); return OPERATION_IGNORED; } rop->setFrom(actor->getId()); rop->setTo(e->getId()); rop->setSeconds(op->getSeconds()); if (argOp->getArgs().empty()) { actor->error(op, "Use arg op has no arguments; one expected.", res, actor->getId()); return OPERATION_IGNORED; } auto arguments = argOp->getArgs().front(); //Check that there's an action registered for this operation auto usagesI = m_usages.find(op_type); if (usagesI != m_usages.end()) { auto& usage = usagesI->second; //Check that the tool is ready auto toolReadyAtProp = e->getPropertyType<double>("ready_at"); if (toolReadyAtProp) { if (toolReadyAtProp->data() > BaseWorld::instance().getTime()) { actor->clientError(op, "Tool is not ready yet.", res, actor->getId()); return OPERATION_IGNORED; } } //Check if the tools is attached, and if so the attachment is ready auto actorReadyAtProp = actor->getPropertyType<MapType>("_ready_at_attached"); if (actorReadyAtProp) { auto plantedOnProp = e->getPropertyClassFixed<PlantedOnProperty>(); //First check if the tool is attached to the actor at an attach point if (plantedOnProp && plantedOnProp->data().entity.get() == actor.get() && plantedOnProp->data().attachment) { auto attachPoint = *plantedOnProp->data().attachment; //Lastly check if there's a value for this attach point. auto attachI = actorReadyAtProp->data().find(attachPoint); if (attachI != actorReadyAtProp->data().end()) { if (attachI->second.isFloat() && attachI->second.Float() > BaseWorld::instance().getTime()) { actor->clientError(op, "Actor is not ready yet.", res, actor->getId()); return OPERATION_IGNORED; } } } } //Populate the usage arguments std::map<std::string, std::vector<UsageInstance::UsageArg>> usage_instance_args; for (auto& param : usage.params) { Atlas::Message::Element element; if (arguments->copyAttr(param.first, element) != 0 || !element.isList()) { actor->clientError(op, String::compose("Could not find required list argument '%1'.", param.first), res, actor->getId()); return OPERATION_IGNORED; } auto& argVector = usage_instance_args[param.first]; for (auto& argElement : element.List()) { switch (param.second.type) { case UsageParameter::Type::ENTITY: { if (!argElement.isMap()) { actor->clientError(op, String::compose("Inner argument in list of arguments for '%1' was not a map.", param.first), res, actor->getId()); return OPERATION_IGNORED; } //The arg is for an RootEntity, expressed as a message. Extract id and pos. auto idI = argElement.Map().find("id"); if (idI == argElement.Map().end() || !idI->second.isString()) { actor->clientError(op, String::compose("Inner argument in list of arguments for '%1' had no id string.", param.first), res, actor->getId()); return OPERATION_IGNORED; } auto involved = BaseWorld::instance().getEntity(idI->second.String()); if (!involved) { actor->error(op, "Involved entity does not exist", res, actor->getId()); return OPERATION_IGNORED; } auto posI = argElement.Map().find("pos"); if (posI != argElement.Map().end() && posI->second.isList()) { argVector.emplace_back(EntityLocation(involved, WFMath::Point<3>(posI->second))); } else { argVector.emplace_back(EntityLocation(involved)); } } break; case UsageParameter::Type::ENTITYLOCATION: argVector.emplace_back(WFMath::Point<3>(argElement)); break; case UsageParameter::Type::POSITION: argVector.emplace_back(WFMath::Point<3>(argElement)); break; case UsageParameter::Type::DIRECTION: argVector.emplace_back(WFMath::Vector<3>(argElement)); break; } } } UsageInstance usageInstance{usage, actor, e, std::move(usage_instance_args), rop}; //Check that the usage is valid before continuing auto validRes = usageInstance.isValid(); if (!validRes.first) { actor->clientError(op, validRes.second, res, actor->getId()); } else { auto lastSeparatorPos = usage.handler.find_last_of('.'); if (lastSeparatorPos != std::string::npos) { auto moduleName = usage.handler.substr(0, lastSeparatorPos); auto functionName = usage.handler.substr(lastSeparatorPos + 1); //Py::Module module(moduleName); Py::Module module(PyImport_Import(Py::String(moduleName).ptr())); //PyImport_ReloadModule(module.ptr()); auto functionObject = module.getDict()[functionName]; if (!functionObject.isCallable()) { actor->error(op, String::compose("Could not find Python function %1", usage.handler), res, actor->getId()); return OPERATION_IGNORED; } try { PythonLogGuard logGuard([functionName, actor]() { return String::compose("Usage '%1', entity %2: ", functionName, actor->describeEntity()); }); auto ret = Py::Callable(functionObject).apply(Py::TupleN(UsageInstance::scriptCreator(std::move(usageInstance)))); return ScriptUtils::processScriptResult(usage.handler, ret, res, e); } catch (const Py::BaseException& py_ex) { log(ERROR, String::compose("Python error calling \"%1\" for entity %2", usage.handler, e->describeEntity())); if (PyErr_Occurred()) { PyErr_Print(); } } } } return OPERATION_BLOCKED; } } //We couldn't find any suitable task. return OPERATION_IGNORED; }
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); }
void Admin::GetOperation(const Operation & op, OpVector & res) { const std::vector<Root> & args = op->getArgs(); if (args.empty()) { error(op, "Get has no args.", res, getId()); return; } const Root & arg = args.front(); if (!arg->hasAttrFlag(Atlas::Objects::OBJTYPE_FLAG)) { error(op, "Get arg has no objtype.", res, getId()); return; } const std::string & objtype = arg->getObjtype(); if (!arg->hasAttrFlag(Atlas::Objects::ID_FLAG)) { error(op, "Get arg has no id.", res, getId()); return; } const std::string & id = arg->getId(); if (id.empty()) { error(op, "Get arg id empty", res, getId()); return; } Info info; if (objtype == "object" || objtype == "obj") { if (m_connection == 0) { return; } long intId = integerId(id); const RouterMap & OOGDict = m_connection->m_server.getObjects(); RouterMap::const_iterator J = OOGDict.find(intId); const EntityDict & worldDict = m_connection->m_server.m_world.getEntities(); EntityDict::const_iterator K = worldDict.find(intId); if (J != OOGDict.end()) { Router * obj = J->second; Anonymous info_arg; obj->addToEntity(info_arg); info->setArgs1(info_arg); } else if (K != worldDict.end()) { Anonymous info_arg; K->second->addToEntity(info_arg); info->setArgs1(info_arg); } else { clientError(op, compose("Unknown object id \"%1\" requested", id), res, getId()); return; } } else if (objtype == "class" || objtype == "meta" || objtype == "op_definition") { const Root & o = Inheritance::instance().getClass(id); if (!o.isValid()) { clientError(op, compose("Unknown type definition for \"%1\" " "requested", id), res); return; } info->setArgs1(o); } else if (objtype == "op") { if (arg->getClassNo() == Atlas::Objects::Operation::THOUGHT_NO) { long intId = integerId(id); const EntityDict & worldDict = m_connection->m_server.m_world.getEntities(); EntityDict::const_iterator K = worldDict.find(intId); if (K != worldDict.end()) { Character* character = dynamic_cast<Character*>(K->second); if (character) { character->sendMind(op, res); } else { clientError(op, compose("Entity with id \"%1\" is not a character", id), res, getId()); return; } std::vector<Root> newRet; //Why can't I do "info->setArgs(res)"? for (auto& operation : res) { newRet.push_back(operation); } info->setArgs(newRet); res.clear(); } else { clientError(op, compose("Unknown object id \"%1\" requested", id), res, getId()); return; } } else if (arg->getClassNo() == Atlas::Objects::Operation::GOAL_INFO_NO) { long intId = integerId(id); const EntityDict & worldDict = m_connection->m_server.m_world.getEntities(); EntityDict::const_iterator K = worldDict.find(intId); if (K != worldDict.end()) { Character* character = dynamic_cast<Character*>(K->second); if (character) { character->sendMind(op, res); } else { clientError(op, compose("Entity with id \"%1\" is not a character", id), res, getId()); return; } std::vector<Root> newRet; //Why can't I do "info->setArgs(res)"? for (auto& operation : res) { newRet.push_back(operation); } info->setArgs(newRet); res.clear(); } else { clientError(op, compose("Unknown object id \"%1\" requested", id), res, getId()); return; } } } else { error(op, compose("Unknown object type \"%1\" requested for \"%2\"", objtype, id), res, getId()); return; } res.push_back(info); }
/// \brief Process the Sight of a Create operation. /// /// @param op The Create operation to be processed. /// @param res The result of the operation is returned here. void BaseMind::sightCreateOperation(const Operation & op, OpVector & res) { const std::vector<Root> & args = op->getArgs(); if (args.empty()) { debug( std::cout << " no args!" << std::endl << std::flush;);