]> git.localhorst.tv Git - blank.git/blob - src/app/runtime.cpp
special treatment for players
[blank.git] / src / app / runtime.cpp
1 #include "Application.hpp"
2 #include "ClientState.hpp"
3 #include "Environment.hpp"
4 #include "Runtime.hpp"
5 #include "ServerState.hpp"
6 #include "WorldState.hpp"
7
8 #include "init.hpp"
9 #include "../io/filesystem.hpp"
10 #include "../io/WorldSave.hpp"
11
12 #include <cctype>
13 #include <cstdlib>
14 #include <fstream>
15 #include <iostream>
16 #include <SDL.h>
17
18 using namespace std;
19
20
21 namespace {
22
23 string default_asset_path() {
24         char *base = SDL_GetBasePath();
25         string assets(base);
26         assets += "assets/";
27         SDL_free(base);
28         return assets;
29 }
30
31 string default_save_path() {
32 #ifndef NDEBUG
33         char *base = SDL_GetBasePath();
34         string save(base);
35         save += "saves/";
36         SDL_free(base);
37         return save;
38 #else
39         char *pref = SDL_GetPrefPath("localhorst", "blank");
40         string save(pref);
41         SDL_free(pref);
42         return save;
43 #endif
44 }
45
46 }
47
48 namespace blank {
49
50 HeadlessEnvironment::HeadlessEnvironment(const string &asset_path)
51 : loader(asset_path)
52 , counter()
53 , state() {
54
55 }
56
57 Environment::Environment(Window &win, const string &asset_path)
58 : HeadlessEnvironment(asset_path)
59 , assets(loader)
60 , audio()
61 , viewport()
62 , window(win)
63 , keymap() {
64         viewport.Clear();
65         window.Flip();
66         keymap.LoadDefault();
67 }
68
69
70 Runtime::Runtime() noexcept
71 : name("blank")
72 , mode(NORMAL)
73 , target(STANDALONE)
74 , n(0)
75 , t(0)
76 , config() {
77
78 }
79
80
81 void Runtime::ReadArgs(int argc, const char *const *argv) {
82         if (argc <= 0) return;
83         name = argv[0];
84
85         bool options = true;
86         bool error = false;
87
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;
92                         continue;
93                 }
94                 if (options && arg[0] == '-') {
95                         if (arg[1] == '\0') {
96                                 cerr << "warning: incomplete option list at position " << i << endl;
97                         } else if (arg[1] == '-') {
98                                 if (arg[2] == '\0') {
99                                         // stopper
100                                         options = false;
101                                 } else {
102                                         const char *param = arg + 2;
103                                         // long option
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) {
115                                                 target = STANDALONE;
116                                         } else if (strcmp(param, "server") == 0) {
117                                                 target = SERVER;
118                                         } else if (strcmp(param, "client") == 0) {
119                                                 target = CLIENT;
120                                         } else if (strcmp(param, "asset-path") == 0) {
121                                                 ++i;
122                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
123                                                         cerr << "missing argument to --asset-path" << endl;
124                                                         error = true;
125                                                 } else {
126                                                         config.asset_path = argv[i];
127                                                 }
128                                         } else if (strcmp(param, "host") == 0) {
129                                                 ++i;
130                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
131                                                         cerr << "missing argument to --host" << endl;
132                                                         error = true;
133                                                 } else {
134                                                         config.client.host = argv[i];
135                                                 }
136                                         } else if (strcmp(param, "port") == 0) {
137                                                 ++i;
138                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
139                                                         cerr << "missing argument to --port" << endl;
140                                                         error = true;
141                                                 } else {
142                                                         config.server.port = strtoul(argv[i], nullptr, 10);
143                                                         config.client.port = config.server.port;
144                                                 }
145                                         } else if (strcmp(param, "player-name") == 0) {
146                                                 ++i;
147                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
148                                                         cerr << "missing argument to --player-name" << endl;
149                                                         error = true;
150                                                 } else {
151                                                         config.interface.player_name = argv[i];
152                                                 }
153                                         } else if (strcmp(param, "save-path") == 0) {
154                                                 ++i;
155                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
156                                                         cerr << "missing argument to --save-path" << endl;
157                                                         error = true;
158                                                 } else {
159                                                         config.save_path = argv[i];
160                                                 }
161                                         } else if (strcmp(param, "world-name") == 0) {
162                                                 ++i;
163                                                 if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
164                                                         cerr << "missing argument to --world-name" << endl;
165                                                         error = true;
166                                                 } else {
167                                                         config.world_name = argv[i];
168                                                 }
169                                         } else {
170                                                 cerr << "unknown option " << arg << endl;
171                                                 error = true;
172                                         }
173                                 }
174                         } else {
175                                 // short options
176                                 for (int j = 1; arg[j] != '\0'; ++j) {
177                                         switch (arg[j]) {
178                                                 case 'd':
179                                                         config.doublebuf = false;
180                                                         break;
181                                                 case 'm':
182                                                         ++i;
183                                                         if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
184                                                                 cerr << "missing argument to -m" << endl;
185                                                                 error = true;
186                                                         } else {
187                                                                 config.multisampling = strtoul(argv[i], nullptr, 10);
188                                                         }
189                                                         break;
190                                                 case 'n':
191                                                         ++i;
192                                                         if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
193                                                                 cerr << "missing argument to -n" << endl;
194                                                                 error = true;
195                                                         } else {
196                                                                 n = strtoul(argv[i], nullptr, 10);
197                                                         }
198                                                         break;
199                                                 case 's':
200                                                         ++i;
201                                                         if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
202                                                                 cerr << "missing argument to -s" << endl;
203                                                                 error = true;
204                                                         } else {
205                                                                 config.world.gen.seed = strtoul(argv[i], nullptr, 10);
206                                                         }
207                                                         break;
208                                                 case 't':
209                                                         ++i;
210                                                         if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
211                                                                 cerr << "missing argument to -t" << endl;
212                                                                 error = true;
213                                                         } else {
214                                                                 t = strtoul(argv[i], nullptr, 10);
215                                                         }
216                                                         break;
217                                                 case '-':
218                                                         // stopper
219                                                         options = false;
220                                                         break;
221                                                 default:
222                                                         cerr << "unknown option " << arg[j] << endl;
223                                                         error = true;
224                                                         break;
225                                         }
226                                 }
227                         }
228                 } else {
229                         cerr << "unable to interpret argument "
230                                 << i << " (" << arg << ")" << endl;
231                         error = true;
232                 }
233         }
234
235         if (error) {
236                 mode = ERROR;
237                 return;
238         }
239
240         if (config.asset_path.empty()) {
241                 config.asset_path = default_asset_path();
242         } else if (
243                 config.asset_path[config.asset_path.size() - 1] != '/' &&
244                 config.asset_path[config.asset_path.size() - 1] != '\\'
245         ) {
246                 config.asset_path += '/';
247         }
248         if (config.save_path.empty()) {
249                 config.save_path = default_save_path();
250         } else if (
251                 config.save_path[config.save_path.size() - 1] != '/' &&
252                 config.save_path[config.save_path.size() - 1] != '\\'
253         ) {
254                 config.save_path += '/';
255         }
256
257         if (n > 0) {
258                 if (t > 0) {
259                         mode = FIXED_FRAME_LIMIT;
260                 } else {
261                         mode = FRAME_LIMIT;
262                 }
263         } else if (t > 0) {
264                 mode = TIME_LIMIT;
265         } else {
266                 mode = NORMAL;
267         }
268 }
269
270 int Runtime::Execute() {
271         if (mode == ERROR) {
272                 return 1;
273         }
274
275         InitHeadless init_headless;
276
277         switch (target) {
278                 default:
279                 case STANDALONE:
280                         RunStandalone();
281                         break;
282                 case SERVER:
283                         RunServer();
284                         break;
285                 case CLIENT:
286                         RunClient();
287                         break;
288         }
289
290         return 0;
291 }
292
293 void Runtime::RunStandalone() {
294         Init init(config.doublebuf, config.multisampling);
295
296         Environment env(init.window, config.asset_path);
297         env.viewport.VSync(config.vsync);
298
299         WorldSave save(config.save_path + config.world_name + '/');
300         if (save.Exists()) {
301                 save.Read(config.world);
302         } else {
303                 save.Write(config.world);
304         }
305
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);
310         } else {
311                 std::ifstream file(keys_path);
312                 env.keymap.Load(file);
313         }
314
315         Application app(env);
316         WorldState world_state(env, config.interface, config.world, save);
317         app.PushState(&world_state);
318         Run(app);
319 }
320
321 void Runtime::RunServer() {
322         HeadlessEnvironment env(config.asset_path);
323
324         WorldSave save(config.save_path + config.world_name + '/');
325         if (save.Exists()) {
326                 save.Read(config.world);
327         } else {
328                 save.Write(config.world);
329         }
330
331         HeadlessApplication app(env);
332         ServerState server_state(env, config.world, save, config.server);
333         app.PushState(&server_state);
334         Run(app);
335 }
336
337 void Runtime::RunClient() {
338         Init init(config.doublebuf, config.multisampling);
339
340         Environment env(init.window, config.asset_path);
341         env.viewport.VSync(config.vsync);
342
343         WorldSave save(config.save_path + config.world_name + '/');
344         if (save.Exists()) {
345                 save.Read(config.world);
346         } else {
347                 save.Write(config.world);
348         }
349
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);
354         } else {
355                 std::ifstream file(keys_path);
356                 env.keymap.Load(file);
357         }
358
359         Application app(env);
360         ClientState client_state(env, config.world, save, config.client);
361         app.PushState(&client_state);
362         Run(app);
363 }
364
365 void Runtime::Run(HeadlessApplication &app) {
366         switch (mode) {
367                 default:
368                 case NORMAL:
369                         app.Run();
370                         break;
371                 case FRAME_LIMIT:
372                         app.RunN(n);
373                         break;
374                 case TIME_LIMIT:
375                         app.RunT(t);
376                         break;
377                 case FIXED_FRAME_LIMIT:
378                         app.RunS(n, t);
379                         break;
380         }
381 }
382
383 }