/********************************************************************** * * FreeDoko a Doppelkopf-Game * * Copyright (C) 2001-2007 by Diether Knof and Borg Enders * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You can find this license in the file 'gpl.txt'. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * * Contact: * Diether Knof dknof@gmx.de * Borg Enders borg@borgsoft.de * ********************************************************************/ /** ** ** Alpha-Version of FreeDoko ** ** developed since fall 2001 ** **/ #include "constants.h" #include #ifdef WINDOWS // for 'mkdir' #include #else // for 'mkdir' #include #endif #include "class/getopt/getopt.h" #include "utils/file.h" #include "utils/string.h" #include "utils.h" #include "basistypes.h" #include "misc/setting.h" #include "misc/translations.h" #include "misc/bug_report.h" #include "party/party.h" #include "ui/ui.wrap.h" #include "ui/ui.dummy.h" #ifdef USE_SOUND #include "sound/sound.h" #endif #ifdef USE_NETWORK #include "network/server.h" #endif #include "os/seed.h" #include "os/party_points.h" #include "os/bug_report.h" #include "os/bug_report_replay.h" #include "os/auto_bug_report.h" #ifdef CHECK_RUNTIME #include "runtime.h" #endif UI* ui = NULL; Translator translator; Setting setting_default; Setting setting; Party party; #include "utils.h" #include "player/namegen.h" #ifndef WINDOWS int main(int argc, char* argv[]) #else // ToDo: use 'WinMain' int main(int argc, char* argv[]) #endif { try { // game status ::game_status = GAMESTATUS::PROGRAMSTART; using DK::Utils::Version; using DK::Utils::Date; // make an assertion create a bug report ::debug_function = &create_assertion_bug_report; // list of all Versions ::all_versions.push_back(new Version(0,6,3, Date(2004, 5, 5), true)); ::all_versions.push_back(new Version(0,6,4, Date(2004, 6, 1), true)); ::all_versions.push_back(new Version(0,6,5, Date(2004, 6, 24), true)); ::all_versions.push_back(new Version(0,6,5,'b', Date(2004, 6, 28), true)); ::all_versions.push_back(new Version(0,6,6, Date(2004, 9, 25), true)); ::all_versions.push_back(new Version(0,6,7, Date(2004, 12, 16), true)); ::all_versions.push_back(new Version(0,6,7,'b', Date(2004, 12, 20), true)); ::all_versions.push_back(new Version(0,6,8, Date(2005, 4, 11), true)); ::all_versions.push_back(new Version(0,6,9, Date(2005, 6, 30), true)); ::all_versions.push_back(new Version(0,7,0, Date(2005, 8, 22), true)); ::all_versions.push_back(new Version(0,7,1, Date(2005, 11, 12), true)); ::all_versions.push_back(new Version(0,7,2, Date(2006, 1, 23), true)); ::all_versions.push_back(new Version(0,7,2,'b', Date(2006, 2, 9), true)); ::all_versions.push_back(new Version(0,7,3, Date(2006, 8, 10), true)); ::all_versions.push_back(new Version(0,7,4, Date(2007, 1, 18), true)); // initialisation for random functions ::srand(static_cast(::time(NULL) % RAND_MAX)); ui = new UI_Wrap; // add the bug report in the ui.wrap, // so it gets the information of the gameplay static_cast(::ui)->append(&::bug_report); // output of the seed each game static_cast(ui)->append(new OS_NS::Seed(new ofstream((::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/seed").c_str(), ios::app))); #ifndef RELEASE #if 0 // output of the party points static_cast(ui)->append(new OS_NS::PartyPoints(new ofstream("FreeDoko.party_points"))); #endif #endif #ifndef RELEASE #ifdef DKNOF //static_cast(ui)->append(new_OS(OS_TYPE::GAMEPLAY)); #endif #endif #ifdef POSTPHONED static_cast(ui)->append(new_OS(OS_TYPE::CHATTER)); #endif #ifdef USE_SOUND static_cast(::ui)->append(Sound::new_()); #endif #ifdef USE_NETWORK // open the network ::server = new Network::Server(); static_cast(::ui)->append(::server); // if not empty, a connection for the program start string connection; #endif // #ifdef USE_NETWORK #ifdef USE_THREADS if (getenv("FREEDOKO_THREADS")) THREADS = atoi(getenv("FREEDOKO_THREADS")); #endif // #ifdef USE_THREADS { // parse options // This file contains the help you get calling the program with '-?'. // It includes what arguments you can use. #ifdef WINDOWS #include "text/gpl.dos.string" #include "text/help.dos.string" #else #include "text/gpl.string" #include "text/help.string" #endif // load the settings ::setting.load(); ::party.set_seed(SEED_START); ::party.set_startplayer(STARTPLAYER); // the ui selected by command line option UI* selected_ui = NULL; // do not load anything, if FreeDoko is called only with '-' if (!( (argc >= 2) && (argv[1][0] == '-') && (argv[1][1] == '\0') ) ) { // load the rules ::party.rule().load(::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/" + ::setting(Setting::RULES_DIRECTORY) + "/last"); ::party.load_players(); { // load the default party string const party_filename = (::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/" + ::setting(Setting::PARTIES_DIRECTORY) + "/" + "current"); if (DK::Utils::File::isfile(party_filename)) if (::party.load(party_filename)) ::game_status = GAMESTATUS::PARTY_INITIAL_LOADED; } // load the default party } // if (do load the last entries) // parse the options GetOpt::Option option; while (option = GetOpt::getopt(argc, argv, "help", '?', GetOpt::Syntax::BOOL, "hilfe", 'h', GetOpt::Syntax::BOOL, "version", 'v', GetOpt::Syntax::BOOL, "license", 'L', GetOpt::Syntax::BOOL, "defines", '\0', GetOpt::Syntax::BOOL, "directories", '\0', GetOpt::Syntax::BOOL, "ui", 'u', GetOpt::Syntax::BSTRING, "settings", '\0', GetOpt::Syntax::BSTRING, "name", 'n', GetOpt::Syntax::BSTRING, "language", 'l', GetOpt::Syntax::BSTRING, "seed", 's', GetOpt::Syntax::BSTRING, "startplayer", 'p', GetOpt::Syntax::BSTRING, "cardset", 'C', GetOpt::Syntax::BSTRING, "cards_height", 'H', GetOpt::Syntax::UNSIGNED, "bug_report", 'b', GetOpt::Syntax::BSTRING, "auto_bug_reports", '\0', GetOpt::Syntax::BOOL, "fast_play", 'F', GetOpt::Syntax::INT, #ifdef USE_THREADS "threads", 'T', GetOpt::Syntax::UNSIGNED, #endif #ifdef USE_NETWORK "start_server", '\0', GetOpt::Syntax::BOOL, "connect", '\0', GetOpt::Syntax::BSTRING, #endif // end of arguments "", 0, GetOpt::Syntax::END ) // { } ) { if (option.fail()) { // wrong usage cerr << argv[0] << "\n" << "wrong usage: " << option.error() << " " << option.value_string() << "\n" << "For the syntax type '" << argv[0] << " -?'" << endl; return EXIT_FAILURE; } // if (option.fail()) if ((option.name() == "help") || (option.name() == "hilfe")) { // output of the help cout << help_string << endl; return EXIT_SUCCESS; } else if (option.name() == "version") { // output of the version cout << "FreeDoko " << ::version << "\n" << "\n" << GPL_string << endl; return EXIT_SUCCESS; } else if (option.name() == "license") { // output of the license (GPL) cout << "FreeDoko -- License:\n\n" << GPL << '\n' << '\n' << "----------\n" << '\n' << "Cardset '" << ::setting(Setting::CARDSET) << "' license:\n" << cardset_license << endl; return EXIT_SUCCESS; } else if (option.name() == "defines") { #ifdef VERSION_DESCRIPTION cout << "VERSION_DESCRIPTION = " << VERSION_DESCRIPTION << '\n'; #endif #ifdef RELEASE cout << "RELEASE\n"; #endif #ifdef ASSERTION_GENERATES_SEGFAULT cout << "ASSERTION_GENERATES_SEGFAULT\n"; #endif #ifdef DKNOF cout << "DKNOF\n"; #endif #ifdef BENDERS cout << "BENDERS\n"; #endif cout << '\n'; cout << "directories:\n"; #ifdef PUBLIC_DATA_DIRECTORY_VALUE cout << " PUBLIC_DATA_DIRECTORY_VALUE = " << PUBLIC_DATA_DIRECTORY_VALUE << '\n'; #endif #ifdef MANUAL_DIRECTORY_VALUE cout << " MANUAL_DIRECTORY_VALUE = " << MANUAL_DIRECTORY_VALUE << '\n'; #endif cout << '\n'; cout << "modules:\n"; #ifdef USE_UI_TEXT cout << " USE_UI_TEXT = " << USE_UI_TEXT << '\n'; #endif #ifdef USE_UI_GTKMM cout << " USE_UI_GTKMM = " << USE_UI_GTKMM << '\n'; #endif #ifdef USE_SOUND cout << " USE_SOUND = " << USE_SOUND << '\n'; #ifdef USE_SOUND_ALUT cout << " USE_SOUND_ALUT = " << USE_SOUND_ALUT << '\n'; #endif #ifdef USE_SOUND_APLAY cout << " USE_SOUND_APLAY = " << USE_SOUND_APLAY << '\n'; #endif #endif #ifdef USE_NETWORK cout << " USE_NETWORK = " << USE_NETWORK << '\n'; #endif #ifdef USE_THREADS cout << " USE_THREADS (" << THREADS << ")\n"; #endif return EXIT_SUCCESS; } else if (option.name() == "directories") { { // current directory char current_dir[PATH_MAX + 1]; current_dir[PATH_MAX] = '\0'; if (getcwd(current_dir, PATH_MAX) != NULL) cout << "current directory\n" << " " << current_dir << '\n'; } // current directory { // data directory cout << "data directories\n"; list const directories = ::setting.data_directories(); for (list::const_iterator d = directories.begin(); d != directories.end(); ++d) cout << " " << *d << '\n'; } // data directory return EXIT_SUCCESS; } else if (option.name() == "ui") { delete selected_ui; selected_ui = NULL; if (option.value_string() == "none") selected_ui = UI::new_(UI_TYPE::DUMMY); #ifdef USE_UI_TEXT else if (option.value_string() == "text") selected_ui = UI::new_(UI_TYPE::TEXT); #endif #ifdef USE_UI_GTKMM else if (option.value_string() == "gtkmm") selected_ui = UI::new_(UI_TYPE::GTKMM_DOKO); #endif else { cerr << "ui '" << option.value_string() << "' not implemented.\n" << "exiting"; return EXIT_FAILURE; } // if (option.value_string() == "...") } else if (option.name() == "settings") { setting.load(option.value_string()); } else if (option.name() == "name") { setting.set(Setting::NAME, option.value_string()); } else if (option.name() == "language") { setting.set(Setting::LANGUAGE, option.value_string()); } else if (option.name() == "seed") { string const seed_str = option.value_string(); if ( (seed_str == "random") || (seed_str == "r") ) { ::party.set_random_seed(); } else { // if !(random seed) istringstream istr(seed_str); unsigned no = 0; istr >> no; if (istr.fail()) { cerr << "Seed not valid: '" << seed_str << "'.\n" << "Must be a number or 'random'\n" << "Ignoring the value.\n" << endl; continue; } ::party.set_seed(no); } // if !(random seed) } else if (option.name() == "startplayer") { string const startplayer_str = option.value_string(); if ( (startplayer_str == "random") || (startplayer_str == "r") ) { ::party.set_random_startplayer(); } else { // if !(random startplayer) istringstream istr(startplayer_str); unsigned no = 0; istr >> no; if ( istr.fail() || !( (no >= 1) && (no <= ::party.playerno()) ) ) { cerr << "Startplayer number '" << startplayer_str << "' not valid.\n" << "Must be between '1' and '" << ::party.playerno() << "'.\n" << "Ignoring the value\n" << endl; continue; } ::party.set_startplayer(no - 1); } // if !(random startplayer) } else if (option.name() == "graphic_size") { setting.set(Setting::CARDS_HEIGHT, option.value(GetOpt::Option::UNSIGNED)); } else if (option.name() == "cardset") { setting.set(Setting::CARDSET, option.value_string()); } else if (option.name() == "bug_report") { OS_NS::BugReportReplay* bug_report_replay = new OS_NS::BugReportReplay(option.value_string()); if (!bug_report_replay->loaded()) { cerr << "Error loading the bug report '" << option.value_string() << "'" << endl; delete bug_report_replay; } else { ::game_status = GAMESTATUS::PROGRAMSTART; } } else if (option.name() == "auto_bug_reports") { // automatic bug reports static_cast(ui)->append(new OS_NS::AutoBugReport); } else if (option.name() == "fast_play") { // PAUSE = 1, // skip pauses // PLAYER = PAUSE << 1 // change human players to ais // RANDOM_AI = PLAYER << 1, // set the players to the random ai // FULL_TRICK = RANDOM_AI << 1 // skip full trick window // PARTY_START = FULL_TRICK << 1 // automatical start party // GAME_FINISHED = PARTY_START << 1 // skip game finished dialog // SHOW_ALL_HANDS = GAME_FINISHED << 1 // show all hands FAST_PLAY = option.value(GetOpt::Option::INT); if (FAST_PLAY & FAST_NS::PLAYER) { // change all humans to ai's for (unsigned p = 0; p < ::party.playerno(); p++) { if (::party.player(p).type() == Player::HUMAN) ::party.set_playertype(p, Player::AI); } } // if (FAST_PLAY | FAST_NS::PLAYER) if (FAST_PLAY & FAST_NS::RANDOM_AI) { // set all ai's to random ai's for (unsigned p = 0; p < ::party.playerno(); p++) { if (::party.player(p).type() == Player::AI) ::party.set_playertype(p, Player::AI_RANDOM); } } // if (FAST_PLAY & FAST_NS::RANDOM_AI) #ifdef USE_THREADS } else if (option.name() == "threads") { THREADS = option.value(GetOpt::Option::UNSIGNED); #endif // #ifdef USE_THREADS #ifdef USE_NETWORK } else if (option.name() == "start_server") { ::server->create_listener(Network::Server::FREEDOKO_STANDARD_PORT); } else if (option.name() == "connect") { connection = option.value_string(); #endif // #ifdef USE_NETWORK } else if (option.name().empty()) { if (option.value_string() == "-") continue; #ifdef USE_NETWORK if (!server->empty()) { cerr << "Cannot load party when the server is not empty.\n" << "ignoring party file.\n"; #ifndef RELEASE exit(1); #endif continue; } // if (!server->empty()) #endif // #ifdef USE_NETWORK string const party_filename = option.value_string(); try { ::party.load(party_filename); ::game_status = GAMESTATUS::PARTY_INITIAL_LOADED; } catch (ReadException const& exception) { ::ui->information(::translation("Party load error: '%sfilename%'", party_filename) + "\n" + exception.message(), INFORMATION::PROBLEM); } } else { // if (option.nam() == ...) cerr << "Option '" << option.name() << "' unknown!" << endl; exit(1); } // if (option.name() == ...) } // while (getopt()) { // set the ui if (selected_ui) { } else if (SEED_OUT) { selected_ui = UI::new_(UI_TYPE::DUMMY); } else { // if !(selected_ui) && (SEED_OUT) selected_ui #if defined(USE_UI_GTKMM) = UI::new_(UI_TYPE::GTKMM_DOKO); #elif defined(USE_UI_AATEXT) = UI::new_(UI_TYPE::AATEXT); #elif defined(USE_UI_TEXT) = UI::new_(UI_TYPE::TEXT); #else = UI::new_(UI_TYPE::DUMMY); #endif } // if !(selected_ui) && (SEED_OUT) if (selected_ui) { delete static_cast(::ui)->ui; static_cast(::ui)->ui = selected_ui; } // if (selected_ui) } // set the ui } // parse options #ifdef USE_THREADS if (THREADS == 0) { // ToDo // automatic detection of the number of cpu's // //"egrep -c ^cpu[0-9]+ /proc/stat || :"; if (THREADS == 0) THREADS = 1; } // if (THREADS == 0) cout << "Thread support is experimental. Use at your own risk.\n"; #endif // #ifdef USE_THREADS { // if this is the first start, create some directories vector dirnames; dirnames.push_back(::setting(Setting::PRIVATE_DATA_DIRECTORY)); dirnames.push_back(::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/" + ::setting(Setting::PARTIES_DIRECTORY)); dirnames.push_back(::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/" + ::setting(Setting::RULES_DIRECTORY)); dirnames.push_back(::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/" + ::setting(Setting::AI_DIRECTORY)); Translator::Translation information = ::translation(""); for (vector::iterator dir = dirnames.begin(); dir != dirnames.end(); dir++) { if (!DK::Utils::File::isdirectory(*dir)) { information += (::translation("Message::creating directory \'%sdir%\'", *dir) + "\n"); #ifdef WINDOWS mkdir(dir->c_str()); #else mkdir(dir->c_str(), 00700); #endif } // if (!isdirectory(*dir)) } // for (dir \in dirnames) if (!information.empty()) ::ui->information(information, INFORMATION::NORMAL); } // if this is the first start, create some directories { // read the version of the last usage of FreeDoko and write the new version // read the old version DK::Utils::Version* const version_old = DK::Utils::Version::new_from_file(::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/Version"); { // write the new version ofstream ostr((::setting(Setting::PRIVATE_DATA_DIRECTORY) + "/Version").c_str()); if (!ostr.fail()) ostr << version; } // write the new version if (version_old == NULL) { // This seems to be the first start -- give some introduction. ::ui->first_run(::translation("Greeting::first run")); } else { // if !(version_old == NULL) #ifdef RELEASE // give information if the version is updated if (*version_old < version) ::ui->program_updated(*version_old); #endif delete version_old; } // if (version_old != NULL) } // read the version of the last usage of FreeDoko and write the new version // init the ui ::ui->init(argc = 1, argv); // If no game has been saved in the party, the ui gets a problem with // a random startplayer. // So we check here, whether a game was already played. // Here, because so the rules and players are taken from the saved party // and not from the other files. if (::party.gameno() == 0) ::game_status = GAMESTATUS::PARTY_NEW; if (SEED_OUT) ::party.rule().reset_to_seed_statistics(); // now the preparations have finished if (::game_status == GAMESTATUS::PROGRAMSTART) ::game_status = GAMESTATUS::PARTY_NEW; while (::game_status != GAMESTATUS::QUIT) { try { try { ::party.open(); ::ui->party_open(::party); #ifdef USE_NETWORK if (!connection.empty()) { ::server->create_connection(connection, Network::Server::FREEDOKO_STANDARD_PORT); connection.clear(); ::game_status = GAMESTATUS::PARTY_NEW; } #endif if ( (::game_status == GAMESTATUS::PARTY_INITIAL_LOADED) || (::game_status == GAMESTATUS::PARTY_LOADED) ) { ::ui->party_loaded(); } else { ::party.remove_auto_save(); ::ui->party_get_settings(); } #ifdef WORKAROUND // in a network game, the gamestatus is set to PARTY_PLAY if (::game_status == GAMESTATUS::PARTY_PLAY) ::game_status = GAMESTATUS::PARTY_NEW; #endif if (::game_status == GAMESTATUS::PARTY_NEW) ::party.init(); // start playing ::game_status = GAMESTATUS::PARTY_PLAY; ::party.play(); } catch (GameStatus const& gs) { if (gs == GAMESTATUS::PARTY_FINISHED) ::game_status = GAMESTATUS::PARTY_FINISHED; else throw; } // try if (FAST_PLAY & FAST_NS::QUIT_WHEN_FINISHED) throw(GAMESTATUS::QUIT); ::ui->party_finish(); ::game_status = GAMESTATUS::PARTY_NEW; } catch (GameStatus const& gs) { DEBUG_ASSERTION(( (gs == GAMESTATUS::PARTY_NEW) || (gs == GAMESTATUS::PARTY_LOADED) || (gs == GAMESTATUS::QUIT)), "main():\n" " caught gamestatus '" << gs << "'"); ::game_status = gs; } catch (...) { ::ui->party_close(); ::party.close(); throw; } // last chance to save the party and the players if (::game_status == GAMESTATUS::QUIT) ::party.save_changes(); ::ui->party_close(); if (::game_status != GAMESTATUS::PARTY_LOADED) ::party.close(); } // while (game_status != QUIT) // last chance to save the changes ::setting.save(); } catch (std::exception const& e) { cerr << "main()\n" << " uncaught standard exception: " << e.what() << " (" << &e << ")" << endl; #ifdef WORKAROUND // with 'throw' the ui is not destructed :-( //return EXIT_FAILURE; #endif throw; } catch (Card const& card) { cerr << "main()\n" << " caught card exception: " << card << endl; return EXIT_FAILURE; } catch (...) { cerr << "main()\n" << " uncaught unknown exception" << endl; #ifdef WORKAROUND // with 'throw' the ui is not destructed :-( return EXIT_FAILURE; #endif throw; } // try // clean up delete ::ui; #ifdef CHECK_RUNTIME cout << ::runtime << endl; #endif return EXIT_SUCCESS; } // int main(argc, argv)