]> git.localhorst.tv Git - blank.git/commitdiff
state management and control
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Thu, 6 Aug 2015 09:03:46 +0000 (11:03 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Thu, 6 Aug 2015 09:03:46 +0000 (11:03 +0200)
12 files changed:
src/app/Application.hpp
src/app/Environment.hpp [new file with mode: 0644]
src/app/Runtime.cpp [deleted file]
src/app/Runtime.hpp
src/app/State.hpp [new file with mode: 0644]
src/app/StateControl.hpp [new file with mode: 0644]
src/app/WorldState.cpp [new file with mode: 0644]
src/app/WorldState.hpp [new file with mode: 0644]
src/app/app.cpp
src/app/runtime.cpp [new file with mode: 0644]
src/ui/Interface.hpp
src/ui/ui.cpp

index 973234916a3afa541c2f2e41a544ef455e340521..bae8435a0084811151300e825b9c5fec402f35d8 100644 (file)
@@ -1,34 +1,20 @@
 #ifndef BLANK_APP_APPLICATION_HPP_
 #define BLANK_APP_APPLICATION_HPP_
 
-#include "Assets.hpp"
-#include "FrameCounter.hpp"
-#include "../ai/Spawner.hpp"
-#include "../audio/Audio.hpp"
-#include "../graphics/Viewport.hpp"
-#include "../ui/Interface.hpp"
-#include "../world/World.hpp"
-
 #include <SDL.h>
+#include <stack>
 
 
 namespace blank {
 
+class Environment;
+class State;
 class Window;
 
 class Application {
 
 public:
-       struct Config {
-               bool vsync = true;
-               bool doublebuf = true;
-               int multisampling = 1;
-
-               Interface::Config interface = Interface::Config();
-               World::Config world = World::Config();
-       };
-
-       Application(Window &, const Config &);
+       explicit Application(Environment &);
        ~Application();
 
        Application(const Application &) = delete;
@@ -48,25 +34,22 @@ public:
 
        /// process all events in SDL's queue
        void HandleEvents();
+       void Handle(const SDL_Event &);
        void Handle(const SDL_WindowEvent &);
        /// integrate to the next step with dt milliseconds passed
        void Update(int dt);
        /// push the current state to display
        void Render();
 
-private:
-       Window &window;
-       Viewport viewport;
-       Assets assets;
-       Audio audio;
-       FrameCounter counter;
+       void PushState(State *);
+       State *PopState();
+       State *SwitchState(State *);
+       State &GetState();
+       bool HasState() const noexcept;
 
-       World world;
-       Interface interface;
-
-       Spawner spawner;
-
-       bool running;
+private:
+       Environment &env;
+       std::stack<State *> states;
 
 };
 
diff --git a/src/app/Environment.hpp b/src/app/Environment.hpp
new file mode 100644 (file)
index 0000000..b465bfb
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef BLANK_APP_ENVIRONMENT_HPP_
+#define BLANK_APP_ENVIRONMENT_HPP_
+
+#include "Assets.hpp"
+#include "FrameCounter.hpp"
+#include "StateControl.hpp"
+#include "../audio/Audio.hpp"
+#include "../graphics/Viewport.hpp"
+
+
+namespace blank {
+
+class Window;
+
+struct Environment {
+
+       Audio audio;
+       Viewport viewport;
+       Window &window;
+
+       Assets assets;
+       FrameCounter counter;
+
+       StateControl state;
+
+
+       explicit Environment(Window &win);
+
+};
+
+}
+
+#endif
diff --git a/src/app/Runtime.cpp b/src/app/Runtime.cpp
deleted file mode 100644 (file)
index 6a4c783..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-#include "Runtime.hpp"
-
-#include "init.hpp"
-
-#include <cctype>
-#include <cstdlib>
-#include <iostream>
-
-using namespace std;
-
-
-namespace blank {
-
-Runtime::Runtime() noexcept
-: name("blank")
-, mode(NORMAL)
-, n(0)
-, t(0)
-, config() {
-
-}
-
-
-void Runtime::ReadArgs(int argc, const char *const *argv) {
-       if (argc <= 0) return;
-       name = argv[0];
-
-       bool options = true;
-       bool error = false;
-
-       for (int i = 1; i < argc; ++i) {
-               const char *arg = argv[i];
-               if (!arg || arg[0] == '\0') {
-                       cerr << "warning: found empty argument at position " << i << endl;
-                       continue;
-               }
-               if (options && arg[0] == '-') {
-                       if (arg[1] == '\0') {
-                               cerr << "warning: incomplete option list at position " << i << endl;
-                       } else if (arg[1] == '-') {
-                               if (arg[2] == '\0') {
-                                       // stopper
-                                       options = false;
-                               } else {
-                                       // long option
-                                       if (strcmp(arg + 2, "no-vsync") == 0) {
-                                               config.vsync = false;
-                                       } else if (strcmp(arg + 2, "no-keyboard") == 0) {
-                                               config.interface.keyboard_disabled = true;
-                                       } else if (strcmp(arg + 2, "no-mouse") == 0) {
-                                               config.interface.mouse_disabled = true;
-                                       } else if (strcmp(arg + 2, "no-hud") == 0) {
-                                               config.interface.visual_disabled = true;
-                                       } else if (strcmp(arg + 2, "no-audio") == 0) {
-                                               config.interface.audio_disabled = true;
-                                       } else {
-                                               cerr << "unknown option " << arg << endl;
-                                               error = true;
-                                       }
-                               }
-                       } else {
-                               // short options
-                               for (int j = 1; arg[j] != '\0'; ++j) {
-                                       switch (arg[j]) {
-                                               case 'd':
-                                                       config.doublebuf = false;
-                                                       break;
-                                               case 'm':
-                                                       ++i;
-                                                       if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
-                                                               cerr << "missing argument to -m" << endl;
-                                                               error = true;
-                                                       } else {
-                                                               config.multisampling = strtoul(argv[i], nullptr, 10);
-                                                       }
-                                                       break;
-                                               case 'n':
-                                                       ++i;
-                                                       if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
-                                                               cerr << "missing argument to -n" << endl;
-                                                               error = true;
-                                                       } else {
-                                                               n = strtoul(argv[i], nullptr, 10);
-                                                       }
-                                                       break;
-                                               case 's':
-                                                       ++i;
-                                                       if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
-                                                               cerr << "missing argument to -s" << endl;
-                                                               error = true;
-                                                       } else {
-                                                               config.world.gen.solid_seed = strtoul(argv[i], nullptr, 10);
-                                                               config.world.gen.type_seed = config.world.gen.solid_seed;
-                                                       }
-                                                       break;
-                                               case 't':
-                                                       ++i;
-                                                       if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
-                                                               cerr << "missing argument to -t" << endl;
-                                                               error = true;
-                                                       } else {
-                                                               t = strtoul(argv[i], nullptr, 10);
-                                                       }
-                                                       break;
-                                               case '-':
-                                                       // stopper
-                                                       options = false;
-                                                       break;
-                                               default:
-                                                       cerr << "unknown option " << arg[j] << endl;
-                                                       error = true;
-                                                       break;
-                                       }
-                               }
-                       }
-               } else {
-                       cerr << "unable to interpret argument "
-                               << i << " (" << arg << ")" << endl;
-                       error = true;
-               }
-       }
-
-       if (error) {
-               mode = ERROR;
-               return;
-       }
-
-       if (n > 0) {
-               if (t > 0) {
-                       mode = FIXED_FRAME_LIMIT;
-               } else {
-                       mode = FRAME_LIMIT;
-               }
-       } else if (t > 0) {
-               mode = TIME_LIMIT;
-       } else {
-               mode = NORMAL;
-       }
-}
-
-int Runtime::Execute() {
-       if (mode == ERROR) {
-               return 1;
-       }
-
-       Init init(config.doublebuf, config.multisampling);
-       Application app(init.window, config);
-       switch (mode) {
-               default:
-               case NORMAL:
-                       app.Run();
-                       break;
-               case FRAME_LIMIT:
-                       app.RunN(n);
-                       break;
-               case TIME_LIMIT:
-                       app.RunT(t);
-                       break;
-               case FIXED_FRAME_LIMIT:
-                       app.RunS(n, t);
-                       break;
-       }
-       return 0;
-}
-
-}
index ee0b4a023737ef1f4d6fea40d7723ab621e10667..036b8dbfc635ef9078454036113626b2b79266b0 100644 (file)
@@ -1,7 +1,8 @@
 #ifndef BLANK_RUNTIME_HPP_
 #define BLANK_RUNTIME_HPP_
 
-#include "Application.hpp"
+#include "../ui/Interface.hpp"
+#include "../world/World.hpp"
 
 #include <cstddef>
 
@@ -25,6 +26,15 @@ public:
                ERROR,
        };
 
+       struct Config {
+               bool vsync = true;
+               bool doublebuf = true;
+               int multisampling = 1;
+
+               Interface::Config interface = Interface::Config();
+               World::Config world = World::Config();
+       };
+
        Runtime() noexcept;
 
        void ReadArgs(int argc, const char *const *argv);
@@ -36,7 +46,7 @@ private:
        Mode mode;
        std::size_t n;
        std::size_t t;
-       Application::Config config;
+       Config config;
 
 };
 
diff --git a/src/app/State.hpp b/src/app/State.hpp
new file mode 100644 (file)
index 0000000..2bdd41b
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef BLANK_APP_STATE_HPP_
+#define BLANK_APP_STATE_HPP_
+
+#include <SDL.h>
+
+
+namespace blank {
+
+class Viewport;
+
+struct State {
+
+       virtual void Handle(const SDL_Event &) = 0;
+
+       virtual void Update(int dt) = 0;
+
+       virtual void Render(Viewport &) = 0;
+
+};
+
+};
+
+#endif
diff --git a/src/app/StateControl.hpp b/src/app/StateControl.hpp
new file mode 100644 (file)
index 0000000..818bce5
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef BLANK_APP_STATECONTROL_HPP_
+#define BLANK_APP_STATECONTROL_HPP_
+
+#include <queue>
+
+
+namespace blank {
+
+class Application;
+class State;
+
+class StateControl {
+
+public:
+       void Push(State *s) {
+               cue.emplace(PUSH, s);
+       }
+
+       void Switch(State *s) {
+               cue.emplace(SWITCH, s);
+       }
+
+       void Pop() {
+               cue.emplace(POP);
+       }
+
+       void PopAll() {
+               cue.emplace(POP_ALL);
+       }
+
+
+       void Commit(Application &);
+
+private:
+       enum Command {
+               PUSH,
+               SWITCH,
+               POP,
+               POP_ALL,
+       };
+       struct Memo {
+               State *state;
+               Command cmd;
+               explicit Memo(Command c, State *s = nullptr): state(s), cmd(c) { }
+       };
+       std::queue<Memo> cue;
+
+};
+
+}
+
+#endif
diff --git a/src/app/WorldState.cpp b/src/app/WorldState.cpp
new file mode 100644 (file)
index 0000000..6ca9959
--- /dev/null
@@ -0,0 +1,67 @@
+#include "WorldState.hpp"
+
+#include "Environment.hpp"
+
+#include <SDL.h>
+
+
+namespace blank {
+
+WorldState::WorldState(
+       Environment &env,
+       const Interface::Config &ic,
+       const World::Config &wc
+)
+: env(env)
+, world(wc)
+, spawner(world)
+, interface(ic, env, world) {
+
+}
+
+
+void WorldState::Handle(const SDL_Event &event) {
+       switch (event.type) {
+               case SDL_KEYDOWN:
+                       interface.HandlePress(event.key);
+                       break;
+               case SDL_KEYUP:
+                       interface.HandleRelease(event.key);
+                       break;
+               case SDL_MOUSEBUTTONDOWN:
+                       interface.HandlePress(event.button);
+                       break;
+               case SDL_MOUSEBUTTONUP:
+                       interface.HandleRelease(event.button);
+                       break;
+               case SDL_MOUSEMOTION:
+                       interface.Handle(event.motion);
+                       break;
+               case SDL_MOUSEWHEEL:
+                       interface.Handle(event.wheel);
+                       break;
+               default:
+                       break;
+       }
+}
+
+void WorldState::Update(int dt) {
+       interface.Update(dt);
+       spawner.Update(dt);
+       world.Update(dt);
+
+       glm::mat4 trans = world.Player().Transform(Chunk::Pos(0, 0, 0));
+       glm::vec3 dir(trans * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
+       glm::vec3 up(trans * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
+       env.audio.Position(world.Player().Position());
+       env.audio.Velocity(world.Player().Velocity());
+       env.audio.Orientation(dir, up);
+
+}
+
+void WorldState::Render(Viewport &viewport) {
+       world.Render(viewport);
+       interface.Render(viewport);
+}
+
+}
diff --git a/src/app/WorldState.hpp b/src/app/WorldState.hpp
new file mode 100644 (file)
index 0000000..38922ff
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef BLANK_APP_WORLDSTATE_HPP_
+#define BLANK_APP_WORLDSTATE_HPP_
+
+#include "State.hpp"
+#include "../ai/Spawner.hpp"
+#include "../ui/Interface.hpp"
+#include "../world/World.hpp"
+
+
+namespace blank {
+
+class Environment;
+
+class WorldState
+: public State {
+
+public:
+       WorldState(
+               Environment &,
+               const Interface::Config &,
+               const World::Config &
+       );
+
+       void Handle(const SDL_Event &) override;
+       void Update(int dt) override;
+       void Render(Viewport &) override;
+
+private:
+       Environment &env;
+       World world;
+       Spawner spawner;
+       Interface interface;
+
+};
+
+}
+
+#endif
index 86c90d83f043d644f83a25fcfecc2b8ce99a0872..420dee70aad191c042ac90d719cc01caa70c5fa0 100644 (file)
@@ -1,6 +1,9 @@
 #include "Application.hpp"
 #include "Assets.hpp"
+#include "Environment.hpp"
 #include "FrameCounter.hpp"
+#include "State.hpp"
+#include "StateControl.hpp"
 
 #include "init.hpp"
 #include "../audio/Sound.hpp"
 using std::string;
 
 
-namespace {
-
-string get_asset_path() {
-       char *base = SDL_GetBasePath();
-       string assets(base);
-       assets += "assets/";
-       SDL_free(base);
-       return assets;
-}
-
-}
-
 namespace blank {
 
-Application::Application(Window &win, const Config &config)
-: window(win)
-, viewport()
-, assets(get_asset_path())
-, audio()
-, counter()
-, world(config.world)
-, interface(config.interface, assets, audio, counter, world)
-, spawner(world)
-, running(false) {
-       viewport.VSync(config.vsync);
+Application::Application(Environment &e)
+: env(e)
+, states() {
+
 }
 
 Application::~Application() {
-       audio.StopAll();
+       env.audio.StopAll();
 }
 
 
 void Application::RunN(size_t n) {
        Uint32 last = SDL_GetTicks();
-       for (size_t i = 0; i < n; ++i) {
+       for (size_t i = 0; HasState() && i < n; ++i) {
                Uint32 now = SDL_GetTicks();
                int delta = now - last;
                Loop(delta);
@@ -59,7 +43,7 @@ void Application::RunN(size_t n) {
 void Application::RunT(size_t t) {
        Uint32 last = SDL_GetTicks();
        Uint32 finish = last + t;
-       while (last < finish) {
+       while (HasState() && last < finish) {
                Uint32 now = SDL_GetTicks();
                int delta = now - last;
                Loop(delta);
@@ -68,17 +52,16 @@ void Application::RunT(size_t t) {
 }
 
 void Application::RunS(size_t n, size_t t) {
-       for (size_t i = 0; i < n; ++i) {
+       for (size_t i = 0; HasState() && i < n; ++i) {
                Loop(t);
        }
 }
 
 
 void Application::Run() {
-       running = true;
        Uint32 last = SDL_GetTicks();
-       window.GrabMouse();
-       while (running) {
+       env.window.GrabMouse();
+       while (HasState()) {
                Uint32 now = SDL_GetTicks();
                int delta = now - last;
                Loop(delta);
@@ -87,60 +70,51 @@ void Application::Run() {
 }
 
 void Application::Loop(int dt) {
-       counter.EnterFrame();
+       env.counter.EnterFrame();
        HandleEvents();
+       if (!HasState()) return;
        Update(dt);
+       env.state.Commit(*this);
+       if (!HasState()) return;
        Render();
-       counter.ExitFrame();
+       env.counter.ExitFrame();
 }
 
 
 void Application::HandleEvents() {
-       counter.EnterHandle();
+       env.counter.EnterHandle();
        SDL_Event event;
-       while (SDL_PollEvent(&event)) {
-               switch (event.type) {
-                       case SDL_KEYDOWN:
-                               interface.HandlePress(event.key);
-                               break;
-                       case SDL_KEYUP:
-                               interface.HandleRelease(event.key);
-                               break;
-                       case SDL_MOUSEBUTTONDOWN:
-                               interface.HandlePress(event.button);
-                               break;
-                       case SDL_MOUSEBUTTONUP:
-                               interface.HandleRelease(event.button);
-                               break;
-                       case SDL_MOUSEMOTION:
-                               interface.Handle(event.motion);
-                               break;
-                       case SDL_MOUSEWHEEL:
-                               interface.Handle(event.wheel);
-                               break;
-                       case SDL_QUIT:
-                               running = false;
-                               break;
-                       case SDL_WINDOWEVENT:
-                               Handle(event.window);
-                               break;
-                       default:
-                               break;
-               }
+       while (HasState() && SDL_PollEvent(&event)) {
+               Handle(event);
+               env.state.Commit(*this);
+       }
+       env.counter.ExitHandle();
+}
+
+void Application::Handle(const SDL_Event &event) {
+       switch (event.type) {
+               case SDL_QUIT:
+                       env.state.PopAll();
+                       break;
+               case SDL_WINDOWEVENT:
+                       Handle(event.window);
+                       break;
+               default:
+                       GetState().Handle(event);
+                       break;
        }
-       counter.ExitHandle();
 }
 
 void Application::Handle(const SDL_WindowEvent &event) {
        switch (event.event) {
                case SDL_WINDOWEVENT_FOCUS_GAINED:
-                       window.GrabMouse();
+                       env.window.GrabMouse();
                        break;
                case SDL_WINDOWEVENT_FOCUS_LOST:
-                       window.ReleaseMouse();
+                       env.window.ReleaseMouse();
                        break;
                case SDL_WINDOWEVENT_RESIZED:
-                       viewport.Resize(event.data1, event.data2);
+                       env.viewport.Resize(event.data1, event.data2);
                        break;
                default:
                        break;
@@ -148,32 +122,74 @@ void Application::Handle(const SDL_WindowEvent &event) {
 }
 
 void Application::Update(int dt) {
-       counter.EnterUpdate();
-       interface.Update(dt);
-       spawner.Update(dt);
-       world.Update(dt);
-
-       glm::mat4 trans = world.Player().Transform(Chunk::Pos(0, 0, 0));
-       glm::vec3 dir(trans * glm::vec4(0.0f, 0.0f, -1.0f, 0.0f));
-       glm::vec3 up(trans * glm::vec4(0.0f, 1.0f, 0.0f, 0.0f));
-       audio.Position(world.Player().Position());
-       audio.Velocity(world.Player().Velocity());
-       audio.Orientation(dir, up);
-
-       counter.ExitUpdate();
+       env.counter.EnterUpdate();
+       if (HasState()) {
+               GetState().Update(dt);
+       }
+       env.counter.ExitUpdate();
 }
 
 void Application::Render() {
        // gl implementation may (and will probably) delay vsync blocking until
        // the first write after flipping, which is this clear call
-       viewport.Clear();
-       counter.EnterRender();
+       env.viewport.Clear();
+       env.counter.EnterRender();
+
+       if (HasState()) {
+               GetState().Render(env.viewport);
+       }
+
+       env.counter.ExitRender();
+       env.window.Flip();
+}
 
-       world.Render(viewport);
-       interface.Render(viewport);
 
-       counter.ExitRender();
-       window.Flip();
+void Application::PushState(State *s) {
+       states.emplace(s);
+}
+
+State *Application::PopState() {
+       State *s = states.top();
+       states.pop();
+       return s;
+}
+
+State *Application::SwitchState(State *s_new) {
+       State *s_old = states.top();
+       states.top() = s_new;
+       return s_old;
+}
+
+State &Application::GetState() {
+       return *states.top();
+}
+
+bool Application::HasState() const noexcept {
+       return !states.empty();
+}
+
+
+void StateControl::Commit(Application &app) {
+       while (!cue.empty()) {
+               Memo m(cue.front());
+               cue.pop();
+               switch (m.cmd) {
+                       case PUSH:
+                               app.PushState(m.state);
+                               break;
+                       case SWITCH:
+                               app.SwitchState(m.state);
+                               break;
+                       case POP:
+                               app.PopState();
+                               break;
+                       case POP_ALL:
+                               while (app.HasState()) {
+                                       app.PopState();
+                               }
+                               break;
+               }
+       }
 }
 
 
diff --git a/src/app/runtime.cpp b/src/app/runtime.cpp
new file mode 100644 (file)
index 0000000..37cca50
--- /dev/null
@@ -0,0 +1,201 @@
+#include "Application.hpp"
+#include "Environment.hpp"
+#include "Runtime.hpp"
+#include "WorldState.hpp"
+
+#include "init.hpp"
+
+#include <cctype>
+#include <cstdlib>
+#include <iostream>
+#include <SDL.h>
+
+using namespace std;
+
+
+namespace {
+
+string get_asset_path() {
+       char *base = SDL_GetBasePath();
+       string assets(base);
+       assets += "assets/";
+       SDL_free(base);
+       return assets;
+}
+
+}
+
+namespace blank {
+
+Environment::Environment(Window &win)
+: audio()
+, viewport()
+, window(win)
+, assets(get_asset_path())
+, counter() {
+
+}
+
+
+Runtime::Runtime() noexcept
+: name("blank")
+, mode(NORMAL)
+, n(0)
+, t(0)
+, config() {
+
+}
+
+
+void Runtime::ReadArgs(int argc, const char *const *argv) {
+       if (argc <= 0) return;
+       name = argv[0];
+
+       bool options = true;
+       bool error = false;
+
+       for (int i = 1; i < argc; ++i) {
+               const char *arg = argv[i];
+               if (!arg || arg[0] == '\0') {
+                       cerr << "warning: found empty argument at position " << i << endl;
+                       continue;
+               }
+               if (options && arg[0] == '-') {
+                       if (arg[1] == '\0') {
+                               cerr << "warning: incomplete option list at position " << i << endl;
+                       } else if (arg[1] == '-') {
+                               if (arg[2] == '\0') {
+                                       // stopper
+                                       options = false;
+                               } else {
+                                       // long option
+                                       if (strcmp(arg + 2, "no-vsync") == 0) {
+                                               config.vsync = false;
+                                       } else if (strcmp(arg + 2, "no-keyboard") == 0) {
+                                               config.interface.keyboard_disabled = true;
+                                       } else if (strcmp(arg + 2, "no-mouse") == 0) {
+                                               config.interface.mouse_disabled = true;
+                                       } else if (strcmp(arg + 2, "no-hud") == 0) {
+                                               config.interface.visual_disabled = true;
+                                       } else if (strcmp(arg + 2, "no-audio") == 0) {
+                                               config.interface.audio_disabled = true;
+                                       } else {
+                                               cerr << "unknown option " << arg << endl;
+                                               error = true;
+                                       }
+                               }
+                       } else {
+                               // short options
+                               for (int j = 1; arg[j] != '\0'; ++j) {
+                                       switch (arg[j]) {
+                                               case 'd':
+                                                       config.doublebuf = false;
+                                                       break;
+                                               case 'm':
+                                                       ++i;
+                                                       if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+                                                               cerr << "missing argument to -m" << endl;
+                                                               error = true;
+                                                       } else {
+                                                               config.multisampling = strtoul(argv[i], nullptr, 10);
+                                                       }
+                                                       break;
+                                               case 'n':
+                                                       ++i;
+                                                       if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+                                                               cerr << "missing argument to -n" << endl;
+                                                               error = true;
+                                                       } else {
+                                                               n = strtoul(argv[i], nullptr, 10);
+                                                       }
+                                                       break;
+                                               case 's':
+                                                       ++i;
+                                                       if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+                                                               cerr << "missing argument to -s" << endl;
+                                                               error = true;
+                                                       } else {
+                                                               config.world.gen.solid_seed = strtoul(argv[i], nullptr, 10);
+                                                               config.world.gen.type_seed = config.world.gen.solid_seed;
+                                                       }
+                                                       break;
+                                               case 't':
+                                                       ++i;
+                                                       if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+                                                               cerr << "missing argument to -t" << endl;
+                                                               error = true;
+                                                       } else {
+                                                               t = strtoul(argv[i], nullptr, 10);
+                                                       }
+                                                       break;
+                                               case '-':
+                                                       // stopper
+                                                       options = false;
+                                                       break;
+                                               default:
+                                                       cerr << "unknown option " << arg[j] << endl;
+                                                       error = true;
+                                                       break;
+                                       }
+                               }
+                       }
+               } else {
+                       cerr << "unable to interpret argument "
+                               << i << " (" << arg << ")" << endl;
+                       error = true;
+               }
+       }
+
+       if (error) {
+               mode = ERROR;
+               return;
+       }
+
+       if (n > 0) {
+               if (t > 0) {
+                       mode = FIXED_FRAME_LIMIT;
+               } else {
+                       mode = FRAME_LIMIT;
+               }
+       } else if (t > 0) {
+               mode = TIME_LIMIT;
+       } else {
+               mode = NORMAL;
+       }
+}
+
+int Runtime::Execute() {
+       if (mode == ERROR) {
+               return 1;
+       }
+
+       Init init(config.doublebuf, config.multisampling);
+
+       Environment env(init.window);
+       env.viewport.VSync(config.vsync);
+
+       Application app(env);
+
+       WorldState state(env, config.interface, config.world);
+       app.PushState(&state);
+
+       switch (mode) {
+               default:
+               case NORMAL:
+                       app.Run();
+                       break;
+               case FRAME_LIMIT:
+                       app.RunN(n);
+                       break;
+               case TIME_LIMIT:
+                       app.RunT(t);
+                       break;
+               case FIXED_FRAME_LIMIT:
+                       app.RunS(n, t);
+                       break;
+       }
+
+       return 0;
+}
+
+}
index 1ade2d1bf326b4d776f60e4eb8e1821ae85a5c40..f810ff0581f1358dd1bdaca2494df44de3dc23f5 100644 (file)
 
 namespace blank {
 
-class Assets;
-class Audio;
 class Chunk;
-class FrameCounter;
+class Environment;
 class Viewport;
 class World;
 
@@ -39,7 +37,7 @@ public:
                bool visual_disabled = false;
        };
 
-       Interface(const Config &, const Assets &, Audio &, const FrameCounter &, World &);
+       Interface(const Config &, Environment &, World &);
 
        void HandlePress(const SDL_KeyboardEvent &);
        void HandleRelease(const SDL_KeyboardEvent &);
@@ -86,8 +84,7 @@ private:
        void CheckAim();
 
 private:
-       Audio &audio;
-       const FrameCounter &counter;
+       Environment &env;
        World &world;
        FPSController ctrl;
        Font font;
index 6fa7356c790b25894a09a4bf22e5dc4076e7980b..e53cff606291d12d4f4fde960a49af38fa169f01 100644 (file)
@@ -2,6 +2,7 @@
 #include "Interface.hpp"
 
 #include "../app/Assets.hpp"
+#include "../app/Environment.hpp"
 #include "../app/FrameCounter.hpp"
 #include "../app/init.hpp"
 #include "../audio/Audio.hpp"
@@ -92,15 +93,12 @@ void HUD::Render(Viewport &viewport) noexcept {
 
 Interface::Interface(
        const Config &config,
-       const Assets &assets,
-       Audio &audio,
-       const FrameCounter &counter,
+       Environment &env,
        World &world)
-: audio(audio)
-, counter(counter)
+: env(env)
 , world(world)
 , ctrl(world.Player())
-, font(assets.LoadFont("DejaVuSans", 16))
+, font(env.assets.LoadFont("DejaVuSans", 16))
 , hud(world.BlockTypes(), font)
 , aim{{ 0, 0, 0 }, { 0, 0, -1 }}
 , aim_chunk(nullptr)
@@ -116,8 +114,8 @@ Interface::Interface(
 , remove_timer(256)
 , remove(0)
 , selection(1)
-, place_sound(assets.LoadSound("thump"))
-, remove_sound(assets.LoadSound("plop"))
+, place_sound(env.assets.LoadSound("thump"))
+, remove_sound(env.assets.LoadSound("plop"))
 , fwd(0)
 , rev(0) {
        counter_text.Hide();
@@ -354,8 +352,8 @@ void Interface::ToggleDebug() {
 void Interface::UpdateCounter() {
        std::stringstream s;
        s << std::setprecision(3) <<
-               "avg: " << counter.Average().running << "ms, "
-               "peak: " << counter.Peak().running << "ms";
+               "avg: " << env.counter.Average().running << "ms, "
+               "peak: " << env.counter.Peak().running << "ms";
        std::string text = s.str();
        counter_text.Set(font, text);
 }
@@ -416,7 +414,7 @@ void Interface::PlaceBlock() {
 
        if (config.audio_disabled) return;
        const Entity &player = ctrl.Controlled();
-       audio.Play(
+       env.audio.Play(
                place_sound,
                mod_chunk->ToSceneCoords(player.ChunkCoords(), next_pos)
        );
@@ -429,7 +427,7 @@ void Interface::RemoveBlock() noexcept {
 
        if (config.audio_disabled) return;
        const Entity &player = ctrl.Controlled();
-       audio.Play(
+       env.audio.Play(
                remove_sound,
                aim_chunk->ToSceneCoords(player.ChunkCoords(), Chunk::ToCoords(aim_block))
        );
@@ -496,7 +494,7 @@ void Interface::Update(int dt) {
                CheckAim();
        }
 
-       if (counter_text.Visible() && counter.Changed()) {
+       if (counter_text.Visible() && env.counter.Changed()) {
                UpdateCounter();
        }
        if (position_text.Visible()) {