add --log-level option, improve Config/Logger documentation, fix NETWORK_EANBLED typos in Engine
#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 0;
// 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 0;
} catch (ArgumentError &e) {
std::cerr
<< "Error: " << e.what() << std::endl
<< std::endl;
args.print_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;
}
}