1 #include "Application.hpp"
2 #include "Environment.hpp"
4 #include "ServerState.hpp"
5 #include "WorldState.hpp"
8 #include "../client/MasterState.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 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.vsync = false;
124 } else if (strcmp(param, "no-keyboard") == 0) {
125 config.interface.keyboard_disabled = true;
126 } else if (strcmp(param, "no-mouse") == 0) {
127 config.interface.mouse_disabled = true;
128 } else if (strcmp(param, "no-hud") == 0) {
129 config.interface.visual_disabled = true;
130 } else if (strcmp(param, "no-audio") == 0) {
131 config.interface.audio_disabled = true;
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.client.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.server.port = strtoul(argv[i], nullptr, 10);
161 config.client.port = config.server.port;
163 } else if (strcmp(param, "player-name") == 0) {
165 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
166 cerr << "missing argument to --player-name" << endl;
169 config.interface.player_name = argv[i];
171 } else if (strcmp(param, "save-path") == 0) {
173 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
174 cerr << "missing argument to --save-path" << endl;
177 config.env.save_path = argv[i];
179 } else if (strcmp(param, "world-name") == 0) {
181 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
182 cerr << "missing argument to --world-name" << endl;
185 config.world.name = argv[i];
188 cerr << "unknown option " << arg << endl;
194 for (int j = 1; arg[j] != '\0'; ++j) {
197 config.doublebuf = false;
201 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
202 cerr << "missing argument to -m" << endl;
205 config.multisampling = strtoul(argv[i], nullptr, 10);
210 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
211 cerr << "missing argument to -n" << endl;
214 n = strtoul(argv[i], nullptr, 10);
219 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
220 cerr << "missing argument to -s" << endl;
223 config.gen.seed = strtoul(argv[i], nullptr, 10);
228 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
229 cerr << "missing argument to -t" << endl;
232 t = strtoul(argv[i], nullptr, 10);
240 cerr << "unknown option " << arg[j] << endl;
247 cerr << "unable to interpret argument "
248 << i << " (" << arg << ")" << endl;
258 if (config.env.asset_path.empty()) {
259 config.env.asset_path = default_asset_path();
261 config.env.asset_path[config.env.asset_path.size() - 1] != '/' &&
262 config.env.asset_path[config.env.asset_path.size() - 1] != '\\'
264 config.env.asset_path += '/';
266 if (config.env.save_path.empty()) {
267 config.env.save_path = default_save_path();
269 config.env.save_path[config.env.save_path.size() - 1] != '/' &&
270 config.env.save_path[config.env.save_path.size() - 1] != '\\'
272 config.env.save_path += '/';
277 mode = FIXED_FRAME_LIMIT;
288 int Runtime::Execute() {
293 InitHeadless init_headless;
311 void Runtime::RunStandalone() {
312 Init init(config.doublebuf, config.multisampling);
314 Environment env(init.window, config.env);
315 env.viewport.VSync(config.vsync);
317 WorldSave save(config.env.GetWorldPath(config.world.name));
319 save.Read(config.world);
320 save.Read(config.gen);
322 save.Write(config.world);
323 save.Write(config.gen);
326 Application app(env);
327 WorldState world_state(env, config.gen, config.interface, config.world, save);
328 app.PushState(&world_state);
332 void Runtime::RunServer() {
333 HeadlessEnvironment env(config.env);
335 WorldSave save(config.env.GetWorldPath(config.world.name));
337 save.Read(config.world);
338 save.Read(config.gen);
340 save.Write(config.world);
341 save.Write(config.gen);
344 HeadlessApplication app(env);
345 ServerState server_state(env, config.gen, config.world, save, config.server);
346 app.PushState(&server_state);
350 void Runtime::RunClient() {
351 Init init(config.doublebuf, config.multisampling);
353 Environment env(init.window, config.env);
354 env.viewport.VSync(config.vsync);
356 Application app(env);
357 client::MasterState client_state(env, config.world, config.interface, config.client);
358 app.PushState(&client_state);
362 void Runtime::Run(HeadlessApplication &app) {
374 case FIXED_FRAME_LIMIT: