1 #include "Application.hpp"
2 #include "Environment.hpp"
6 #include "../client/MasterState.hpp"
7 #include "../io/filesystem.hpp"
8 #include "../io/TokenStreamReader.hpp"
9 #include "../io/WorldSave.hpp"
10 #include "../server/ServerState.hpp"
11 #include "../standalone/MasterState.hpp"
24 string default_asset_path() {
25 char *base = SDL_GetBasePath();
32 string default_save_path() {
34 char *base = SDL_GetBasePath();
40 char *pref = SDL_GetPrefPath("localhorst", "blank");
51 void Config::Load(std::istream &is) {
52 TokenStreamReader in(is);
54 while (in.HasMore()) {
55 if (in.Peek().type == Token::STRING) {
58 in.ReadIdentifier(name);
60 in.Skip(Token::EQUALS);
61 if (name == "audio.enabled") {
62 in.ReadBoolean(audio.enabled);
63 } else if (name == "input.keyboard") {
64 in.ReadBoolean(input.keyboard);
65 } else if (name == "input.mouse") {
66 in.ReadBoolean(input.mouse);
67 } else if (name == "input.pitch_sensitivity") {
68 in.ReadNumber(input.pitch_sensitivity);
69 } else if (name == "input.yaw_sensitivity") {
70 in.ReadNumber(input.yaw_sensitivity);
71 } else if (name == "net.host") {
72 in.ReadString(net.host);
73 } else if (name == "net.port") {
77 } else if (name == "player.name") {
78 in.ReadString(player.name);
79 } else if (name == "video.dblbuf") {
80 in.ReadBoolean(video.dblbuf);
81 } else if (name == "video.vsync") {
82 in.ReadBoolean(video.vsync);
83 } else if (name == "video.msaa") {
84 in.ReadNumber(video.msaa);
85 } else if (name == "video.hud") {
86 in.ReadBoolean(video.hud);
87 } else if (name == "video.world") {
88 in.ReadBoolean(video.world);
89 } else if (name == "video.debug") {
90 in.ReadBoolean(video.debug);
92 if (in.HasMore() && in.Peek().type == Token::SEMICOLON) {
93 in.Skip(Token::SEMICOLON);
98 void Config::Save(std::ostream &out) {
99 out << "audio.enabled = " << (audio.enabled ? "yes" : "no") << ';' << std::endl;
100 out << "input.keyboard = " << (input.keyboard ? "on" : "off") << ';' << std::endl;
101 out << "input.mouse = " << (input.keyboard ? "on" : "off") << ';' << std::endl;
102 out << "input.pitch_sensitivity = " << input.pitch_sensitivity << ';' << std::endl;
103 out << "input.yaw_sensitivity = " << input.yaw_sensitivity << ';' << std::endl;
104 out << "net.host = \"" << net.host << "\";" << std::endl;
105 out << "net.port = " << net.port << ';' << std::endl;
106 out << "player.name = \"" << player.name << "\";" << std::endl;
107 out << "video.dblbuf = " << (video.dblbuf ? "on" : "off") << ';' << std::endl;
108 out << "video.vsync = " << (video.vsync ? "on" : "off") << ';' << std::endl;
109 out << "video.msaa = " << net.port << ';' << std::endl;
110 out << "video.hud = " << (video.hud ? "on" : "off") << ';' << std::endl;
111 out << "video.world = " << (video.world ? "on" : "off") << ';' << std::endl;
112 out << "video.debug = " << (video.world ? "on" : "off") << ';' << std::endl;
116 HeadlessEnvironment::HeadlessEnvironment(const Config &config)
118 , loader(config.asset_path)
124 string HeadlessEnvironment::Config::GetWorldPath(const string &world_name) const {
125 return save_path + "worlds/" + world_name + '/';
128 string HeadlessEnvironment::Config::GetWorldPath(const string &world_name, const string &host_name) const {
129 return save_path + "cache/" + host_name + '/' + world_name + '/';
132 Environment::Environment(Window &win, const Config &config)
133 : HeadlessEnvironment(config)
141 keymap.LoadDefault();
143 string keys_path = config.save_path + "keys.conf";
144 if (!is_file(keys_path)) {
145 std::ofstream file(keys_path);
148 std::ifstream file(keys_path);
154 Runtime::Runtime() noexcept
165 void Runtime::ReadArgs(int argc, const char *const *argv) {
166 if (argc <= 0) return;
172 for (int i = 1; i < argc; ++i) {
173 const char *arg = argv[i];
174 if (!arg || arg[0] == '\0') {
175 cerr << "warning: found empty argument at position " << i << endl;
178 if (options && arg[0] == '-') {
179 if (arg[1] == '\0') {
180 cerr << "warning: incomplete option list at position " << i << endl;
181 } else if (arg[1] == '-') {
182 if (arg[2] == '\0') {
186 const char *param = arg + 2;
188 if (strcmp(param, "no-vsync") == 0) {
189 config.game.video.vsync = false;
190 } else if (strcmp(param, "no-keyboard") == 0) {
191 config.game.input.keyboard = false;
192 } else if (strcmp(param, "no-mouse") == 0) {
193 config.game.input.mouse = false;
194 } else if (strcmp(param, "no-hud") == 0) {
195 config.game.video.hud = false;
196 } else if (strcmp(param, "no-audio") == 0) {
197 config.game.audio.enabled = false;
198 } else if (strcmp(param, "standalone") == 0) {
200 } else if (strcmp(param, "server") == 0) {
202 } else if (strcmp(param, "client") == 0) {
204 } else if (strcmp(param, "asset-path") == 0) {
206 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
207 cerr << "missing argument to --asset-path" << endl;
210 config.env.asset_path = argv[i];
212 } else if (strcmp(param, "host") == 0) {
214 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
215 cerr << "missing argument to --host" << endl;
218 config.game.net.host = argv[i];
220 } else if (strcmp(param, "port") == 0) {
222 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
223 cerr << "missing argument to --port" << endl;
226 config.game.net.port = strtoul(argv[i], nullptr, 10);
228 } else if (strcmp(param, "player-name") == 0) {
230 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
231 cerr << "missing argument to --player-name" << endl;
234 config.game.player.name = argv[i];
236 } else if (strcmp(param, "save-path") == 0) {
238 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
239 cerr << "missing argument to --save-path" << endl;
242 config.env.save_path = argv[i];
244 } else if (strcmp(param, "world-name") == 0) {
246 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
247 cerr << "missing argument to --world-name" << endl;
250 config.world.name = argv[i];
253 cerr << "unknown option " << arg << endl;
259 for (int j = 1; arg[j] != '\0'; ++j) {
262 config.game.video.dblbuf = false;
266 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
267 cerr << "missing argument to -m" << endl;
270 config.game.video.msaa = strtoul(argv[i], nullptr, 10);
275 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
276 cerr << "missing argument to -n" << endl;
279 n = strtoul(argv[i], nullptr, 10);
284 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
285 cerr << "missing argument to -s" << endl;
288 config.gen.seed = strtoul(argv[i], nullptr, 10);
293 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
294 cerr << "missing argument to -t" << endl;
297 t = strtoul(argv[i], nullptr, 10);
305 cerr << "unknown option " << arg[j] << endl;
312 cerr << "unable to interpret argument "
313 << i << " (" << arg << ")" << endl;
323 if (config.env.asset_path.empty()) {
324 config.env.asset_path = default_asset_path();
326 config.env.asset_path[config.env.asset_path.size() - 1] != '/' &&
327 config.env.asset_path[config.env.asset_path.size() - 1] != '\\'
329 config.env.asset_path += '/';
331 if (config.env.save_path.empty()) {
332 config.env.save_path = default_save_path();
334 config.env.save_path[config.env.save_path.size() - 1] != '/' &&
335 config.env.save_path[config.env.save_path.size() - 1] != '\\'
337 config.env.save_path += '/';
342 mode = FIXED_FRAME_LIMIT;
353 int Runtime::Execute() {
358 InitHeadless init_headless;
376 void Runtime::RunStandalone() {
377 Init init(config.game.video.dblbuf, config.game.video.msaa);
379 Environment env(init.window, config.env);
380 env.viewport.VSync(config.game.video.vsync);
382 WorldSave save(config.env.GetWorldPath(config.world.name));
384 save.Read(config.world);
385 save.Read(config.gen);
387 save.Write(config.world);
388 save.Write(config.gen);
391 Application app(env);
392 standalone::MasterState world_state(env, config.game, config.gen, config.world, save);
393 app.PushState(&world_state);
397 void Runtime::RunServer() {
398 HeadlessEnvironment env(config.env);
400 WorldSave save(config.env.GetWorldPath(config.world.name));
402 save.Read(config.world);
403 save.Read(config.gen);
405 save.Write(config.world);
406 save.Write(config.gen);
409 HeadlessApplication app(env);
410 server::ServerState server_state(env, config.gen, config.world, save, config.game);
411 app.PushState(&server_state);
415 void Runtime::RunClient() {
416 Init init(config.game.video.dblbuf, config.game.video.msaa);
418 Environment env(init.window, config.env);
419 env.viewport.VSync(config.game.video.vsync);
421 Application app(env);
422 client::MasterState client_state(env, config.game, config.world);
423 app.PushState(&client_state);
427 void Runtime::Run(HeadlessApplication &app) {
439 case FIXED_FRAME_LIMIT: