1 #include "Application.hpp"
2 #include "Environment.hpp"
6 #include "../client/MasterState.hpp"
7 #include "../io/filesystem.hpp"
8 #include "../io/WorldSave.hpp"
9 #include "../server/ServerState.hpp"
10 #include "../standalone/MasterState.hpp"
23 string default_asset_path() {
24 char *base = SDL_GetBasePath();
31 string default_save_path() {
33 char *base = SDL_GetBasePath();
39 char *pref = SDL_GetPrefPath("localhorst", "blank");
50 HeadlessEnvironment::HeadlessEnvironment(const Config &config)
52 , loader(config.asset_path)
58 string HeadlessEnvironment::Config::GetWorldPath(const string &world_name) const {
59 return save_path + "worlds/" + world_name + '/';
62 string HeadlessEnvironment::Config::GetWorldPath(const string &world_name, const string &host_name) const {
63 return save_path + "cache/" + host_name + '/' + world_name + '/';
66 Environment::Environment(Window &win, const Config &config)
67 : HeadlessEnvironment(config)
77 string keys_path = config.save_path + "keys.conf";
78 if (!is_file(keys_path)) {
79 std::ofstream file(keys_path);
82 std::ifstream file(keys_path);
88 Runtime::Runtime() noexcept
99 void Runtime::ReadArgs(int argc, const char *const *argv) {
100 if (argc <= 0) return;
106 for (int i = 1; i < argc; ++i) {
107 const char *arg = argv[i];
108 if (!arg || arg[0] == '\0') {
109 cerr << "warning: found empty argument at position " << i << endl;
112 if (options && arg[0] == '-') {
113 if (arg[1] == '\0') {
114 cerr << "warning: incomplete option list at position " << i << endl;
115 } else if (arg[1] == '-') {
116 if (arg[2] == '\0') {
120 const char *param = arg + 2;
122 if (strcmp(param, "no-vsync") == 0) {
123 config.game.video.vsync = false;
124 } else if (strcmp(param, "no-keyboard") == 0) {
125 config.game.input.keyboard = false;
126 } else if (strcmp(param, "no-mouse") == 0) {
127 config.game.input.mouse = false;
128 } else if (strcmp(param, "no-hud") == 0) {
129 config.game.video.hud = false;
130 } else if (strcmp(param, "no-audio") == 0) {
131 config.game.audio.enabled = false;
132 } else if (strcmp(param, "standalone") == 0) {
134 } else if (strcmp(param, "server") == 0) {
136 } else if (strcmp(param, "client") == 0) {
138 } else if (strcmp(param, "asset-path") == 0) {
140 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
141 cerr << "missing argument to --asset-path" << endl;
144 config.env.asset_path = argv[i];
146 } else if (strcmp(param, "host") == 0) {
148 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
149 cerr << "missing argument to --host" << endl;
152 config.game.net.host = argv[i];
154 } else if (strcmp(param, "port") == 0) {
156 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
157 cerr << "missing argument to --port" << endl;
160 config.game.net.port = strtoul(argv[i], nullptr, 10);
162 } else if (strcmp(param, "player-name") == 0) {
164 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
165 cerr << "missing argument to --player-name" << endl;
168 config.game.player.name = argv[i];
170 } else if (strcmp(param, "save-path") == 0) {
172 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
173 cerr << "missing argument to --save-path" << endl;
176 config.env.save_path = argv[i];
178 } else if (strcmp(param, "world-name") == 0) {
180 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
181 cerr << "missing argument to --world-name" << endl;
184 config.world.name = argv[i];
187 cerr << "unknown option " << arg << endl;
193 for (int j = 1; arg[j] != '\0'; ++j) {
196 config.game.video.dblbuf = false;
200 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
201 cerr << "missing argument to -m" << endl;
204 config.game.video.msaa = strtoul(argv[i], nullptr, 10);
209 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
210 cerr << "missing argument to -n" << endl;
213 n = strtoul(argv[i], nullptr, 10);
218 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
219 cerr << "missing argument to -s" << endl;
222 config.gen.seed = strtoul(argv[i], nullptr, 10);
227 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
228 cerr << "missing argument to -t" << endl;
231 t = strtoul(argv[i], nullptr, 10);
239 cerr << "unknown option " << arg[j] << endl;
246 cerr << "unable to interpret argument "
247 << i << " (" << arg << ")" << endl;
257 if (config.env.asset_path.empty()) {
258 config.env.asset_path = default_asset_path();
260 config.env.asset_path[config.env.asset_path.size() - 1] != '/' &&
261 config.env.asset_path[config.env.asset_path.size() - 1] != '\\'
263 config.env.asset_path += '/';
265 if (config.env.save_path.empty()) {
266 config.env.save_path = default_save_path();
268 config.env.save_path[config.env.save_path.size() - 1] != '/' &&
269 config.env.save_path[config.env.save_path.size() - 1] != '\\'
271 config.env.save_path += '/';
276 mode = FIXED_FRAME_LIMIT;
287 int Runtime::Execute() {
292 InitHeadless init_headless;
310 void Runtime::RunStandalone() {
311 Init init(config.game.video.dblbuf, config.game.video.msaa);
313 Environment env(init.window, config.env);
314 env.viewport.VSync(config.game.video.vsync);
316 WorldSave save(config.env.GetWorldPath(config.world.name));
318 save.Read(config.world);
319 save.Read(config.gen);
321 save.Write(config.world);
322 save.Write(config.gen);
325 Application app(env);
326 standalone::MasterState world_state(env, config.game, config.gen, config.world, save);
327 app.PushState(&world_state);
331 void Runtime::RunServer() {
332 HeadlessEnvironment env(config.env);
334 WorldSave save(config.env.GetWorldPath(config.world.name));
336 save.Read(config.world);
337 save.Read(config.gen);
339 save.Write(config.world);
340 save.Write(config.gen);
343 HeadlessApplication app(env);
344 server::ServerState server_state(env, config.gen, config.world, save, config.game);
345 app.PushState(&server_state);
349 void Runtime::RunClient() {
350 Init init(config.game.video.dblbuf, config.game.video.msaa);
352 Environment env(init.window, config.env);
353 env.viewport.VSync(config.game.video.vsync);
355 Application app(env);
356 client::MasterState client_state(env, config.game, config.world);
357 app.PushState(&client_state);
361 void Runtime::Run(HeadlessApplication &app) {
373 case FIXED_FRAME_LIMIT: