summaryrefslogtreecommitdiffstats
path: root/game/shared/game.cc
blob: 73de8b51c26bd13a12d8dd754ef3455efb494086 (plain)
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
#include "shared/pch.hh"

#include "shared/game.hh"

#include "core/cmdline.hh"

static std::filesystem::path get_gamepath(void)
{
    if(auto gamepath = cmdline::get("gamepath")) {
        // Allow users and third-party launchers to override
        // content location. Perhaps this would work to allow
        // for a Minecraft-like versioning approach?
        return std::filesystem::path(gamepath);
    }

    return std::filesystem::current_path() / "data";
}

static std::filesystem::path get_userpath(void)
{
    if(auto userpath = cmdline::get("userpath")) {
        // Allow users and third-party launchers to override
        // user data location. Perhaps this would work to allow
        // for a Minecraft-like versioning approach?
        return std::filesystem::path(userpath);
    }

    if(auto win_appdata = std::getenv("APPDATA")) {
        // On pre-seven Windows systems it's just AppData
        // On post-seven Windows systems it's AppData/Roaming
        return std::filesystem::path(win_appdata) / "voxelius";
    }

    if(auto xdg_home = std::getenv("XDG_DATA_HOME")) {
        // Systems with an active X11/Wayland session
        // theoretically should have this defined, and
        // it can be different from ${HOME} (I think).
        return std::filesystem::path(xdg_home) / ".voxelius";
    }

    if(auto unix_home = std::getenv("HOME")) {
        // Any spherical UNIX/UNIX-like system in vacuum
        // has this defined for every single user process.
        return std::filesystem::path(unix_home) / ".voxelius";
    }

    // Give up and save stuff into CWD
    return std::filesystem::current_path();
}

void shared_game::init(int argc, char** argv)
{
    auto logger = spdlog::default_logger();
    auto& logger_sinks = logger->sinks();

    logger_sinks.clear();
    logger_sinks.push_back(std::make_shared<spdlog::sinks::stderr_color_sink_mt>());
    logger_sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("voxelius.log", false));

#if defined(NDEBUG)
    constexpr auto default_loglevel = spdlog::level::info;
#else
    constexpr auto default_loglevel = spdlog::level::trace;
#endif

    if(cmdline::contains("quiet")) {
        logger->set_level(spdlog::level::warn);
    } else if(cmdline::contains("verbose")) {
        logger->set_level(spdlog::level::trace);
    } else {
        logger->set_level(default_loglevel);
    }

    logger->set_pattern("%H:%M:%S.%e %^[%L]%$ %v");
    logger->flush();

    if(!PHYSFS_init(argv[0])) {
        spdlog::critical("physfs: init failed: {}", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
        std::terminate();
    }

    auto gamepath = get_gamepath();
    auto userpath = get_userpath();

    spdlog::info("shared_game: set gamepath to {}", gamepath.string());
    spdlog::info("shared_game: set userpath to {}", userpath.string());

    std::error_code ignore_error = {};
    std::filesystem::create_directories(gamepath, ignore_error);
    std::filesystem::create_directories(userpath, ignore_error);

    if(!PHYSFS_mount(gamepath.string().c_str(), nullptr, false)) {
        spdlog::critical("physfs: mount {} failed: {}", gamepath.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
        std::terminate();
    }

    if(!PHYSFS_mount(userpath.string().c_str(), nullptr, false)) {
        spdlog::critical("physfs: mount {} failed: {}", userpath.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
        std::terminate();
    }

    if(!PHYSFS_setWriteDir(userpath.string().c_str())) {
        spdlog::critical("physfs: setwritedir {} failed: {}", userpath.string(), PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
        std::terminate();
    }

    if(enet_initialize()) {
        spdlog::critical("enet: init failed");
        std::terminate();
    }
}

void shared_game::deinit(void)
{
    enet_deinitialize();

    if(!PHYSFS_deinit()) {
        spdlog::critical("physfs: deinit failed: {}", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
        std::terminate();
    }
}