1 #include "Application.hpp"
2 #include "ClientState.hpp"
3 #include "Environment.hpp"
5 #include "ServerState.hpp"
6 #include "WorldState.hpp"
9 #include "../io/filesystem.hpp"
10 #include "../io/WorldSave.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 string &asset_path)
57 Environment::Environment(Window &win, const string &asset_path)
58 : HeadlessEnvironment(asset_path)
70 Runtime::Runtime() noexcept
81 void Runtime::ReadArgs(int argc, const char *const *argv) {
82 if (argc <= 0) return;
88 for (int i = 1; i < argc; ++i) {
89 const char *arg = argv[i];
90 if (!arg || arg[0] == '\0') {
91 cerr << "warning: found empty argument at position " << i << endl;
94 if (options && arg[0] == '-') {
96 cerr << "warning: incomplete option list at position " << i << endl;
97 } else if (arg[1] == '-') {
102 const char *param = arg + 2;
104 if (strcmp(param, "no-vsync") == 0) {
105 config.vsync = false;
106 } else if (strcmp(param, "no-keyboard") == 0) {
107 config.interface.keyboard_disabled = true;
108 } else if (strcmp(param, "no-mouse") == 0) {
109 config.interface.mouse_disabled = true;
110 } else if (strcmp(param, "no-hud") == 0) {
111 config.interface.visual_disabled = true;
112 } else if (strcmp(param, "no-audio") == 0) {
113 config.interface.audio_disabled = true;
114 } else if (strcmp(param, "standalone") == 0) {
116 } else if (strcmp(param, "server") == 0) {
118 } else if (strcmp(param, "client") == 0) {
120 } else if (strcmp(param, "asset-path") == 0) {
122 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
123 cerr << "missing argument to --asset-path" << endl;
126 config.asset_path = argv[i];
128 } else if (strcmp(param, "host") == 0) {
130 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
131 cerr << "missing argument to --host" << endl;
134 config.client.host = argv[i];
136 } else if (strcmp(param, "port") == 0) {
138 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
139 cerr << "missing argument to --port" << endl;
142 config.server.port = strtoul(argv[i], nullptr, 10);
143 config.client.port = config.server.port;
145 } else if (strcmp(param, "player-name") == 0) {
147 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
148 cerr << "missing argument to --player-name" << endl;
151 config.interface.player_name = argv[i];
153 } else if (strcmp(param, "save-path") == 0) {
155 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
156 cerr << "missing argument to --save-path" << endl;
159 config.save_path = argv[i];
161 } else if (strcmp(param, "world-name") == 0) {
163 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
164 cerr << "missing argument to --world-name" << endl;
167 config.world_name = argv[i];
170 cerr << "unknown option " << arg << endl;
176 for (int j = 1; arg[j] != '\0'; ++j) {
179 config.doublebuf = false;
183 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
184 cerr << "missing argument to -m" << endl;
187 config.multisampling = strtoul(argv[i], nullptr, 10);
192 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
193 cerr << "missing argument to -n" << endl;
196 n = strtoul(argv[i], nullptr, 10);
201 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
202 cerr << "missing argument to -s" << endl;
205 config.world.gen.seed = strtoul(argv[i], nullptr, 10);
210 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
211 cerr << "missing argument to -t" << endl;
214 t = strtoul(argv[i], nullptr, 10);
222 cerr << "unknown option " << arg[j] << endl;
229 cerr << "unable to interpret argument "
230 << i << " (" << arg << ")" << endl;
240 if (config.asset_path.empty()) {
241 config.asset_path = default_asset_path();
243 config.asset_path[config.asset_path.size() - 1] != '/' &&
244 config.asset_path[config.asset_path.size() - 1] != '\\'
246 config.asset_path += '/';
248 if (config.save_path.empty()) {
249 config.save_path = default_save_path();
251 config.save_path[config.save_path.size() - 1] != '/' &&
252 config.save_path[config.save_path.size() - 1] != '\\'
254 config.save_path += '/';
259 mode = FIXED_FRAME_LIMIT;
270 int Runtime::Execute() {
275 InitHeadless init_headless;
293 void Runtime::RunStandalone() {
294 Init init(config.doublebuf, config.multisampling);
296 Environment env(init.window, config.asset_path);
297 env.viewport.VSync(config.vsync);
299 WorldSave save(config.save_path + config.world_name + '/');
301 save.Read(config.world);
303 save.Write(config.world);
306 std::string keys_path = config.save_path + "keys.conf";
307 if (!is_file(keys_path)) {
308 std::ofstream file(keys_path);
309 env.keymap.Save(file);
311 std::ifstream file(keys_path);
312 env.keymap.Load(file);
315 Application app(env);
316 WorldState world_state(env, config.interface, config.world, save);
317 app.PushState(&world_state);
321 void Runtime::RunServer() {
322 HeadlessEnvironment env(config.asset_path);
324 WorldSave save(config.save_path + config.world_name + '/');
326 save.Read(config.world);
328 save.Write(config.world);
331 HeadlessApplication app(env);
332 ServerState server_state(env, config.world, save, config.server);
333 app.PushState(&server_state);
337 void Runtime::RunClient() {
338 Init init(config.doublebuf, config.multisampling);
340 Environment env(init.window, config.asset_path);
341 env.viewport.VSync(config.vsync);
343 WorldSave save(config.save_path + config.world_name + '/');
345 save.Read(config.world);
347 save.Write(config.world);
350 std::string keys_path = config.save_path + "keys.conf";
351 if (!is_file(keys_path)) {
352 std::ofstream file(keys_path);
353 env.keymap.Save(file);
355 std::ifstream file(keys_path);
356 env.keymap.Load(file);
359 Application app(env);
360 ClientState client_state(env, config.world, save, config.interface, config.client);
361 app.PushState(&client_state);
365 void Runtime::Run(HeadlessApplication &app) {
377 case FIXED_FRAME_LIMIT: