]> git.localhorst.tv Git - gong.git/blob - src/app/runtime.cpp
code, assets, and other stuff stolen from blank
[gong.git] / src / app / runtime.cpp
1 #include "Application.hpp"
2 #include "Environment.hpp"
3 #include "Runtime.hpp"
4
5 #include "init.hpp"
6 #include "../io/filesystem.hpp"
7 #include "../io/TokenStreamReader.hpp"
8
9 #include <cctype>
10 #include <cstdlib>
11 #include <ctime>
12 #include <fstream>
13 #include <iostream>
14 #include <SDL.h>
15
16 using namespace std;
17
18
19 namespace {
20
21 string default_asset_path() {
22         char *base = SDL_GetBasePath();
23         string assets(base);
24         assets += "assets/";
25         SDL_free(base);
26         return assets;
27 }
28
29 string default_save_path() {
30 #ifndef NDEBUG
31         char *base = SDL_GetBasePath();
32         string save(base);
33         save += "saves/";
34         SDL_free(base);
35         return save;
36 #else
37         char *pref = SDL_GetPrefPath("localhorst", "gong");
38         string save(pref);
39         SDL_free(pref);
40         return save;
41 #endif
42 }
43
44 }
45
46 namespace gong {
47 namespace app {
48
49 void Config::Load(istream &is) {
50         io::TokenStreamReader in(is);
51         string name;
52         while (in.HasMore()) {
53                 if (in.Peek().type == io::Token::STRING) {
54                         in.ReadString(name);
55                 } else {
56                         in.ReadIdentifier(name);
57                 }
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") {
72                         int port;
73                         in.ReadNumber(port);
74                         net.port = port;
75                 } else if (name == "net.cmd_port") {
76                         int port;
77                         in.ReadNumber(port);
78                         net.cmd_port = 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);
93                 }
94                 if (in.HasMore() && in.Peek().type == io::Token::SEMICOLON) {
95                         in.Skip(io::Token::SEMICOLON);
96                 }
97         }
98 }
99
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;
116 }
117
118
119 HeadlessEnvironment::HeadlessEnvironment(const Config &config)
120 : config(config)
121 , loader(config.asset_path)
122 , counter()
123 , state() {
124
125 }
126
127 Environment::Environment(Window &win, const Config &config)
128 : HeadlessEnvironment(config)
129 , assets(loader)
130 , audio()
131 , viewport()
132 , window(win)
133 , msg_state(*this) {
134         viewport.Clear();
135         window.Flip();
136 }
137
138 void Environment::ShowMessage(const char *msg) {
139         cout << msg << endl;
140         msg_state.SetMessage(msg);
141         state.Push(&msg_state);
142 }
143
144
145 Runtime::Runtime() noexcept
146 : name("gong")
147 , mode(NORMAL)
148 , target(STANDALONE)
149 , n(0)
150 , t(0)
151 , config() {
152
153 }
154
155
156 void Runtime::Initialize(int argc, const char *const *argv) {
157         ReadArgs(argc, argv);
158         if (mode == ERROR) return;
159         ReadPreferences();
160         ReadArgs(argc, argv);
161 }
162
163 void Runtime::ReadArgs(int argc, const char *const *argv) {
164         if (argc <= 0) return;
165         name = argv[0];
166
167         bool options = true;
168         bool error = false;
169
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;
174                         continue;
175                 }
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') {
181                                         // stopper
182                                         options = false;
183                                 } else {
184                                         const char *param = arg + 2;
185                                         // long option
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) {
197                                                 target = STANDALONE;
198                                         } else if (strcmp(param, "server") == 0) {
199                                                 target = SERVER;
200                                         } else if (strcmp(param, "client") == 0) {
201                                                 target = CLIENT;
202                                         } else if (strcmp(param, "asset-path") == 0) {
203                                                 ++i;
204                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
205                                                         cerr << "missing argument to --asset-path" << endl;
206                                                         error = true;
207                                                 } else {
208                                                         config.env.asset_path = argv[i];
209                                                 }
210                                         } else if (strcmp(param, "host") == 0) {
211                                                 ++i;
212                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
213                                                         cerr << "missing argument to --host" << endl;
214                                                         error = true;
215                                                 } else {
216                                                         config.game.net.host = argv[i];
217                                                 }
218                                         } else if (strcmp(param, "port") == 0) {
219                                                 ++i;
220                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
221                                                         cerr << "missing argument to --port" << endl;
222                                                         error = true;
223                                                 } else {
224                                                         config.game.net.port = strtoul(argv[i], nullptr, 10);
225                                                 }
226                                         } else if (strcmp(param, "cmd-port") == 0) {
227                                                 ++i;
228                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
229                                                         cerr << "missing argument to --cmd-port" << endl;
230                                                         error = true;
231                                                 } else {
232                                                         config.game.net.cmd_port = strtoul(argv[i], nullptr, 10);
233                                                 }
234                                         } else if (strcmp(param, "player-name") == 0) {
235                                                 ++i;
236                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
237                                                         cerr << "missing argument to --player-name" << endl;
238                                                         error = true;
239                                                 } else {
240                                                         config.game.player.name = argv[i];
241                                                 }
242                                         } else if (strcmp(param, "save-path") == 0) {
243                                                 ++i;
244                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
245                                                         cerr << "missing argument to --save-path" << endl;
246                                                         error = true;
247                                                 } else {
248                                                         config.env.save_path = argv[i];
249                                                 }
250                                         } else {
251                                                 cerr << "unknown option " << arg << endl;
252                                                 error = true;
253                                         }
254                                 }
255                         } else {
256                                 // short options
257                                 for (int j = 1; arg[j] != '\0'; ++j) {
258                                         switch (arg[j]) {
259                                                 case 'd':
260                                                         config.game.video.dblbuf = false;
261                                                         break;
262                                                 case 'm':
263                                                         ++i;
264                                                         if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
265                                                                 cerr << "missing argument to -m" << endl;
266                                                                 error = true;
267                                                         } else {
268                                                                 config.game.video.msaa = strtoul(argv[i], nullptr, 10);
269                                                         }
270                                                         break;
271                                                 case 'n':
272                                                         ++i;
273                                                         if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
274                                                                 cerr << "missing argument to -n" << endl;
275                                                                 error = true;
276                                                         } else {
277                                                                 n = strtoul(argv[i], nullptr, 10);
278                                                         }
279                                                         break;
280                                                 case 't':
281                                                         ++i;
282                                                         if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
283                                                                 cerr << "missing argument to -t" << endl;
284                                                                 error = true;
285                                                         } else {
286                                                                 t = strtoul(argv[i], nullptr, 10);
287                                                         }
288                                                         break;
289                                                 case '-':
290                                                         // stopper
291                                                         options = false;
292                                                         break;
293                                                 default:
294                                                         cerr << "unknown option " << arg[j] << endl;
295                                                         error = true;
296                                                         break;
297                                         }
298                                 }
299                         }
300                 } else {
301                         cerr << "unable to interpret argument "
302                                 << i << " (" << arg << ")" << endl;
303                         error = true;
304                 }
305         }
306
307         if (error) {
308                 mode = ERROR;
309         } else if (n > 0) {
310                 if (t > 0) {
311                         mode = FIXED_FRAME_LIMIT;
312                 } else {
313                         mode = FRAME_LIMIT;
314                 }
315         } else if (t > 0) {
316                 mode = TIME_LIMIT;
317         } else {
318                 mode = NORMAL;
319         }
320
321         if (config.env.asset_path.empty()) {
322                 config.env.asset_path = default_asset_path();
323         } else if (
324                 config.env.asset_path.back() != '/' &&
325                 config.env.asset_path.back() != '\\'
326         ) {
327                 config.env.asset_path += '/';
328         }
329         if (config.env.save_path.empty()) {
330                 config.env.save_path = default_save_path();
331         } else if (
332                 config.env.save_path.back() != '/' &&
333                 config.env.save_path.back() != '\\'
334         ) {
335                 config.env.save_path += '/';
336         }
337 }
338
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);
344         } else {
345                 io::make_dirs(config.env.save_path);
346                 ofstream file(prefs_path);
347                 config.game.Save(file);
348         }
349 }
350
351 int Runtime::Execute() {
352         if (mode == ERROR) {
353                 return 1;
354         }
355
356         InitHeadless init_headless;
357
358         switch (target) {
359                 default:
360                 case STANDALONE:
361                         RunStandalone();
362                         break;
363                 case SERVER:
364                         RunServer();
365                         break;
366                 case CLIENT:
367                         RunClient();
368                         break;
369         }
370
371         return 0;
372 }
373
374 void Runtime::RunStandalone() {
375         Init init(config.game.video.dblbuf, config.game.video.msaa);
376
377         Environment env(init.window, config.env);
378         env.viewport.VSync(config.game.video.vsync);
379
380         throw invalid_argument("standalone mode not implemented");
381
382         //Application app(env);
383         //app.PushState(...);
384         //Run(app);
385 }
386
387 void Runtime::RunServer() {
388         HeadlessEnvironment env(config.env);
389
390         throw invalid_argument("server mode not implemented");
391
392         //HeadlessApplication app(env);
393         //app.PushState(...);
394         //Run(app);
395 }
396
397 void Runtime::RunClient() {
398         Init init(config.game.video.dblbuf, config.game.video.msaa);
399
400         Environment env(init.window, config.env);
401         env.viewport.VSync(config.game.video.vsync);
402         throw invalid_argument("client mode not implemented");
403
404         //Application app(env);
405         //app.PushState(...);
406         //Run(app);
407 }
408
409 void Runtime::Run(HeadlessApplication &app) {
410         switch (mode) {
411                 default:
412                 case NORMAL:
413                         app.Run();
414                         break;
415                 case FRAME_LIMIT:
416                         app.RunN(n);
417                         break;
418                 case TIME_LIMIT:
419                         app.RunT(t);
420                         break;
421                 case FIXED_FRAME_LIMIT:
422                         app.RunS(n, t);
423                         break;
424         }
425 }
426
427 }
428 }