Sanitize GameSave/SaveInfo/SaveFile ownership

This commit is contained in:
Tamás Bálint Misius 2023-05-11 12:40:23 +02:00
parent 5c5354655c
commit 0f95ce82f8
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
45 changed files with 520 additions and 555 deletions

View File

@ -415,11 +415,10 @@ int main(int argc, char * argv[])
} }
else else
{ {
SaveFile * newFile = new SaveFile(openArg.value()); auto newFile = std::make_unique<SaveFile>(openArg.value());
GameSave * newSave = new GameSave(std::move(gameSaveData)); auto newSave = std::make_unique<GameSave>(std::move(gameSaveData));
newFile->SetGameSave(newSave); newFile->SetGameSave(std::move(newSave));
gameController->LoadSaveFile(newFile); gameController->LoadSaveFile(std::move(newFile));
delete newFile;
} }
} }
@ -463,17 +462,16 @@ int main(int argc, char * argv[])
} }
int saveId = saveIdPart.ToNumber<int>(); int saveId = saveIdPart.ToNumber<int>();
SaveInfo * newSave = Client::Ref().GetSave(saveId, 0); auto newSave = Client::Ref().GetSave(saveId, 0);
if (!newSave) if (!newSave)
throw std::runtime_error("Could not load save info"); throw std::runtime_error("Could not load save info");
auto saveData = Client::Ref().GetSaveData(saveId, 0); auto saveData = Client::Ref().GetSaveData(saveId, 0);
if (!saveData.size()) if (!saveData.size())
throw std::runtime_error(("Could not load save\n" + Client::Ref().GetLastError()).ToUtf8()); throw std::runtime_error(("Could not load save\n" + Client::Ref().GetLastError()).ToUtf8());
GameSave * newGameSave = new GameSave(std::move(saveData)); auto newGameSave = std::make_unique<GameSave>(std::move(saveData));
newSave->SetGameSave(newGameSave); newSave->SetGameSave(std::move(newGameSave));
gameController->LoadSave(newSave); gameController->LoadSave(std::move(newSave));
delete newSave;
} }
catch (std::exception & e) catch (std::exception & e)
{ {

View File

@ -27,10 +27,10 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
GameSave * gameSave = NULL; std::unique_ptr<GameSave> gameSave;
try try
{ {
gameSave = new GameSave(fileData, false); gameSave = std::make_unique<GameSave>(fileData, false);
} }
catch (ParseException &e) catch (ParseException &e)
{ {
@ -44,7 +44,7 @@ int main(int argc, char *argv[])
if (gameSave) if (gameSave)
{ {
sim->Load(gameSave, true); sim->Load(gameSave.get(), true);
//Render save //Render save
ren->decorations_enable = true; ren->decorations_enable = true;

View File

@ -495,10 +495,10 @@ void Client::MoveStampToFront(ByteString stampID)
} }
} }
SaveFile * Client::GetStamp(ByteString stampID) std::unique_ptr<SaveFile> Client::GetStamp(ByteString stampID)
{ {
ByteString stampFile = ByteString(ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, stampID, ".stm")); ByteString stampFile = ByteString(ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, stampID, ".stm"));
SaveFile *saveFile = LoadSaveFile(stampFile); auto saveFile = LoadSaveFile(stampFile);
if (!saveFile) if (!saveFile)
saveFile = LoadSaveFile(stampID); saveFile = LoadSaveFile(stampID);
else else
@ -517,7 +517,7 @@ void Client::DeleteStamp(ByteString stampID)
} }
} }
ByteString Client::AddStamp(GameSave * saveData) ByteString Client::AddStamp(std::unique_ptr<GameSave> saveData)
{ {
auto now = (uint64_t)time(NULL); auto now = (uint64_t)time(NULL);
if (lastStampTime != now) if (lastStampTime != now)
@ -854,7 +854,7 @@ RequestStatus Client::PublishSave(int saveID)
return ret; return ret;
} }
SaveInfo * Client::GetSave(int saveID, int saveDate) std::unique_ptr<SaveInfo> Client::GetSave(int saveID, int saveDate)
{ {
lastError = ""; lastError = "";
ByteStringBuilder urlStream; ByteStringBuilder urlStream;
@ -902,7 +902,7 @@ SaveInfo * Client::GetSave(int saveID, int saveDate)
for (Json::UInt j = 0; j < tagsArray.size(); j++) for (Json::UInt j = 0; j < tagsArray.size(); j++)
tempTags.push_back(tagsArray[j].asString()); tempTags.push_back(tagsArray[j].asString());
SaveInfo * tempSave = new SaveInfo(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp, auto tempSave = std::make_unique<SaveInfo>(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp,
tempScoreDown, tempMyScore, tempUsername, tempName, tempScoreDown, tempMyScore, tempUsername, tempName,
tempDescription, tempPublished, tempTags); tempDescription, tempPublished, tempTags);
tempSave->Comments = tempComments; tempSave->Comments = tempComments;
@ -914,29 +914,29 @@ SaveInfo * Client::GetSave(int saveID, int saveDate)
catch (std::exception & e) catch (std::exception & e)
{ {
lastError = "Could not read response: " + ByteString(e.what()).FromUtf8(); lastError = "Could not read response: " + ByteString(e.what()).FromUtf8();
return NULL; return nullptr;
} }
} }
else else
{ {
lastError = http::StatusText(dataStatus); lastError = http::StatusText(dataStatus);
} }
return NULL; return nullptr;
} }
SaveFile * Client::LoadSaveFile(ByteString filename) std::unique_ptr<SaveFile> Client::LoadSaveFile(ByteString filename)
{ {
ByteString err; ByteString err;
SaveFile *file = nullptr; std::unique_ptr<SaveFile> file;
if (Platform::FileExists(filename)) if (Platform::FileExists(filename))
{ {
file = new SaveFile(filename); file = std::make_unique<SaveFile>(filename);
try try
{ {
std::vector<char> data; std::vector<char> data;
if (Platform::ReadFile(data, filename)) if (Platform::ReadFile(data, filename))
{ {
file->SetGameSave(new GameSave(std::move(data))); file->SetGameSave(std::make_unique<GameSave>(std::move(data)));
} }
else else
{ {

View File

@ -114,9 +114,9 @@ public:
RequestStatus ExecVote(int saveID, int direction); RequestStatus ExecVote(int saveID, int direction);
RequestStatus UploadSave(SaveInfo & save); RequestStatus UploadSave(SaveInfo & save);
SaveFile * GetStamp(ByteString stampID); std::unique_ptr<SaveFile> GetStamp(ByteString stampID);
void DeleteStamp(ByteString stampID); void DeleteStamp(ByteString stampID);
ByteString AddStamp(GameSave * saveData); ByteString AddStamp(std::unique_ptr<GameSave> saveData);
void RescanStamps(); void RescanStamps();
const std::vector<ByteString> &GetStamps() const; const std::vector<ByteString> &GetStamps() const;
void MoveStampToFront(ByteString stampID); void MoveStampToFront(ByteString stampID);
@ -127,8 +127,8 @@ public:
LoginStatus Login(ByteString username, ByteString password, User & user); LoginStatus Login(ByteString username, ByteString password, User & user);
SaveInfo * GetSave(int saveID, int saveDate); std::unique_ptr<SaveInfo> GetSave(int saveID, int saveDate);
SaveFile * LoadSaveFile(ByteString filename); std::unique_ptr<SaveFile> LoadSaveFile(ByteString filename);
RequestStatus DeleteSave(int saveID); RequestStatus DeleteSave(int saveID);
RequestStatus ReportSave(int saveID, String message); RequestStatus ReportSave(int saveID, String message);

View File

@ -2,19 +2,7 @@
#include "GameSave.h" #include "GameSave.h"
#include "common/platform/Platform.h" #include "common/platform/Platform.h"
SaveFile::SaveFile(SaveFile & save):
gameSave(NULL),
filename(save.filename),
displayName(save.displayName),
loadingError(save.loadingError),
lazyLoad(save.lazyLoad)
{
if (save.gameSave)
gameSave = new GameSave(*save.gameSave);
}
SaveFile::SaveFile(ByteString filename, bool newLazyLoad): SaveFile::SaveFile(ByteString filename, bool newLazyLoad):
gameSave(NULL),
filename(filename), filename(filename),
displayName(filename.FromUtf8()), displayName(filename.FromUtf8()),
loadingError(""), loadingError(""),
@ -23,7 +11,7 @@ SaveFile::SaveFile(ByteString filename, bool newLazyLoad):
} }
GameSave * SaveFile::GetGameSave() const GameSave *SaveFile::LazyGetGameSave() // non-owning
{ {
if (!gameSave && !loadingError.size() && lazyLoad) if (!gameSave && !loadingError.size() && lazyLoad)
{ {
@ -32,7 +20,7 @@ GameSave * SaveFile::GetGameSave()
std::vector<char> data; std::vector<char> data;
if (Platform::ReadFile(data, filename)) if (Platform::ReadFile(data, filename))
{ {
gameSave = new GameSave(std::move(data)); gameSave = std::make_unique<GameSave>(std::move(data));
} }
else else
{ {
@ -44,24 +32,33 @@ GameSave * SaveFile::GetGameSave()
loadingError = ByteString(e.what()).FromUtf8(); loadingError = ByteString(e.what()).FromUtf8();
} }
} }
return gameSave; return gameSave.get();
}
const GameSave *SaveFile::GetGameSave() const
{
return gameSave.get();
}
std::unique_ptr<GameSave> SaveFile::TakeGameSave()
{
return std::move(gameSave);
} }
void SaveFile::LazyUnload() void SaveFile::LazyUnload()
{ {
if (lazyLoad && gameSave) if (lazyLoad)
{ {
delete gameSave; gameSave.reset();
gameSave = nullptr;
} }
} }
void SaveFile::SetGameSave(GameSave * save) void SaveFile::SetGameSave(std::unique_ptr<GameSave> newGameSave)
{ {
gameSave = save; gameSave = std::move(newGameSave);
} }
ByteString SaveFile::GetName() const ByteString &SaveFile::GetName() const
{ {
return filename; return filename;
} }
@ -71,7 +68,7 @@ void SaveFile::SetFileName(ByteString fileName)
this->filename = fileName; this->filename = fileName;
} }
String SaveFile::GetDisplayName() const String &SaveFile::GetDisplayName() const
{ {
return displayName; return displayName;
} }
@ -81,7 +78,7 @@ void SaveFile::SetDisplayName(String displayName)
this->displayName = displayName; this->displayName = displayName;
} }
String SaveFile::GetError() const String &SaveFile::GetError() const
{ {
return loadingError; return loadingError;
} }
@ -90,10 +87,3 @@ void SaveFile::SetLoadingError(String error)
{ {
loadingError = error; loadingError = error;
} }
SaveFile::~SaveFile() {
if (gameSave)
{
delete gameSave;
}
}

View File

@ -1,27 +1,27 @@
#pragma once #pragma once
#include "common/String.h" #include "common/String.h"
#include <memory>
class GameSave; class GameSave;
class SaveFile { class SaveFile {
public: public:
SaveFile(SaveFile & save);
SaveFile(ByteString filename, bool newLazyLoad = false); SaveFile(ByteString filename, bool newLazyLoad = false);
GameSave * GetGameSave(); const GameSave *LazyGetGameSave();
void SetGameSave(GameSave * save); const GameSave *GetGameSave() const;
String GetDisplayName(); std::unique_ptr<GameSave> TakeGameSave();
void SetGameSave(std::unique_ptr<GameSave> newSameSave);
const String &GetDisplayName() const;
void SetDisplayName(String displayName); void SetDisplayName(String displayName);
ByteString GetName(); const ByteString &GetName() const;
void SetFileName(ByteString fileName); void SetFileName(ByteString fileName);
String GetError(); const String &GetError() const;
void SetLoadingError(String error); void SetLoadingError(String error);
void LazyUnload(); void LazyUnload();
virtual ~SaveFile();
private: private:
GameSave * gameSave; std::unique_ptr<GameSave> gameSave;
ByteString filename; ByteString filename;
String displayName; String displayName;
String loadingError; String loadingError;

View File

@ -1,30 +1,6 @@
#include "SaveInfo.h" #include "SaveInfo.h"
#include "GameSave.h" #include "GameSave.h"
SaveInfo::SaveInfo(SaveInfo & save):
id(save.id),
createdDate(save.createdDate),
updatedDate(save.updatedDate),
votesUp(save.votesUp),
votesDown(save.votesDown),
vote(save.vote),
Favourite(false),
Comments(save.Comments),
Views(save.Views),
Version(save.Version),
userName(save.userName),
name(save.name),
Description(save.Description),
Published(save.Published),
gameSave(NULL)
{
std::list<ByteString> tagsSorted = save.tags;
tagsSorted.sort();
tags = tagsSorted;
if (save.gameSave)
gameSave = new GameSave(*save.gameSave);
}
SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name): SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name):
id(_id), id(_id),
createdDate(_createdDate), createdDate(_createdDate),
@ -39,9 +15,7 @@ SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, in
userName(_userName), userName(_userName),
name(_name), name(_name),
Description(""), Description(""),
Published(false), Published(false)
tags(),
gameSave(NULL)
{ {
} }
@ -60,23 +34,13 @@ SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, in
userName(_userName), userName(_userName),
name(_name), name(_name),
Description(description_), Description(description_),
Published(published_), Published(published_)
tags(),
gameSave(NULL)
{ {
std::list<ByteString> tagsSorted = tags_; std::list<ByteString> tagsSorted = tags_;
tagsSorted.sort(); tagsSorted.sort();
tags=tagsSorted; tags=tagsSorted;
} }
SaveInfo::~SaveInfo()
{
if(gameSave)
{
delete gameSave;
}
}
void SaveInfo::SetName(String name) void SaveInfo::SetName(String name)
{ {
this->name = name; this->name = name;
@ -99,7 +63,7 @@ void SaveInfo::SetPublished(bool published)
{ {
Published = published; Published = published;
} }
bool SaveInfo::GetPublished() bool SaveInfo::GetPublished() const
{ {
return Published; return Published;
} }
@ -108,7 +72,7 @@ void SaveInfo::SetVote(int vote)
{ {
this->vote = vote; this->vote = vote;
} }
int SaveInfo::GetVote() int SaveInfo::GetVote() const
{ {
return vote; return vote;
} }
@ -118,7 +82,7 @@ void SaveInfo::SetUserName(ByteString userName)
this->userName = userName; this->userName = userName;
} }
ByteString SaveInfo::GetUserName() const ByteString &SaveInfo::GetUserName() const
{ {
return userName; return userName;
} }
@ -127,7 +91,7 @@ void SaveInfo::SetID(int id)
{ {
this->id = id; this->id = id;
} }
int SaveInfo::GetID() int SaveInfo::GetID() const
{ {
return id; return id;
} }
@ -136,7 +100,7 @@ void SaveInfo::SetVotesUp(int votesUp)
{ {
this->votesUp = votesUp; this->votesUp = votesUp;
} }
int SaveInfo::GetVotesUp() int SaveInfo::GetVotesUp() const
{ {
return votesUp; return votesUp;
} }
@ -145,7 +109,7 @@ void SaveInfo::SetVotesDown(int votesDown)
{ {
this->votesDown = votesDown; this->votesDown = votesDown;
} }
int SaveInfo::GetVotesDown() int SaveInfo::GetVotesDown() const
{ {
return votesDown; return votesDown;
} }
@ -154,7 +118,7 @@ void SaveInfo::SetVersion(int version)
{ {
this->Version = version; this->Version = version;
} }
int SaveInfo::GetVersion() int SaveInfo::GetVersion() const
{ {
return Version; return Version;
} }
@ -166,18 +130,33 @@ void SaveInfo::SetTags(std::list<ByteString> tags)
this->tags=tagsSorted; this->tags=tagsSorted;
} }
std::list<ByteString> SaveInfo::GetTags() std::list<ByteString> SaveInfo::GetTags() const
{ {
return tags; return tags;
} }
GameSave * SaveInfo::GetGameSave() const GameSave *SaveInfo::GetGameSave() const
{ {
return gameSave; return gameSave.get();
} }
void SaveInfo::SetGameSave(GameSave * saveGame) std::unique_ptr<GameSave> SaveInfo::TakeGameSave()
{ {
delete gameSave; return std::move(gameSave);
gameSave = saveGame; }
void SaveInfo::SetGameSave(std::unique_ptr<GameSave> newGameSave)
{
gameSave = std::move(newGameSave);
}
std::unique_ptr<SaveInfo> SaveInfo::CloneInfo() const
{
auto clone = std::make_unique<SaveInfo>(id, createdDate, updatedDate, votesUp, votesDown, vote, userName, name, Description, Published, tags);
clone->Favourite = false;
clone->Comments = Comments;
clone->Views = Views;
clone->Version = Version;
clone->tags.sort();
return clone;
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "common/String.h" #include "common/String.h"
#include <list> #include <list>
#include <memory>
#ifdef GetUserName #ifdef GetUserName
# undef GetUserName // dammit windows # undef GetUserName // dammit windows
@ -29,16 +30,12 @@ public:
bool Published; bool Published;
std::list<ByteString> tags; std::list<ByteString> tags;
GameSave * gameSave; std::unique_ptr<GameSave> gameSave;
SaveInfo(SaveInfo & save);
SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name); SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name);
SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list<ByteString> tags); SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list<ByteString> tags);
~SaveInfo();
void SetName(String name); void SetName(String name);
String GetName(); String GetName();
@ -46,29 +43,32 @@ public:
String GetDescription(); String GetDescription();
void SetPublished(bool published); void SetPublished(bool published);
bool GetPublished(); bool GetPublished() const;
void SetUserName(ByteString userName); void SetUserName(ByteString userName);
ByteString GetUserName(); const ByteString &GetUserName() const;
void SetID(int id); void SetID(int id);
int GetID(); int GetID() const;
void SetVote(int vote); void SetVote(int vote);
int GetVote(); int GetVote() const;
void SetVotesUp(int votesUp); void SetVotesUp(int votesUp);
int GetVotesUp(); int GetVotesUp() const;
void SetVotesDown(int votesDown); void SetVotesDown(int votesDown);
int GetVotesDown(); int GetVotesDown() const;
void SetVersion(int version); void SetVersion(int version);
int GetVersion(); int GetVersion() const;
void SetTags(std::list<ByteString> tags); void SetTags(std::list<ByteString> tags);
std::list<ByteString> GetTags(); std::list<ByteString> GetTags() const;
GameSave * GetGameSave(); const GameSave *GetGameSave() const;
void SetGameSave(GameSave * gameSave); std::unique_ptr<GameSave> TakeGameSave();
void SetGameSave(std::unique_ptr<GameSave> newGameSave);
std::unique_ptr<SaveInfo> CloneInfo() const;
}; };

View File

@ -343,7 +343,7 @@ public:
constexpr explicit operator bool() const constexpr explicit operator bool() const
{ {
return BottomRight.X >= TopLeft.X || BottomRight.Y >= TopLeft.Y; return BottomRight.X >= TopLeft.X && BottomRight.Y >= TopLeft.Y;
} }
// Return the smallest rectangle that contains both input rectangles, // Return the smallest rectangle that contains both input rectangles,

View File

@ -24,7 +24,7 @@ class LoadFilesTask: public Task
{ {
ByteString directory; ByteString directory;
ByteString search; ByteString search;
std::vector<SaveFile*> saveFiles; std::vector<std::unique_ptr<SaveFile>> saveFiles;
void before() override void before() override
{ {
@ -44,20 +44,20 @@ class LoadFilesTask: public Task
notifyProgress(-1); notifyProgress(-1);
for(std::vector<ByteString>::iterator iter = files.begin(), end = files.end(); iter != end; ++iter) for(std::vector<ByteString>::iterator iter = files.begin(), end = files.end(); iter != end; ++iter)
{ {
SaveFile * saveFile = new SaveFile(directory + *iter, true); auto saveFile = std::make_unique<SaveFile>(directory + *iter, true);
saveFiles.push_back(saveFile);
ByteString filename = (*iter).SplitFromEndBy(PATH_SEP_CHAR).After(); ByteString filename = (*iter).SplitFromEndBy(PATH_SEP_CHAR).After();
filename = filename.SplitFromEndBy('.').Before(); filename = filename.SplitFromEndBy('.').Before();
saveFile->SetDisplayName(filename.FromUtf8()); saveFile->SetDisplayName(filename.FromUtf8());
saveFiles.push_back(std::move(saveFile));
} }
return true; return true;
} }
public: public:
std::vector<SaveFile*> GetSaveFiles() std::vector<std::unique_ptr<SaveFile>> TakeSaveFiles()
{ {
return saveFiles; return std::move(saveFiles);
} }
LoadFilesTask(ByteString directory, ByteString search): LoadFilesTask(ByteString directory, ByteString search):
@ -128,15 +128,20 @@ void FileBrowserActivity::DoSearch(ByteString search)
} }
} }
void FileBrowserActivity::SelectSave(SaveFile * file) void FileBrowserActivity::SelectSave(int index)
{ {
if (onSelected) if (onSelected)
onSelected(std::unique_ptr<SaveFile>(new SaveFile(*file))); {
auto file = std::move(files[index]);
files.clear();
onSelected(std::move(file));
}
Exit(); Exit();
} }
void FileBrowserActivity::DeleteSave(SaveFile * file) void FileBrowserActivity::DeleteSave(int index)
{ {
auto &file = files[index];
String deleteMessage = "Are you sure you want to delete " + file->GetDisplayName() + ".cps?"; String deleteMessage = "Are you sure you want to delete " + file->GetDisplayName() + ".cps?";
if (ConfirmPrompt::Blocking("Delete Save", deleteMessage)) if (ConfirmPrompt::Blocking("Delete Save", deleteMessage))
{ {
@ -145,8 +150,9 @@ void FileBrowserActivity::DeleteSave(SaveFile * file)
} }
} }
void FileBrowserActivity::RenameSave(SaveFile * file) void FileBrowserActivity::RenameSave(int index)
{ {
auto &file = files[index];
ByteString newName = TextPrompt::Blocking("Rename", "Change save name", file->GetDisplayName(), "", 0).ToUtf8(); ByteString newName = TextPrompt::Blocking("Rename", "Change save name", file->GetDisplayName(), "", 0).ToUtf8();
if (newName.length()) if (newName.length())
{ {
@ -168,10 +174,6 @@ void FileBrowserActivity::cleanup()
} }
componentsQueue.clear(); componentsQueue.clear();
for (auto file : files)
{
delete file;
}
files.clear(); files.clear();
} }
@ -199,7 +201,8 @@ void FileBrowserActivity::NotifyDone(Task * task)
{ {
fileX = 0; fileX = 0;
fileY = 0; fileY = 0;
files = ((LoadFilesTask*)task)->GetSaveFiles(); files = ((LoadFilesTask*)task)->TakeSaveFiles();
createButtons = true;
totalFiles = files.size(); totalFiles = files.size();
delete loadFiles; delete loadFiles;
loadFiles = NULL; loadFiles = NULL;
@ -254,35 +257,37 @@ void FileBrowserActivity::OnTick(float dt)
if(loadFiles) if(loadFiles)
loadFiles->Poll(); loadFiles->Poll();
while(files.size()) if (createButtons)
{ {
SaveFile * saveFile = files.back(); createButtons = false;
files.pop_back(); for (auto i = 0; i < int(files.size()); ++i)
if(fileX == filesX)
{ {
fileX = 0; auto &saveFile = files[i];
fileY++; if(fileX == filesX)
} {
ui::SaveButton * saveButton = new ui::SaveButton( fileX = 0;
ui::Point( fileY++;
buttonXOffset + buttonPadding + fileX*(buttonWidth+buttonPadding*2), }
buttonYOffset + buttonPadding + fileY*(buttonHeight+buttonPadding*2) ui::SaveButton * saveButton = new ui::SaveButton(
), ui::Point(
ui::Point(buttonWidth, buttonHeight), buttonXOffset + buttonPadding + fileX*(buttonWidth+buttonPadding*2),
saveFile); buttonYOffset + buttonPadding + fileY*(buttonHeight+buttonPadding*2)
saveButton->AddContextMenu(1); ),
saveButton->Tick(dt); ui::Point(buttonWidth, buttonHeight),
saveButton->SetActionCallback({ saveFile.get());
[this, saveButton] { SelectSave(saveButton->GetSaveFile()); }, saveButton->AddContextMenu(1);
[this, saveButton] { RenameSave(saveButton->GetSaveFile()); }, saveButton->Tick(dt);
[this, saveButton] { DeleteSave(saveButton->GetSaveFile()); } saveButton->SetActionCallback({
}); [this, i] { SelectSave(i); },
[this, i] { RenameSave(i); },
[this, i] { DeleteSave(i); }
});
progressBar->SetStatus("Rendering thumbnails"); progressBar->SetStatus("Rendering thumbnails");
progressBar->SetProgress(totalFiles ? (totalFiles - files.size()) * 100 / totalFiles : 0); progressBar->SetProgress(totalFiles ? (totalFiles - files.size()) * 100 / totalFiles : 0);
componentsQueue.push_back(saveButton); componentsQueue.push_back(saveButton);
fileX++; fileX++;
}
} }
if(componentsQueue.size()) if(componentsQueue.size())
{ {

View File

@ -26,7 +26,8 @@ class FileBrowserActivity: public TaskListener, public WindowActivity
OnSelected onSelected; OnSelected onSelected;
ui::ScrollPanel * itemList; ui::ScrollPanel * itemList;
ui::Label * infoText; ui::Label * infoText;
std::vector<SaveFile*> files; std::vector<std::unique_ptr<SaveFile>> files;
bool createButtons = false;
std::vector<ui::Component*> components; std::vector<ui::Component*> components;
std::vector<ui::Component*> componentsQueue; std::vector<ui::Component*> componentsQueue;
ByteString directory; ByteString directory;
@ -51,9 +52,9 @@ public:
void OnTryExit(ExitMethod method) override; void OnTryExit(ExitMethod method) override;
void OnMouseDown(int x, int y, unsigned button) override; void OnMouseDown(int x, int y, unsigned button) override;
void loadDirectory(ByteString directory, ByteString search); void loadDirectory(ByteString directory, ByteString search);
void SelectSave(SaveFile * file); void SelectSave(int index);
void DeleteSave(SaveFile * file); void DeleteSave(int index);
void RenameSave(SaveFile * file); void RenameSave(int index);
void DoSearch(ByteString search); void DoSearch(ByteString search);
void NotifyDone(Task * task) override; void NotifyDone(Task * task) override;

View File

@ -238,7 +238,7 @@ std::pair<int, sign::Type> GameController::GetSignSplit(int signID)
void GameController::PlaceSave(ui::Point position) void GameController::PlaceSave(ui::Point position)
{ {
GameSave *placeSave = gameModel->GetPlaceSave(); auto *placeSave = gameModel->GetPlaceSave();
if (placeSave) if (placeSave)
{ {
HistorySnapshot(); HistorySnapshot();
@ -408,33 +408,35 @@ void GameController::DrawPoints(int toolSelection, ui::Point oldPos, ui::Point n
bool GameController::LoadClipboard() bool GameController::LoadClipboard()
{ {
GameSave *clip = gameModel->GetClipboard(); auto *clip = gameModel->GetClipboard();
if (!clip) if (!clip)
return false; return false;
gameModel->SetPlaceSave(clip); gameModel->SetPlaceSave(std::make_unique<GameSave>(*clip));
return true; return true;
} }
void GameController::LoadStamp(GameSave *stamp) void GameController::LoadStamp(std::unique_ptr<GameSave> stamp)
{ {
gameModel->SetPlaceSave(stamp); gameModel->SetPlaceSave(std::move(stamp));
} }
void GameController::TranslateSave(ui::Point point) void GameController::TranslateSave(ui::Point point)
{ {
vector2d translate = v2d_new(float(point.X), float(point.Y)); vector2d translate = v2d_new(float(point.X), float(point.Y));
vector2d translated = gameModel->GetPlaceSave()->Translate(translate); auto save = gameModel->TakePlaceSave();
vector2d translated = save->Translate(translate);
ui::Point currentPlaceSaveOffset = gameView->GetPlaceSaveOffset(); ui::Point currentPlaceSaveOffset = gameView->GetPlaceSaveOffset();
// resets placeSaveOffset to 0, which is why we back it up first // resets placeSaveOffset to 0, which is why we back it up first
gameModel->SetPlaceSave(gameModel->GetPlaceSave()); gameModel->SetPlaceSave(std::move(save));
gameView->SetPlaceSaveOffset(ui::Point(int(translated.x), int(translated.y)) + currentPlaceSaveOffset); gameView->SetPlaceSaveOffset(ui::Point(int(translated.x), int(translated.y)) + currentPlaceSaveOffset);
} }
void GameController::TransformSave(matrix2d transform) void GameController::TransformSave(matrix2d transform)
{ {
vector2d translate = v2d_zero; vector2d translate = v2d_zero;
gameModel->GetPlaceSave()->Transform(transform, translate); auto save = gameModel->TakePlaceSave();
gameModel->SetPlaceSave(gameModel->GetPlaceSave()); save->Transform(transform, translate);
gameModel->SetPlaceSave(std::move(save));
} }
void GameController::ToolClick(int toolSelection, ui::Point point) void GameController::ToolClick(int toolSelection, ui::Point point)
@ -449,12 +451,11 @@ void GameController::ToolClick(int toolSelection, ui::Point point)
ByteString GameController::StampRegion(ui::Point point1, ui::Point point2) ByteString GameController::StampRegion(ui::Point point1, ui::Point point2)
{ {
GameSave * newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), point1.X, point1.Y, point2.X, point2.Y); auto newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), point1.X, point1.Y, point2.X, point2.Y);
if(newSave) if(newSave)
{ {
newSave->paused = gameModel->GetPaused(); newSave->paused = gameModel->GetPaused();
ByteString stampName = Client::Ref().AddStamp(newSave); ByteString stampName = Client::Ref().AddStamp(std::move(newSave));
delete newSave;
if (stampName.length() == 0) if (stampName.length() == 0)
new ErrorMessage("Could not create stamp", "Error serializing save file"); new ErrorMessage("Could not create stamp", "Error serializing save file");
return stampName; return stampName;
@ -468,7 +469,7 @@ ByteString GameController::StampRegion(ui::Point point1, ui::Point point2)
void GameController::CopyRegion(ui::Point point1, ui::Point point2) void GameController::CopyRegion(ui::Point point1, ui::Point point2)
{ {
GameSave * newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), point1.X, point1.Y, point2.X, point2.Y); auto newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), point1.X, point1.Y, point2.X, point2.Y);
if(newSave) if(newSave)
{ {
Json::Value clipboardInfo; Json::Value clipboardInfo;
@ -479,7 +480,7 @@ void GameController::CopyRegion(ui::Point point1, ui::Point point2)
newSave->authors = clipboardInfo; newSave->authors = clipboardInfo;
newSave->paused = gameModel->GetPaused(); newSave->paused = gameModel->GetPaused();
gameModel->SetClipboard(newSave); gameModel->SetClipboard(std::move(newSave));
} }
} }
@ -1133,8 +1134,7 @@ void GameController::OpenSearch(String searchText)
try try
{ {
HistorySnapshot(); HistorySnapshot();
gameModel->SetSave(search->GetLoadedSave(), gameView->ShiftBehaviour()); gameModel->SetSave(search->TakeLoadedSave(), gameView->ShiftBehaviour());
search->ReleaseLoadedSave();
} }
catch(GameModelException & ex) catch(GameModelException & ex)
{ {
@ -1150,7 +1150,7 @@ void GameController::OpenSearch(String searchText)
void GameController::OpenLocalSaveWindow(bool asCurrent) void GameController::OpenLocalSaveWindow(bool asCurrent)
{ {
Simulation * sim = gameModel->GetSimulation(); Simulation * sim = gameModel->GetSimulation();
GameSave * gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour()); auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour());
if(!gameSave) if(!gameSave)
{ {
new ErrorMessage("Error", "Unable to build save."); new ErrorMessage("Error", "Unable to build save.");
@ -1159,18 +1159,18 @@ void GameController::OpenLocalSaveWindow(bool asCurrent)
{ {
gameSave->paused = gameModel->GetPaused(); gameSave->paused = gameModel->GetPaused();
SaveFile tempSave(""); auto tempSave = std::make_unique<SaveFile>("");
if (gameModel->GetSaveFile()) if (gameModel->GetSaveFile())
{ {
tempSave.SetFileName(gameModel->GetSaveFile()->GetName()); tempSave->SetFileName(gameModel->GetSaveFile()->GetName());
tempSave.SetDisplayName(gameModel->GetSaveFile()->GetDisplayName()); tempSave->SetDisplayName(gameModel->GetSaveFile()->GetDisplayName());
} }
tempSave.SetGameSave(gameSave); tempSave->SetGameSave(std::move(gameSave));
if (!asCurrent || !gameModel->GetSaveFile()) if (!asCurrent || !gameModel->GetSaveFile())
{ {
new LocalSaveActivity(tempSave, [this](SaveFile *file) { new LocalSaveActivity(std::move(tempSave), [this](auto file) {
gameModel->SetSaveFile(file, gameView->ShiftBehaviour()); gameModel->SetSaveFile(std::move(file), gameView->ShiftBehaviour());
}); });
} }
else if (gameModel->GetSaveFile()) else if (gameModel->GetSaveFile())
@ -1183,9 +1183,9 @@ void GameController::OpenLocalSaveWindow(bool asCurrent)
Client::Ref().SaveAuthorInfo(&localSaveInfo); Client::Ref().SaveAuthorInfo(&localSaveInfo);
gameSave->authors = localSaveInfo; gameSave->authors = localSaveInfo;
gameModel->SetSaveFile(&tempSave, gameView->ShiftBehaviour());
Platform::MakeDirectory(LOCAL_SAVE_DIR); Platform::MakeDirectory(LOCAL_SAVE_DIR);
auto [ fromNewerVersion, saveData ] = gameSave->Serialise(); auto [ fromNewerVersion, saveData ] = gameSave->Serialise();
gameModel->SetSaveFile(std::move(tempSave), gameView->ShiftBehaviour());
(void)fromNewerVersion; (void)fromNewerVersion;
if (saveData.size() == 0) if (saveData.size() == 0)
new ErrorMessage("Error", "Unable to serialize game data."); new ErrorMessage("Error", "Unable to serialize game data.");
@ -1197,15 +1197,15 @@ void GameController::OpenLocalSaveWindow(bool asCurrent)
} }
} }
void GameController::LoadSaveFile(SaveFile * file) void GameController::LoadSaveFile(std::unique_ptr<SaveFile> file)
{ {
gameModel->SetSaveFile(file, gameView->ShiftBehaviour()); gameModel->SetSaveFile(std::move(file), gameView->ShiftBehaviour());
} }
void GameController::LoadSave(SaveInfo * save) void GameController::LoadSave(std::unique_ptr<SaveInfo> save)
{ {
gameModel->SetSave(save, gameView->ShiftBehaviour()); gameModel->SetSave(std::move(save), gameView->ShiftBehaviour());
} }
void GameController::OpenSaveDone() void GameController::OpenSaveDone()
@ -1215,7 +1215,7 @@ void GameController::OpenSaveDone()
try try
{ {
HistorySnapshot(); HistorySnapshot();
LoadSave(activePreview->GetSaveInfo()); LoadSave(activePreview->TakeSaveInfo());
} }
catch(GameModelException & ex) catch(GameModelException & ex)
{ {
@ -1241,9 +1241,9 @@ void GameController::OpenSavePreview()
void GameController::OpenLocalBrowse() void GameController::OpenLocalBrowse()
{ {
new FileBrowserActivity(ByteString::Build(LOCAL_SAVE_DIR, PATH_SEP_CHAR), [this](std::unique_ptr<SaveFile> file) { new FileBrowserActivity(ByteString::Build(LOCAL_SAVE_DIR, PATH_SEP_CHAR), [this](auto file) {
HistorySnapshot(); HistorySnapshot();
LoadSaveFile(file.get()); LoadSaveFile(std::move(file));
}); });
} }
@ -1313,14 +1313,14 @@ void GameController::OpenTags()
void GameController::OpenStamps() void GameController::OpenStamps()
{ {
localBrowser = new LocalBrowserController([this] { localBrowser = new LocalBrowserController([this] {
SaveFile *file = localBrowser->GetSave(); auto file = localBrowser->TakeSave();
if (file) if (file)
{ {
if (file->GetError().length()) if (file->GetError().length())
new ErrorMessage("Error loading stamp", file->GetError()); new ErrorMessage("Error loading stamp", file->GetError());
else if (localBrowser->GetMoveToFront()) else if (localBrowser->GetMoveToFront())
Client::Ref().MoveStampToFront(file->GetDisplayName().ToUtf8()); Client::Ref().MoveStampToFront(file->GetDisplayName().ToUtf8());
LoadStamp(file->GetGameSave()); LoadStamp(file->TakeGameSave());
} }
}); });
ui::Engine::Ref().ShowWindow(localBrowser->GetView()); ui::Engine::Ref().ShowWindow(localBrowser->GetView());
@ -1361,7 +1361,7 @@ void GameController::OpenSaveWindow()
if(gameModel->GetUser().UserID) if(gameModel->GetUser().UserID)
{ {
Simulation * sim = gameModel->GetSimulation(); Simulation * sim = gameModel->GetSimulation();
GameSave * gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour()); auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour());
if(!gameSave) if(!gameSave)
{ {
new ErrorMessage("Error", "Unable to build save."); new ErrorMessage("Error", "Unable to build save.");
@ -1372,22 +1372,22 @@ void GameController::OpenSaveWindow()
if(gameModel->GetSave()) if(gameModel->GetSave())
{ {
SaveInfo tempSave(*gameModel->GetSave()); auto tempSave = gameModel->GetSave()->CloneInfo();
tempSave.SetGameSave(gameSave); tempSave->SetGameSave(std::move(gameSave));
new ServerSaveActivity(tempSave, [this](SaveInfo &save) { new ServerSaveActivity(std::move(tempSave), [this](auto save) {
save.SetVote(1); save->SetVote(1);
save.SetVotesUp(1); save->SetVotesUp(1);
LoadSave(&save); LoadSave(std::move(save));
}); });
} }
else else
{ {
SaveInfo tempSave(0, 0, 0, 0, 0, gameModel->GetUser().Username, ""); auto tempSave = std::make_unique<SaveInfo>(0, 0, 0, 0, 0, gameModel->GetUser().Username, "");
tempSave.SetGameSave(gameSave); tempSave->SetGameSave(std::move(gameSave));
new ServerSaveActivity(tempSave, [this](SaveInfo &save) { new ServerSaveActivity(std::move(tempSave), [this](auto save) {
save.SetVote(1); save->SetVote(1);
save.SetVotesUp(1); save->SetVotesUp(1);
LoadSave(&save); LoadSave(std::move(save));
}); });
} }
} }
@ -1403,7 +1403,7 @@ void GameController::SaveAsCurrent()
if(gameModel->GetSave() && gameModel->GetUser().UserID && gameModel->GetUser().Username == gameModel->GetSave()->GetUserName()) if(gameModel->GetSave() && gameModel->GetUser().UserID && gameModel->GetUser().Username == gameModel->GetSave()->GetUserName())
{ {
Simulation * sim = gameModel->GetSimulation(); Simulation * sim = gameModel->GetSimulation();
GameSave * gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour()); auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour());
if(!gameSave) if(!gameSave)
{ {
new ErrorMessage("Error", "Unable to build save."); new ErrorMessage("Error", "Unable to build save.");
@ -1414,15 +1414,15 @@ void GameController::SaveAsCurrent()
if(gameModel->GetSave()) if(gameModel->GetSave())
{ {
SaveInfo tempSave(*gameModel->GetSave()); auto tempSave = gameModel->GetSave()->CloneInfo();
tempSave.SetGameSave(gameSave); tempSave->SetGameSave(std::move(gameSave));
new ServerSaveActivity(tempSave, true, [this](SaveInfo &save) { LoadSave(&save); }); new ServerSaveActivity(std::move(tempSave), true, [this](auto save) { LoadSave(std::move(save)); });
} }
else else
{ {
SaveInfo tempSave(0, 0, 0, 0, 0, gameModel->GetUser().Username, ""); auto tempSave = std::make_unique<SaveInfo>(0, 0, 0, 0, 0, gameModel->GetUser().Username, "");
tempSave.SetGameSave(gameSave); tempSave->SetGameSave(std::move(gameSave));
new ServerSaveActivity(tempSave, true, [this](SaveInfo &save) { LoadSave(&save); }); new ServerSaveActivity(std::move(tempSave), true, [this](auto save) { LoadSave(std::move(save)); });
} }
} }
} }
@ -1497,12 +1497,12 @@ void GameController::ReloadSim()
if(gameModel->GetSave() && gameModel->GetSave()->GetGameSave()) if(gameModel->GetSave() && gameModel->GetSave()->GetGameSave())
{ {
HistorySnapshot(); HistorySnapshot();
gameModel->SetSave(gameModel->GetSave(), gameView->ShiftBehaviour()); gameModel->SetSave(gameModel->TakeSave(), gameView->ShiftBehaviour());
} }
else if(gameModel->GetSaveFile() && gameModel->GetSaveFile()->GetGameSave()) else if(gameModel->GetSaveFile() && gameModel->GetSaveFile()->GetGameSave())
{ {
HistorySnapshot(); HistorySnapshot();
gameModel->SetSaveFile(gameModel->GetSaveFile(), gameView->ShiftBehaviour()); gameModel->SetSaveFile(gameModel->TakeSaveFile(), gameView->ShiftBehaviour());
} }
} }

View File

@ -127,8 +127,8 @@ public:
void SetActiveColourPreset(int preset); void SetActiveColourPreset(int preset);
void SetColour(ui::Colour colour); void SetColour(ui::Colour colour);
void SetToolStrength(float value); void SetToolStrength(float value);
void LoadSaveFile(SaveFile * file); void LoadSaveFile(std::unique_ptr<SaveFile> file);
void LoadSave(SaveInfo * save); void LoadSave(std::unique_ptr<SaveInfo> save);
void OpenSearch(String searchText); void OpenSearch(String searchText);
void OpenLogin(); void OpenLogin();
void OpenProfile(); void OpenProfile();
@ -175,7 +175,7 @@ public:
void ToggleNewtonianGravity(); void ToggleNewtonianGravity();
bool LoadClipboard(); bool LoadClipboard();
void LoadStamp(GameSave *stamp); void LoadStamp(std::unique_ptr<GameSave> stamp);
void RemoveNotification(Notification * notification); void RemoveNotification(Notification * notification);

View File

@ -41,12 +41,8 @@ HistoryEntry::~HistoryEntry()
} }
GameModel::GameModel(): GameModel::GameModel():
clipboard(NULL),
placeSave(NULL),
activeMenu(-1), activeMenu(-1),
currentBrush(0), currentBrush(0),
currentSave(NULL),
currentFile(NULL),
currentUser(0, ""), currentUser(0, ""),
toolStrength(1.0f), toolStrength(1.0f),
historyPosition(0), historyPosition(0),
@ -187,10 +183,6 @@ GameModel::~GameModel()
} }
delete sim; delete sim;
delete ren; delete ren;
delete placeSave;
delete clipboard;
delete currentSave;
delete currentFile;
//if(activeTools) //if(activeTools)
// delete[] activeTools; // delete[] activeTools;
} }
@ -929,60 +921,58 @@ std::vector<Menu*> GameModel::GetMenuList()
return menuList; return menuList;
} }
SaveInfo * GameModel::GetSave() SaveInfo *GameModel::GetSave() // non-owning
{ {
return currentSave; return currentSave.get();
} }
void GameModel::SaveToSimParameters(const GameSave *saveData) std::unique_ptr<SaveInfo> GameModel::TakeSave()
{ {
SetPaused(saveData->paused | GetPaused()); // we don't notify listeners because we'll get a new save soon anyway
sim->gravityMode = saveData->gravityMode; return std::move(currentSave);
sim->customGravityX = saveData->customGravityX; }
sim->customGravityY = saveData->customGravityY;
sim->air->airMode = saveData->airMode; void GameModel::SaveToSimParameters(const GameSave &saveData)
sim->air->ambientAirTemp = saveData->ambientAirTemp; {
sim->edgeMode = saveData->edgeMode; SetPaused(saveData.paused | GetPaused());
sim->legacy_enable = saveData->legacyEnable; sim->gravityMode = saveData.gravityMode;
sim->water_equal_test = saveData->waterEEnabled; sim->customGravityX = saveData.customGravityX;
sim->aheat_enable = saveData->aheatEnable; sim->customGravityY = saveData.customGravityY;
if (saveData->gravityEnable && !sim->grav->IsEnabled()) sim->air->airMode = saveData.airMode;
sim->air->ambientAirTemp = saveData.ambientAirTemp;
sim->edgeMode = saveData.edgeMode;
sim->legacy_enable = saveData.legacyEnable;
sim->water_equal_test = saveData.waterEEnabled;
sim->aheat_enable = saveData.aheatEnable;
if (saveData.gravityEnable && !sim->grav->IsEnabled())
{ {
sim->grav->start_grav_async(); sim->grav->start_grav_async();
} }
else if (!saveData->gravityEnable && sim->grav->IsEnabled()) else if (!saveData.gravityEnable && sim->grav->IsEnabled())
{ {
sim->grav->stop_grav_async(); sim->grav->stop_grav_async();
} }
sim->frameCount = saveData->frameCount; sim->frameCount = saveData.frameCount;
if (saveData->hasRngState) if (saveData.hasRngState)
{ {
sim->rng.state(saveData->rngState); sim->rng.state(saveData.rngState);
} }
else else
{ {
sim->rng = RNG(); sim->rng = RNG();
} }
sim->ensureDeterminism = saveData->ensureDeterminism; sim->ensureDeterminism = saveData.ensureDeterminism;
} }
void GameModel::SetSave(SaveInfo * newSave, bool invertIncludePressure) void GameModel::SetSave(std::unique_ptr<SaveInfo> newSave, bool invertIncludePressure)
{ {
if(currentSave != newSave) currentSave = std::move(newSave);
{ currentFile.reset();
delete currentSave;
if(newSave == NULL)
currentSave = NULL;
else
currentSave = new SaveInfo(*newSave);
}
delete currentFile;
currentFile = NULL;
if (newSave && newSave->GetGameSave()) if (currentSave && currentSave->GetGameSave())
{ {
GameSave *saveData = newSave->GetGameSave(); auto *saveData = currentSave->GetGameSave();
SaveToSimParameters(saveData); SaveToSimParameters(*saveData);
sim->clear_sim(); sim->clear_sim();
ren->ClearAccumulation(); ren->ClearAccumulation();
if (!sim->Load(saveData, !invertIncludePressure)) if (!sim->Load(saveData, !invertIncludePressure))
@ -991,19 +981,23 @@ void GameModel::SetSave(SaveInfo * newSave, bool invertIncludePressure)
// Add in the correct info // Add in the correct info
if (saveData->authors.size() == 0) if (saveData->authors.size() == 0)
{ {
saveData->authors["type"] = "save"; auto gameSave = currentSave->TakeGameSave();
saveData->authors["id"] = newSave->id; gameSave->authors["type"] = "save";
saveData->authors["username"] = newSave->userName; gameSave->authors["id"] = currentSave->id;
saveData->authors["title"] = newSave->name.ToUtf8(); gameSave->authors["username"] = currentSave->userName;
saveData->authors["description"] = newSave->Description.ToUtf8(); gameSave->authors["title"] = currentSave->name.ToUtf8();
saveData->authors["published"] = (int)newSave->Published; gameSave->authors["description"] = currentSave->Description.ToUtf8();
saveData->authors["date"] = newSave->updatedDate; gameSave->authors["published"] = (int)currentSave->Published;
gameSave->authors["date"] = currentSave->updatedDate;
currentSave->SetGameSave(std::move(gameSave));
} }
// This save was probably just created, and we didn't know the ID when creating it // This save was probably just created, and we didn't know the ID when creating it
// Update with the proper ID // Update with the proper ID
else if (saveData->authors.get("id", -1) == 0 || saveData->authors.get("id", -1) == -1) else if (saveData->authors.get("id", -1) == 0 || saveData->authors.get("id", -1) == -1)
{ {
saveData->authors["id"] = newSave->id; auto gameSave = currentSave->TakeGameSave();
gameSave->authors["id"] = currentSave->id;
currentSave->SetGameSave(std::move(gameSave));
} }
Client::Ref().OverwriteAuthorInfo(saveData->authors); Client::Ref().OverwriteAuthorInfo(saveData->authors);
} }
@ -1012,28 +1006,26 @@ void GameModel::SetSave(SaveInfo * newSave, bool invertIncludePressure)
UpdateQuickOptions(); UpdateQuickOptions();
} }
SaveFile * GameModel::GetSaveFile() const SaveFile *GameModel::GetSaveFile() const
{ {
return currentFile; return currentFile.get();
} }
void GameModel::SetSaveFile(SaveFile * newSave, bool invertIncludePressure) std::unique_ptr<SaveFile> GameModel::TakeSaveFile()
{ {
if(currentFile != newSave) // we don't notify listeners because we'll get a new save soon anyway
{ return std::move(currentFile);
delete currentFile; }
if(newSave == NULL)
currentFile = NULL;
else
currentFile = new SaveFile(*newSave);
}
delete currentSave;
currentSave = NULL;
if (newSave && newSave->GetGameSave()) void GameModel::SetSaveFile(std::unique_ptr<SaveFile> newSave, bool invertIncludePressure)
{
currentFile = std::move(newSave);
currentSave.reset();
if (currentFile && currentFile->GetGameSave())
{ {
GameSave *saveData = newSave->GetGameSave(); auto *saveData = currentFile->GetGameSave();
SaveToSimParameters(saveData); SaveToSimParameters(*saveData);
sim->clear_sim(); sim->clear_sim();
ren->ClearAccumulation(); ren->ClearAccumulation();
if (!sim->Load(saveData, !invertIncludePressure)) if (!sim->Load(saveData, !invertIncludePressure))
@ -1342,33 +1334,31 @@ void GameModel::ClearSimulation()
UpdateQuickOptions(); UpdateQuickOptions();
} }
void GameModel::SetPlaceSave(GameSave * save) void GameModel::SetPlaceSave(std::unique_ptr<GameSave> save)
{ {
if (save != placeSave) placeSave = std::move(save);
{
delete placeSave;
if (save)
placeSave = new GameSave(*save);
else
placeSave = NULL;
}
notifyPlaceSaveChanged(); notifyPlaceSaveChanged();
} }
void GameModel::SetClipboard(GameSave * save) void GameModel::SetClipboard(std::unique_ptr<GameSave> save)
{ {
delete clipboard; clipboard = std::move(save);
clipboard = save;
} }
GameSave * GameModel::GetClipboard() const GameSave *GameModel::GetClipboard() const
{ {
return clipboard; return clipboard.get();
} }
GameSave * GameModel::GetPlaceSave() const GameSave *GameModel::GetPlaceSave() const
{ {
return placeSave; return placeSave.get();
}
std::unique_ptr<GameSave> GameModel::TakePlaceSave()
{
// we don't notify listeners because we'll get a new save soon anyway
return std::move(placeSave);
} }
void GameModel::Log(String message, bool printToFile) void GameModel::Log(String message, bool printToFile)

View File

@ -44,8 +44,8 @@ private:
std::vector<Notification*> notifications; std::vector<Notification*> notifications;
//int clipboardSize; //int clipboardSize;
//unsigned char * clipboardData; //unsigned char * clipboardData;
GameSave * clipboard; std::unique_ptr<GameSave> clipboard;
GameSave * placeSave; std::unique_ptr<GameSave> placeSave;
std::deque<String> consoleLog; std::deque<String> consoleLog;
std::vector<GameView*> observers; std::vector<GameView*> observers;
std::vector<Tool*> toolList; std::vector<Tool*> toolList;
@ -62,8 +62,8 @@ private:
int activeMenu; int activeMenu;
int currentBrush; int currentBrush;
std::vector<std::unique_ptr<Brush>> brushList; std::vector<std::unique_ptr<Brush>> brushList;
SaveInfo * currentSave; std::unique_ptr<SaveInfo> currentSave;
SaveFile * currentFile; std::unique_ptr<SaveFile> currentFile;
Tool * lastTool; Tool * lastTool;
Tool ** activeTools; Tool ** activeTools;
Tool * decoToolset[4]; Tool * decoToolset[4];
@ -115,7 +115,7 @@ private:
void notifyQuickOptionsChanged(); void notifyQuickOptionsChanged();
void notifyLastToolChanged(); void notifyLastToolChanged();
void SaveToSimParameters(const GameSave *saveData); void SaveToSimParameters(const GameSave &saveData);
public: public:
GameModel(); GameModel();
@ -184,10 +184,12 @@ public:
void SetBrushID(int i); void SetBrushID(int i);
void SetVote(int direction); void SetVote(int direction);
SaveInfo * GetSave(); SaveInfo *GetSave(); // non-owning
SaveFile * GetSaveFile(); std::unique_ptr<SaveInfo> TakeSave();
void SetSave(SaveInfo * newSave, bool invertIncludePressure); const SaveFile *GetSaveFile() const;
void SetSaveFile(SaveFile * newSave, bool invertIncludePressure); std::unique_ptr<SaveFile> TakeSaveFile();
void SetSave(std::unique_ptr<SaveInfo> newSave, bool invertIncludePressure);
void SetSaveFile(std::unique_ptr<SaveFile> newSave, bool invertIncludePressure);
void AddObserver(GameView * observer); void AddObserver(GameView * observer);
void SetPaused(bool pauseState); void SetPaused(bool pauseState);
@ -223,12 +225,13 @@ public:
ui::Point AdjustZoomCoords(ui::Point position); ui::Point AdjustZoomCoords(ui::Point position);
void SetZoomWindowPosition(ui::Point position); void SetZoomWindowPosition(ui::Point position);
ui::Point GetZoomWindowPosition(); ui::Point GetZoomWindowPosition();
void SetClipboard(GameSave * save); void SetClipboard(std::unique_ptr<GameSave> save);
void SetPlaceSave(GameSave * save); void SetPlaceSave(std::unique_ptr<GameSave> save);
void Log(String message, bool printToFile); void Log(String message, bool printToFile);
std::deque<String> GetLog(); std::deque<String> GetLog();
GameSave * GetClipboard(); const GameSave *GetClipboard() const;
GameSave * GetPlaceSave(); const GameSave *GetPlaceSave() const;
std::unique_ptr<GameSave> TakePlaceSave();
bool GetMouseClickRequired(); bool GetMouseClickRequired();
void SetMouseClickRequired(bool mouseClickRequired); void SetMouseClickRequired(bool mouseClickRequired);
bool GetIncludePressure(); bool GetIncludePressure();

View File

@ -17,6 +17,7 @@
#include "client/SaveInfo.h" #include "client/SaveInfo.h"
#include "client/SaveFile.h" #include "client/SaveFile.h"
#include "client/Client.h" #include "client/Client.h"
#include "client/GameSave.h"
#include "common/platform/Platform.h" #include "common/platform/Platform.h"
#include "graphics/Graphics.h" #include "graphics/Graphics.h"
#include "graphics/Renderer.h" #include "graphics/Renderer.h"
@ -1542,11 +1543,10 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl,
auto &stampIDs = Client::Ref().GetStamps(); auto &stampIDs = Client::Ref().GetStamps();
if (stampIDs.size()) if (stampIDs.size())
{ {
SaveFile *saveFile = Client::Ref().GetStamp(stampIDs[0]); auto saveFile = Client::Ref().GetStamp(stampIDs[0]);
if (!saveFile || !saveFile->GetGameSave()) if (!saveFile || !saveFile->GetGameSave())
break; break;
c->LoadStamp(saveFile->GetGameSave()); c->LoadStamp(saveFile->TakeGameSave());
delete saveFile;
selectPoint1 = selectPoint2 = mousePosition; selectPoint1 = selectPoint2 = mousePosition;
isMouseDown = false; isMouseDown = false;
break; break;
@ -1641,7 +1641,7 @@ void GameView::OnFileDrop(ByteString filename)
return; return;
} }
SaveFile *saveFile = Client::Ref().LoadSaveFile(filename); auto saveFile = Client::Ref().LoadSaveFile(filename);
if (!saveFile) if (!saveFile)
return; return;
if (saveFile->GetError().length()) if (saveFile->GetError().length())
@ -1649,8 +1649,7 @@ void GameView::OnFileDrop(ByteString filename)
new ErrorMessage("Error loading save", "Dropped save file could not be loaded: " + saveFile->GetError()); new ErrorMessage("Error loading save", "Dropped save file could not be loaded: " + saveFile->GetError());
return; return;
} }
c->LoadSaveFile(saveFile); c->LoadSaveFile(std::move(saveFile));
delete saveFile;
// hide the info text if it's not already hidden // hide the info text if it's not already hidden
introText = 0; introText = 0;

View File

@ -78,7 +78,9 @@ void Panel::Draw(const Point& screenPos)
auto rect = RectSized(child->Position + ViewportPosition, child->Size); auto rect = RectSized(child->Position + ViewportPosition, child->Size);
//check if the component is in the screen, draw if it is //check if the component is in the screen, draw if it is
if (rect & Size.OriginRect()) if (rect & Size.OriginRect())
{
child->Draw(screenPos + rect.TopLeft); child->Draw(screenPos + rect.TopLeft);
}
} }
GetGraphics()->SwapClipRect(clip); // apply old cliprect GetGraphics()->SwapClipRect(clip); // apply old cliprect

View File

@ -18,8 +18,6 @@ namespace ui {
SaveButton::SaveButton(Point position, Point size) : SaveButton::SaveButton(Point position, Point size) :
Component(position, size), Component(position, size),
file(nullptr),
save(nullptr),
wantsDraw(false), wantsDraw(false),
triedThumbnail(false), triedThumbnail(false),
isMouseInsideAuthor(false), isMouseInsideAuthor(false),
@ -33,9 +31,9 @@ SaveButton::SaveButton(Point position, Point size) :
{ {
} }
SaveButton::SaveButton(Point position, Point size, SaveInfo * save_) : SaveButton(position, size) SaveButton::SaveButton(Point position, Point size, SaveInfo *newSave /* non-owning */) : SaveButton(position, size)
{ {
save = save_; save = newSave;
if(save) if(save)
{ {
name = save->name; name = save->name;
@ -94,9 +92,9 @@ SaveButton::SaveButton(Point position, Point size, SaveInfo * save_) : SaveButto
} }
} }
SaveButton::SaveButton(Point position, Point size, SaveFile * file_) : SaveButton(position, size) SaveButton::SaveButton(Point position, Point size, SaveFile *newFile /* non-owning */) : SaveButton(position, size)
{ {
file = file_; file = newFile;
if(file) if(file)
{ {
name = file->GetDisplayName(); name = file->GetDisplayName();
@ -115,8 +113,6 @@ SaveButton::~SaveButton()
{ {
thumbnailRenderer->Abandon(); thumbnailRenderer->Abandon();
} }
delete save;
delete file;
} }
void SaveButton::Tick(float dt) void SaveButton::Tick(float dt)
@ -198,7 +194,7 @@ void SaveButton::Draw(const Point& screenPos)
auto space = Size - Vec2{ 0, 21 }; auto space = Size - Vec2{ 0, 21 };
g->BlendImage(tex->Data(), 255, RectSized(screenPos + ((save && save->id) ? ((space - thumbBoxSize) / 2 - Vec2{ 3, 0 }) : (space - thumbSize) / 2), tex->Size())); g->BlendImage(tex->Data(), 255, RectSized(screenPos + ((save && save->id) ? ((space - thumbBoxSize) / 2 - Vec2{ 3, 0 }) : (space - thumbSize) / 2), tex->Size()));
} }
else if (file && !file->GetGameSave()) else if (file && !file->LazyGetGameSave())
g->BlendText(screenPos + Vec2{ (Size.X-(Graphics::TextSize("Error loading save").X - 1))/2, (Size.Y-28)/2 }, "Error loading save", 0xB4B4B4_rgb .WithAlpha(255)); g->BlendText(screenPos + Vec2{ (Size.X-(Graphics::TextSize("Error loading save").X - 1))/2, (Size.Y-28)/2 }, "Error loading save", 0xB4B4B4_rgb .WithAlpha(255));
if(save) if(save)
{ {

View File

@ -15,8 +15,8 @@ namespace ui
{ {
class SaveButton : public Component class SaveButton : public Component
{ {
SaveFile * file; SaveFile *file = nullptr; // non-owning
SaveInfo * save; SaveInfo *save = nullptr; // non-owning
std::unique_ptr<VideoBuffer> thumbnail; std::unique_ptr<VideoBuffer> thumbnail;
ui::Point thumbSize = ui::Point(0, 0); ui::Point thumbSize = ui::Point(0, 0);
String name; String name;
@ -43,8 +43,8 @@ class SaveButton : public Component
SaveButton(Point position, Point size); SaveButton(Point position, Point size);
public: public:
SaveButton(Point position, Point size, SaveInfo * save); SaveButton(Point position, Point size, SaveInfo *newSave /* non-owning */);
SaveButton(Point position, Point size, SaveFile * file); SaveButton(Point position, Point size, SaveFile *newFile /* non-owning */);
virtual ~SaveButton(); virtual ~SaveButton();
void OnMouseClick(int x, int y, unsigned int button) override; void OnMouseClick(int x, int y, unsigned int button) override;
@ -67,8 +67,8 @@ public:
bool GetSelectable() { return selectable; } bool GetSelectable() { return selectable; }
void SetShowVotes(bool showVotes_) { showVotes = showVotes_; } void SetShowVotes(bool showVotes_) { showVotes = showVotes_; }
SaveInfo * GetSave() { return save; } const SaveInfo *GetSave() const { return save; }
SaveFile * GetSaveFile() { return file; } const SaveFile *GetSaveFile() const { return file; }
inline bool GetState() { return state; } inline bool GetState() { return state; }
void DoAction(); void DoAction();
void DoAltAction(); void DoAltAction();

View File

@ -4,6 +4,8 @@
#include "LocalBrowserView.h" #include "LocalBrowserView.h"
#include "client/Client.h" #include "client/Client.h"
#include "client/GameSave.h"
#include "client/SaveFile.h"
#include "gui/dialogues/ConfirmPrompt.h" #include "gui/dialogues/ConfirmPrompt.h"
#include "tasks/TaskWindow.h" #include "tasks/TaskWindow.h"
#include "tasks/Task.h" #include "tasks/Task.h"
@ -25,14 +27,14 @@ LocalBrowserController::LocalBrowserController(std::function<void ()> onDone_):
browserModel->UpdateSavesList(1); browserModel->UpdateSavesList(1);
} }
void LocalBrowserController::OpenSave(SaveFile * save) void LocalBrowserController::OpenSave(int index)
{ {
browserModel->SetSave(save); browserModel->OpenSave(index);
} }
SaveFile * LocalBrowserController::GetSave() std::unique_ptr<SaveFile> LocalBrowserController::TakeSave()
{ {
return browserModel->GetSave(); return browserModel->TakeSave();
} }
void LocalBrowserController::RemoveSelected() void LocalBrowserController::RemoveSelected()
@ -105,7 +107,7 @@ void LocalBrowserController::SetPageRelative(int offset)
void LocalBrowserController::Update() void LocalBrowserController::Update()
{ {
if(browserModel->GetSave()) if (browserModel->GetSave())
{ {
Exit(); Exit();
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "common/String.h" #include "common/String.h"
#include <functional> #include <functional>
#include <memory>
class SaveFile; class SaveFile;
class LocalBrowserView; class LocalBrowserView;
@ -13,14 +14,14 @@ public:
bool HasDone; bool HasDone;
LocalBrowserController(std::function<void ()> onDone = nullptr); LocalBrowserController(std::function<void ()> onDone = nullptr);
LocalBrowserView * GetView() {return browserView;} LocalBrowserView * GetView() {return browserView;}
SaveFile * GetSave(); std::unique_ptr<SaveFile> TakeSave();
void RemoveSelected(); void RemoveSelected();
void removeSelectedC(); void removeSelectedC();
void ClearSelection(); void ClearSelection();
void Selected(ByteString stampID, bool selected); void Selected(ByteString stampID, bool selected);
void RescanStamps(); void RescanStamps();
void RefreshSavesList(); void RefreshSavesList();
void OpenSave(SaveFile * stamp); void OpenSave(int index);
bool GetMoveToFront(); bool GetMoveToFront();
void SetMoveToFront(bool move); void SetMoveToFront(bool move);
void SetPage(int page); void SetPage(int page);

View File

@ -2,13 +2,13 @@
#include "LocalBrowserView.h" #include "LocalBrowserView.h"
#include "client/Client.h" #include "client/Client.h"
#include "client/SaveFile.h" #include "client/SaveFile.h"
#include "client/GameSave.h"
#include "common/tpt-minmax.h" #include "common/tpt-minmax.h"
#include <algorithm> #include <algorithm>
constexpr auto pageSize = 20; constexpr auto pageSize = 20;
LocalBrowserModel::LocalBrowserModel(): LocalBrowserModel::LocalBrowserModel():
stamp(NULL),
currentPage(1), currentPage(1),
stampToFront(1) stampToFront(1)
{ {
@ -16,9 +16,13 @@ LocalBrowserModel::LocalBrowserModel():
} }
std::vector<SaveFile*> LocalBrowserModel::GetSavesList() std::vector<SaveFile *> LocalBrowserModel::GetSavesList() // non-owning
{ {
return savesList; std::vector<SaveFile *> nonOwningSaveList;
std::transform(savesList.begin(), savesList.end(), std::back_inserter(nonOwningSaveList), [](auto &ptr) {
return ptr.get();
});
return nonOwningSaveList;
} }
void LocalBrowserModel::AddObserver(LocalBrowserView * observer) void LocalBrowserModel::AddObserver(LocalBrowserView * observer)
@ -45,15 +49,20 @@ void LocalBrowserModel::notifyPageChanged()
} }
} }
SaveFile * LocalBrowserModel::GetSave() const SaveFile *LocalBrowserModel::GetSave()
{ {
return stamp; return stamp.get();
} }
void LocalBrowserModel::SetSave(SaveFile * newStamp) std::unique_ptr<SaveFile> LocalBrowserModel::TakeSave()
{ {
delete stamp; return std::move(stamp);
stamp = new SaveFile(*newStamp); }
void LocalBrowserModel::OpenSave(int index)
{
stamp = std::move(savesList[index]);
savesList.clear();
} }
bool LocalBrowserModel::GetMoveToFront() bool LocalBrowserModel::GetMoveToFront()
@ -68,25 +77,19 @@ void LocalBrowserModel::SetMoveToFront(bool move)
void LocalBrowserModel::UpdateSavesList(int pageNumber) void LocalBrowserModel::UpdateSavesList(int pageNumber)
{ {
std::vector<SaveFile*> tempSavesList = savesList;
savesList.clear(); savesList.clear();
currentPage = pageNumber; currentPage = pageNumber;
notifyPageChanged(); notifyPageChanged();
notifySavesListChanged(); notifySavesListChanged();
//notifyStampsListChanged();
/*for(int i = 0; i < tempSavesList.size(); i++)
{
delete tempSavesList[i];
}*/
stampIDs = Client::Ref().GetStamps(); stampIDs = Client::Ref().GetStamps();
auto size = int(stampIDs.size()); auto size = int(stampIDs.size());
for (int i = (currentPage - 1) * pageSize; i < size && i < currentPage * pageSize; i++) for (int i = (currentPage - 1) * pageSize; i < size && i < currentPage * pageSize; i++)
{ {
SaveFile * tempSave = Client::Ref().GetStamp(stampIDs[i]); auto tempSave = Client::Ref().GetStamp(stampIDs[i]);
if (tempSave) if (tempSave)
{ {
savesList.push_back(tempSave); savesList.push_back(std::move(tempSave));
} }
} }
notifySavesListChanged(); notifySavesListChanged();
@ -131,8 +134,3 @@ void LocalBrowserModel::notifySelectedChanged()
cObserver->NotifySelectedChanged(this); cObserver->NotifySelectedChanged(this);
} }
} }
LocalBrowserModel::~LocalBrowserModel() {
delete stamp;
}

View File

@ -1,15 +1,16 @@
#pragma once #pragma once
#include "common/String.h" #include "common/String.h"
#include <vector> #include <vector>
#include <memory>
class SaveFile; class SaveFile;
class LocalBrowserView; class LocalBrowserView;
class LocalBrowserModel { class LocalBrowserModel {
std::vector<ByteString> selected; std::vector<ByteString> selected;
SaveFile * stamp; std::unique_ptr<SaveFile> stamp;
std::vector<ByteString> stampIDs; std::vector<ByteString> stampIDs;
std::vector<SaveFile*> savesList; std::vector<std::unique_ptr<SaveFile>> savesList;
std::vector<LocalBrowserView*> observers; std::vector<LocalBrowserView*> observers;
int currentPage; int currentPage;
bool stampToFront; bool stampToFront;
@ -21,16 +22,16 @@ public:
int GetPageCount(); int GetPageCount();
int GetPageNum() { return currentPage; } int GetPageNum() { return currentPage; }
void AddObserver(LocalBrowserView * observer); void AddObserver(LocalBrowserView * observer);
std::vector<SaveFile *> GetSavesList(); std::vector<SaveFile *> GetSavesList(); // non-owning
void UpdateSavesList(int pageNumber); void UpdateSavesList(int pageNumber);
void RescanStamps(); void RescanStamps();
SaveFile * GetSave(); const SaveFile *GetSave();
void SetSave(SaveFile * newStamp); std::unique_ptr<SaveFile> TakeSave();
void OpenSave(int index);
bool GetMoveToFront(); bool GetMoveToFront();
void SetMoveToFront(bool move); void SetMoveToFront(bool move);
std::vector<ByteString> GetSelected() { return selected; } std::vector<ByteString> GetSelected() { return selected; }
void ClearSelected() { selected.clear(); notifySelectedChanged(); } void ClearSelected() { selected.clear(); notifySelectedChanged(); }
void SelectSave(ByteString stampID); void SelectSave(ByteString stampID);
void DeselectSave(ByteString stampID); void DeselectSave(ByteString stampID);
virtual ~LocalBrowserModel();
}; };

View File

@ -118,7 +118,7 @@ void LocalBrowserView::NotifySavesListChanged(LocalBrowserModel * sender)
int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 2; int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 2;
int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset; int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset;
std::vector<SaveFile*> saves = sender->GetSavesList(); auto saves = sender->GetSavesList(); // non-owning
for (size_t i = 0; i < stampButtons.size(); i++) for (size_t i = 0; i < stampButtons.size(); i++)
{ {
RemoveComponent(stampButtons[i]); RemoveComponent(stampButtons[i]);
@ -131,7 +131,7 @@ void LocalBrowserView::NotifySavesListChanged(LocalBrowserModel * sender)
buttonAreaHeight = Size.Y - buttonYOffset - 18; buttonAreaHeight = Size.Y - buttonYOffset - 18;
buttonWidth = (buttonAreaWidth/savesX) - buttonPadding*2; buttonWidth = (buttonAreaWidth/savesX) - buttonPadding*2;
buttonHeight = (buttonAreaHeight/savesY) - buttonPadding*2; buttonHeight = (buttonAreaHeight/savesY) - buttonPadding*2;
for (size_t i = 0; i < saves.size(); i++) for (auto i = 0; i < int(saves.size()); i++)
{ {
if(saveX == savesX) if(saveX == savesX)
{ {
@ -150,9 +150,9 @@ void LocalBrowserView::NotifySavesListChanged(LocalBrowserModel * sender)
saves[i]); saves[i]);
saveButton->SetSelectable(true); saveButton->SetSelectable(true);
saveButton->SetActionCallback({ saveButton->SetActionCallback({
[this, saveButton] { [this, saveButton, i] {
if (saveButton->GetSaveFile()) if (saveButton->GetSaveFile())
c->OpenSave(saveButton->GetSaveFile()); c->OpenSave(i);
}, },
nullptr, nullptr,
nullptr, nullptr,

View File

@ -5,6 +5,7 @@
#include "PreviewView.h" #include "PreviewView.h"
#include "client/Client.h" #include "client/Client.h"
#include "client/SaveInfo.h" #include "client/SaveInfo.h"
#include "client/GameSave.h"
#include "common/platform/Platform.h" #include "common/platform/Platform.h"
#include "gui/dialogues/ErrorMessage.h" #include "gui/dialogues/ErrorMessage.h"
#include "gui/dialogues/InformationMessage.h" #include "gui/dialogues/InformationMessage.h"
@ -84,11 +85,16 @@ void PreviewController::NotifyAuthUserChanged(Client * sender)
previewModel->SetCommentBoxEnabled(sender->GetAuthUser().UserID); previewModel->SetCommentBoxEnabled(sender->GetAuthUser().UserID);
} }
SaveInfo * PreviewController::GetSaveInfo() const SaveInfo *PreviewController::GetSaveInfo() const
{ {
return previewModel->GetSaveInfo(); return previewModel->GetSaveInfo();
} }
std::unique_ptr<SaveInfo> PreviewController::TakeSaveInfo()
{
return previewModel->TakeSaveInfo();
}
bool PreviewController::GetDoOpen() bool PreviewController::GetDoOpen()
{ {
return previewModel->GetDoOpen(); return previewModel->GetDoOpen();

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "client/ClientListener.h" #include "client/ClientListener.h"
#include <functional> #include <functional>
#include <memory>
class SaveInfo; class SaveInfo;
class LoginController; class LoginController;
@ -24,7 +25,8 @@ public:
void Report(String message); void Report(String message);
void ShowLogin(); void ShowLogin();
bool GetDoOpen(); bool GetDoOpen();
SaveInfo * GetSaveInfo(); const SaveInfo *GetSaveInfo() const;
std::unique_ptr<SaveInfo> TakeSaveInfo();
PreviewView * GetView() { return previewView; } PreviewView * GetView() { return previewView; }
void Update(); void Update();
void FavouriteSave(); void FavouriteSave();

View File

@ -15,7 +15,6 @@
PreviewModel::PreviewModel(): PreviewModel::PreviewModel():
doOpen(false), doOpen(false),
canOpen(true), canOpen(true),
saveInfo(NULL),
saveData(NULL), saveData(NULL),
saveComments(NULL), saveComments(NULL),
commentBoxEnabled(false), commentBoxEnabled(false),
@ -27,7 +26,6 @@ PreviewModel::PreviewModel():
PreviewModel::~PreviewModel() PreviewModel::~PreviewModel()
{ {
delete saveInfo;
delete saveData; delete saveData;
ClearComments(); ClearComments();
} }
@ -65,11 +63,7 @@ void PreviewModel::UpdateSave(int saveID, int saveDate)
this->saveID = saveID; this->saveID = saveID;
this->saveDate = saveDate; this->saveDate = saveDate;
if (saveInfo) saveInfo.reset();
{
delete saveInfo;
saveInfo = NULL;
}
if (saveData) if (saveData)
{ {
delete saveData; delete saveData;
@ -120,9 +114,14 @@ bool PreviewModel::GetCanOpen()
return canOpen; return canOpen;
} }
SaveInfo * PreviewModel::GetSaveInfo() const SaveInfo *PreviewModel::GetSaveInfo() const
{ {
return saveInfo; return saveInfo.get();
}
std::unique_ptr<SaveInfo> PreviewModel::TakeSaveInfo()
{
return std::move(saveInfo);
} }
int PreviewModel::GetCommentsPageNum() int PreviewModel::GetCommentsPageNum()
@ -173,10 +172,10 @@ void PreviewModel::OnSaveReady()
commentsTotal = saveInfo->Comments; commentsTotal = saveInfo->Comments;
try try
{ {
GameSave *gameSave = new GameSave(*saveData); auto gameSave = std::make_unique<GameSave>(*saveData);
if (gameSave->fromNewerVersion) if (gameSave->fromNewerVersion)
new ErrorMessage("This save is from a newer version", "Please update TPT in game or at https://powdertoy.co.uk"); new ErrorMessage("This save is from a newer version", "Please update TPT in game or at https://powdertoy.co.uk");
saveInfo->SetGameSave(gameSave); saveInfo->SetGameSave(std::move(gameSave));
} }
catch(ParseException &e) catch(ParseException &e)
{ {
@ -204,7 +203,7 @@ void PreviewModel::ClearComments()
bool PreviewModel::ParseSaveInfo(ByteString &saveInfoResponse) bool PreviewModel::ParseSaveInfo(ByteString &saveInfoResponse)
{ {
delete saveInfo; saveInfo.reset();
try // how does this differ from Client::GetSave? try // how does this differ from Client::GetSave?
{ {
@ -232,13 +231,13 @@ bool PreviewModel::ParseSaveInfo(ByteString &saveInfoResponse)
for (Json::UInt j = 0; j < tagsArray.size(); j++) for (Json::UInt j = 0; j < tagsArray.size(); j++)
tempTags.push_back(tagsArray[j].asString()); tempTags.push_back(tagsArray[j].asString());
saveInfo = new SaveInfo(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp, auto newSaveInfo = std::make_unique<SaveInfo>(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp,
tempScoreDown, tempMyScore, tempUsername, tempName, tempScoreDown, tempMyScore, tempUsername, tempName,
tempDescription, tempPublished, tempTags); tempDescription, tempPublished, tempTags);
saveInfo->Comments = tempComments; newSaveInfo->Comments = tempComments;
saveInfo->Favourite = tempFavourite; newSaveInfo->Favourite = tempFavourite;
saveInfo->Views = tempViews; newSaveInfo->Views = tempViews;
saveInfo->Version = tempVersion; newSaveInfo->Version = tempVersion;
// This is a workaround for a bug on the TPT server where the wrong 404 save is returned // This is a workaround for a bug on the TPT server where the wrong 404 save is returned
// Redownload the .cps file for a fixed version of the 404 save // Redownload the .cps file for a fixed version of the 404 save
@ -249,13 +248,13 @@ bool PreviewModel::ParseSaveInfo(ByteString &saveInfoResponse)
saveDataDownload = std::make_unique<http::Request>(ByteString::Build(STATICSCHEME, STATICSERVER, "/2157797.cps")); saveDataDownload = std::make_unique<http::Request>(ByteString::Build(STATICSCHEME, STATICSERVER, "/2157797.cps"));
saveDataDownload->Start(); saveDataDownload->Start();
} }
return true; saveInfo = std::move(newSaveInfo);
} }
catch (std::exception &e) catch (std::exception &e)
{ {
saveInfo = NULL;
return false; return false;
} }
return true;
} }
bool PreviewModel::ParseComments(ByteString &commentsResponse) bool PreviewModel::ParseComments(ByteString &commentsResponse)

View File

@ -16,7 +16,7 @@ class PreviewModel
bool doOpen; bool doOpen;
bool canOpen; bool canOpen;
std::vector<PreviewView*> observers; std::vector<PreviewView*> observers;
SaveInfo * saveInfo; std::unique_ptr<SaveInfo> saveInfo;
std::vector<char> * saveData; std::vector<char> * saveData;
std::vector<SaveComment*> * saveComments; std::vector<SaveComment*> * saveComments;
void notifySaveChanged(); void notifySaveChanged();
@ -39,7 +39,8 @@ public:
PreviewModel(); PreviewModel();
~PreviewModel(); ~PreviewModel();
SaveInfo * GetSaveInfo(); const SaveInfo *GetSaveInfo() const;
std::unique_ptr<SaveInfo> TakeSaveInfo();
std::vector<SaveComment*> * GetComments(); std::vector<SaveComment*> * GetComments();
bool GetCommentBoxEnabled(); bool GetCommentBoxEnabled();

View File

@ -415,7 +415,7 @@ void PreviewView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ct
void PreviewView::NotifySaveChanged(PreviewModel * sender) void PreviewView::NotifySaveChanged(PreviewModel * sender)
{ {
SaveInfo * save = sender->GetSaveInfo(); auto *save = sender->GetSaveInfo();
savePreview = nullptr; savePreview = nullptr;
if(save) if(save)
{ {

View File

@ -15,9 +15,9 @@
#include "Config.h" #include "Config.h"
LocalSaveActivity::LocalSaveActivity(SaveFile save, OnSaved onSaved_) : LocalSaveActivity::LocalSaveActivity(std::unique_ptr<SaveFile> newSave, OnSaved onSaved_) :
WindowActivity(ui::Point(-1, -1), ui::Point(220, 200)), WindowActivity(ui::Point(-1, -1), ui::Point(220, 200)),
save(save), save(std::move(newSave)),
thumbnailRenderer(nullptr), thumbnailRenderer(nullptr),
onSaved(onSaved_) onSaved(onSaved_)
{ {
@ -27,7 +27,7 @@ LocalSaveActivity::LocalSaveActivity(SaveFile save, OnSaved onSaved_) :
titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
AddComponent(titleLabel); AddComponent(titleLabel);
filenameField = new ui::Textbox(ui::Point(8, 25), ui::Point(Size.X-16, 16), save.GetDisplayName(), "[filename]"); filenameField = new ui::Textbox(ui::Point(8, 25), ui::Point(Size.X-16, 16), save->GetDisplayName(), "[filename]");
filenameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; filenameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
filenameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; filenameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
AddComponent(filenameField); AddComponent(filenameField);
@ -53,9 +53,9 @@ LocalSaveActivity::LocalSaveActivity(SaveFile save, OnSaved onSaved_) :
AddComponent(okayButton); AddComponent(okayButton);
SetOkayButton(okayButton); SetOkayButton(okayButton);
if(save.GetGameSave()) if(save->GetGameSave())
{ {
thumbnailRenderer = new ThumbnailRendererTask(*save.GetGameSave(), Size - Vec2(16, 16), true, false); thumbnailRenderer = new ThumbnailRendererTask(*save->GetGameSave(), Size - Vec2(16, 16), true, false);
thumbnailRenderer->Start(); thumbnailRenderer->Start();
} }
} }
@ -82,8 +82,8 @@ void LocalSaveActivity::Save()
else if (filenameField->GetText().length()) else if (filenameField->GetText().length())
{ {
ByteString finalFilename = ByteString::Build(LOCAL_SAVE_DIR, PATH_SEP_CHAR, filenameField->GetText().ToUtf8(), ".cps"); ByteString finalFilename = ByteString::Build(LOCAL_SAVE_DIR, PATH_SEP_CHAR, filenameField->GetText().ToUtf8(), ".cps");
save.SetDisplayName(filenameField->GetText()); save->SetDisplayName(filenameField->GetText());
save.SetFileName(finalFilename); save->SetFileName(finalFilename);
if (Platform::FileExists(finalFilename)) if (Platform::FileExists(finalFilename))
{ {
new ConfirmPrompt("Overwrite file", "Are you sure you wish to overwrite\n"+finalFilename.FromUtf8(), { [this, finalFilename] { new ConfirmPrompt("Overwrite file", "Are you sure you wish to overwrite\n"+finalFilename.FromUtf8(), { [this, finalFilename] {
@ -104,15 +104,18 @@ void LocalSaveActivity::Save()
void LocalSaveActivity::saveWrite(ByteString finalFilename) void LocalSaveActivity::saveWrite(ByteString finalFilename)
{ {
Platform::MakeDirectory(LOCAL_SAVE_DIR); Platform::MakeDirectory(LOCAL_SAVE_DIR);
GameSave *gameSave = save.GetGameSave();
Json::Value localSaveInfo; Json::Value localSaveInfo;
localSaveInfo["type"] = "localsave"; localSaveInfo["type"] = "localsave";
localSaveInfo["username"] = Client::Ref().GetAuthUser().Username; localSaveInfo["username"] = Client::Ref().GetAuthUser().Username;
localSaveInfo["title"] = finalFilename; localSaveInfo["title"] = finalFilename;
localSaveInfo["date"] = (Json::Value::UInt64)time(NULL); localSaveInfo["date"] = (Json::Value::UInt64)time(NULL);
Client::Ref().SaveAuthorInfo(&localSaveInfo); Client::Ref().SaveAuthorInfo(&localSaveInfo);
gameSave->authors = localSaveInfo; {
auto [ fromNewerVersion, saveData ] = gameSave->Serialise(); auto gameSave = save->TakeGameSave();
gameSave->authors = localSaveInfo;
save->SetGameSave(std::move(gameSave));
}
auto [ fromNewerVersion, saveData ] = save->GetGameSave()->Serialise();
(void)fromNewerVersion; (void)fromNewerVersion;
if (saveData.size() == 0) if (saveData.size() == 0)
new ErrorMessage("Error", "Unable to serialize game data."); new ErrorMessage("Error", "Unable to serialize game data.");
@ -122,7 +125,7 @@ void LocalSaveActivity::saveWrite(ByteString finalFilename)
{ {
if (onSaved) if (onSaved)
{ {
onSaved(&save); onSaved(std::move(save));
} }
Exit(); Exit();
} }

View File

@ -22,19 +22,19 @@ class ThumbnailRendererTask;
class LocalSaveActivity: public WindowActivity class LocalSaveActivity: public WindowActivity
{ {
using OnSaved = std::function<void (SaveFile *)>; using OnSaved = std::function<void (std::unique_ptr<SaveFile>)>;
std::unique_ptr<PlaneAdapter<std::vector<pixel_rgba>>> saveToDiskImage = format::PixelsFromPNG( std::unique_ptr<PlaneAdapter<std::vector<pixel_rgba>>> saveToDiskImage = format::PixelsFromPNG(
std::vector<char>(save_local_png, save_local_png + save_local_png_size) std::vector<char>(save_local_png, save_local_png + save_local_png_size)
); );
SaveFile save; std::unique_ptr<SaveFile> save;
ThumbnailRendererTask *thumbnailRenderer; ThumbnailRendererTask *thumbnailRenderer;
std::unique_ptr<VideoBuffer> thumbnail; std::unique_ptr<VideoBuffer> thumbnail;
ui::Textbox * filenameField; ui::Textbox * filenameField;
OnSaved onSaved; OnSaved onSaved;
public: public:
LocalSaveActivity(SaveFile save, OnSaved onSaved = nullptr); LocalSaveActivity(std::unique_ptr<SaveFile> newSave, OnSaved onSaved = nullptr);
void saveWrite(ByteString finalFilename); void saveWrite(ByteString finalFilename);
void Save(); void Save();
void OnDraw() override; void OnDraw() override;

View File

@ -21,7 +21,7 @@
class SaveUploadTask: public Task class SaveUploadTask: public Task
{ {
SaveInfo save; SaveInfo &save;
void before() override void before() override
{ {
@ -40,22 +40,17 @@ class SaveUploadTask: public Task
} }
public: public:
SaveInfo GetSave() SaveUploadTask(SaveInfo &newSave):
{ save(newSave)
return save;
}
SaveUploadTask(SaveInfo save):
save(save)
{ {
} }
}; };
ServerSaveActivity::ServerSaveActivity(SaveInfo save, OnUploaded onUploaded_) : ServerSaveActivity::ServerSaveActivity(std::unique_ptr<SaveInfo> newSave, OnUploaded onUploaded_) :
WindowActivity(ui::Point(-1, -1), ui::Point(440, 200)), WindowActivity(ui::Point(-1, -1), ui::Point(440, 200)),
thumbnailRenderer(nullptr), thumbnailRenderer(nullptr),
save(save), save(std::move(newSave)),
onUploaded(onUploaded_), onUploaded(onUploaded_),
saveUploadTask(NULL) saveUploadTask(NULL)
{ {
@ -64,7 +59,7 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, OnUploaded onUploaded_) :
titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
AddComponent(titleLabel); AddComponent(titleLabel);
CheckName(save.GetName()); //set titleLabel text CheckName(save->GetName()); //set titleLabel text
ui::Label * previewLabel = new ui::Label(ui::Point((Size.X/2)+4, 5), ui::Point((Size.X/2)-8, 16), "Preview:"); ui::Label * previewLabel = new ui::Label(ui::Point((Size.X/2)+4, 5), ui::Point((Size.X/2)-8, 16), "Preview:");
previewLabel->SetTextColour(style::Colour::InformationTitle); previewLabel->SetTextColour(style::Colour::InformationTitle);
@ -72,14 +67,14 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, OnUploaded onUploaded_) :
previewLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; previewLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
AddComponent(previewLabel); AddComponent(previewLabel);
nameField = new ui::Textbox(ui::Point(8, 25), ui::Point((Size.X/2)-16, 16), save.GetName(), "[save name]"); nameField = new ui::Textbox(ui::Point(8, 25), ui::Point((Size.X/2)-16, 16), save->GetName(), "[save name]");
nameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; nameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
nameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; nameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
nameField->SetActionCallback({ [this] { CheckName(nameField->GetText()); } }); nameField->SetActionCallback({ [this] { CheckName(nameField->GetText()); } });
AddComponent(nameField); AddComponent(nameField);
FocusComponent(nameField); FocusComponent(nameField);
descriptionField = new ui::Textbox(ui::Point(8, 65), ui::Point((Size.X/2)-16, Size.Y-(65+16+4)), save.GetDescription(), "[save description]"); descriptionField = new ui::Textbox(ui::Point(8, 65), ui::Point((Size.X/2)-16, Size.Y-(65+16+4)), save->GetDescription(), "[save description]");
descriptionField->SetMultiline(true); descriptionField->SetMultiline(true);
descriptionField->SetLimit(254); descriptionField->SetLimit(254);
descriptionField->Appearance.VerticalAlign = ui::Appearance::AlignTop; descriptionField->Appearance.VerticalAlign = ui::Appearance::AlignTop;
@ -87,7 +82,7 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, OnUploaded onUploaded_) :
AddComponent(descriptionField); AddComponent(descriptionField);
publishedCheckbox = new ui::Checkbox(ui::Point(8, 45), ui::Point((Size.X/2)-80, 16), "Publish", ""); publishedCheckbox = new ui::Checkbox(ui::Point(8, 45), ui::Point((Size.X/2)-80, 16), "Publish", "");
if(Client::Ref().GetAuthUser().Username != save.GetUserName()) if(Client::Ref().GetAuthUser().Username != save->GetUserName())
{ {
//Save is not owned by the user, disable by default //Save is not owned by the user, disable by default
publishedCheckbox->SetChecked(false); publishedCheckbox->SetChecked(false);
@ -95,12 +90,12 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, OnUploaded onUploaded_) :
else else
{ {
//Save belongs to the current user, use published state already set //Save belongs to the current user, use published state already set
publishedCheckbox->SetChecked(save.GetPublished()); publishedCheckbox->SetChecked(save->GetPublished());
} }
AddComponent(publishedCheckbox); AddComponent(publishedCheckbox);
pausedCheckbox = new ui::Checkbox(ui::Point(160, 45), ui::Point(55, 16), "Paused", ""); pausedCheckbox = new ui::Checkbox(ui::Point(160, 45), ui::Point(55, 16), "Paused", "");
pausedCheckbox->SetChecked(save.GetGameSave()->paused); pausedCheckbox->SetChecked(save->GetGameSave()->paused);
AddComponent(pausedCheckbox); AddComponent(pausedCheckbox);
ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point((Size.X/2)-75, 16), "Cancel"); ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point((Size.X/2)-75, 16), "Cancel");
@ -141,17 +136,17 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, OnUploaded onUploaded_) :
} }); } });
AddComponent(RulesButton); AddComponent(RulesButton);
if (save.GetGameSave()) if (save->GetGameSave())
{ {
thumbnailRenderer = new ThumbnailRendererTask(*save.GetGameSave(), Size / 2 - Vec2(16, 16), false, true); thumbnailRenderer = new ThumbnailRendererTask(*save->GetGameSave(), Size / 2 - Vec2(16, 16), false, true);
thumbnailRenderer->Start(); thumbnailRenderer->Start();
} }
} }
ServerSaveActivity::ServerSaveActivity(SaveInfo save, bool saveNow, OnUploaded onUploaded_) : ServerSaveActivity::ServerSaveActivity(std::unique_ptr<SaveInfo> newSave, bool saveNow, OnUploaded onUploaded_) :
WindowActivity(ui::Point(-1, -1), ui::Point(200, 50)), WindowActivity(ui::Point(-1, -1), ui::Point(200, 50)),
thumbnailRenderer(nullptr), thumbnailRenderer(nullptr),
save(save), save(std::move(newSave)),
onUploaded(onUploaded_), onUploaded(onUploaded_),
saveUploadTask(NULL) saveUploadTask(NULL)
{ {
@ -163,7 +158,7 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, bool saveNow, OnUploaded o
AddAuthorInfo(); AddAuthorInfo();
saveUploadTask = new SaveUploadTask(this->save); saveUploadTask = new SaveUploadTask(*this->save);
saveUploadTask->AddTaskListener(this); saveUploadTask->AddTaskListener(this);
saveUploadTask->Start(); saveUploadTask->Start();
} }
@ -179,7 +174,7 @@ void ServerSaveActivity::NotifyDone(Task * task)
{ {
if (onUploaded) if (onUploaded)
{ {
onUploaded(save); onUploaded(std::move(save));
} }
Exit(); Exit();
} }
@ -189,9 +184,9 @@ void ServerSaveActivity::Save()
{ {
if(nameField->GetText().length()) if(nameField->GetText().length())
{ {
if(Client::Ref().GetAuthUser().Username != save.GetUserName() && publishedCheckbox->GetChecked()) if(Client::Ref().GetAuthUser().Username != save->GetUserName() && publishedCheckbox->GetChecked())
{ {
new ConfirmPrompt("Publish", "This save was created by " + save.GetUserName().FromUtf8() + ", you're about to publish this under your own name; If you haven't been given permission by the author to do so, please uncheck the publish box, otherwise continue", { [this] { new ConfirmPrompt("Publish", "This save was created by " + save->GetUserName().FromUtf8() + ", you're about to publish this under your own name; If you haven't been given permission by the author to do so, please uncheck the publish box, otherwise continue", { [this] {
Exit(); Exit();
saveUpload(); saveUpload();
} }); } });
@ -212,34 +207,42 @@ void ServerSaveActivity::AddAuthorInfo()
{ {
Json::Value serverSaveInfo; Json::Value serverSaveInfo;
serverSaveInfo["type"] = "save"; serverSaveInfo["type"] = "save";
serverSaveInfo["id"] = save.GetID(); serverSaveInfo["id"] = save->GetID();
serverSaveInfo["username"] = Client::Ref().GetAuthUser().Username; serverSaveInfo["username"] = Client::Ref().GetAuthUser().Username;
serverSaveInfo["title"] = save.GetName().ToUtf8(); serverSaveInfo["title"] = save->GetName().ToUtf8();
serverSaveInfo["description"] = save.GetDescription().ToUtf8(); serverSaveInfo["description"] = save->GetDescription().ToUtf8();
serverSaveInfo["published"] = (int)save.GetPublished(); serverSaveInfo["published"] = (int)save->GetPublished();
serverSaveInfo["date"] = (Json::Value::UInt64)time(NULL); serverSaveInfo["date"] = (Json::Value::UInt64)time(NULL);
Client::Ref().SaveAuthorInfo(&serverSaveInfo); Client::Ref().SaveAuthorInfo(&serverSaveInfo);
save.GetGameSave()->authors = serverSaveInfo; {
auto gameSave = save->TakeGameSave();
gameSave->authors = serverSaveInfo;
save->SetGameSave(std::move(gameSave));
}
} }
void ServerSaveActivity::saveUpload() void ServerSaveActivity::saveUpload()
{ {
save.SetName(nameField->GetText()); save->SetName(nameField->GetText());
save.SetDescription(descriptionField->GetText()); save->SetDescription(descriptionField->GetText());
save.SetPublished(publishedCheckbox->GetChecked()); save->SetPublished(publishedCheckbox->GetChecked());
save.SetUserName(Client::Ref().GetAuthUser().Username); save->SetUserName(Client::Ref().GetAuthUser().Username);
save.SetID(0); save->SetID(0);
save.GetGameSave()->paused = pausedCheckbox->GetChecked(); {
auto gameSave = save->TakeGameSave();
gameSave->paused = pausedCheckbox->GetChecked();
save->SetGameSave(std::move(gameSave));
}
AddAuthorInfo(); AddAuthorInfo();
if(Client::Ref().UploadSave(save) != RequestOkay) if(Client::Ref().UploadSave(*save) != RequestOkay)
{ {
new ErrorMessage("Error", "Upload failed with error:\n"+Client::Ref().GetLastError()); new ErrorMessage("Error", "Upload failed with error:\n"+Client::Ref().GetLastError());
} }
else if (onUploaded) else if (onUploaded)
{ {
new SaveIDMessage(save.GetID()); new SaveIDMessage(save->GetID());
onUploaded(save); onUploaded(std::move(save));
} }
} }
@ -343,7 +346,7 @@ void ServerSaveActivity::ShowRules()
void ServerSaveActivity::CheckName(String newname) void ServerSaveActivity::CheckName(String newname)
{ {
if (newname.length() && newname == save.GetName() && save.GetUserName() == Client::Ref().GetAuthUser().Username) if (newname.length() && newname == save->GetName() && save->GetUserName() == Client::Ref().GetAuthUser().Username)
titleLabel->SetText("Modify simulation properties:"); titleLabel->SetText("Modify simulation properties:");
else else
titleLabel->SetText("Upload new simulation:"); titleLabel->SetText("Upload new simulation:");

View File

@ -25,14 +25,14 @@ class Task;
class VideoBuffer; class VideoBuffer;
class ServerSaveActivity: public WindowActivity, public TaskListener class ServerSaveActivity: public WindowActivity, public TaskListener
{ {
using OnUploaded = std::function<void (SaveInfo &)>; using OnUploaded = std::function<void (std::unique_ptr<SaveInfo>)>;
std::unique_ptr<PlaneAdapter<std::vector<pixel_rgba>>> saveToServerImage = format::PixelsFromPNG( std::unique_ptr<PlaneAdapter<std::vector<pixel_rgba>>> saveToServerImage = format::PixelsFromPNG(
std::vector<char>(save_online_png, save_online_png + save_online_png_size) std::vector<char>(save_online_png, save_online_png + save_online_png_size)
); );
public: public:
ServerSaveActivity(SaveInfo save, OnUploaded onUploaded); ServerSaveActivity(std::unique_ptr<SaveInfo> newSave, OnUploaded onUploaded);
ServerSaveActivity(SaveInfo save, bool saveNow, OnUploaded onUploaded); ServerSaveActivity(std::unique_ptr<SaveInfo> newSave, bool saveNow, OnUploaded onUploaded);
void saveUpload(); void saveUpload();
void Save(); void Save();
virtual void Exit() override; virtual void Exit() override;
@ -47,7 +47,7 @@ protected:
void NotifyDone(Task * task) override; void NotifyDone(Task * task) override;
ThumbnailRendererTask *thumbnailRenderer; ThumbnailRendererTask *thumbnailRenderer;
std::unique_ptr<VideoBuffer> thumbnail; std::unique_ptr<VideoBuffer> thumbnail;
SaveInfo save; std::unique_ptr<SaveInfo> save;
private: private:
OnUploaded onUploaded; OnUploaded onUploaded;
protected: protected:

View File

@ -5,6 +5,8 @@
#include "SearchView.h" #include "SearchView.h"
#include "client/Client.h" #include "client/Client.h"
#include "client/SaveInfo.h"
#include "client/GameSave.h"
#include "common/platform/Platform.h" #include "common/platform/Platform.h"
#include "common/tpt-minmax.h" #include "common/tpt-minmax.h"
#include "graphics/Graphics.h" #include "graphics/Graphics.h"
@ -34,14 +36,14 @@ SearchController::SearchController(std::function<void ()> onDone_):
onDone = onDone_; onDone = onDone_;
} }
SaveInfo * SearchController::GetLoadedSave() const SaveInfo *SearchController::GetLoadedSave() const
{ {
return searchModel->GetLoadedSave(); return searchModel->GetLoadedSave();
} }
void SearchController::ReleaseLoadedSave() std::unique_ptr<SaveInfo> SearchController::TakeLoadedSave()
{ {
searchModel->SetLoadedSave(NULL); return searchModel->TakeLoadedSave();
} }
void SearchController::Update() void SearchController::Update()
@ -194,11 +196,11 @@ void SearchController::OpenSaveDone()
{ {
if (activePreview->GetDoOpen() && activePreview->GetSaveInfo()) if (activePreview->GetDoOpen() && activePreview->GetSaveInfo())
{ {
searchModel->SetLoadedSave(activePreview->GetSaveInfo()); searchModel->SetLoadedSave(activePreview->TakeSaveInfo());
} }
else else
{ {
searchModel->SetLoadedSave(NULL); searchModel->SetLoadedSave(nullptr);
} }
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "common/String.h" #include "common/String.h"
#include <functional> #include <functional>
#include <memory>
class SaveInfo; class SaveInfo;
class PreviewController; class PreviewController;
@ -48,6 +49,6 @@ public:
void RemoveSelected(); void RemoveSelected();
void UnpublishSelected(bool publish); void UnpublishSelected(bool publish);
void FavouriteSelected(); void FavouriteSelected();
void ReleaseLoadedSave(); const SaveInfo *GetLoadedSave() const;
SaveInfo * GetLoadedSave(); std::unique_ptr<SaveInfo> TakeLoadedSave();
}; };

View File

@ -2,13 +2,13 @@
#include "SearchView.h" #include "SearchView.h"
#include "Format.h" #include "Format.h"
#include "client/SaveInfo.h" #include "client/SaveInfo.h"
#include "client/GameSave.h"
#include "client/Client.h" #include "client/Client.h"
#include "common/tpt-minmax.h" #include "common/tpt-minmax.h"
#include <thread> #include <thread>
#include <cmath> #include <cmath>
SearchModel::SearchModel(): SearchModel::SearchModel():
loadedSave(NULL),
currentSort("best"), currentSort("best"),
currentPage(1), currentPage(1),
resultCount(0), resultCount(0),
@ -60,9 +60,9 @@ void SearchModel::BeginSearchSaves(int start, int count, String query, ByteStrin
searchSaves->Start(); searchSaves->Start();
} }
std::vector<SaveInfo *> SearchModel::EndSearchSaves() std::vector<std::unique_ptr<SaveInfo>> SearchModel::EndSearchSaves()
{ {
std::vector<SaveInfo *> saveArray; std::vector<std::unique_ptr<SaveInfo>> saveArray;
auto [ dataStatus, data ] = searchSaves->Finish(); auto [ dataStatus, data ] = searchSaves->Finish();
searchSaves.reset(); searchSaves.reset();
auto &client = Client::Ref(); auto &client = Client::Ref();
@ -88,10 +88,10 @@ std::vector<SaveInfo *> SearchModel::EndSearchSaves()
String tempName = ByteString(savesArray[j]["Name"].asString()).FromUtf8(); String tempName = ByteString(savesArray[j]["Name"].asString()).FromUtf8();
int tempVersion = savesArray[j]["Version"].asInt(); int tempVersion = savesArray[j]["Version"].asInt();
bool tempPublished = savesArray[j]["Published"].asBool(); bool tempPublished = savesArray[j]["Published"].asBool();
SaveInfo * tempSaveInfo = new SaveInfo(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp, tempScoreDown, tempUsername, tempName); auto tempSaveInfo = std::make_unique<SaveInfo>(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp, tempScoreDown, tempUsername, tempName);
tempSaveInfo->Version = tempVersion; tempSaveInfo->Version = tempVersion;
tempSaveInfo->SetPublished(tempPublished); tempSaveInfo->SetPublished(tempPublished);
saveArray.push_back(tempSaveInfo); saveArray.push_back(std::move(tempSaveInfo));
} }
} }
catch (std::exception &e) catch (std::exception &e)
@ -193,27 +193,28 @@ bool SearchModel::UpdateSaveList(int pageNumber, String query)
return false; return false;
} }
void SearchModel::SetLoadedSave(SaveInfo * save) void SearchModel::SetLoadedSave(std::unique_ptr<SaveInfo> save)
{ {
if(loadedSave != save && loadedSave) loadedSave = std::move(save);
delete loadedSave;
if(save)
{
loadedSave = new SaveInfo(*save);
}
else
{
loadedSave = NULL;
}
} }
SaveInfo * SearchModel::GetLoadedSave(){ const SaveInfo *SearchModel::GetLoadedSave() const
return loadedSave; {
return loadedSave.get();
} }
std::vector<SaveInfo*> SearchModel::GetSaveList() std::unique_ptr<SaveInfo> SearchModel::TakeLoadedSave()
{ {
return saveList; return std::move(loadedSave);
}
std::vector<SaveInfo *> SearchModel::GetSaveList() // non-owning
{
std::vector<SaveInfo *> nonOwningSaveList;
std::transform(saveList.begin(), saveList.end(), std::back_inserter(nonOwningSaveList), [](auto &ptr) {
return ptr.get();
});
return nonOwningSaveList;
} }
std::vector<std::pair<ByteString, int> > SearchModel::GetTagList() std::vector<std::pair<ByteString, int> > SearchModel::GetTagList()
@ -360,11 +361,6 @@ void SearchModel::notifySelectedChanged()
} }
} }
SearchModel::~SearchModel()
{
delete loadedSave;
}
int SearchModel::GetPageCount() int SearchModel::GetPageCount()
{ {
if (!showOwn && !showFavourite && currentSort == "best" && lastQuery == "") if (!showOwn && !showFavourite && currentSort == "best" && lastQuery == "")

View File

@ -13,19 +13,19 @@ class SearchModel
private: private:
std::unique_ptr<http::Request> searchSaves; std::unique_ptr<http::Request> searchSaves;
void BeginSearchSaves(int start, int count, String query, ByteString sort, ByteString category); void BeginSearchSaves(int start, int count, String query, ByteString sort, ByteString category);
std::vector<SaveInfo *> EndSearchSaves(); std::vector<std::unique_ptr<SaveInfo>> EndSearchSaves();
void BeginGetTags(int start, int count, String query); void BeginGetTags(int start, int count, String query);
std::vector<std::pair<ByteString, int>> EndGetTags(); std::vector<std::pair<ByteString, int>> EndGetTags();
std::unique_ptr<http::Request> getTags; std::unique_ptr<http::Request> getTags;
SaveInfo * loadedSave; std::unique_ptr<SaveInfo> loadedSave;
ByteString currentSort; ByteString currentSort;
String lastQuery; String lastQuery;
String lastError; String lastError;
std::vector<int> selected; std::vector<int> selected;
std::vector<SearchView*> observers; std::vector<SearchView*> observers;
std::vector<SaveInfo*> saveList; std::vector<std::unique_ptr<SaveInfo>> saveList;
std::vector<std::pair<ByteString, int> > tagList; std::vector<std::pair<ByteString, int> > tagList;
int currentPage; int currentPage;
int resultCount; int resultCount;
@ -44,13 +44,12 @@ private:
bool saveListLoaded = false; bool saveListLoaded = false;
public: public:
SearchModel(); SearchModel();
~SearchModel();
void SetShowTags(bool show); void SetShowTags(bool show);
bool GetShowTags(); bool GetShowTags();
void AddObserver(SearchView * observer); void AddObserver(SearchView * observer);
bool UpdateSaveList(int pageNumber, String query); bool UpdateSaveList(int pageNumber, String query);
std::vector<SaveInfo*> GetSaveList(); std::vector<SaveInfo *> GetSaveList(); // non-owning
std::vector<std::pair<ByteString, int> > GetTagList(); std::vector<std::pair<ByteString, int> > GetTagList();
String GetLastError() { return lastError; } String GetLastError() { return lastError; }
int GetPageCount(); int GetPageCount();
@ -62,8 +61,9 @@ public:
bool GetShowOwn() { return showOwn; } bool GetShowOwn() { return showOwn; }
void SetShowFavourite(bool show) { if(show!=showFavourite && !searchSaves) { showFavourite = show; } notifyShowFavouriteChanged(); } void SetShowFavourite(bool show) { if(show!=showFavourite && !searchSaves) { showFavourite = show; } notifyShowFavouriteChanged(); }
bool GetShowFavourite() { return showFavourite; } bool GetShowFavourite() { return showFavourite; }
void SetLoadedSave(SaveInfo * save); void SetLoadedSave(std::unique_ptr<SaveInfo> save);
SaveInfo * GetLoadedSave(); const SaveInfo *GetLoadedSave() const;
std::unique_ptr<SaveInfo> TakeLoadedSave();
bool GetSavesLoaded() { return saveListLoaded; } bool GetSavesLoaded() { return saveListLoaded; }
std::vector<int> GetSelected() { return selected; } std::vector<int> GetSelected() { return selected; }
void ClearSelected() { selected.clear(); notifySelectedChanged(); } void ClearSelected() { selected.clear(); notifySelectedChanged(); }

View File

@ -456,7 +456,7 @@ void SearchView::NotifySaveListChanged(SearchModel * sender)
int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 1; int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 1;
int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset; int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset;
std::vector<SaveInfo*> saves = sender->GetSaveList(); auto saves = sender->GetSaveList();
//string messageOfTheDay = sender->GetMessageOfTheDay(); //string messageOfTheDay = sender->GetMessageOfTheDay();
if(sender->GetShowFavourite()) if(sender->GetShowFavourite())

View File

@ -6,19 +6,13 @@
#include "client/Client.h" #include "client/Client.h"
#include "client/SaveInfo.h" #include "client/SaveInfo.h"
TagsModel::TagsModel(): void TagsModel::SetSave(SaveInfo *newSave /* non-owning */)
save(NULL)
{ {
this->save = newSave;
}
void TagsModel::SetSave(SaveInfo * save)
{
this->save = save;
notifyTagsChanged(); notifyTagsChanged();
} }
SaveInfo * TagsModel::GetSave() SaveInfo *TagsModel::GetSave() // non-owning
{ {
return save; return save;
} }
@ -72,7 +66,3 @@ void TagsModel::notifyTagsChanged()
observers[i]->NotifyTagsChanged(this); observers[i]->NotifyTagsChanged(this);
} }
} }
TagsModel::~TagsModel() {
}

View File

@ -6,15 +6,13 @@ class SaveInfo;
class TagsView; class TagsView;
class TagsModel { class TagsModel {
SaveInfo * save; SaveInfo *save = nullptr; // non-owning
std::vector<TagsView*> observers; std::vector<TagsView*> observers;
void notifyTagsChanged(); void notifyTagsChanged();
public: public:
TagsModel();
void AddObserver(TagsView * observer); void AddObserver(TagsView * observer);
void SetSave(SaveInfo * save); void SetSave(SaveInfo *newSave /* non-owning */);
void RemoveTag(ByteString tag); void RemoveTag(ByteString tag);
void AddTag(ByteString tag); void AddTag(ByteString tag);
SaveInfo * GetSave(); SaveInfo *GetSave(); // non-owning
virtual ~TagsModel();
}; };

View File

@ -1966,7 +1966,7 @@ int LuaScriptInterface::simulation_loadStamp(lua_State * l)
auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface); auto *luacon_ci = static_cast<LuaScriptInterface *>(commandInterface);
int i = -1; int i = -1;
int pushed = 1; int pushed = 1;
SaveFile * tempfile = NULL; std::unique_ptr<SaveFile> tempfile;
int x = luaL_optint(l,2,0); int x = luaL_optint(l,2,0);
int y = luaL_optint(l,3,0); int y = luaL_optint(l,3,0);
auto &client = Client::Ref(); auto &client = Client::Ref();
@ -1993,8 +1993,10 @@ int LuaScriptInterface::simulation_loadStamp(lua_State * l)
if (tempfile->GetGameSave()->authors.size()) if (tempfile->GetGameSave()->authors.size())
{ {
tempfile->GetGameSave()->authors["type"] = "luastamp"; auto gameSave = tempfile->TakeGameSave();
client.MergeStampAuthorInfo(tempfile->GetGameSave()->authors); gameSave->authors["type"] = "luastamp";
client.MergeStampAuthorInfo(gameSave->authors);
tempfile->SetGameSave(std::move(gameSave));
} }
} }
else else
@ -2003,7 +2005,6 @@ int LuaScriptInterface::simulation_loadStamp(lua_State * l)
lua_pushnil(l); lua_pushnil(l);
tpt_lua_pushString(l, luacon_ci->GetLastError()); tpt_lua_pushString(l, luacon_ci->GetLastError());
} }
delete tempfile;
} }
else else
{ {
@ -2060,7 +2061,7 @@ int LuaScriptInterface::simulation_reloadSave(lua_State * l)
int LuaScriptInterface::simulation_getSaveID(lua_State *l) int LuaScriptInterface::simulation_getSaveID(lua_State *l)
{ {
SaveInfo *tempSave = luacon_model->GetSave(); auto *tempSave = luacon_model->GetSave();
if (tempSave) if (tempSave)
{ {
lua_pushinteger(l, tempSave->GetID()); lua_pushinteger(l, tempSave->GetID());

View File

@ -20,7 +20,7 @@ void SaveRenderer::Flush(int begin, int end)
std::fill(ren->graphicscache + begin, ren->graphicscache + end, gcache_item()); std::fill(ren->graphicscache + begin, ren->graphicscache + end, gcache_item());
} }
std::unique_ptr<VideoBuffer> SaveRenderer::Render(GameSave * save, bool decorations, bool fire, Renderer *renderModeSource) std::unique_ptr<VideoBuffer> SaveRenderer::Render(const GameSave *save, bool decorations, bool fire, Renderer *renderModeSource)
{ {
std::lock_guard<std::mutex> gx(renderMutex); std::lock_guard<std::mutex> gx(renderMutex);

View File

@ -15,7 +15,7 @@ class SaveRenderer: public ExplicitSingleton<SaveRenderer> {
std::mutex renderMutex; std::mutex renderMutex;
public: public:
SaveRenderer(); SaveRenderer();
std::unique_ptr<VideoBuffer> Render(GameSave * save, bool decorations = true, bool fire = true, Renderer *renderModeSource = nullptr); std::unique_ptr<VideoBuffer> Render(const GameSave *save, bool decorations = true, bool fire = true, Renderer *renderModeSource = nullptr);
void Flush(int begin, int end); void Flush(int begin, int end);
virtual ~SaveRenderer(); virtual ~SaveRenderer();
}; };

View File

@ -343,12 +343,12 @@ int Simulation::Load(const GameSave * originalSave, bool includePressure, int fu
return 0; return 0;
} }
GameSave * Simulation::Save(bool includePressure) std::unique_ptr<GameSave> Simulation::Save(bool includePressure)
{ {
return Save(includePressure, 0, 0, XRES-1, YRES-1); return Save(includePressure, 0, 0, XRES-1, YRES-1);
} }
GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int fullX2, int fullY2) std::unique_ptr<GameSave> Simulation::Save(bool includePressure, int fullX, int fullY, int fullX2, int fullY2)
{ {
int blockX, blockY, blockX2, blockY2, blockW, blockH; int blockX, blockY, blockX2, blockY2, blockW, blockH;
//Normalise incoming coords //Normalise incoming coords
@ -376,7 +376,7 @@ GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int full
blockW = blockX2-blockX; blockW = blockX2-blockX;
blockH = blockY2-blockY; blockH = blockY2-blockY;
GameSave * newSave = new GameSave(blockW, blockH); auto newSave = std::make_unique<GameSave>(blockW, blockH);
auto &possiblyCarriesType = Particle::PossiblyCarriesType(); auto &possiblyCarriesType = Particle::PossiblyCarriesType();
auto &properties = Particle::GetProperties(); auto &properties = Particle::GetProperties();
newSave->frameCount = frameCount; newSave->frameCount = frameCount;
@ -507,25 +507,23 @@ GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int full
newSave->stkm.fanFigh.push_back(i); newSave->stkm.fanFigh.push_back(i);
} }
SaveSimOptions(newSave); SaveSimOptions(*newSave);
newSave->pmapbits = PMAPBITS; newSave->pmapbits = PMAPBITS;
return newSave; return newSave;
} }
void Simulation::SaveSimOptions(GameSave * gameSave) void Simulation::SaveSimOptions(GameSave &gameSave)
{ {
if (!gameSave) gameSave.gravityMode = gravityMode;
return; gameSave.customGravityX = customGravityX;
gameSave->gravityMode = gravityMode; gameSave.customGravityY = customGravityY;
gameSave->customGravityX = customGravityX; gameSave.airMode = air->airMode;
gameSave->customGravityY = customGravityY; gameSave.ambientAirTemp = air->ambientAirTemp;
gameSave->airMode = air->airMode; gameSave.edgeMode = edgeMode;
gameSave->ambientAirTemp = air->ambientAirTemp; gameSave.legacyEnable = legacy_enable;
gameSave->edgeMode = edgeMode; gameSave.waterEEnabled = water_equal_test;
gameSave->legacyEnable = legacy_enable; gameSave.gravityEnable = grav->IsEnabled();
gameSave->waterEEnabled = water_equal_test; gameSave.aheatEnable = aheat_enable;
gameSave->gravityEnable = grav->IsEnabled();
gameSave->aheatEnable = aheat_enable;
} }
bool Simulation::FloodFillPmapCheck(int x, int y, int type) bool Simulation::FloodFillPmapCheck(int x, int y, int type)

View File

@ -123,9 +123,9 @@ public:
int Load(const GameSave * save, bool includePressure); int Load(const GameSave * save, bool includePressure);
int Load(const GameSave * save, bool includePressure, int x, int y); int Load(const GameSave * save, bool includePressure, int x, int y);
GameSave * Save(bool includePressure); std::unique_ptr<GameSave> Save(bool includePressure);
GameSave * Save(bool includePressure, int x1, int y1, int x2, int y2); std::unique_ptr<GameSave> Save(bool includePressure, int x1, int y1, int x2, int y2);
void SaveSimOptions(GameSave * gameSave); void SaveSimOptions(GameSave &gameSave);
SimulationSample GetSample(int x, int y); SimulationSample GetSample(int x, int y);
std::unique_ptr<Snapshot> CreateSnapshot(); std::unique_ptr<Snapshot> CreateSnapshot();