File: chmain.cpp

package info (click to toggle)
freeorion 0.5.1-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 194,940 kB
  • sloc: cpp: 186,508; python: 40,969; ansic: 1,164; xml: 719; makefile: 32; sh: 7
file content (328 lines) | stat: -rw-r--r-- 16,491 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#include "GGHumanClientApp.h"
#include "../../util/OptionsDB.h"
#include "../../util/Directories.h"
#include "../../util/Logger.h"
#include "../../util/Version.h"
#include "../../util/i18n.h"
#include "../../UI/Hotkeys.h"

#include <GG/utf8/checked.h>

#include <boost/format.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/fstream.hpp>

#include <thread>
#include <chrono>
#include <iostream>

#if defined(FREEORION_LINUX)
/* Freeorion aims to have exceptions handled and operation continue normally.
An example of good exception handling is the exceptions caught around config.xml loading.
After catching and informing the user it continues normally with the default values.

An exception that can not be handled should allow freeorion to crash and keep
a complete stack trace of the intial exception.
Some platforms do not support this behavior.

When FREEORION_CHMAIN_KEEP_BACKTRACE is defined, do not catch an unhandled exceptions,
unroll and hide the stack trace, print a message and still crash anyways. */
#define FREEORION_CHMAIN_KEEP_STACKTRACE
#endif

// The STORE_FULLSCREEN_FLAG parameter below controls whether the fullscreen
// option is stored in the XML config file.  On Win32 it is not, because the
// installed version of FO is run with the command-line flag added in as
// appropriate.
#ifdef FREEORION_WIN32
constexpr bool STORE_FULLSCREEN_FLAG = false;
// Windows keeps good care of the resolution state itself,
// so there is no reason to default to not touching it.
constexpr bool FAKE_MODE_CHANGE_FLAG = false;
#else
constexpr bool STORE_FULLSCREEN_FLAG = true;
// The X window system does not always work
// well with resolution changes, so we avoid them
// by default
constexpr bool FAKE_MODE_CHANGE_FLAG = true;
#endif

int mainSetupAndRun();
int mainConfigOptionsSetup(const std::vector<std::string>& args);


#if defined(FREEORION_LINUX) || defined(FREEORION_FREEBSD) || defined(FREEORION_OPENBSD) || defined(FREEORION_NETBSD) || defined(FREEORION_DRAGONFLY) || defined(FREEORION_HAIKU)
int main(int argc, char* argv[]) {
    // copy command line arguments to vector
    std::vector<std::string> args;
    for (int i = 0; i < argc; ++i)
        args.push_back(argv[i]);

    // set options from command line or config.xml, or generate config.xml
    if (mainConfigOptionsSetup(args) != 0) {
        std::cerr << "main() failed config." << std::endl;
        ShutdownLoggingSystemFileSink() ;
        return 1;
    }
#endif
#ifdef FREEORION_WIN32
int wmain(int argc, wchar_t* argv[], wchar_t* envp[]) {
    // copy UTF-16 command line arguments to UTF-8 vector
    std::vector<std::string> args;
    for (int i = 0; i < argc; ++i) {
        std::wstring argi16(argv[i]);
        std::string argi8;
        utf8::utf16to8(argi16.begin(), argi16.end(), std::back_inserter(argi8));
        args.push_back(argi8);
    }

    // set options from command line or config.xml, or generate config.xml
    if (mainConfigOptionsSetup(args) != 0) {
        std::cerr << "main() failed config." << std::endl;
        return 1;
    }
#endif
#ifndef FREEORION_MACOSX
    // did the player request help output?
    auto help_arg = GetOptionsDB().Get<std::string>("help");
    if (help_arg != "NOOP") {
        ShutdownLoggingSystemFileSink();
        GetOptionsDB().GetUsage(std::cout, help_arg, true);
        return 0;   // quit without actually starting game
    }

    // did the player request the version output?
    if (GetOptionsDB().Get<bool>("version")) {
        ShutdownLoggingSystemFileSink();
        std::cout << "FreeOrion Human Client " << FreeOrionVersionString() << std::endl;
        return 0;   // quit without actually starting game
    }

    // set up rendering and run game
    if (mainSetupAndRun() != 0) {
        ShutdownLoggingSystemFileSink();
        std::cerr << "main() failed to setup or run SDL." << std::endl;
        return 1;
    }
    ShutdownLoggingSystemFileSink();
    return 0;
}
#endif


int mainConfigOptionsSetup(const std::vector<std::string>& args) {
    InitDirs((args.empty() ? "" : args.front()));

    // read and process command-line arguments, if any
#ifndef FREEORION_CHMAIN_KEEP_STACKTRACE
    try {
#endif
        // add entries in options DB that have no other obvious place
        GetOptionsDB().Add<std::string>('h', "help",                UserStringNop("OPTIONS_DB_HELP"),                   "NOOP",
                                        Validator<std::string>(),                                                       false);
        GetOptionsDB().AddFlag('v', "version",                      UserStringNop("OPTIONS_DB_VERSION"),                false,
                               "version");
        GetOptionsDB().AddFlag('g', "generate-config-xml",          UserStringNop("OPTIONS_DB_GENERATE_CONFIG_XML"),    false);
        GetOptionsDB().AddFlag('f', "video.fullscreen.enabled",     UserStringNop("OPTIONS_DB_FULLSCREEN"),             STORE_FULLSCREEN_FLAG);
        GetOptionsDB().Add("video.fullscreen.reset",                UserStringNop("OPTIONS_DB_RESET_FSSIZE"),           true);
        GetOptionsDB().Add("video.fullscreen.fake.enabled",         UserStringNop("OPTIONS_DB_FAKE_MODE_CHANGE"),       FAKE_MODE_CHANGE_FLAG);
        GetOptionsDB().Add("video.monitor.id",                      UserStringNop("OPTIONS_DB_FULLSCREEN_MONITOR_ID"),  0,
                           RangedValidator<int>(0, 5));
        GetOptionsDB().AddFlag("continue",                          UserStringNop("OPTIONS_DB_CONTINUE"),               false);
        GetOptionsDB().AddFlag("auto-quit",                         UserStringNop("OPTIONS_DB_AUTO_QUIT"),              false);
        GetOptionsDB().Add("auto-advance-n-turns",                  UserStringNop("OPTIONS_DB_AUTO_N_TURNS"),           0,
                           RangedValidator<int>(0, 400),                                                           false);
        GetOptionsDB().Add("audio.music.enabled",                   UserStringNop("OPTIONS_DB_MUSIC_ON"),               true);
        GetOptionsDB().Add("audio.effects.enabled",                 UserStringNop("OPTIONS_DB_SOUND_ON"),               true);
        GetOptionsDB().Add("version.string",                        UserStringNop("OPTIONS_DB_VERSION_STRING"),         FreeOrionVersionString(),
                           Validator<std::string>(),                                                                    true);
        GetOptionsDB().AddFlag('r', "render-simple",                UserStringNop("OPTIONS_DB_RENDER_SIMPLE"),          false);
#ifdef FREEORION_WIN32
        GetOptionsDB().Add("misc.server-local-binary.path",         UserStringNop("OPTIONS_DB_FREEORIOND_PATH"),        PathToString(GetBinDir() / "freeoriond.exe"));
#else
        GetOptionsDB().Add<std::string>("misc.server-local-binary.path", UserStringNop("OPTIONS_DB_FREEORIOND_PATH"),   PathToString(GetBinDir() / "freeoriond"));
#endif

        // add sections for option sorting
        GetOptionsDB().AddSection("audio", UserStringNop("OPTIONS_DB_SECTION_AUDIO"));
        GetOptionsDB().AddSection("audio.music", UserStringNop("OPTIONS_DB_SECTION_AUDIO_MUSIC"));
        GetOptionsDB().AddSection("audio.effects", UserStringNop("OPTIONS_DB_SECTION_AUDIO_EFFECTS"));
        GetOptionsDB().AddSection("audio.effects.paths", UserStringNop("OPTIONS_DB_SECTION_AUDIO_EFFECTS_PATHS"),
                                  [](std::string_view name)->bool {
                                      static constexpr std::string_view suffix{"sound.path"};
                                      return name.size() > suffix.size() &&
                                             name.substr(name.size() - suffix.size()) == suffix;
                                  });
        GetOptionsDB().AddSection("effects", UserStringNop("OPTIONS_DB_SECTION_EFFECTS"));
        GetOptionsDB().AddSection("logging", UserStringNop("OPTIONS_DB_SECTION_LOGGING"));
        GetOptionsDB().AddSection("network", UserStringNop("OPTIONS_DB_SECTION_NETWORK"));
        GetOptionsDB().AddSection("resource", UserStringNop("OPTIONS_DB_SECTION_RESOURCE"));
        GetOptionsDB().AddSection("save", UserStringNop("OPTIONS_DB_SECTION_SAVE"));
        GetOptionsDB().AddSection("setup", UserStringNop("OPTIONS_DB_SECTION_SETUP"));
        GetOptionsDB().AddSection("ui", UserStringNop("OPTIONS_DB_SECTION_UI"));
        GetOptionsDB().AddSection("ui.colors", UserStringNop("OPTIONS_DB_SECTION_UI_COLORS"),
                                  [](std::string_view name)->bool {
                                      static constexpr std::string_view suffix{".color"};
                                      return name.size() > suffix.size() &&
                                             name.substr(name.size() - suffix.size()) == suffix;
                                  });
        GetOptionsDB().AddSection("ui.hotkeys", UserStringNop("OPTIONS_DB_SECTION_UI_HOTKEYS"),
                                  [](std::string_view name)->bool {
                                      static constexpr std::string_view suffix{".hotkey"};
                                      return name.size() > suffix.size() &&
                                             name.substr(name.size() - suffix.size()) == suffix;
                                  });
        GetOptionsDB().AddSection("version", UserStringNop("OPTIONS_DB_SECTION_VERSION"));
        GetOptionsDB().AddSection("video", UserStringNop("OPTIONS_DB_SECTION_VIDEO"));
        GetOptionsDB().AddSection("video.fullscreen", UserStringNop("OPTIONS_DB_SECTION_VIDEO_FULLSCREEN"));
        GetOptionsDB().AddSection("video.windowed", UserStringNop("OPTIONS_DB_SECTION_VIDEO_WINDOWED"));

        // Add the keyboard shortcuts
        Hotkey::AddOptions(GetOptionsDB());

        // if config.xml and persistent_config.xml are present, read and set options entries
        GetOptionsDB().SetFromFile(GetConfigPath(), FreeOrionVersionString());
        GetOptionsDB().SetFromFile(GetPersistentConfigPath());

        // override previously-saved and default options with command line parameters and flags
        GetOptionsDB().SetFromCommandLine(args);

        CompleteXDGMigration();

        // Handle the case where the resource.path does not exist anymore
        // gracefully by resetting it to the standard path into the
        // application bundle.  This may happen if a previous installed
        // version of FreeOrion was residing in a different directory.
        if (!boost::filesystem::exists(GetResourceDir()) ||
            !boost::filesystem::exists(GetResourceDir() / "credits.xml") ||
            !boost::filesystem::exists(GetResourceDir() / "data" / "art" / "misc" / "missing.png"))
        {
            DebugLogger() << "Resources directory from config.xml missing or does not contain expected files. Resetting to default.";

            GetOptionsDB().Set<std::string>("resource.path", "");

            // double-check that resetting actually fixed things...
            if (!boost::filesystem::exists(GetResourceDir()) ||
                !boost::filesystem::exists(GetResourceDir() / "credits.xml") ||
                !boost::filesystem::exists(GetResourceDir() / "data" / "art" / "misc" / "missing.png"))
            {
                DebugLogger() << "Default Resources directory missing or does not contain expected files. Cannot start game.";
                throw std::runtime_error("Unable to load game resources at default location: " +
                                         PathToString(GetResourceDir()) + " : Install may be broken.");
            }
        }


        // did the player request generation of config.xml, saving the default (or current) options to disk?
        if (GetOptionsDB().Get<bool>("generate-config-xml")) {
            try {
                GetOptionsDB().Commit(false);
            } catch (...) {
                std::cerr << UserString("UNABLE_TO_WRITE_CONFIG_XML") << std::endl;
            }
        }

        if (GetOptionsDB().Get<bool>("render-simple")) {
            GetOptionsDB().Set<bool>("ui.map.background.gas.shown", false);
            GetOptionsDB().Set<bool>("ui.map.background.starfields.shown", false);
            GetOptionsDB().Set<bool>("video.fps.shown", true);
        }

#ifndef FREEORION_CHMAIN_KEEP_STACKTRACE
    } catch (const std::invalid_argument& e) {
        std::cerr << "main() caught exception(std::invalid_argument): " << e.what() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));
        return 1;
    } catch (const std::runtime_error& e) {
        std::cerr << "main() caught exception(std::runtime_error): " << e.what() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));
        return 1;
    } catch (const std::exception& e) {
        std::cerr << "main() caught exception(std::exception): " << e.what() << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(3));
        return 1;
    }
#endif

    return 0;
}


int mainSetupAndRun() {
#ifndef FREEORION_CHMAIN_KEEP_STACKTRACE
    try {
#endif
        RegisterOptions(&GGHumanClientApp::AddWindowSizeOptionsAfterMainStart);

        bool fullscreen = GetOptionsDB().Get<bool>("video.fullscreen.enabled");
        bool fake_mode_change = GetOptionsDB().Get<bool>("video.fullscreen.fake.enabled");

        auto width_height = GGHumanClientApp::GetWindowWidthHeight();
        int width(width_height.first), height(width_height.second);
        auto left_top = GGHumanClientApp::GetWindowLeftTop();
        int left(left_top.first), top(left_top.second);

#ifdef FREEORION_WIN32
#  ifdef IDI_ICON1
        // set window icon to embedded application icon
        HWND hwnd;
        window->getCustomAttribute("WINDOW", &hwnd);
        HINSTANCE hInst = (HINSTANCE)GetModuleHandle(nullptr);
        SetClassLong (hwnd, GCL_HICON,
            (LONG)LoadIcon (hInst, MAKEINTRESOURCE (IDI_ICON1)));
#  endif
#endif

        GGHumanClientApp app(width, height, true, "FreeOrion " + FreeOrionVersionString(),
                             left, top, fullscreen, fake_mode_change);

        if (GetOptionsDB().Get<bool>("quickstart")) {
            // immediately start the server, establish network connections, and
            // go into a single player game, using default universe options (a
            // standard quickstart, without requiring the user to click the
            // quickstart button).
            app.NewSinglePlayerGame(true);  // acceptable to call before app()
        }

        if (GetOptionsDB().Get<bool>("continue")) {
            // immediately start the server, establish network connections, and
            // go into a single player game, continuing from the newest
            // save game.
            app.ContinueSinglePlayerGame();  // acceptable to call before app()
        }

        std::string load_filename = GetOptionsDB().Get<std::string>("load");
        if (!load_filename.empty()) {
            // immediately start the server, establish network connections, and
            // go into a single player game, loading the indicated file
            // (without requiring the user to click the load button).
            app.LoadSinglePlayerGame(load_filename);  // acceptable to call before app()
        }

        // run rendering loop
        app.Run();
#ifndef FREEORION_CHMAIN_KEEP_STACKTRACE
    } catch (const std::invalid_argument& e) {
        ErrorLogger() << "main() caught exception(std::invalid_argument): " << e.what();
        std::cerr << "main() caught exception(std::invalid_arg): " << e.what() << std::endl;
        return 1;
    } catch (const std::runtime_error& e) {
        ErrorLogger() << "main() caught exception(std::runtime_error): " << e.what();
        std::cerr << "main() caught exception(std::runtime_error): " << e.what() << std::endl;
        return 1;
    } catch (const boost::io::format_error& e) {
        ErrorLogger() << "main() caught exception(boost::io::format_error): " << e.what();
        std::cerr << "main() caught exception(boost::io::format_error): " << e.what() << std::endl;
        return 1;
    } catch (const std::exception& e) {
        ErrorLogger() << "main() caught exception(std::exception): " << e.what();
        std::cerr << "main() caught exception(std::exception): " << e.what() << std::endl;
        return 1;
    }
#endif

    DebugLogger() << "Human client main exited cleanly.";
    return 0;
}

#undef FREEORION_CHMAIN_KEEP_STACKTRACE