#include "Application.hpp"
#include "Environment.hpp"
#include "Runtime.hpp"
-#include "WorldState.hpp"
#include "init.hpp"
+#include "../client/MasterState.hpp"
+#include "../io/filesystem.hpp"
+#include "../io/TokenStreamReader.hpp"
#include "../io/WorldSave.hpp"
+#include "../server/ServerState.hpp"
+#include "../standalone/MasterState.hpp"
#include <cctype>
#include <cstdlib>
+#include <ctime>
+#include <fstream>
#include <iostream>
#include <SDL.h>
namespace blank {
-Environment::Environment(Window &win, const string &asset_path)
-: audio()
+void Config::Load(std::istream &is) {
+ TokenStreamReader in(is);
+ std::string name;
+ while (in.HasMore()) {
+ if (in.Peek().type == Token::STRING) {
+ in.ReadString(name);
+ } else {
+ in.ReadIdentifier(name);
+ }
+ in.Skip(Token::EQUALS);
+ if (name == "audio.enabled") {
+ in.ReadBoolean(audio.enabled);
+ } else if (name == "input.keyboard") {
+ in.ReadBoolean(input.keyboard);
+ } else if (name == "input.mouse") {
+ in.ReadBoolean(input.mouse);
+ } else if (name == "input.pitch_sensitivity") {
+ in.ReadNumber(input.pitch_sensitivity);
+ } else if (name == "input.yaw_sensitivity") {
+ in.ReadNumber(input.yaw_sensitivity);
+ } else if (name == "net.host") {
+ in.ReadString(net.host);
+ } else if (name == "net.port") {
+ int port;
+ in.ReadNumber(port);
+ net.port = port;
+ } else if (name == "player.name") {
+ in.ReadString(player.name);
+ } else if (name == "video.dblbuf") {
+ in.ReadBoolean(video.dblbuf);
+ } else if (name == "video.vsync") {
+ in.ReadBoolean(video.vsync);
+ } else if (name == "video.msaa") {
+ in.ReadNumber(video.msaa);
+ } else if (name == "video.hud") {
+ in.ReadBoolean(video.hud);
+ } else if (name == "video.world") {
+ in.ReadBoolean(video.world);
+ } else if (name == "video.debug") {
+ in.ReadBoolean(video.debug);
+ }
+ if (in.HasMore() && in.Peek().type == Token::SEMICOLON) {
+ in.Skip(Token::SEMICOLON);
+ }
+ }
+}
+
+void Config::Save(std::ostream &out) {
+ out << "audio.enabled = " << (audio.enabled ? "yes" : "no") << ';' << std::endl;
+ out << "input.keyboard = " << (input.keyboard ? "on" : "off") << ';' << std::endl;
+ out << "input.mouse = " << (input.keyboard ? "on" : "off") << ';' << std::endl;
+ out << "input.pitch_sensitivity = " << input.pitch_sensitivity << ';' << std::endl;
+ out << "input.yaw_sensitivity = " << input.yaw_sensitivity << ';' << std::endl;
+ out << "net.host = \"" << net.host << "\";" << std::endl;
+ out << "net.port = " << net.port << ';' << std::endl;
+ out << "player.name = \"" << player.name << "\";" << std::endl;
+ out << "video.dblbuf = " << (video.dblbuf ? "on" : "off") << ';' << std::endl;
+ out << "video.vsync = " << (video.vsync ? "on" : "off") << ';' << std::endl;
+ out << "video.msaa = " << video.msaa << ';' << std::endl;
+ out << "video.hud = " << (video.hud ? "on" : "off") << ';' << std::endl;
+ out << "video.world = " << (video.world ? "on" : "off") << ';' << std::endl;
+ out << "video.debug = " << (video.debug ? "on" : "off") << ';' << std::endl;
+}
+
+
+HeadlessEnvironment::HeadlessEnvironment(const Config &config)
+: config(config)
+, loader(config.asset_path)
+, counter()
+, state()
+, rng(
+#ifdef BLANK_PROFILING
+0
+#else
+std::time(nullptr)
+#endif
+){
+ for (int i = 0; i < 4; ++i) {
+ rng.Next<int>();
+ }
+}
+
+string HeadlessEnvironment::Config::GetWorldPath(const string &world_name) const {
+ return save_path + "worlds/" + world_name + '/';
+}
+
+string HeadlessEnvironment::Config::GetWorldPath(const string &world_name, const string &host_name) const {
+ return save_path + "cache/" + host_name + '/' + world_name + '/';
+}
+
+Environment::Environment(Window &win, const Config &config)
+: HeadlessEnvironment(config)
+, assets(loader)
+, audio()
, viewport()
, window(win)
-, assets(asset_path)
-, counter() {
+, keymap()
+, msg_state(*this) {
viewport.Clear();
window.Flip();
+ keymap.LoadDefault();
+
+ string keys_path = config.save_path + "keys.conf";
+ if (!is_file(keys_path)) {
+ std::ofstream file(keys_path);
+ keymap.Save(file);
+ } else {
+ std::ifstream file(keys_path);
+ keymap.Load(file);
+ }
+}
+
+void Environment::ShowMessage(const char *msg) {
+ cout << msg << endl;
+ msg_state.SetMessage(msg);
+ state.Push(&msg_state);
}
Runtime::Runtime() noexcept
: name("blank")
, mode(NORMAL)
+, target(STANDALONE)
, n(0)
, t(0)
, config() {
}
+void Runtime::Initialize(int argc, const char *const *argv) {
+ ReadArgs(argc, argv);
+ if (mode == ERROR) return;
+ ReadPreferences();
+ ReadArgs(argc, argv);
+}
+
void Runtime::ReadArgs(int argc, const char *const *argv) {
if (argc <= 0) return;
name = argv[0];
const char *param = arg + 2;
// long option
if (strcmp(param, "no-vsync") == 0) {
- config.vsync = false;
+ config.game.video.vsync = false;
} else if (strcmp(param, "no-keyboard") == 0) {
- config.interface.keyboard_disabled = true;
+ config.game.input.keyboard = false;
} else if (strcmp(param, "no-mouse") == 0) {
- config.interface.mouse_disabled = true;
+ config.game.input.mouse = false;
} else if (strcmp(param, "no-hud") == 0) {
- config.interface.visual_disabled = true;
+ config.game.video.hud = false;
} else if (strcmp(param, "no-audio") == 0) {
- config.interface.audio_disabled = true;
+ config.game.audio.enabled = false;
+ } else if (strcmp(param, "standalone") == 0) {
+ target = STANDALONE;
+ } else if (strcmp(param, "server") == 0) {
+ target = SERVER;
+ } else if (strcmp(param, "client") == 0) {
+ target = CLIENT;
} else if (strcmp(param, "asset-path") == 0) {
++i;
if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
cerr << "missing argument to --asset-path" << endl;
error = true;
} else {
- config.asset_path = argv[i];
+ config.env.asset_path = argv[i];
+ }
+ } else if (strcmp(param, "host") == 0) {
+ ++i;
+ if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+ cerr << "missing argument to --host" << endl;
+ error = true;
+ } else {
+ config.game.net.host = argv[i];
+ }
+ } else if (strcmp(param, "port") == 0) {
+ ++i;
+ if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+ cerr << "missing argument to --port" << endl;
+ error = true;
+ } else {
+ config.game.net.port = strtoul(argv[i], nullptr, 10);
+ }
+ } else if (strcmp(param, "player-name") == 0) {
+ ++i;
+ if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+ cerr << "missing argument to --player-name" << endl;
+ error = true;
+ } else {
+ config.game.player.name = argv[i];
}
} else if (strcmp(param, "save-path") == 0) {
++i;
cerr << "missing argument to --save-path" << endl;
error = true;
} else {
- config.save_path = argv[i];
+ config.env.save_path = argv[i];
}
} else if (strcmp(param, "world-name") == 0) {
++i;
cerr << "missing argument to --world-name" << endl;
error = true;
} else {
- config.world_name = argv[i];
+ config.world.name = argv[i];
}
} else {
cerr << "unknown option " << arg << endl;
for (int j = 1; arg[j] != '\0'; ++j) {
switch (arg[j]) {
case 'd':
- config.doublebuf = false;
+ config.game.video.dblbuf = false;
break;
case 'm':
++i;
cerr << "missing argument to -m" << endl;
error = true;
} else {
- config.multisampling = strtoul(argv[i], nullptr, 10);
+ config.game.video.msaa = strtoul(argv[i], nullptr, 10);
}
break;
case 'n':
cerr << "missing argument to -s" << endl;
error = true;
} else {
- config.world.gen.seed = strtoul(argv[i], nullptr, 10);
+ config.gen.seed = strtoul(argv[i], nullptr, 10);
}
break;
case 't':
if (error) {
mode = ERROR;
- return;
+ } else if (n > 0) {
+ if (t > 0) {
+ mode = FIXED_FRAME_LIMIT;
+ } else {
+ mode = FRAME_LIMIT;
+ }
+ } else if (t > 0) {
+ mode = TIME_LIMIT;
+ } else {
+ mode = NORMAL;
}
+}
- if (config.asset_path.empty()) {
- config.asset_path = default_asset_path();
+void Runtime::ReadPreferences() {
+ if (config.env.asset_path.empty()) {
+ config.env.asset_path = default_asset_path();
} else if (
- config.asset_path[config.asset_path.size() - 1] != '/' &&
- config.asset_path[config.asset_path.size() - 1] != '\\'
+ config.env.asset_path[config.env.asset_path.size() - 1] != '/' &&
+ config.env.asset_path[config.env.asset_path.size() - 1] != '\\'
) {
- config.asset_path += '/';
+ config.env.asset_path += '/';
}
- if (config.save_path.empty()) {
- config.save_path = default_save_path();
+ if (config.env.save_path.empty()) {
+ config.env.save_path = default_save_path();
} else if (
- config.save_path[config.save_path.size() - 1] != '/' &&
- config.save_path[config.save_path.size() - 1] != '\\'
+ config.env.save_path[config.env.save_path.size() - 1] != '/' &&
+ config.env.save_path[config.env.save_path.size() - 1] != '\\'
) {
- config.save_path += '/';
+ config.env.save_path += '/';
}
- if (n > 0) {
- if (t > 0) {
- mode = FIXED_FRAME_LIMIT;
- } else {
- mode = FRAME_LIMIT;
- }
- } else if (t > 0) {
- mode = TIME_LIMIT;
+ string prefs_path = config.env.save_path + "prefs.conf";
+ if (is_file(prefs_path)) {
+ ifstream file(prefs_path);
+ config.game.Load(file);
} else {
- mode = NORMAL;
+ make_dirs(config.env.save_path);
+ ofstream file(prefs_path);
+ config.game.Save(file);
}
}
return 1;
}
- Init init(config.doublebuf, config.multisampling);
+ InitHeadless init_headless;
- Environment env(init.window, config.asset_path);
- env.viewport.VSync(config.vsync);
+ switch (target) {
+ default:
+ case STANDALONE:
+ RunStandalone();
+ break;
+ case SERVER:
+ RunServer();
+ break;
+ case CLIENT:
+ RunClient();
+ break;
+ }
- WorldSave save(config.save_path + config.world_name + '/');
+ return 0;
+}
+
+void Runtime::RunStandalone() {
+ Init init(config.game.video.dblbuf, config.game.video.msaa);
+
+ Environment env(init.window, config.env);
+ env.viewport.VSync(config.game.video.vsync);
+
+ WorldSave save(config.env.GetWorldPath(config.world.name));
if (save.Exists()) {
save.Read(config.world);
+ save.Read(config.gen);
} else {
save.Write(config.world);
+ save.Write(config.gen);
}
Application app(env);
-
- WorldState world_state(env, config.interface, config.world, save);
+ standalone::MasterState world_state(env, config.game, config.gen, config.world, save);
app.PushState(&world_state);
+ Run(app);
+}
+
+void Runtime::RunServer() {
+ HeadlessEnvironment env(config.env);
+ WorldSave save(config.env.GetWorldPath(config.world.name));
+ if (save.Exists()) {
+ save.Read(config.world);
+ save.Read(config.gen);
+ } else {
+ save.Write(config.world);
+ save.Write(config.gen);
+ }
+
+ HeadlessApplication app(env);
+ server::ServerState server_state(env, config.gen, config.world, save, config.game);
+ app.PushState(&server_state);
+ Run(app);
+}
+
+void Runtime::RunClient() {
+ Init init(config.game.video.dblbuf, config.game.video.msaa);
+
+ Environment env(init.window, config.env);
+ env.viewport.VSync(config.game.video.vsync);
+
+ Application app(env);
+ client::MasterState client_state(env, config.game, config.world);
+ app.PushState(&client_state);
+ Run(app);
+}
+
+void Runtime::Run(HeadlessApplication &app) {
switch (mode) {
default:
case NORMAL:
app.RunS(n, t);
break;
}
-
- return 0;
}
}