From afd253b2dd10fdf2d4655d3d4a5766e6aa8c1a2c Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Thu, 6 Aug 2015 11:03:46 +0200 Subject: [PATCH] state management and control --- src/app/Application.hpp | 43 ++----- src/app/Environment.hpp | 33 +++++ src/app/Runtime.hpp | 14 +- src/app/State.hpp | 23 ++++ src/app/StateControl.hpp | 52 ++++++++ src/app/WorldState.cpp | 67 ++++++++++ src/app/WorldState.hpp | 38 ++++++ src/app/app.cpp | 186 +++++++++++++++------------ src/app/{Runtime.cpp => runtime.cpp} | 37 +++++- src/ui/Interface.hpp | 9 +- src/ui/ui.cpp | 24 ++-- 11 files changed, 389 insertions(+), 137 deletions(-) create mode 100644 src/app/Environment.hpp create mode 100644 src/app/State.hpp create mode 100644 src/app/StateControl.hpp create mode 100644 src/app/WorldState.cpp create mode 100644 src/app/WorldState.hpp rename src/app/{Runtime.cpp => runtime.cpp} (86%) diff --git a/src/app/Application.hpp b/src/app/Application.hpp index 9732349..bae8435 100644 --- a/src/app/Application.hpp +++ b/src/app/Application.hpp @@ -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 +#include 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 states; }; diff --git a/src/app/Environment.hpp b/src/app/Environment.hpp new file mode 100644 index 0000000..b465bfb --- /dev/null +++ b/src/app/Environment.hpp @@ -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.hpp b/src/app/Runtime.hpp index ee0b4a0..036b8db 100644 --- a/src/app/Runtime.hpp +++ b/src/app/Runtime.hpp @@ -1,7 +1,8 @@ #ifndef BLANK_RUNTIME_HPP_ #define BLANK_RUNTIME_HPP_ -#include "Application.hpp" +#include "../ui/Interface.hpp" +#include "../world/World.hpp" #include @@ -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 index 0000000..2bdd41b --- /dev/null +++ b/src/app/State.hpp @@ -0,0 +1,23 @@ +#ifndef BLANK_APP_STATE_HPP_ +#define BLANK_APP_STATE_HPP_ + +#include + + +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 index 0000000..818bce5 --- /dev/null +++ b/src/app/StateControl.hpp @@ -0,0 +1,52 @@ +#ifndef BLANK_APP_STATECONTROL_HPP_ +#define BLANK_APP_STATECONTROL_HPP_ + +#include + + +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 cue; + +}; + +} + +#endif diff --git a/src/app/WorldState.cpp b/src/app/WorldState.cpp new file mode 100644 index 0000000..6ca9959 --- /dev/null +++ b/src/app/WorldState.cpp @@ -0,0 +1,67 @@ +#include "WorldState.hpp" + +#include "Environment.hpp" + +#include + + +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 index 0000000..38922ff --- /dev/null +++ b/src/app/WorldState.hpp @@ -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 diff --git a/src/app/app.cpp b/src/app/app.cpp index 86c90d8..420dee7 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -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" @@ -14,41 +17,22 @@ 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 similarity index 86% rename from src/app/Runtime.cpp rename to src/app/runtime.cpp index 6a4c783..37cca50 100644 --- a/src/app/Runtime.cpp +++ b/src/app/runtime.cpp @@ -1,16 +1,42 @@ +#include "Application.hpp" +#include "Environment.hpp" #include "Runtime.hpp" +#include "WorldState.hpp" #include "init.hpp" #include #include #include +#include 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) @@ -144,7 +170,15 @@ int Runtime::Execute() { } Init init(config.doublebuf, config.multisampling); - Application app(init.window, config); + + 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: @@ -160,6 +194,7 @@ int Runtime::Execute() { app.RunS(n, t); break; } + return 0; } diff --git a/src/ui/Interface.hpp b/src/ui/Interface.hpp index 1ade2d1..f810ff0 100644 --- a/src/ui/Interface.hpp +++ b/src/ui/Interface.hpp @@ -18,10 +18,8 @@ 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; diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp index 6fa7356..e53cff6 100644 --- a/src/ui/ui.cpp +++ b/src/ui/ui.cpp @@ -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()) { -- 2.39.2