From ea0724411976346976d94a2f67cecf4bb9fa8a15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20B=C3=A1lint=20Misius?= Date: Sat, 24 Jul 2021 13:50:16 +0200 Subject: [PATCH] Refresh history code in GameMVC --- src/common/tpt-compat.h | 41 ++++++++++++++ src/gui/game/GameController.cpp | 91 ++++++++++--------------------- src/gui/game/GameController.h | 3 ++ src/gui/game/GameModel.cpp | 44 ++++++++++----- src/gui/game/GameModel.h | 17 +++--- src/simulation/Simulation.cpp | 96 ++++++++++++++++----------------- src/simulation/Simulation.h | 5 +- 7 files changed, 161 insertions(+), 136 deletions(-) diff --git a/src/common/tpt-compat.h b/src/common/tpt-compat.h index 476f42bac..8ab0456e1 100644 --- a/src/common/tpt-compat.h +++ b/src/common/tpt-compat.h @@ -16,6 +16,12 @@ #ifndef TPT_COMPAT_H #define TPT_COMPAT_H + +#include +#include +#include +#include + //some compatibility stuff for non-standard compilers #if defined(WIN) && !defined(strcasecmp) #define strcasecmp stricmp @@ -31,4 +37,39 @@ typedef unsigned short Uint16; # define NULL 0 #endif +// From https://stackoverflow.com/questions/17902405/how-to-implement-make-unique-function-in-c11 + +#if !(__cplusplus >= 201402L || defined(_MSC_VER)) +namespace std { + template struct _Unique_if { + typedef unique_ptr _Single_object; + }; + + template struct _Unique_if { + typedef unique_ptr _Unknown_bound; + }; + + template struct _Unique_if { + typedef void _Known_bound; + }; + + template + typename _Unique_if::_Single_object + make_unique(Args&&... args) { + return unique_ptr(new T(std::forward(args)...)); + } + + template + typename _Unique_if::_Unknown_bound + make_unique(size_t n) { + typedef typename remove_extent::type U; + return unique_ptr(new U[n]()); + } + + template + typename _Unique_if::_Known_bound + make_unique(Args&&...) = delete; +} +#endif + #endif diff --git a/src/gui/game/GameController.cpp b/src/gui/game/GameController.cpp index 0a3d75144..74a44b82c 100644 --- a/src/gui/game/GameController.cpp +++ b/src/gui/game/GameController.cpp @@ -146,12 +146,6 @@ GameController::~GameController() { delete *iter; } - //deleted here because it refuses to be deleted when deleted from gameModel even with the same code - std::deque history = gameModel->GetHistory(); - for(std::deque::iterator iter = history.begin(), end = history.end(); iter != end; ++iter) - { - delete *iter; - } std::vector quickOptions = gameModel->GetQuickOptions(); for(std::vector::iterator iter = quickOptions.begin(), end = quickOptions.end(); iter != end; ++iter) { @@ -171,75 +165,44 @@ GameController::~GameController() void GameController::HistoryRestore() { - std::deque history = gameModel->GetHistory(); - if (!history.size()) - return; - unsigned int historyPosition = gameModel->GetHistoryPosition(); - unsigned int newHistoryPosition = std::max((int)historyPosition-1, 0); - // When undoing, save the current state as a final redo - // This way ctrl+y will always bring you back to the point right before your last ctrl+z - if (historyPosition == history.size()) + if (!gameModel->HistoryCanRestore()) { - Snapshot * newSnap = gameModel->GetSimulation()->CreateSnapshot(); - if (newSnap) - newSnap->Authors = Client::Ref().GetAuthorInfo(); - delete gameModel->GetRedoHistory(); - gameModel->SetRedoHistory(newSnap); + return; } - Snapshot * snap = history[newHistoryPosition]; - gameModel->GetSimulation()->Restore(*snap); - Client::Ref().OverwriteAuthorInfo(snap->Authors); - gameModel->SetHistory(history); - gameModel->SetHistoryPosition(newHistoryPosition); + // * When undoing for the first time since the last call to HistorySnapshot, save the current state. + // Ctrl+Y needs this in order to bring you back to the point right before your last Ctrl+Z, because + // the last history entry is what this Ctrl+Z brings you back to, not the current state. + if (!beforeRestore) + { + beforeRestore = gameModel->GetSimulation()->CreateSnapshot(); + beforeRestore->Authors = Client::Ref().GetAuthorInfo(); + } + gameModel->HistoryRestore(); + auto ¤t = *gameModel->HistoryCurrent(); + gameModel->GetSimulation()->Restore(current); + Client::Ref().OverwriteAuthorInfo(current.Authors); } void GameController::HistorySnapshot() { - std::deque history = gameModel->GetHistory(); - unsigned int historyPosition = gameModel->GetHistoryPosition(); - Snapshot * newSnap = gameModel->GetSimulation()->CreateSnapshot(); - if (newSnap) - { - newSnap->Authors = Client::Ref().GetAuthorInfo(); - while (historyPosition < history.size()) - { - Snapshot * snap = history.back(); - history.pop_back(); - delete snap; - } - if (history.size() >= gameModel->GetUndoHistoryLimit()) - { - Snapshot * snap = history.front(); - history.pop_front(); - delete snap; - if (historyPosition > history.size()) - historyPosition--; - } - history.push_back(newSnap); - gameModel->SetHistory(history); - gameModel->SetHistoryPosition(std::min((size_t)historyPosition+1, history.size())); - delete gameModel->GetRedoHistory(); - gameModel->SetRedoHistory(NULL); - } + beforeRestore.reset(); + gameModel->HistoryPush(gameModel->GetSimulation()->CreateSnapshot()); } void GameController::HistoryForward() { - std::deque history = gameModel->GetHistory(); - if (!history.size()) + if (!gameModel->HistoryCanForward()) + { return; - unsigned int historyPosition = gameModel->GetHistoryPosition(); - unsigned int newHistoryPosition = std::min((size_t)historyPosition+1, history.size()); - Snapshot *snap; - if (newHistoryPosition == history.size()) - snap = gameModel->GetRedoHistory(); - else - snap = history[newHistoryPosition]; - if (!snap) - return; - gameModel->GetSimulation()->Restore(*snap); - Client::Ref().OverwriteAuthorInfo(snap->Authors); - gameModel->SetHistoryPosition(newHistoryPosition); + } + gameModel->HistoryForward(); + auto ¤t = gameModel->HistoryCurrent() ? *gameModel->HistoryCurrent() : *beforeRestore; + gameModel->GetSimulation()->Restore(current); + Client::Ref().OverwriteAuthorInfo(current.Authors); + if (¤t == beforeRestore.get()) + { + beforeRestore.reset(); + } } GameView * GameController::GetView() diff --git a/src/gui/game/GameController.h b/src/gui/game/GameController.h index 05e66495d..e510f8797 100644 --- a/src/gui/game/GameController.h +++ b/src/gui/game/GameController.h @@ -4,6 +4,7 @@ #include #include +#include #include "client/ClientListener.h" @@ -20,6 +21,7 @@ class SaveFile; class Notification; class GameModel; class GameView; +class Snapshot; class OptionsController; class LocalBrowserController; class SearchController; @@ -51,6 +53,7 @@ private: OptionsController * options; CommandInterface * commandInterface; std::vector debugInfo; + std::unique_ptr beforeRestore; unsigned int debugFlags; void OpenSaveDone(); diff --git a/src/gui/game/GameModel.cpp b/src/gui/game/GameModel.cpp index 9e6fa9f06..ca981fa2a 100644 --- a/src/gui/game/GameModel.cpp +++ b/src/gui/game/GameModel.cpp @@ -20,6 +20,7 @@ #include "client/SaveFile.h" #include "client/SaveInfo.h" #include "common/Platform.h" +#include "common/tpt-compat.h" #include "graphics/Renderer.h" #include "simulation/Air.h" #include "simulation/GOLString.h" @@ -42,7 +43,6 @@ GameModel::GameModel(): currentFile(NULL), currentUser(0, ""), toolStrength(1.0f), - redoHistory(NULL), historyPosition(0), activeColourPreset(0), colourSelector(false), @@ -196,7 +196,6 @@ GameModel::~GameModel() delete clipboard; delete currentSave; delete currentFile; - delete redoHistory; //if(activeTools) // delete[] activeTools; } @@ -551,34 +550,51 @@ int GameModel::GetDecoSpace() return this->decoSpace; } -std::deque GameModel::GetHistory() +const Snapshot *GameModel::HistoryCurrent() const { - return history; + if (historyPosition > history.size()) + { + return nullptr; + } + return historyCurrent.get(); } -unsigned int GameModel::GetHistoryPosition() +bool GameModel::HistoryCanRestore() const { - return historyPosition; + return historyPosition > 0U; } -void GameModel::SetHistory(std::deque newHistory) +void GameModel::HistoryRestore() { - history = newHistory; + historyPosition -= 1U; + historyCurrent = std::make_unique(*history[historyPosition]); } -void GameModel::SetHistoryPosition(unsigned int newHistoryPosition) +bool GameModel::HistoryCanForward() const { - historyPosition = newHistoryPosition; + return historyPosition < history.size(); } -Snapshot * GameModel::GetRedoHistory() +void GameModel::HistoryForward() { - return redoHistory; + historyPosition += 1U; + historyCurrent = historyPosition < history.size() ? std::make_unique(*history[historyPosition]) : nullptr; } -void GameModel::SetRedoHistory(Snapshot * redo) +void GameModel::HistoryPush(std::unique_ptr last) { - redoHistory = redo; + while (historyPosition < history.size()) + { + history.pop_back(); + } + history.push_back(std::move(last)); + historyPosition += 1U; + historyCurrent.reset(); + while (undoHistoryLimit < history.size()) + { + history.pop_front(); + historyPosition -= 1U; + } } unsigned int GameModel::GetUndoHistoryLimit() diff --git a/src/gui/game/GameModel.h b/src/gui/game/GameModel.h index 9bef6c496..5801bac4c 100644 --- a/src/gui/game/GameModel.h +++ b/src/gui/game/GameModel.h @@ -4,6 +4,7 @@ #include #include +#include #include "gui/interface/Colour.h" #include "client/User.h" @@ -64,8 +65,8 @@ private: Tool * regularToolset[4]; User currentUser; float toolStrength; - std::deque history; - Snapshot *redoHistory; + std::deque> history; + std::unique_ptr historyCurrent; unsigned int historyPosition; unsigned int undoHistoryLimit; bool mouseClickRequired; @@ -141,12 +142,12 @@ public: void BuildBrushList(); void BuildQuickOptionMenu(GameController * controller); - std::deque GetHistory(); - unsigned int GetHistoryPosition(); - void SetHistory(std::deque newHistory); - void SetHistoryPosition(unsigned int newHistoryPosition); - Snapshot * GetRedoHistory(); - void SetRedoHistory(Snapshot * redo); + const Snapshot *HistoryCurrent() const; + bool HistoryCanRestore() const; + void HistoryRestore(); + bool HistoryCanForward() const; + void HistoryForward(); + void HistoryPush(std::unique_ptr last); unsigned int GetUndoHistoryLimit(); void SetUndoHistoryLimit(unsigned int undoHistoryLimit_); diff --git a/src/simulation/Simulation.cpp b/src/simulation/Simulation.cpp index 9bd36c140..c14a366f4 100644 --- a/src/simulation/Simulation.cpp +++ b/src/simulation/Simulation.cpp @@ -562,67 +562,67 @@ void Simulation::SaveSimOptions(GameSave * gameSave) gameSave->aheatEnable = aheat_enable; } -Snapshot * Simulation::CreateSnapshot() +std::unique_ptr Simulation::CreateSnapshot() { - Snapshot * snap = new Snapshot(); - snap->AirPressure.insert(snap->AirPressure.begin(), &pv[0][0], &pv[0][0]+((XRES/CELL)*(YRES/CELL))); - snap->AirVelocityX.insert(snap->AirVelocityX.begin(), &vx[0][0], &vx[0][0]+((XRES/CELL)*(YRES/CELL))); - snap->AirVelocityY.insert(snap->AirVelocityY.begin(), &vy[0][0], &vy[0][0]+((XRES/CELL)*(YRES/CELL))); - snap->AmbientHeat.insert(snap->AmbientHeat.begin(), &hv[0][0], &hv[0][0]+((XRES/CELL)*(YRES/CELL))); - snap->Particles.insert(snap->Particles.begin(), parts, parts+parts_lastActiveIndex+1); - snap->PortalParticles.insert(snap->PortalParticles.begin(), &portalp[0][0][0], &portalp[CHANNELS-1][8-1][80-1]); - snap->WirelessData.insert(snap->WirelessData.begin(), &wireless[0][0], &wireless[CHANNELS-1][2-1]); - snap->GravVelocityX.insert(snap->GravVelocityX.begin(), gravx, gravx+((XRES/CELL)*(YRES/CELL))); - snap->GravVelocityY.insert(snap->GravVelocityY.begin(), gravy, gravy+((XRES/CELL)*(YRES/CELL))); - snap->GravValue.insert(snap->GravValue.begin(), gravp, gravp+((XRES/CELL)*(YRES/CELL))); - snap->GravMap.insert(snap->GravMap.begin(), gravmap, gravmap+((XRES/CELL)*(YRES/CELL))); - snap->BlockMap.insert(snap->BlockMap.begin(), &bmap[0][0], &bmap[0][0]+((XRES/CELL)*(YRES/CELL))); - snap->ElecMap.insert(snap->ElecMap.begin(), &emap[0][0], &emap[0][0]+((XRES/CELL)*(YRES/CELL))); - snap->FanVelocityX.insert(snap->FanVelocityX.begin(), &fvx[0][0], &fvx[0][0]+((XRES/CELL)*(YRES/CELL))); - snap->FanVelocityY.insert(snap->FanVelocityY.begin(), &fvy[0][0], &fvy[0][0]+((XRES/CELL)*(YRES/CELL))); - snap->stickmen.push_back(player2); - snap->stickmen.push_back(player); - snap->stickmen.insert(snap->stickmen.begin(), &fighters[0], &fighters[MAX_FIGHTERS]); + auto snap = std::make_unique(); + snap->AirPressure .insert (snap->AirPressure .begin(), &pv [0][0] , &pv [0][0] + ((XRES / CELL) * (YRES / CELL))); + snap->AirVelocityX .insert (snap->AirVelocityX .begin(), &vx [0][0] , &vx [0][0] + ((XRES / CELL) * (YRES / CELL))); + snap->AirVelocityY .insert (snap->AirVelocityY .begin(), &vy [0][0] , &vy [0][0] + ((XRES / CELL) * (YRES / CELL))); + snap->AmbientHeat .insert (snap->AmbientHeat .begin(), &hv [0][0] , &hv [0][0] + ((XRES / CELL) * (YRES / CELL))); + snap->BlockMap .insert (snap->BlockMap .begin(), &bmap[0][0] , &bmap[0][0] + ((XRES / CELL) * (YRES / CELL))); + snap->ElecMap .insert (snap->ElecMap .begin(), &emap[0][0] , &emap[0][0] + ((XRES / CELL) * (YRES / CELL))); + snap->FanVelocityX .insert (snap->FanVelocityX .begin(), &fvx [0][0] , &fvx [0][0] + ((XRES / CELL) * (YRES / CELL))); + snap->FanVelocityY .insert (snap->FanVelocityY .begin(), &fvy [0][0] , &fvy [0][0] + ((XRES / CELL) * (YRES / CELL))); + snap->GravVelocityX .insert (snap->GravVelocityX .begin(), &gravx [0] , &gravx [0] + ((XRES / CELL) * (YRES / CELL))); + snap->GravVelocityY .insert (snap->GravVelocityY .begin(), &gravy [0] , &gravy [0] + ((XRES / CELL) * (YRES / CELL))); + snap->GravValue .insert (snap->GravValue .begin(), &gravp [0] , &gravp [0] + ((XRES / CELL) * (YRES / CELL))); + snap->GravMap .insert (snap->GravMap .begin(), &gravmap[0] , &gravmap[0] + ((XRES / CELL) * (YRES / CELL))); + snap->Particles .insert (snap->Particles .begin(), &parts [0] , &parts[parts_lastActiveIndex + 1] ); + snap->PortalParticles.insert (snap->PortalParticles.begin(), &portalp[0][0][0], &portalp [CHANNELS - 1][8 - 1][80 - 1] ); + snap->WirelessData .insert (snap->WirelessData .begin(), &wireless[0][0] , &wireless[CHANNELS - 1][2 - 1] ); + snap->stickmen .insert (snap->stickmen .begin(), &fighters[0] , &fighters[MAX_FIGHTERS] ); + snap->stickmen .push_back(player2); + snap->stickmen .push_back(player); snap->signs = signs; return snap; } -void Simulation::Restore(const Snapshot & snap) +void Simulation::Restore(const Snapshot &snap) { - parts_lastActiveIndex = NPART-1; - std::fill(elementCount, elementCount+PT_NUM, 0); + std::fill(elementCount, elementCount + PT_NUM, 0); elementRecount = true; force_stacking_check = true; - - std::copy(snap.AirPressure.begin(), snap.AirPressure.end(), &pv[0][0]); - std::copy(snap.AirVelocityX.begin(), snap.AirVelocityX.end(), &vx[0][0]); - std::copy(snap.AirVelocityY.begin(), snap.AirVelocityY.end(), &vy[0][0]); - std::copy(snap.AmbientHeat.begin(), snap.AmbientHeat.end(), &hv[0][0]); - for (int i = 0; i < NPART; i++) - parts[i].type = 0; - std::copy(snap.Particles.begin(), snap.Particles.end(), parts); - parts_lastActiveIndex = NPART-1; - air->RecalculateBlockAirMaps(); - RecalcFreeParticles(false); - std::copy(snap.PortalParticles.begin(), snap.PortalParticles.end(), &portalp[0][0][0]); - std::copy(snap.WirelessData.begin(), snap.WirelessData.end(), &wireless[0][0]); + for (auto &part : parts) + { + part.type = 0; + } + std::copy(snap.AirPressure .begin(), snap.AirPressure .end(), &pv[0][0] ); + std::copy(snap.AirVelocityX .begin(), snap.AirVelocityX .end(), &vx[0][0] ); + std::copy(snap.AirVelocityY .begin(), snap.AirVelocityY .end(), &vy[0][0] ); + std::copy(snap.AmbientHeat .begin(), snap.AmbientHeat .end(), &hv[0][0] ); + std::copy(snap.BlockMap .begin(), snap.BlockMap .end(), &bmap[0][0] ); + std::copy(snap.ElecMap .begin(), snap.ElecMap .end(), &emap[0][0] ); + std::copy(snap.FanVelocityX .begin(), snap.FanVelocityX .end(), &fvx[0][0] ); + std::copy(snap.FanVelocityY .begin(), snap.FanVelocityY .end(), &fvy[0][0] ); if (grav->IsEnabled()) { grav->Clear(); - std::copy(snap.GravVelocityX.begin(), snap.GravVelocityX.end(), gravx); - std::copy(snap.GravVelocityY.begin(), snap.GravVelocityY.end(), gravy); - std::copy(snap.GravValue.begin(), snap.GravValue.end(), gravp); - std::copy(snap.GravMap.begin(), snap.GravMap.end(), gravmap); + std::copy(snap.GravVelocityX.begin(), snap.GravVelocityX.end(), &gravx [0] ); + std::copy(snap.GravVelocityY.begin(), snap.GravVelocityY.end(), &gravy [0] ); + std::copy(snap.GravValue .begin(), snap.GravValue .end(), &gravp [0] ); + std::copy(snap.GravMap .begin(), snap.GravMap .end(), &gravmap[0] ); } - gravWallChanged = true; - std::copy(snap.BlockMap.begin(), snap.BlockMap.end(), &bmap[0][0]); - std::copy(snap.ElecMap.begin(), snap.ElecMap.end(), &emap[0][0]); - std::copy(snap.FanVelocityX.begin(), snap.FanVelocityX.end(), &fvx[0][0]); - std::copy(snap.FanVelocityY.begin(), snap.FanVelocityY.end(), &fvy[0][0]); - std::copy(snap.stickmen.begin(), snap.stickmen.end()-2, &fighters[0]); - player = snap.stickmen[snap.stickmen.size()-1]; - player2 = snap.stickmen[snap.stickmen.size()-2]; + std::copy(snap.Particles .begin(), snap.Particles .end(), &parts[0] ); + std::copy(snap.PortalParticles.begin(), snap.PortalParticles.end(), &portalp[0][0][0]); + std::copy(snap.WirelessData .begin(), snap.WirelessData .end(), &wireless[0][0] ); + std::copy(snap.stickmen .begin(), snap.stickmen.end() - 2 , &fighters[0] ); + player = snap.stickmen[snap.stickmen.size() - 1]; + player2 = snap.stickmen[snap.stickmen.size() - 2]; signs = snap.signs; + parts_lastActiveIndex = NPART - 1; + air->RecalculateBlockAirMaps(); + RecalcFreeParticles(false); + gravWallChanged = true; } void Simulation::clear_area(int area_x, int area_y, int area_w, int area_h) diff --git a/src/simulation/Simulation.h b/src/simulation/Simulation.h index d76e94f5c..c32d6e9d3 100644 --- a/src/simulation/Simulation.h +++ b/src/simulation/Simulation.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "Particle.h" #include "Stickman.h" @@ -121,8 +122,8 @@ public: void SaveSimOptions(GameSave * gameSave); SimulationSample GetSample(int x, int y); - Snapshot * CreateSnapshot(); - void Restore(const Snapshot & snap); + std::unique_ptr CreateSnapshot(); + void Restore(const Snapshot &snap); int is_blocking(int t, int x, int y); int is_boundary(int pt, int x, int y);