src/Application.cc
author Tero Marttila <terom@fixme.fi>
Tue, 27 Jan 2009 00:25:58 +0200
changeset 439 9823e6cd1086
parent 426 510c83aab425
permissions -rw-r--r--
some README text

#include "Application.hh"
#include "Config.hh"

#if GRAPHICS_ENABLED
    // for dump_display_modes
    #include "Graphics/Display.hh"
    
    // for CL_SetupGL
    #include <ClanLib/gl.h>
#endif

#include <stdexcept>
#include <sstream>
#include <cstdio>
#include <cassert>

enum ArgumentCodes {
    ARG_HELP        = 'h',
    ARG_PORT        = 'p',
    ARG_SERVER      = 's',
    ARG_CLIENT      = 'c',
    ARG_GRAPHICS    = 'g',
    ARG_FULLSCREEN  = 'F',
    ARG_RESOLUTION  = 'R',

    ARG_LIST_MODES      = 0xff01,
    ARG_VERSION         = 0xff02,
    ARG_TERRAIN_SEED    = 0xff03,
    ARG_TERRAIN_SIZE    = 0xff04,
    ARG_LOG_LEVEL       = 0xff05,

};

        
Main::Main (void) :
    graphics_enabled(false),
    net_port(NETWORK_PORT_STR),
    net_server(false),
    net_connect("")
{

}

/**
 * Set the arg_* members
 */
bool Main::parse_args (int argc, char **argv) {

    // set up the options
    args.add_usage("[OPTIONS]");
    args.set_help_indent(30);
    
    if (NETWORK_ENABLED) {
        args.add_group("Network");

        args.add_option(ARG_PORT, "port", "PORT",
                "set network port used");

        args.add_option(ARG_SERVER, "server", "",
                "act as a network server");
        
        // require graphics for network-client
        if (GRAPHICS_ENABLED)
            args.add_option(ARG_CLIENT, "client", "SERVERHOST",
                    "act as a network client");
    }

    if (GRAPHICS_ENABLED) {
        args.add_group("Graphics");

        args.add_option(ARG_GRAPHICS, "graphics", "",        
                "run graphics/local input. Implied with --client");

        args.add_option(ARG_FULLSCREEN, "fullscreen", "",
                "run graphics in fullscreen mode");

        args.add_option(ARG_RESOLUTION, "resolution", "WIDTHxHEIGHT",
                "set graphics resolution");
        
        args.add_option(ARG_LIST_MODES, "list-modes", "",
                "output a list of available display modes and exit");
    }

    args.add_group("Game");
    args.add_option(ARG_TERRAIN_SEED, "terrain-seed", "SEED",
            "set seed for terrain random generator");

    args.add_option(ARG_TERRAIN_SIZE, "terrain-size", "WIDTHxHEIGHT",
            "set terrain size for random generator");

    
    args.add_group("General");
    args.add_option(ARG_LOG_LEVEL, "log-level", "LEVEL",
            "set maximum log level to one of FATAL/ERROR/WARN/INFO/DEBUG");

    args.add_option(ARG_HELP, "help", "",
            "display argument help and exit");

    args.add_option(ARG_VERSION, "version", "",
            "output application version plus configuration and exit");

#if GRAPHICS_ENABLED    
    // extra state
    bool resolution_default = true;

#endif    
    
    try {
        // parse args
        args.parse_args(argc, argv);

    } catch (CL_Error &e) {
        throw ArgumentError(e.message);
    }

    while (args.next()) {
        switch (args.get_key()) {
            case ARG_HELP:
                args.print_help();
                return false;

            case ARG_LOG_LEVEL:
                engine.log_level = parse_arg_loglevel(args.get_argument(), "--log-level");
                break;

#if NETWORK_ENABLED                
            case ARG_PORT:
                net_port = args.get_argument();
                break;

            case ARG_SERVER:
                net_server = true;
                break;

            case ARG_CLIENT:
                net_connect = args.get_argument();
                break;

#endif                

#if GRAPHICS_ENABLED                
            case ARG_GRAPHICS:
                graphics_enabled = true;
                break;

            case ARG_FULLSCREEN:
                display.fullscreen = true;
                
                // choose best resolution unless explicitly set
                if (resolution_default) {
                    const CL_DisplayMode best_mode = graphics::Display::getBestMode();
                    const CL_Size best_resolution = best_mode.get_resolution();

                    display.resolution = PixelDimensions(best_resolution.width, best_resolution.height);
                }

                break;

            case ARG_RESOLUTION:
                display.resolution = parse_arg_dimensions(args.get_argument(), "--resolution");
                resolution_default = false;
                break;

            case ARG_LIST_MODES:
                dump_display_modes();
                return false;
#endif

            case ARG_VERSION:
                dump_version();
                return false;

            case ARG_TERRAIN_SEED:
                terrain.random_seed = parse_arg_int(args.get_argument(), "--terrain-seed");
                break;

            case ARG_TERRAIN_SIZE:
                terrain.dimensions = parse_arg_dimensions(args.get_argument(), "--terrain-size");
                break;

            case CL_CommandLine::REST_ARG:
                throw ArgumentError("Trailing arguments: " + args.get_argument());

            default:
                throw ArgumentError(std::string() + "Unknown argument key: " + (char) args.get_key());

        }
    }

#if NETWORK_ENABLED    
    // check for invalid combinations of arugments
    if (net_server and !net_connect.empty())
        throw ArgumentError("cannot be both server and client");
#endif

#if GRAPHICS_ENABLED    
    // enable graphics by default unless server
    if (!net_server)
        graphics_enabled = true;
#endif    

    // continue
    return true;
}
        
int Main::parse_arg_int (const std::string &arg_val, const char *arg_name) {
    int int_val;
    
    // read using istringstream
    std::istringstream ss(arg_val);
    
    if (!(ss >> int_val))
        throw ArgumentError(std::string() + "invalid integer arugment for " + arg_name + ": " + arg_val);

    return int_val;
}

PixelDimensions Main::parse_arg_dimensions (const std::string &arg_val, const char *arg_name) {
    unsigned int w, h;
    
    // sccanf as unsigned
    if (sscanf(arg_val.c_str(), "%ux%u", &w, &h) != 2)
        throw ArgumentError(std::string() + "invalid format for " + arg_name + ": " + arg_val);
    
    return PixelDimensions(w, h);
}

LogLevel Main::parse_arg_loglevel (const std::string &arg_val, const char *arg_name) {
    // brute-force if-elseif
    if (arg_val == "FATAL")
        return FATAL;

    else if (arg_val == "ERROR")
        return ERROR;

    else if (arg_val == "WARN")
        return WARN;

    else if (arg_val == "INFO")
        return INFO;

    else if (arg_val == "DEBUG")
        return DEBUG;

    else
        throw ArgumentError(std::string() + "invalid log level for " + arg_name + ": " + arg_val);
}

#if GRAPHICS_ENABLED
void Main::dump_display_modes (void) {
    // get the list of display modes from graphics
    const std::vector<CL_DisplayMode> &modes = graphics::Display::getDisplayModes();
    PixelCoordinate last_resolution;
    
    std::cout << "Available display modes:" << std::endl;
    
    // iterate over the list of available display modes
    for (std::vector<CL_DisplayMode>::const_iterator it = modes.begin(); it != modes.end(); it++) {
        PixelCoordinate resolution(it->get_resolution().width, it->get_resolution().height);
        
        // filter out those that haven't changed
        if (resolution != last_resolution)
            std::cout << "\t" << it->get_resolution().width << "x" << it->get_resolution().height << std::endl;
        
        // update for next item
        last_resolution = resolution;
    }
}
#endif
        
void Main::dump_version (void) {
    std::cout 
        << PROJECT_LONG_NAME << " version " << PROJECT_VERSION << " built [" << PROJECT_BUILD_TIMESTAMP << "]" << std::endl;

    // then some additional stuff about what's enabled
    std::cout
        << std::endl
        << "Enabled components:" << std::endl
        << "\tGraphics:\t" << (GRAPHICS_ENABLED ? "Yes" : "No") << std::endl
        << "\tNetwork:\t" << (NETWORK_ENABLED ? "Yes" : "No") << std::endl;
}

/**
 * IT BEGAN IN AFRIKA
 */
int Main::main (int argc, char **argv) {
    // initialize the ClanLib components that we use
    CL_SetupCore setup_core;

#if GRAPHICS_ENABLED    
    // XXX: move to Graphics/Graphics.hh?
    CL_SetupDisplay setup_disp;
    CL_SetupGL setup_gl;
#endif

    try {
        // parse arugments, exit if false
        if (parse_args(argc, argv) == false)
            return EXIT_SUCCESS;

        // our engine
        Engine engine(this->engine);

        // setup game unless client
        if (net_connect.empty())
            engine.setupGame(terrain);
        
        // setup graphics
        if (graphics_enabled)
            engine.setupGraphics(display);

        // setup either network server, client or singleplayer
        if (net_server) {
            engine.setupNetworkServer(net_port);

        } else if (!net_connect.empty()) {
            engine.setupNetworkClient(net_connect, net_port);
        
        } else if (graphics_enabled) {
            engine.setupSinglePlayer();

        } else {
            throw ArgumentError("Nothing to do");

        }

        // run the main loop
        engine.run();
        
        // succesful return
        return EXIT_SUCCESS;
    
    } catch (ArgumentError &e) {
        std::cerr 
            << "Error: " << e.what() << std::endl
            << std::endl;

        args.print_help();

    } catch (CL_Error &e) {
        std::cerr << "main: CL_Error:" << e.message << std::endl;

    } catch (std::exception &e) {
        std::cerr << "FATAL [uncaught_exception] " << e.what() << std::endl;

    }
    
    // fallthrough from catch's
    return EXIT_FAILURE;
}