src/Application.cc
author Tero Marttila <terom@fixme.fi>
Tue, 20 Jan 2009 23:30:18 +0200
changeset 408 e6cfc44266af
parent 397 13fa0546ef87
child 409 1a03ff151abc
permissions -rw-r--r--
reorganize Terrain/PhysicsWorld/GameState/Engine to use NetworkClientConnect, and hence handle the connection process asynchronously, and finally properly implement receiving the terrain data from the server

#include "Application.hh"

#include <stdexcept>
#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,
};


/**
 * Set the arg_* members
 */
bool Main::parse_args (int argc, char **argv) {
    // set up the options
    args.add_option(ARG_HELP, "help", "",
            "display argument help and exit");

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

    args.add_option(ARG_SERVER, "server", "",
            "act as a network server");

    args.add_option(ARG_CLIENT, "client", "SERVERHOST",
            "act as a network client");

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

    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_option(ARG_VERSION, "version", "",
            "output application version and exit");

    // set defaults
    arg_graphics = false;
    arg_port = NETWORK_PORT_STR;
    arg_server = false;
    arg_connect = "";
    graphics.fullscreen = GRAPHICS_FULLSCREEN;
    graphics.resolution = PixelCoordinate(GRAPHICS_RESOLUTION_WIDTH, GRAPHICS_RESOLUTION_HEIGHT);

    // extra state
    bool resolution_default = true;
    
    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_PORT:
                arg_port = args.get_argument();
                break;

            case ARG_SERVER:
                arg_server = true;
                break;

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

            case ARG_GRAPHICS:
                arg_graphics = true;
                break;

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

                    graphics.resolution = PixelCoordinate(best_resolution.width, best_resolution.height);
                }

                break;

            case ARG_RESOLUTION:
                parse_arg_resolution(args.get_argument());
                resolution_default = false;
                break;

            case ARG_LIST_MODES:
                dump_display_modes();
                return false;

            case ARG_VERSION:
                dump_version();
                return false;

            case CL_CommandLine::REST_ARG:
                throw ArgumentError(args.get_argument());

            default:
                throw ArgumentError(std::string(1, (char) args.get_key()));

        }
    }

    // check for invalid combinations of arugments
    if (arg_server and !arg_connect.empty())
        throw ArgumentError("cannot be both server and client");

    // enable graphics by default unless server
    if (!arg_server)
        arg_graphics = true;
    
    // continue
    return true;
}
        
void Main::parse_arg_resolution (const std::string &val) {
    unsigned int w, h;
    
    // sccanf as unsigned
    if (sscanf(val.c_str(), "%ux%u", &w, &h) != 2)
        throw ArgumentError("invalid format for --resolution");
    
    // store as PixelCoordinate
    graphics.resolution = PixelCoordinate(w, h);
}
        
void Main::dump_display_modes (void) {
    const std::vector<CL_DisplayMode> &modes = Graphics::getDisplayModes();
    
    std::cout << "Available display modes:" << std::endl;

    for (std::vector<CL_DisplayMode>::const_iterator it = modes.begin(); it != modes.end(); it++)
        std::cout << "\t" << it->get_resolution().width << "x" << it->get_resolution().height << std::endl;
}
        
void Main::dump_version (void) {
    std::cout << PROJECT_LONG_NAME << " version " << PROJECT_VERSION << " built " << PROJECT_BUILD_TIMESTAMP << std::endl;
}

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

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

        // our engine
        Engine engine;
        
        // setup graphics
        if (arg_graphics)
            engine.setupGraphics(graphics);

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

        } else if (!arg_connect.empty()) {
            engine.setupNetworkClient(arg_connect, arg_port);
        
        } else {
            engine.setupSinglePlayer();
        }

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

        // XXX: handle --help
        return 1;
    } catch (CL_Error &e) {
        std::cerr << "main: CL_Error:" << e.message << std::endl;

        return 1;

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

        return 1;
    }
}