/// \brief Idle function called from the main loop. /// /// Poll all the Idle objects that want to be polled regularly, /// Call the core server object idle function. /// @return true if the core server wants to be called again as soon as /// possible. bool CommServer::idle(const SystemTime & time, bool busy) { // We only call the idlers if the world has returned that it is not busy, // and the last call to select/poll with a sleep time provided did not // return any traffic. if (!busy && !m_congested && m_tick != time.seconds()) { IdleSet::const_iterator I = m_idlers.begin(); IdleSet::const_iterator Iend = m_idlers.end(); for (; I != Iend; ++I) { (*I)->idle(time.seconds()); } } else { // if (busy) { std::cout << "No idle because server busy" << std::endl << std::flush; } // if (m_congested) { std::cout << "No idle because clients busy" << std::endl << std::flush; } } m_tick = time.seconds(); return busy; }
void DebugInfoXmlWriter::WriteTimeAttribute(const CString& cszAttributeName, const SystemTime& time) { if (time.IsEmpty()) { m_spWriter->WriteAttributeString(cszAttributeName, _T("N/A")); } else { DateTime dt(time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, time.wMilliseconds); m_spWriter->WriteAttributeString(cszAttributeName, dt.FormatISO8601(DateTime::formatYMD_HMSF_Z, false)); } }
/// \brief Constructor for the world object. /// /// The Entity representing the world is implicitly constructed. /// Currently the world entity is included in the perceptives list, /// but I am not clear why. Need to look into why. WorldRouter::WorldRouter(const SystemTime & time) : BaseWorld(*new World(consts::rootWorldId, consts::rootWorldIntId)), m_entityCount(1), m_operation_queues_dirty(false) { m_initTime = time.seconds(); m_gameWorld.incRef(); EntityBuilder::init(); m_gameWorld.setType(Inheritance::instance().getType("world")); m_eobjects[m_gameWorld.getIntId()] = &m_gameWorld; m_perceptives.insert(&m_gameWorld); //WorldTime tmp_date("612-1-1 08:57:00"); Monitors::instance()->watch("entities", new Variable<int>(m_entityCount)); }
/// \brief Update the in-game time. /// /// Reads the system time, and applies the necessary offsets to calculate /// the in-game time. This is the stored, and can be accessed using getTime(). void WorldRouter::updateTime(const SystemTime & time) { double tmp_time = (double)(time.seconds() + timeoffset - m_initTime) + (double)time.microseconds()/1000000; m_realTime = tmp_time; }
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; } } }
int main(int argc, char ** argv) { //Kill ourselves if our parent is killed. prctl(PR_SET_PDEATHSIG, SIGTERM); interactive_signals(); int config_status = loadConfig(argc, argv, USAGE_AICLIENT); if (config_status < 0) { if (config_status == CONFIG_VERSION) { reportVersion(argv[0]); return 0; } else if (config_status == CONFIG_HELP) { showUsage(argv[0], USAGE_AICLIENT, "[ local_socket_path ]"); return 0; } else if (config_status != CONFIG_ERROR) { log(ERROR, "Unknown error reading configuration."); } // Fatal error loading config file return 1; } int optind = config_status; assert(optind <= argc); if (optind == (argc - 1)) { std::string arg(argv[optind]); } else if (optind != argc) { usage(argv[0]); return 1; } init_python_api(ruleset_name, false); //Initialize inheritance explicitly here. Inheritance::instance(); SystemTime time; time.update(); AwareMindFactory mindFactory; // MindFactory mindFactory; // AwareMindFactory awareMindFactory; //TODO: perhaps don't hardcode this; instead allowing for different classes for different minds? std::string script_package = "mind.NPCMind"; std::string script_class = "NPCMind"; if (mindFactory.m_scriptFactory != 0) { if (mindFactory.m_scriptFactory->package() != script_package) { delete mindFactory.m_scriptFactory; mindFactory.m_scriptFactory = 0; } } if (mindFactory.m_scriptFactory == 0) { PythonScriptFactory<BaseMind> * psf = new PythonScriptFactory<BaseMind>(script_package, script_class); if (psf->setup() == 0) { log(INFO, String::compose("Initialized mind code with Python class %1.%2.", script_package, script_class)); mindFactory.m_scriptFactory = psf; } else { log(ERROR, String::compose("Python class \"%1.%2\" failed to load", script_package, script_class)); delete psf; } } std::unique_ptr<PossessionClient> possessionClient(new PossessionClient(mindFactory)); log(INFO, "Trying to connect to server."); while (tryToConnect(*possessionClient) != 0 && !exit_flag) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); } while (!exit_flag) { try { double secondsUntilNextOp = possessionClient->secondsUntilNextOp(); boost::posix_time::microseconds waitTime((long long)(secondsUntilNextOp * 1000000)); int netResult = possessionClient->pollOne(waitTime); if (netResult >= 0) { //As long as we're connected we'll keep on processing minds possessionClient->idle(); possessionClient->markQueueAsClean(); } else if (!exit_flag) { log(ERROR, "Disconnected from server; will try to reconnect every one second."); //We're disconnected. We'll now enter a loop where we'll try to reconnect at an interval. //First we need to shut down the current client. Perhaps we could find a way to persist the minds in a better way? possessionClient.reset(new PossessionClient(mindFactory)); while (tryToConnect(*possessionClient) != 0 && !exit_flag) { std::this_thread::sleep_for(std::chrono::milliseconds(1000)); } } // It is hoped that commonly thrown exception, particularly // exceptions that can be caused by external influences // should be caught close to where they are thrown. If // an exception makes it here then it should be debugged. } catch (const std::exception& e) { log(ERROR, String::compose("Exception caught in main(): %1", e.what())); } catch (...) { log(ERROR, "Exception caught in main()"); } } log(INFO, "Shutting down."); }
static void paintWrapper() { // clear the palette newCubes.clear(); lostCubes.clear(); reconnectedCubes.clear(); dirtyCubes.clear(); if(previousLearningTask != currentLearningTask){ previousLearningTask++; } // fire events System::paint(); // dynamically load assets just-in-time if (!(newCubes | reconnectedCubes).empty()) { AudioTracker::pause(); playSfx(SfxConnect); loader.start(config); while(!loader.isComplete()) { for(CubeID cid : (newCubes | reconnectedCubes)) { vbuf[cid].bg0rom.hBargraph( vec(0, 4), loader.cubeProgress(cid, 128), BG0ROMDrawable::ORANGE, 8 ); } // fire events while we wait System::paint(); } loader.finish(); AudioTracker::resume(); } // // repaint cubes (will this paint right? If not, try repainting all of them) // for(CubeID cid : dirtyCubes) { // activateCube(cid); // } //If the shaken timer flag is too old, turn it off again here. if(distractTime.isValid() && (currentLearningTask==2)) { TimeDelta timeSinceShook = SystemTime::now() - distractTime; double duration = timeSinceShook.milliseconds() / 1000; if((duration > 11) && isDistracted) { currentBackgrounds[2] = 3; currentBackgrounds[1] = 1; isDistracted = false; } } //update art to new task int j = 0; for(CubeID cid : CubeSet::connected()) { vbuf[cid].attach(cid); activateCube(cid); cbs [j] = cid; j++; } // also, handle lost cubes, if you so desire :) }
double BaseWorld::getTime() const { SystemTime time; time.update(); return (double)(time.seconds() + timeoffset - m_initTime) + (double)time.microseconds()/1000000.; }
int main(int argc, char ** argv) { if (security_init() != 0) { log(CRITICAL, "Security initialisation Error. Exiting."); return EXIT_SECURITY_ERROR; } if (security_check() != SECURITY_OKAY) { log(CRITICAL, "Security check error. Exiting."); return EXIT_SECURITY_ERROR; } interactive_signals(); int config_status = loadConfig(argc, argv, USAGE_SERVER); if (config_status < 0) { if (config_status == CONFIG_VERSION) { std::cout << argv[0] << " (cyphesis) " << consts::version << " (Cyphesis build " << consts::buildId << ")" << std::endl << std::flush; return 0; } else if (config_status == CONFIG_HELP) { showUsage(argv[0], USAGE_SERVER); return 0; } else if (config_status != CONFIG_ERROR) { log(ERROR, "Unknown error reading configuration."); } // Fatal error loading config file. return EXIT_CONFIG_ERROR; } if (daemon_flag) { int pid = daemonise(); if (pid == -1) { return EXIT_FORK_ERROR; } else if (pid > 0) { return EXIT_SUCCESS; } } readConfigItem(instance, "usedatabase", database_flag); // If we are a daemon logging to syslog, we need to set it up. initLogger(); // Initialise the persistance subsystem. If we have been built with // database support, this will open the various databases used to // store server data. if (database_flag) { Persistence * p = Persistence::instance(); int dbstatus = p->init(); if (dbstatus < 0) { database_flag = false; log(ERROR, "Error opening database. Database disabled."); if (dbstatus == DATABASE_TABERR) { log(INFO, "Database connection established, " "but unable to create required tables."); log(INFO, "Please ensure that any obsolete database " "tables have been removed."); } else { log(INFO, "Unable to connect to the RDBMS."); log(INFO, "Please ensure that the RDBMS is running, " "the cyphesis database exists and is accessible " "to the user running cyphesis."); } log(INFO, String::compose("To disable this message please run:\n\n" " cyconfig --%1:usedatabase=false\n\n" "to permanently disable database usage.", instance)); } } // If the restricted flag is set in the config file, then we // don't allow connecting users to create accounts. Accounts must // be created manually by the server administrator. if (restricted_flag) { log(INFO, "Setting restricted mode."); } readConfigItem(instance, "inittime", timeoffset); std::string server_name; if (readConfigItem(instance, "servername", server_name) != 0) { if (instance == CYPHESIS) { server_name = get_hostname(); } else { server_name = instance; } } int nice = 1; readConfigItem(instance, "nice", nice); // Start up the python subsystem. init_python_api(ruleset_name); Inheritance::instance(); new BulletDomain; SystemTime time; time.update(); WorldRouter * world = new WorldRouter(time); Ruleset::init(ruleset_name); TeleportAuthenticator::init(); StorageManager * store = new StorageManager(*world); // This ID is currently generated every time, but should perhaps be // persistent in future. std::string server_id, lobby_id; long int_id, lobby_int_id; if (((int_id = newId(server_id)) < 0) || ((lobby_int_id = newId(lobby_id)) < 0)) { log(CRITICAL, "Unable to get server IDs from Database"); return EXIT_DATABASE_ERROR; } // Create the core server object, which stores central data, // and track objects ServerRouting * server = new ServerRouting(*world, ruleset_name, server_name, server_id, int_id, lobby_id, lobby_int_id); // Create commserver instance that will handle connections from clients. // The commserver will create the other server related objects, and the // world object pair (World + WorldRouter), and initialise the admin // account. The primary ruleset name is passed in so it // can be stored and queried by clients. CommServer * commServer = new CommServer; if (commServer->setup() != 0) { log(CRITICAL, "Internal error setting up network infrastructure"); return EXIT_SOCKET_ERROR; } // This is where we should restore the database, before // the listen sockets are open. Unlike earlier code, we are // attempting to construct the internal state from the database, // not creating a new world using the contents of the database as a // template if (database_flag) { // log(INFO, _("Restoring world from database...")); store->restoreWorld(); // FIXME Do the following steps. // Read the world entity if any from the database, or set it up. // If it was there, make sure it did not get any of the wrong // position or orientation data. store->initWorld(); // log(INFO, _("Restored world.")); CommPSQLSocket * dbsocket = new CommPSQLSocket(*commServer, Persistence::instance()->m_db); commServer->addSocket(dbsocket); commServer->addIdle(dbsocket); IdleConnector * storage_idle = new IdleConnector(*commServer); storage_idle->idling.connect(sigc::mem_fun(store, &StorageManager::tick)); commServer->addIdle(storage_idle); } else { std::string adminId; long intId = newId(adminId); assert(intId >= 0); Admin * admin = new Admin(0, "admin", "BAD_HASH", adminId, intId); server->addAccount(admin); } // Add the test object, and call it regularly so it can do what it does. // UpdateTester * update_tester = new UpdateTester(*commServer); // commServer->addIdle(update_tester); shared_ptr<CommClientFactory<CommUserClient, Connection> > atlas_clients = make_shared<CommClientFactory<CommUserClient, Connection>, ServerRouting & >(*server); if (client_port_num < 0) { client_port_num = dynamic_port_start; for (; client_port_num <= dynamic_port_end; client_port_num++) { if (TCPListenFactory::listen(*commServer, client_port_num, atlas_clients) == 0) { break; } } if (client_port_num > dynamic_port_end) { log(ERROR, String::compose("Could not find free client listen " "socket in range %1-%2. Init failed.", dynamic_port_start, dynamic_port_end)); log(INFO, String::compose("To allocate 8 more ports please run:" "\n\n cyconfig " "--cyphesis:dynamic_port_end=%1\n\n", dynamic_port_end + 8)); return EXIT_PORT_ERROR; } log(INFO, String::compose("Auto configuring new instance \"%1\" " "to use port %2.", instance, client_port_num)); global_conf->setItem(instance, "tcpport", client_port_num, varconf::USER); global_conf->setItem(CYPHESIS, "dynamic_port_start", client_port_num + 1, varconf::USER); } else if (TCPListenFactory::listen(*commServer, client_port_num, atlas_clients) != 0) { log(ERROR, String::compose("Could not create client listen socket " "on port %1. Init failed.", client_port_num)); return EXIT_SOCKET_ERROR; } #ifdef HAVE_SYS_UN_H CommUnixListener * localListener = new CommUnixListener(*commServer, make_shared<CommClientFactory<CommAdminClient, TrustedConnection>, ServerRouting &>(*server)); if (localListener->setup(client_socket_name) != 0) { log(ERROR, String::compose("Could not create local listen socket " "with address \"%1\"", localListener->getPath())); delete localListener; } else { commServer->addSocket(localListener); } CommUnixListener * pythonListener = new CommUnixListener(*commServer, make_shared<CommPythonClientFactory>()); if (pythonListener->setup(python_socket_name) != 0) { log(ERROR, String::compose("Could not create python listen socket " "with address %1.", pythonListener->getPath())); delete pythonListener; } else { commServer->addSocket(pythonListener); } #endif if (TCPListenFactory::listen(*commServer, http_port_num, make_shared<CommHttpClientFactory>()) != 0) { log(ERROR, String::compose("Could not create http listen" " socket on port %1.", http_port_num)); } if (useMetaserver) { CommMetaClient * cmc = new CommMetaClient(*commServer); if (cmc->setup(mserver) == 0) { commServer->addIdle(cmc); } else { log(ERROR, "Error creating metaserver comm channel."); delete cmc; } } #if defined(HAVE_AVAHI) CommMDNSPublisher * cmdns = new CommMDNSPublisher(*commServer, *server); if (cmdns->setup() == 0) { commServer->addSocket(cmdns); commServer->addIdle(cmdns); } else { log(ERROR, "Unable to register service with MDNS daemon."); delete cmdns; } #endif // defined(HAVE_AVAHI) // Configuration is now complete, and verified as somewhat sane, so // we save the updated user config. updateUserConfiguration(); log(INFO, "Running"); logEvent(START, "- - - Standalone server startup"); // Inform things that want to know that we are running. running(); // Reduce our system priority to make it easier to debug a runaway // server. if (nice != 0) { reduce_priority(nice); } // Loop until the exit flag is set. The exit flag can be set anywhere in // the code easily. while (!exit_flag) { try { time.update(); bool busy = world->idle(time); commServer->idle(time, busy); commServer->poll(busy); } catch (...) { // It is hoped that commonly thrown exception, particularly // exceptions that can be caused by external influences // should be caught close to where they are thrown. If // an exception makes it here then it should be debugged. log(ERROR, "Exception caught in main()"); } } // exit flag has been set so we close down the databases, and indicate // to the metaserver (if we are using one) that this server is going down. // It is assumed that any preparation for the shutdown that is required // by the game has been done before exit flag was set. log(NOTICE, "Performing clean shutdown..."); delete commServer; delete server; delete store; delete world; Persistence::instance()->shutdown(); EntityBuilder::instance()->flushFactories(); EntityBuilder::del(); ArithmeticBuilder::del(); TeleportAuthenticator::del(); Inheritance::clear(); // Shutdown the python interpretter. This frees lots of memory, and if // the malloc heap is in any way corrupt, a segfault is likely to // occur at this point. Previous occassions where pointers have been // deleted twice elsewhere in the code, have resulted in a segfault // at this point. AlRiddoch 10th November 2001 shutdown_python_api(); delete global_conf; log(INFO, "Clean shutdown complete."); logEvent(STOP, "- - - Standalone server shutdown"); return 0; }