示例#1
0
/// \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;
}
示例#2
0
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));
   }
}
示例#3
0
/// \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));
}
示例#4
0
/// \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;
}
示例#5
0
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;
       }
    }
}
示例#6
0
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.");
}
示例#7
0
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 :)
}
示例#8
0
double BaseWorld::getTime() const {
    SystemTime time;
    time.update();
    return (double)(time.seconds() + timeoffset - m_initTime) + (double)time.microseconds()/1000000.;
}
示例#9
0
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;
}