void World::LookOperation(const Operation & op, OpVector & res) { // We must be the top level entity assert(m_location.m_parent == nullptr); // We must contains something, or where the hell did the look come from? assert(m_contains != nullptr); //The top level entity is a little special, since its properties can be inspected by all entities, although it's children can not. //First check if there's a movement domain. If so we'll handle Look ops just like usually. However, if not we'll send the properties sans the "contains" property. auto from = BaseWorld::instance().getEntity(op->getFrom()); if (!from) { log(ERROR, String::compose("Look op has invalid from %1. %2", op->getFrom(), describeEntity())); return; } Domain* domain = nullptr; if (m_location.m_parent) { domain = m_location.m_parent->getDomain(); } if (domain) { generateSightOp(*from, op, res); } else { Sight s; Anonymous sarg; addToEntity(sarg); //Hide all contents of the root entity. sarg->removeAttr("contains"); s->setArgs1(sarg); s->setTo(op->getFrom()); res.push_back(s); } }
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 World::EatOperation(const Operation & op, OpVector & res) { const std::string & from_id = op->getFrom(); Entity * from = BaseWorld::instance().getEntity(from_id); if (from == 0) { log(ERROR, String::compose("World got eat op from non-existant " "entity %1.", from_id)); return; } TerrainProperty * tp = modPropertyClass<TerrainProperty>("terrain"); if (tp == 0) { log(ERROR, "No terrain in getSurface"); return; } Point3D from_pos = relativePos(m_location, from->m_location); int material; if (tp->getSurface(from_pos, material) != 0) { debug(std::cout << "no surface hit" << std::endl << std::flush;);
/// \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 Interactive::exec(const std::string & cmd, const std::string & arg) { bool reply_expected = true; reply_flag = false; error_flag = false; boost::shared_ptr<ObjectContext> command_context = m_currentContext.lock(); if (!command_context) { std::cout << "ERROR: Context free" << std::endl << std::flush; return; } if (cmd == "stat") { Get g; send(g); } else if (cmd == "install") { size_t space = arg.find(' '); if (space == std::string::npos || space >= (arg.size() - 1)) { std::cout << "usage: install <type id> <parent id>" << std::endl << std::flush; } else { Create c; c->setFrom(m_accountId); Anonymous ent; ent->setId(std::string(arg, 0, space)); ent->setObjtype("class"); ent->setParents(std::list<std::string>(1, std::string(arg, space + 1))); c->setArgs1(ent); send(c); } reply_expected = false; } else if (cmd == "look") { Look l; if (!arg.empty()) { Anonymous cmap; cmap->setId(arg); l->setArgs1(cmap); } l->setSerialno(newSerialNo()); command_context->setFromContext(l); send(l); reply_expected = false; } else if (cmd == "logout") { Logout l; l->setFrom(m_accountId); if (!arg.empty()) { Anonymous lmap; lmap->setId(arg); l->setArgs1(lmap); reply_expected = false; } send(l); } else if (cmd == "say") { Talk t; Anonymous ent; ent->setAttr("say", arg); t->setArgs1(ent); t->setFrom(m_accountId); send(t); } else if (cmd == "help" || cmd == "?") { reply_expected = false; help(); } else if (cmd == "query") { Get g; if (!arg.empty()) { Anonymous cmap; if (::isdigit(arg[0])) { cmap->setObjtype("obj"); } else { cmap->setObjtype("meta"); } cmap->setId(arg); g->setArgs1(cmap); } g->setFrom(m_accountId); send(g); } else if (cmd == "reload") { if (arg.empty()) { reply_expected = false; std::cout << "reload: Argument required" << std::endl << std::flush; } else { Set s; Anonymous tmap; tmap->setObjtype("class"); tmap->setId(arg); s->setArgs1(tmap); s->setFrom(m_accountId); send(s); } } else if (cmd == "get") { Get g; if (!arg.empty()) { Anonymous cmap; if (::isdigit(arg[0])) { cmap->setObjtype("obj"); } else { cmap->setObjtype("meta"); } cmap->setId(arg); g->setArgs1(cmap); } g->setFrom(m_accountId); send(g); } else if (cmd == "monitor") { ClientTask * task = new OperationMonitor; if (runTask(task, arg) == 0) { Monitor m; m->setArgs1(Anonymous()); m->setFrom(m_accountId); send(m); } reply_expected = false; } else if (cmd == "unmonitor") { OperationMonitor * om = dynamic_cast<OperationMonitor *>(m_currentTask); if (om != 0) { Monitor m; m->setFrom(m_accountId); send(m); reply_expected = false; SystemTime now; now.update(); time_t monitor_time = now.seconds() - om->startTime(); std::cout << om->count() << " operations monitored in " << monitor_time << " seconds = " << om->count() / monitor_time << " operations per second" << std::endl << std::flush; endTask(); } } else if (cmd == "connect") { std::vector<std::string> args; tokenize(arg, args); if (args.size() != 2) { std::cout << "usage: connect <hostname> <port>" << std::endl << std::flush; reply_expected = false; } else { Anonymous cmap; cmap->setAttr("hostname", args[0]); cmap->setAttr("port", strtol(args[1].c_str(), 0, 10)); Connect m; m->setArgs1(cmap); // No serialno yet // FIXME add serialno once Juncture context can handle this command_context->setFromContext(m); send(m); } } else if (cmd == "add_agent") { std::string agent_type("creator"); if (!arg.empty()) { agent_type = arg; } Create c; Anonymous cmap; cmap->setParents(std::list<std::string>(1, agent_type)); cmap->setName("cycmd agent"); cmap->setObjtype("obj"); c->setArgs1(cmap); c->setSerialno(newSerialNo()); command_context->setFromContext(c); send(c); } else if (cmd == "delete") { if (arg.empty()) { std::cout << "Please specify the entity to delete" << std::endl << std::flush; reply_expected = false; } else { Delete del; Anonymous del_arg; del_arg->setId(arg); del->setArgs1(del_arg); command_context->setFromContext(del); send(del); reply_expected = false; } } else if (cmd == "find_by_name") { if (arg.empty()) { std::cout << "Please specify the name to search for" << std::endl << std::flush; reply_expected = false; } else { Look l; Anonymous lmap; lmap->setName(arg); l->setArgs1(lmap); l->setSerialno(newSerialNo()); command_context->setFromContext(l); send(l); reply_expected = false; } } else if (cmd == "find_by_type") { if (arg.empty()) { std::cout << "Please specify the type to search for" << std::endl << std::flush; reply_expected = false; } else { Look l; Anonymous lmap; lmap->setParents(std::list<std::string>(1, arg)); l->setArgs1(lmap); l->setSerialno(newSerialNo()); command_context->setFromContext(l); send(l); reply_expected = false; } } else if (cmd == "flush") { if (arg.empty()) { // FIXME usage std::cout << "Please specify the type to flush" << std::endl << std::flush; reply_expected = false; } else { ClientTask * task = new Flusher(command_context); runTask(task, arg); reply_expected = false; } } else if (cmd == "cancel") { if (endTask() != 0) { std::cout << "No task currently running" << std::endl << std::flush; } } else if (cmd == "dump") { if (command_context->repr() != "avatar") { std::cout << "You must have an agent in the world in order to dump the world." << std::endl << std::flush; } else { //Extract the avatar id by "misusing" the setFromContext method Operation op; command_context->setFromContext(op); ClientTask * task = new EntityExporter(m_accountId, op->getFrom()); runTask(task, "world.xml"); reply_expected = false; } } else if (cmd == "restore") { if (command_context->repr() != "avatar") { std::cout << "You must have an agent in the world in order to dump the world." << std::endl << std::flush; } else { //Extract the avatar id by "misusing" the setFromContext method Operation op; command_context->setFromContext(op); ClientTask * task = new EntityImporter(m_accountId, op->getFrom()); runTask(task, "world.xml"); reply_expected = false; } } else if (cmd == "create") { std::vector<std::string> args; tokenize(arg, args); if (args.size() < 1) { std::cout << "usage: create <type> <params> ... " << std::endl << std::flush; } else { Anonymous cmap; cmap->setParents(std::list<std::string>(1, args[0])); cmap->setObjtype("obj"); Create c; c->setArgs1(cmap); c->setSerialno(newSerialNo()); command_context->setFromContext(c); send(c); } reply_expected = false; } else if (cmd == "login") { std::vector<std::string> args; tokenize(arg, args); if (args.size() != 2) { std::cout << "usage: login <username> <password>" << std::endl << std::flush; reply_expected = false; } else { Anonymous cmap; cmap->setAttr("username", args[0]); cmap->setAttr("password", args[1]); Login m; m->setArgs1(cmap); m->setSerialno(newSerialNo()); command_context->setFromContext(m); send(m); } } else { reply_expected = false; std::cout << cmd << ": Command not known" << std::endl << std::flush; } if (!reply_expected) { updatePrompt(); return; } // Wait for reply time_t wait_start_time = time(NULL); while (!reply_flag) { if (time(NULL) - wait_start_time > 5) { std::cout << cmd << ": No reply from server" << std::endl << std::flush; return; } if (select(false) != 0) { return; } } }
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 OperationMonitor::operation(const Operation & op, OpVector &) { ++op_count; std::cout << op->getParents().front() << "(from=\"" << op->getFrom() << "\",to=\"" << op->getTo() << "\")" << std::endl << std::flush; }