1 #include "Application.hpp"
2 #include "Environment.hpp"
6 #include "../io/filesystem.hpp"
7 #include "../io/TokenStreamReader.hpp"
21 string default_asset_path() {
22 char *base = SDL_GetBasePath();
29 string default_save_path() {
31 char *base = SDL_GetBasePath();
37 char *pref = SDL_GetPrefPath("localhorst", "gong");
49 void Config::Load(istream &is) {
50 io::TokenStreamReader in(is);
52 while (in.HasMore()) {
53 if (in.Peek().type == io::Token::STRING) {
56 in.ReadIdentifier(name);
58 in.Skip(io::Token::EQUALS);
59 if (name == "audio.enabled") {
60 in.ReadBoolean(audio.enabled);
61 } else if (name == "input.keyboard") {
62 in.ReadBoolean(input.keyboard);
63 } else if (name == "input.mouse") {
64 in.ReadBoolean(input.mouse);
65 } else if (name == "input.pitch_sensitivity") {
66 in.ReadNumber(input.pitch_sensitivity);
67 } else if (name == "input.yaw_sensitivity") {
68 in.ReadNumber(input.yaw_sensitivity);
69 } else if (name == "net.host") {
70 in.ReadString(net.host);
71 } else if (name == "net.port") {
75 } else if (name == "net.cmd_port") {
79 } else if (name == "player.name") {
80 in.ReadString(player.name);
81 } else if (name == "video.dblbuf") {
82 in.ReadBoolean(video.dblbuf);
83 } else if (name == "video.vsync") {
84 in.ReadBoolean(video.vsync);
85 } else if (name == "video.msaa") {
86 in.ReadNumber(video.msaa);
87 } else if (name == "video.hud") {
88 in.ReadBoolean(video.hud);
89 } else if (name == "video.world") {
90 in.ReadBoolean(video.world);
91 } else if (name == "video.debug") {
92 in.ReadBoolean(video.debug);
94 if (in.HasMore() && in.Peek().type == io::Token::SEMICOLON) {
95 in.Skip(io::Token::SEMICOLON);
100 void Config::Save(ostream &out) {
101 out << "audio.enabled = " << (audio.enabled ? "yes" : "no") << ';' << endl;
102 out << "input.keyboard = " << (input.keyboard ? "on" : "off") << ';' << endl;
103 out << "input.mouse = " << (input.keyboard ? "on" : "off") << ';' << endl;
104 out << "input.pitch_sensitivity = " << input.pitch_sensitivity << ';' << endl;
105 out << "input.yaw_sensitivity = " << input.yaw_sensitivity << ';' << endl;
106 out << "net.host = \"" << net.host << "\";" << endl;
107 out << "net.port = " << net.port << ';' << endl;
108 out << "net.cmd_port = " << net.cmd_port << ';' << endl;
109 out << "player.name = \"" << player.name << "\";" << endl;
110 out << "video.dblbuf = " << (video.dblbuf ? "on" : "off") << ';' << endl;
111 out << "video.vsync = " << (video.vsync ? "on" : "off") << ';' << endl;
112 out << "video.msaa = " << video.msaa << ';' << endl;
113 out << "video.hud = " << (video.hud ? "on" : "off") << ';' << endl;
114 out << "video.world = " << (video.world ? "on" : "off") << ';' << endl;
115 out << "video.debug = " << (video.debug ? "on" : "off") << ';' << endl;
119 HeadlessEnvironment::HeadlessEnvironment(const Config &config)
121 , loader(config.asset_path)
127 Environment::Environment(Window &win, const Config &config)
128 : HeadlessEnvironment(config)
138 void Environment::ShowMessage(const char *msg) {
140 msg_state.SetMessage(msg);
141 state.Push(&msg_state);
145 Runtime::Runtime() noexcept
156 void Runtime::Initialize(int argc, const char *const *argv) {
157 ReadArgs(argc, argv);
158 if (mode == ERROR) return;
160 ReadArgs(argc, argv);
163 void Runtime::ReadArgs(int argc, const char *const *argv) {
164 if (argc <= 0) return;
170 for (int i = 1; i < argc; ++i) {
171 const char *arg = argv[i];
172 if (!arg || arg[0] == '\0') {
173 cerr << "warning: found empty argument at position " << i << endl;
176 if (options && arg[0] == '-') {
177 if (arg[1] == '\0') {
178 cerr << "warning: incomplete option list at position " << i << endl;
179 } else if (arg[1] == '-') {
180 if (arg[2] == '\0') {
184 const char *param = arg + 2;
186 if (strcmp(param, "no-vsync") == 0) {
187 config.game.video.vsync = false;
188 } else if (strcmp(param, "no-keyboard") == 0) {
189 config.game.input.keyboard = false;
190 } else if (strcmp(param, "no-mouse") == 0) {
191 config.game.input.mouse = false;
192 } else if (strcmp(param, "no-hud") == 0) {
193 config.game.video.hud = false;
194 } else if (strcmp(param, "no-audio") == 0) {
195 config.game.audio.enabled = false;
196 } else if (strcmp(param, "standalone") == 0) {
198 } else if (strcmp(param, "server") == 0) {
200 } else if (strcmp(param, "client") == 0) {
202 } else if (strcmp(param, "asset-path") == 0) {
204 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
205 cerr << "missing argument to --asset-path" << endl;
208 config.env.asset_path = argv[i];
210 } else if (strcmp(param, "host") == 0) {
212 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
213 cerr << "missing argument to --host" << endl;
216 config.game.net.host = argv[i];
218 } else if (strcmp(param, "port") == 0) {
220 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
221 cerr << "missing argument to --port" << endl;
224 config.game.net.port = strtoul(argv[i], nullptr, 10);
226 } else if (strcmp(param, "cmd-port") == 0) {
228 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
229 cerr << "missing argument to --cmd-port" << endl;
232 config.game.net.cmd_port = strtoul(argv[i], nullptr, 10);
234 } else if (strcmp(param, "player-name") == 0) {
236 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
237 cerr << "missing argument to --player-name" << endl;
240 config.game.player.name = argv[i];
242 } else if (strcmp(param, "save-path") == 0) {
244 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
245 cerr << "missing argument to --save-path" << endl;
248 config.env.save_path = argv[i];
251 cerr << "unknown option " << arg << endl;
257 for (int j = 1; arg[j] != '\0'; ++j) {
260 config.game.video.dblbuf = false;
264 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
265 cerr << "missing argument to -m" << endl;
268 config.game.video.msaa = strtoul(argv[i], nullptr, 10);
273 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
274 cerr << "missing argument to -n" << endl;
277 n = strtoul(argv[i], nullptr, 10);
282 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
283 cerr << "missing argument to -t" << endl;
286 t = strtoul(argv[i], nullptr, 10);
294 cerr << "unknown option " << arg[j] << endl;
301 cerr << "unable to interpret argument "
302 << i << " (" << arg << ")" << endl;
311 mode = FIXED_FRAME_LIMIT;
321 if (config.env.asset_path.empty()) {
322 config.env.asset_path = default_asset_path();
324 config.env.asset_path.back() != '/' &&
325 config.env.asset_path.back() != '\\'
327 config.env.asset_path += '/';
329 if (config.env.save_path.empty()) {
330 config.env.save_path = default_save_path();
332 config.env.save_path.back() != '/' &&
333 config.env.save_path.back() != '\\'
335 config.env.save_path += '/';
339 void Runtime::ReadPreferences() {
340 string prefs_path = config.env.save_path + "prefs.conf";
341 if (io::is_file(prefs_path)) {
342 ifstream file(prefs_path);
343 config.game.Load(file);
345 io::make_dirs(config.env.save_path);
346 ofstream file(prefs_path);
347 config.game.Save(file);
351 int Runtime::Execute() {
356 InitHeadless init_headless;
374 void Runtime::RunStandalone() {
375 Init init(config.game.video.dblbuf, config.game.video.msaa);
377 Environment env(init.window, config.env);
378 env.viewport.VSync(config.game.video.vsync);
380 throw invalid_argument("standalone mode not implemented");
382 //Application app(env);
383 //app.PushState(...);
387 void Runtime::RunServer() {
388 HeadlessEnvironment env(config.env);
390 throw invalid_argument("server mode not implemented");
392 //HeadlessApplication app(env);
393 //app.PushState(...);
397 void Runtime::RunClient() {
398 Init init(config.game.video.dblbuf, config.game.video.msaa);
400 Environment env(init.window, config.env);
401 env.viewport.VSync(config.game.video.vsync);
402 throw invalid_argument("client mode not implemented");
404 //Application app(env);
405 //app.PushState(...);
409 void Runtime::Run(HeadlessApplication &app) {
421 case FIXED_FRAME_LIMIT: