mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-22 10:22:52 +01:00
Overhaul State and StateManager to remove pointers
Removed raw State pointers in favour of unique_ptrs Avoid allowing control flow to re-enter States that have exited Defer releasing states until the end of the frame
This commit is contained in:
parent
b4aa01e4bb
commit
6a7802de87
@ -15,6 +15,8 @@ set(RWGAME_SOURCES
|
|||||||
GameConfig.cpp
|
GameConfig.cpp
|
||||||
GameWindow.cpp
|
GameWindow.cpp
|
||||||
|
|
||||||
|
StateManager.hpp
|
||||||
|
StateManager.cpp
|
||||||
State.cpp
|
State.cpp
|
||||||
|
|
||||||
states/LoadingState.hpp
|
states/LoadingState.hpp
|
||||||
|
@ -35,7 +35,6 @@ RWGame::RWGame(Logger& log, int argc, char* argv[])
|
|||||||
: GameBase(log, argc, argv)
|
: GameBase(log, argc, argv)
|
||||||
, data(&log, &work, config.getGameDataPath())
|
, data(&log, &work, config.getGameDataPath())
|
||||||
, renderer(&log, &data) {
|
, renderer(&log, &data) {
|
||||||
|
|
||||||
bool newgame = options.count("newgame");
|
bool newgame = options.count("newgame");
|
||||||
bool test = options.count("test");
|
bool test = options.count("test");
|
||||||
std::string startSave(
|
std::string startSave(
|
||||||
@ -73,7 +72,7 @@ RWGame::RWGame(Logger& log, int argc, char* argv[])
|
|||||||
data.loadGXT("text/" + config.getGameLanguage() + ".gxt");
|
data.loadGXT("text/" + config.getGameLanguage() + ".gxt");
|
||||||
|
|
||||||
getRenderer().water.setWaterTable(data.waterHeights, 48, data.realWater,
|
getRenderer().water.setWaterTable(data.waterHeights, 48, data.realWater,
|
||||||
128 * 128);
|
128 * 128);
|
||||||
|
|
||||||
for (int m = 0; m < MAP_BLOCK_SIZE; ++m) {
|
for (int m = 0; m < MAP_BLOCK_SIZE; ++m) {
|
||||||
std::string num = (m < 10 ? "0" : "");
|
std::string num = (m < 10 ? "0" : "");
|
||||||
@ -81,20 +80,19 @@ RWGame::RWGame(Logger& log, int argc, char* argv[])
|
|||||||
data.loadTXD(name + ".txd");
|
data.loadTXD(name + ".txd");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto loading = new LoadingState(this);
|
StateManager::get().enter<LoadingState>(this, [=]() {
|
||||||
if (!benchFile.empty()) {
|
if (!benchFile.empty()) {
|
||||||
loading->setNextState(new BenchmarkState(this, benchFile));
|
StateManager::get().enter<BenchmarkState>(this, benchFile);
|
||||||
} else if (test) {
|
} else if (test) {
|
||||||
loading->setNextState(new IngameState(this, true, "test"));
|
StateManager::get().enter<IngameState>(this, true, "test");
|
||||||
} else if (newgame) {
|
} else if (newgame) {
|
||||||
loading->setNextState(new IngameState(this, true));
|
StateManager::get().enter<IngameState>(this, true);
|
||||||
} else if (!startSave.empty()) {
|
} else if (!startSave.empty()) {
|
||||||
loading->setNextState(new IngameState(this, true, startSave));
|
StateManager::get().enter<IngameState>(this, true, startSave);
|
||||||
} else {
|
} else {
|
||||||
loading->setNextState(new MenuState(this));
|
StateManager::get().enter<MenuState>(this);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
StateManager::get().enter(loading);
|
|
||||||
|
|
||||||
log.info("Game", "Started");
|
log.info("Game", "Started");
|
||||||
}
|
}
|
||||||
@ -384,8 +382,9 @@ int RWGame::run() {
|
|||||||
last_clock_time = clock.now();
|
last_clock_time = clock.now();
|
||||||
|
|
||||||
// Loop until we run out of states.
|
// Loop until we run out of states.
|
||||||
while (StateManager::get().states.size()) {
|
bool running = true;
|
||||||
State* state = StateManager::get().states.back();
|
while (!StateManager::get().states.empty() && running) {
|
||||||
|
State* state = StateManager::get().states.back().get();
|
||||||
|
|
||||||
RW_PROFILE_FRAME_BOUNDARY();
|
RW_PROFILE_FRAME_BOUNDARY();
|
||||||
|
|
||||||
@ -394,7 +393,7 @@ int RWGame::run() {
|
|||||||
while (SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_QUIT:
|
case SDL_QUIT:
|
||||||
StateManager::get().clear();
|
running = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SDL_WINDOWEVENT:
|
case SDL_WINDOWEVENT:
|
||||||
@ -474,8 +473,13 @@ int RWGame::run() {
|
|||||||
renderProfile();
|
renderProfile();
|
||||||
|
|
||||||
getWindow().swap();
|
getWindow().swap();
|
||||||
|
|
||||||
|
// Make sure the topmost state is the correct state
|
||||||
|
StateManager::get().updateStack();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StateManager::get().clear();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -483,7 +487,7 @@ void RWGame::tick(float dt) {
|
|||||||
// Process the Engine's background work.
|
// Process the Engine's background work.
|
||||||
world->_work->update();
|
world->_work->update();
|
||||||
|
|
||||||
State* currState = StateManager::get().states.back();
|
State* currState = StateManager::get().states.back().get();
|
||||||
|
|
||||||
world->chase.update(dt);
|
world->chase.update(dt);
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
#ifndef _GAME_STATE_HPP_
|
#ifndef RWGAME_STATE_HPP
|
||||||
#define _GAME_STATE_HPP_
|
#define RWGAME_STATE_HPP
|
||||||
#include <functional>
|
|
||||||
#include <glm/gtc/quaternion.hpp>
|
|
||||||
#include <queue>
|
|
||||||
#include <render/ViewCamera.hpp>
|
#include <render/ViewCamera.hpp>
|
||||||
#include "GameWindow.hpp"
|
#include "GameWindow.hpp"
|
||||||
#include "MenuSystem.hpp"
|
#include "MenuSystem.hpp"
|
||||||
@ -11,8 +8,10 @@
|
|||||||
|
|
||||||
class RWGame;
|
class RWGame;
|
||||||
class GameWorld;
|
class GameWorld;
|
||||||
|
class StateManager;
|
||||||
|
|
||||||
struct State {
|
class State {
|
||||||
|
public:
|
||||||
// Helper for global menu behaviour
|
// Helper for global menu behaviour
|
||||||
Menu* currentMenu;
|
Menu* currentMenu;
|
||||||
Menu* nextMenu;
|
Menu* nextMenu;
|
||||||
@ -66,46 +65,15 @@ struct State {
|
|||||||
|
|
||||||
GameWorld* getWorld();
|
GameWorld* getWorld();
|
||||||
GameWindow& getWindow();
|
GameWindow& getWindow();
|
||||||
};
|
|
||||||
|
|
||||||
struct StateManager {
|
bool hasExited() const {
|
||||||
static StateManager& get() {
|
return hasexited_;
|
||||||
static StateManager m;
|
|
||||||
return m;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::deque<State*> states;
|
private:
|
||||||
|
bool hasexited_ = false;
|
||||||
void clear() {
|
protected:
|
||||||
states.clear();
|
void done() { hasexited_ = true; }
|
||||||
}
|
|
||||||
|
|
||||||
void enter(State* state) {
|
|
||||||
states.push_back(state);
|
|
||||||
state->enter();
|
|
||||||
}
|
|
||||||
|
|
||||||
void exec(State* state) {
|
|
||||||
exit();
|
|
||||||
enter(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void tick(float dt) {
|
|
||||||
states.back()->tick(dt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void draw(GameRenderer* r) {
|
|
||||||
states.back()->draw(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
void exit() {
|
|
||||||
// TODO: Resole states being leaked.
|
|
||||||
states.back()->exit();
|
|
||||||
states.pop_back();
|
|
||||||
if (states.size() > 0) {
|
|
||||||
states.back()->enter();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
0
rwgame/StateManager.cpp
Normal file
0
rwgame/StateManager.cpp
Normal file
70
rwgame/StateManager.hpp
Normal file
70
rwgame/StateManager.hpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#ifndef RWGAME_STATEMANAGER_HPP
|
||||||
|
#define RWGAME_STATEMANAGER_HPP
|
||||||
|
#include "State.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Handles current state focus and transitions
|
||||||
|
*
|
||||||
|
* Possible states:
|
||||||
|
* Foreground (topmost state)
|
||||||
|
* Background (any other position)
|
||||||
|
*
|
||||||
|
* Transitions:
|
||||||
|
* New State (at the top)
|
||||||
|
* Suspended (a state was created above us)
|
||||||
|
* Resumed (blocking state was removed)
|
||||||
|
*/
|
||||||
|
class StateManager {
|
||||||
|
public:
|
||||||
|
static StateManager& get() {
|
||||||
|
static StateManager m;
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::deque<std::unique_ptr<State>> states;
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
cleared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class... Targs>
|
||||||
|
void enter(Targs&&... args) {
|
||||||
|
// Notify the previous state it has been suspended
|
||||||
|
if (!states.empty()) {
|
||||||
|
states.back()->exit();
|
||||||
|
}
|
||||||
|
states.emplace_back(std::move(std::make_unique<T>(args...)));
|
||||||
|
states.back()->enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateStack() {
|
||||||
|
if (cleared) {
|
||||||
|
states.clear();
|
||||||
|
cleared = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!states.empty() && states.back()->hasExited()) {
|
||||||
|
states.back()->exit();
|
||||||
|
states.pop_back();
|
||||||
|
if (!states.empty()) {
|
||||||
|
states.back()->enter();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tick(float dt) {
|
||||||
|
states.back()->tick(dt);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(GameRenderer* r) {
|
||||||
|
states.back()->draw(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool cleared = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -77,7 +77,7 @@ void BenchmarkState::tick(float dt) {
|
|||||||
a = p;
|
a = p;
|
||||||
}
|
}
|
||||||
if (benchmarkTime > duration) {
|
if (benchmarkTime > duration) {
|
||||||
StateManager::get().exit();
|
done();
|
||||||
}
|
}
|
||||||
if (b.time != a.time) {
|
if (b.time != a.time) {
|
||||||
float alpha = (benchmarkTime - a.time) / (b.time - a.time);
|
float alpha = (benchmarkTime - a.time) / (b.time - a.time);
|
||||||
|
@ -363,7 +363,7 @@ void DebugState::handleEvent(const SDL_Event& event) {
|
|||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
switch (event.key.keysym.sym) {
|
switch (event.key.keysym.sym) {
|
||||||
case SDLK_ESCAPE:
|
case SDLK_ESCAPE:
|
||||||
StateManager::get().exit();
|
done();
|
||||||
break;
|
break;
|
||||||
case SDLK_w:
|
case SDLK_w:
|
||||||
_movement.x = 1.f;
|
_movement.x = 1.f;
|
||||||
|
@ -412,11 +412,11 @@ void IngameState::handleEvent(const SDL_Event& event) {
|
|||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
switch (event.key.keysym.sym) {
|
switch (event.key.keysym.sym) {
|
||||||
case SDLK_ESCAPE:
|
case SDLK_ESCAPE:
|
||||||
StateManager::get().enter(new PauseState(game));
|
StateManager::get().enter<PauseState>(game);
|
||||||
break;
|
break;
|
||||||
case SDLK_m:
|
case SDLK_m:
|
||||||
StateManager::get().enter(
|
StateManager::get().enter<DebugState>(game, _look.position,
|
||||||
new DebugState(game, _look.position, _look.rotation));
|
_look.rotation);
|
||||||
break;
|
break;
|
||||||
case SDLK_SPACE:
|
case SDLK_SPACE:
|
||||||
if (getWorld()->state->currentCutscene) {
|
if (getWorld()->state->currentCutscene) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef INGAMESTATE_HPP
|
#ifndef INGAMESTATE_HPP
|
||||||
#define INGAMESTATE_HPP
|
#define INGAMESTATE_HPP
|
||||||
|
|
||||||
#include "State.hpp"
|
#include "StateManager.hpp"
|
||||||
|
|
||||||
class PlayerController;
|
class PlayerController;
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
#include "LoadingState.hpp"
|
#include "LoadingState.hpp"
|
||||||
#include <render/OpenGLRenderer.hpp>
|
|
||||||
#include "RWGame.hpp"
|
#include "RWGame.hpp"
|
||||||
|
|
||||||
LoadingState::LoadingState(RWGame* game) : State(game), next(nullptr) {
|
LoadingState::LoadingState(RWGame* game, std::function<void(void)> callback)
|
||||||
|
: State(game), complete(callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadingState::enter() {
|
void LoadingState::enter() {
|
||||||
@ -15,9 +15,11 @@ void LoadingState::exit() {
|
|||||||
void LoadingState::tick(float dt) {
|
void LoadingState::tick(float dt) {
|
||||||
RW_UNUSED(dt);
|
RW_UNUSED(dt);
|
||||||
|
|
||||||
// If background work is completed, switch to the next state
|
// If background work is completed, do callback
|
||||||
if (getWorld()->_work->isEmpty()) {
|
if (getWorld()->_work->isEmpty()) {
|
||||||
StateManager::get().exec(next);
|
// If we ever get back to this state it should be completed
|
||||||
|
done();
|
||||||
|
complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,10 +27,6 @@ bool LoadingState::shouldWorldUpdate() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadingState::setNextState(State* nextState) {
|
|
||||||
next = nextState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoadingState::handleEvent(const SDL_Event& e) {
|
void LoadingState::handleEvent(const SDL_Event& e) {
|
||||||
State::handleEvent(e);
|
State::handleEvent(e);
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
#ifndef LOADINGSTATE_HPP
|
#ifndef LOADINGSTATE_HPP
|
||||||
#define LOADINGSTATE_HPP
|
#define LOADINGSTATE_HPP
|
||||||
|
#include "StateManager.hpp"
|
||||||
|
|
||||||
#include "State.hpp"
|
#include <functional>
|
||||||
|
|
||||||
class LoadingState : public State {
|
class LoadingState : public State {
|
||||||
State* next;
|
std::function<void(void)> complete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LoadingState(RWGame* game);
|
LoadingState(RWGame* game, std::function<void(void)> callback);
|
||||||
|
|
||||||
virtual void enter();
|
virtual void enter();
|
||||||
virtual void exit();
|
virtual void exit();
|
||||||
@ -16,8 +17,6 @@ public:
|
|||||||
|
|
||||||
virtual void draw(GameRenderer* r);
|
virtual void draw(GameRenderer* r);
|
||||||
|
|
||||||
void setNextState(State* nextState);
|
|
||||||
|
|
||||||
virtual bool shouldWorldUpdate();
|
virtual bool shouldWorldUpdate();
|
||||||
|
|
||||||
virtual void handleEvent(const SDL_Event& event);
|
virtual void handleEvent(const SDL_Event& event);
|
||||||
|
@ -16,12 +16,12 @@ void MenuState::enterMainMenu() {
|
|||||||
Menu* m = new Menu;
|
Menu* m = new Menu;
|
||||||
m->offset = glm::vec2(200.f, 200.f);
|
m->offset = glm::vec2(200.f, 200.f);
|
||||||
m->addEntry(Menu::lambda(t.text(MenuDefaults::kStartGameId), [=] {
|
m->addEntry(Menu::lambda(t.text(MenuDefaults::kStartGameId), [=] {
|
||||||
StateManager::get().enter(new IngameState(game));
|
StateManager::get().enter<IngameState>(game);
|
||||||
}));
|
}));
|
||||||
m->addEntry(Menu::lambda(t.text(MenuDefaults::kLoadGameId),
|
m->addEntry(Menu::lambda(t.text(MenuDefaults::kLoadGameId),
|
||||||
[=] { enterLoadMenu(); }));
|
[=] { enterLoadMenu(); }));
|
||||||
m->addEntry(Menu::lambda(t.text(MenuDefaults::kDebugId), [=] {
|
m->addEntry(Menu::lambda(t.text(MenuDefaults::kDebugId), [=] {
|
||||||
StateManager::get().enter(new IngameState(game, true, "test"));
|
StateManager::get().enter<IngameState>(game, true, "test");
|
||||||
}));
|
}));
|
||||||
m->addEntry(Menu::lambda(t.text(MenuDefaults::kOptionsId),
|
m->addEntry(Menu::lambda(t.text(MenuDefaults::kOptionsId),
|
||||||
[] { RW_UNIMPLEMENTED("Options Menu"); }));
|
[] { RW_UNIMPLEMENTED("Options Menu"); }));
|
||||||
@ -45,13 +45,11 @@ void MenuState::enterLoadMenu() {
|
|||||||
<< save.basicState.saveTime.minute << " ";
|
<< save.basicState.saveTime.minute << " ";
|
||||||
auto name = GameStringUtil::fromString(ss.str());
|
auto name = GameStringUtil::fromString(ss.str());
|
||||||
name += save.basicState.saveName;
|
name += save.basicState.saveName;
|
||||||
m->addEntry(Menu::lambda(name,
|
auto loadsave = [=] {
|
||||||
[=] {
|
StateManager::get().enter<IngameState>(game, false);
|
||||||
StateManager::get().enter(
|
game->loadGame(save.savePath);
|
||||||
new IngameState(game, false));
|
};
|
||||||
game->loadGame(save.savePath);
|
m->addEntry(Menu::lambda(name, loadsave, 20.f));
|
||||||
},
|
|
||||||
20.f));
|
|
||||||
} else {
|
} else {
|
||||||
m->addEntry(Menu::lambda("CORRUPT", [=] {}));
|
m->addEntry(Menu::lambda("CORRUPT", [=] {}));
|
||||||
}
|
}
|
||||||
@ -75,7 +73,7 @@ void MenuState::handleEvent(const SDL_Event& e) {
|
|||||||
case SDL_KEYUP:
|
case SDL_KEYUP:
|
||||||
switch (e.key.keysym.sym) {
|
switch (e.key.keysym.sym) {
|
||||||
case SDLK_ESCAPE:
|
case SDLK_ESCAPE:
|
||||||
StateManager::get().exit();
|
done();
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef MENUSTATE_HPP
|
#ifndef MENUSTATE_HPP
|
||||||
#define MENUSTATE_HPP
|
#define MENUSTATE_HPP
|
||||||
|
|
||||||
#include "State.hpp"
|
#include "StateManager.hpp"
|
||||||
|
|
||||||
class MenuState : public State {
|
class MenuState : public State {
|
||||||
public:
|
public:
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
#include "PauseState.hpp"
|
#include "PauseState.hpp"
|
||||||
#include <ai/PlayerController.hpp>
|
|
||||||
#include <objects/CharacterObject.hpp>
|
|
||||||
#include "RWGame.hpp"
|
#include "RWGame.hpp"
|
||||||
|
|
||||||
PauseState::PauseState(RWGame* game) : State(game) {
|
PauseState::PauseState(RWGame* game) : State(game) {
|
||||||
@ -9,7 +7,7 @@ PauseState::PauseState(RWGame* game) : State(game) {
|
|||||||
Menu* m = new Menu;
|
Menu* m = new Menu;
|
||||||
m->offset = glm::vec2(200.f, 200.f);
|
m->offset = glm::vec2(200.f, 200.f);
|
||||||
m->addEntry(Menu::lambda(t.text(MenuDefaults::kResumeGameId),
|
m->addEntry(Menu::lambda(t.text(MenuDefaults::kResumeGameId),
|
||||||
[] { StateManager::get().exit(); }));
|
[&] { done(); }));
|
||||||
m->addEntry(Menu::lambda(t.text(MenuDefaults::kOptionsId),
|
m->addEntry(Menu::lambda(t.text(MenuDefaults::kOptionsId),
|
||||||
[] { std::cout << "Options" << std::endl; }));
|
[] { std::cout << "Options" << std::endl; }));
|
||||||
m->addEntry(Menu::lambda(t.text(MenuDefaults::kQuitGameId),
|
m->addEntry(Menu::lambda(t.text(MenuDefaults::kQuitGameId),
|
||||||
@ -51,7 +49,7 @@ void PauseState::handleEvent(const SDL_Event& e) {
|
|||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
switch (e.key.keysym.sym) {
|
switch (e.key.keysym.sym) {
|
||||||
case SDLK_ESCAPE:
|
case SDLK_ESCAPE:
|
||||||
StateManager::get().exit();
|
done();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#ifndef PAUSESTATE_HPP
|
#ifndef PAUSESTATE_HPP
|
||||||
#define PAUSESTATE_HPP
|
#define PAUSESTATE_HPP
|
||||||
|
|
||||||
#include "State.hpp"
|
#include "StateManager.hpp"
|
||||||
|
|
||||||
class PauseState : public State {
|
class PauseState : public State {
|
||||||
public:
|
public:
|
||||||
|
Loading…
Reference in New Issue
Block a user