Add logging inside saves which remembers which other saves material was taken from

The logging is saved inside the bson data in all online saves, local saves, stamps, and clipboard pieces. It is loaded back when reloading each of those.

See #474 for the format of the data. It is the same format for the bson data. Note that "links" is an array of objects. It can be recursive.

There is some effort to not duplicate information, we don't care if you loaded a stamp 10 times or if you are using the clipboard in your own save. Extra information is mostly not saved for your own stuff, only when you take material from other saves.

Press ctrl+a in debug builds to show what info it is currently saving in Client. Also enabled in snapshots for now.

There is one unrelated change in here, which fixes a crash pointed out by QuanTech. It was also save related and it was too close to the other changes to separate it into another commit. It fixes a crash when saving signs with invalid unicode. the BSON library doesn't like this, it was returning an error but we ignored it, which caused a crash. It now notices those errors. I also had to update several Serialize calls to check if it actually returned save data, or else it then would have started crashing there instead.
Also some debug prints were removed
This commit is contained in:
jacob1
2017-07-13 00:19:35 -04:00
parent be6ac1d91e
commit 8e5b0c760e
12 changed files with 328 additions and 51 deletions

View File

@@ -1011,13 +1011,14 @@ RequestStatus Client::UploadSave(SaveInfo & save)
lastError = "Empty game save"; lastError = "Empty game save";
return RequestFailure; return RequestFailure;
} }
save.SetID(0); save.SetID(0);
gameData = save.GetGameSave()->Serialise(gameDataLength); gameData = save.GetGameSave()->Serialise(gameDataLength);
if (!gameData) if (!gameData)
{ {
lastError = "Cannot upload game save"; lastError = "Cannot serialize game save";
return RequestFailure; return RequestFailure;
} }
#ifdef SNAPSHOT #ifdef SNAPSHOT
@@ -1141,14 +1142,29 @@ std::string Client::AddStamp(GameSave * saveData)
saveID saveID
<< std::setw(8) << std::setfill('0') << std::hex << lastStampTime << std::setw(8) << std::setfill('0') << std::hex << lastStampTime
<< std::setw(2) << std::setfill('0') << std::hex << lastStampName; << std::setw(2) << std::setfill('0') << std::hex << lastStampName;
std::string filename = std::string(STAMPS_DIR PATH_SEP + saveID.str()+".stm").c_str();
MakeDirectory(STAMPS_DIR); MakeDirectory(STAMPS_DIR);
Json::Value stampInfo;
stampInfo["type"] = "stamp";
stampInfo["username"] = authUser.Username;
stampInfo["name"] = filename;
stampInfo["date"] = (Json::Value::UInt64)time(NULL);
if (authors.size() != 0)
{
// This is a stamp, always append full authorship info (even if same user)
stampInfo["links"].append(Client::Ref().authors);
}
saveData->authors = stampInfo;
unsigned int gameDataLength; unsigned int gameDataLength;
char * gameData = saveData->Serialise(gameDataLength); char * gameData = saveData->Serialise(gameDataLength);
if (gameData == NULL)
return "";
std::ofstream stampStream; std::ofstream stampStream;
stampStream.open(std::string(STAMPS_DIR PATH_SEP + saveID.str()+".stm").c_str(), std::ios::binary); stampStream.open(filename.c_str(), std::ios::binary);
stampStream.write((const char *)gameData, gameDataLength); stampStream.write((const char *)gameData, gameDataLength);
stampStream.close(); stampStream.close();
@@ -1997,6 +2013,73 @@ std::list<std::string> * Client::AddTag(int saveID, std::string tag)
return tags; return tags;
} }
// stamp-specific wrapper for MergeAuthorInfo
// also used for clipboard and lua stamps
void Client::MergeStampAuthorInfo(Json::Value stampAuthors)
{
if (stampAuthors.size())
{
// when loading stamp/clipboard, only append info to authorship info (since we aren't replacing the save)
// unless there is nothing loaded currently, then set authors directly
if (authors.size())
{
if (authors["username"] != stampAuthors["username"])
{
// Don't add if it's exactly the same
if (stampAuthors["links"].size() != 1 || stampAuthors["links"][0] != Client::Ref().authors)
{
// 2nd arg of MergeAuthorInfo needs to be an array
Json::Value toAdd;
toAdd.append(stampAuthors);
MergeAuthorInfo(toAdd);
}
}
else if (stampAuthors["links"].size())
{
MergeAuthorInfo(stampAuthors["links"]);
}
}
else
authors = stampAuthors;
}
}
// linksToAdd is an array (NOT an object) of links to add to authors["links"]
void Client::MergeAuthorInfo(Json::Value linksToAdd)
{
for (Json::Value::ArrayIndex i = 0; i < linksToAdd.size(); i++)
{
// link is the same exact json we have open, don't do anything
if (linksToAdd[i] == authors)
return;
bool hasLink = false;
for (Json::Value::ArrayIndex j = 0; j < authors["links"].size(); j++)
{
// check everything in authors["links"] to see if it's the same json as what we are already adding
if (authors["links"][j] == linksToAdd[i])
hasLink = true;
}
if (!hasLink)
authors["links"].append(linksToAdd[i]);
}
}
// load current authors information into a json value (when saving everything: stamps, clipboard, local saves, and online saves)
void Client::SaveAuthorInfo(Json::Value *saveInto)
{
if (authors.size() != 0)
{
// Different username? Save full original save info
if (authors["username"] != (*saveInto)["username"])
(*saveInto)["links"].append(authors);
// This is probalby the same save
// Don't append another layer of links, just keep existing links
else if (authors["links"].size())
(*saveInto)["links"] = authors["links"];
}
}
// powder.pref preference getting / setting functions // powder.pref preference getting / setting functions
// Recursively go down the json to get the setting we want // Recursively go down the json to get the setting we want

View File

@@ -85,10 +85,25 @@ private:
Json::Value preferences; Json::Value preferences;
Json::Value GetPref(Json::Value root, std::string prop, Json::Value defaultValue = Json::nullValue); Json::Value GetPref(Json::Value root, std::string prop, Json::Value defaultValue = Json::nullValue);
Json::Value SetPrefHelper(Json::Value root, std::string prop, Json::Value value); Json::Value SetPrefHelper(Json::Value root, std::string prop, Json::Value value);
// Save stealing info
Json::Value authors;
public: public:
std::vector<ClientListener*> listeners; std::vector<ClientListener*> listeners;
// Save stealing info
void MergeStampAuthorInfo(Json::Value linksToAdd);
void MergeAuthorInfo(Json::Value linksToAdd);
void OverwriteAuthorInfo(Json::Value overwrite) { authors = overwrite; }
void SaveAuthorInfo(Json::Value *saveInto);
void ClearAuthorInfo() { authors.clear(); }
bool IsAuthorsEmpty() { return authors.size() == 0; }
#if defined(DEBUG) || defined(SNAPSHOT)
std::string GetAuthorString() { return authors.toStyledString(); }
#endif
UpdateInfo GetUpdateInfo(); UpdateInfo GetUpdateInfo();
Client(); Client();

View File

@@ -52,6 +52,7 @@ originalData(save.originalData)
blockHeight = save.blockHeight; blockHeight = save.blockHeight;
} }
particlesCount = save.particlesCount; particlesCount = save.particlesCount;
authors = save.authors;
} }
GameSave::GameSave(int width, int height) GameSave::GameSave(int width, int height)
@@ -73,9 +74,6 @@ GameSave::GameSave(std::vector<char> data)
expanded = false; expanded = false;
hasOriginalData = true; hasOriginalData = true;
originalData = data; originalData = data;
#ifdef DEBUG
std::cout << "Creating Collapsed save from data" << std::endl;
#endif
try try
{ {
Expand(); Expand();
@@ -99,9 +97,6 @@ GameSave::GameSave(std::vector<unsigned char> data)
expanded = false; expanded = false;
hasOriginalData = true; hasOriginalData = true;
originalData = std::vector<char>(data.begin(), data.end()); originalData = std::vector<char>(data.begin(), data.end());
#ifdef DEBUG
std::cout << "Creating Collapsed save from data" << std::endl;
#endif
try try
{ {
Expand(); Expand();
@@ -153,6 +148,7 @@ void GameSave::InitData()
ambientHeat = NULL; ambientHeat = NULL;
fromNewerVersion = false; fromNewerVersion = false;
hasAmbientHeat = false; hasAmbientHeat = false;
authors.clear();
} }
void GameSave::InitVars() void GameSave::InitVars()
@@ -198,16 +194,10 @@ void GameSave::read(char * data, int dataSize)
{ {
if ((data[0]==0x66 && data[1]==0x75 && data[2]==0x43) || (data[0]==0x50 && data[1]==0x53 && data[2]==0x76)) if ((data[0]==0x66 && data[1]==0x75 && data[2]==0x43) || (data[0]==0x50 && data[1]==0x53 && data[2]==0x76))
{ {
#ifdef DEBUG
std::cout << "Reading PSv..." << std::endl;
#endif
readPSv(data, dataSize); readPSv(data, dataSize);
} }
else if(data[0] == 'O' && data[1] == 'P' && data[2] == 'S') else if(data[0] == 'O' && data[1] == 'P' && data[2] == 'S')
{ {
#ifdef DEBUG
std::cout << "Reading OPS..." << std::endl;
#endif
if (data[3] != '1') if (data[3] != '1')
throw ParseException(ParseException::WrongVersion, "Save format from newer version"); throw ParseException(ParseException::WrongVersion, "Save format from newer version");
readOPS(data, dataSize); readOPS(data, dataSize);
@@ -257,6 +247,8 @@ std::vector<char> GameSave::Serialise()
{ {
unsigned int dataSize; unsigned int dataSize;
char * data = Serialise(dataSize); char * data = Serialise(dataSize);
if (data == NULL)
return std::vector<char>();
std::vector<char> dataVect(data, data+dataSize); std::vector<char> dataVect(data, data+dataSize);
delete[] data; delete[] data;
return dataVect; return dataVect;
@@ -644,6 +636,20 @@ void GameSave::readOPS(char * data, int dataLength)
fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter)); fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
} }
} }
else if (!strcmp(bson_iterator_key(&iter), "authors"))
{
if (bson_iterator_type(&iter) == BSON_OBJECT)
{
// we need to clear authors because the save may be read multiple times in the stamp browser (loading and rendering twice)
// seems inefficient ...
authors.clear();
ConvertBsonToJson(&iter, &authors);
}
else
{
fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
}
}
} }
//Read wall and fan data //Read wall and fan data
@@ -1130,6 +1136,38 @@ fin:
free(partsSimIndex); free(partsSimIndex);
} }
void GameSave::ConvertBsonToJson(bson_iterator *iter, Json::Value *j)
{
bson_iterator subiter;
bson_iterator_subiterator(iter, &subiter);
while (bson_iterator_next(&subiter))
{
std::string key = bson_iterator_key(&subiter);
if (bson_iterator_type(&subiter) == BSON_STRING)
(*j)[key] = bson_iterator_string(&subiter);
else if (bson_iterator_type(&subiter) == BSON_BOOL)
(*j)[key] = bson_iterator_bool(&subiter);
else if (bson_iterator_type(&subiter) == BSON_INT)
(*j)[key] = bson_iterator_int(&subiter);
else if (bson_iterator_type(&subiter) == BSON_LONG)
(*j)[key] = (Json::Value::Int64)bson_iterator_long(&subiter);
else if (bson_iterator_type(&subiter) == BSON_ARRAY)
{
bson_iterator arrayiter;
bson_iterator_subiterator(&subiter, &arrayiter);
while (bson_iterator_next(&arrayiter))
{
if (bson_iterator_type(&arrayiter) == BSON_OBJECT && !strcmp(bson_iterator_key(&arrayiter), "part"))
{
Json::Value tempPart;
ConvertBsonToJson(&arrayiter, &tempPart);
(*j)["links"].append(tempPart);
}
}
}
}
}
void GameSave::readPSv(char * data, int dataLength) void GameSave::readPSv(char * data, int dataLength)
{ {
unsigned char * d = NULL, * c = (unsigned char *)data; unsigned char * d = NULL, * c = (unsigned char *)data;
@@ -2277,10 +2315,14 @@ char * GameSave::serialiseOPS(unsigned int & dataLength)
} }
bson_append_finish_array(&b); bson_append_finish_array(&b);
} }
bson_finish(&b); if (authors.size())
#ifdef DEBUG {
bson_print(&b); bson_append_start_object(&b, "authors");
#endif ConvertJsonToBson(&b, authors);
bson_append_finish_object(&b);
}
if (bson_finish(&b) == BSON_ERROR)
goto fin;
finalData = (unsigned char *)bson_data(&b); finalData = (unsigned char *)bson_data(&b);
finalDataLen = bson_size(&b); finalDataLen = bson_size(&b);
@@ -2335,6 +2377,38 @@ fin:
return (char*)outputData; return (char*)outputData;
} }
// converts a json object to bson
void GameSave::ConvertJsonToBson(bson *b, Json::Value j)
{
Json::Value::Members members = j.getMemberNames();
for (Json::Value::Members::iterator iter = members.begin(), end = members.end(); iter != end; ++iter)
{
std::string member = *iter;
if (j[member].isString())
bson_append_string(b, member.c_str(), j[member].asCString());
else if (j[member].isBool())
bson_append_bool(b, member.c_str(), j[member].asBool());
else if (j[member].isInt() || j[member].isUInt())
bson_append_int(b, member.c_str(), j[member].asInt());
else if (j[member].isInt64() || j[member].isUInt64())
bson_append_long(b, member.c_str(), j[member].asInt64());
else if (j[member].isArray())
{
bson_append_start_array(b, member.c_str());
for (Json::Value::ArrayIndex i = 0; i < j[member].size(); i++)
{
// only supports objects here because that is all we need
if (!j[member][i].isObject())
continue;
bson_append_start_object(b, "part");
ConvertJsonToBson(b, j[member][i]);
bson_append_finish_object(b);
}
bson_append_finish_array(b);
}
}
}
// deallocates a pointer to a 2D array and sets it to NULL // deallocates a pointer to a 2D array and sets it to NULL
template <typename T> template <typename T>
void GameSave::Deallocate2DArray(T ***array, int blockHeight) void GameSave::Deallocate2DArray(T ***array, int blockHeight)

View File

@@ -7,6 +7,7 @@
#include "Misc.h" #include "Misc.h"
#include "bson/BSON.h" #include "bson/BSON.h"
#include "json/json.h"
#include "simulation/Sign.h" #include "simulation/Sign.h"
#include "simulation/Particle.h" #include "simulation/Particle.h"
@@ -61,7 +62,10 @@ public:
//Element palette //Element palette
typedef std::pair<std::string, int> PaletteItem; typedef std::pair<std::string, int> PaletteItem;
std::vector<PaletteItem> palette; std::vector<PaletteItem> palette;
// author information
Json::Value authors;
GameSave(); GameSave();
GameSave(GameSave & save); GameSave(GameSave & save);
GameSave(int width, int height); GameSave(int width, int height);
@@ -112,7 +116,8 @@ private:
void readOPS(char * data, int dataLength); void readOPS(char * data, int dataLength);
void readPSv(char * data, int dataLength); void readPSv(char * data, int dataLength);
char * serialiseOPS(unsigned int & dataSize); char * serialiseOPS(unsigned int & dataSize);
//serialisePSv(); void ConvertJsonToBson(bson *b, Json::Value j);
void ConvertBsonToJson(bson_iterator *b, Json::Value *j);
}; };
#endif #endif

View File

@@ -29,9 +29,9 @@ RequestBroker::ProcessResponse ImageRequest::Process(RequestBroker & rb)
if((*iter).first == URL) if((*iter).first == URL)
{ {
image = (*iter).second; image = (*iter).second;
#ifdef DEBUG /*#ifdef DEBUG
std::cout << typeid(*this).name() << " " << URL << " found in cache" << std::endl; std::cout << typeid(*this).name() << " " << URL << " found in cache" << std::endl;
#endif #endif*/
} }
} }
@@ -74,9 +74,9 @@ RequestBroker::ProcessResponse ImageRequest::Process(RequestBroker & rb)
} }
else else
{ {
#ifdef DEBUG #ifdef DEBUG
std::cout << typeid(*this).name() << " Request for " << URL << " failed with status " << status << std::endl; std::cout << typeid(*this).name() << " Request for " << URL << " failed with status " << status << std::endl;
#endif #endif
free(data); free(data);
return RequestBroker::Failed; return RequestBroker::Failed;
@@ -93,9 +93,9 @@ RequestBroker::ProcessResponse ImageRequest::Process(RequestBroker & rb)
ImageRequest * otherReq = (ImageRequest*)(*iter); ImageRequest * otherReq = (ImageRequest*)(*iter);
if(otherReq->URL == URL && otherReq != this) if(otherReq->URL == URL && otherReq != this)
{ {
#ifdef DEBUG /*#ifdef DEBUG
std::cout << typeid(*this).name() << " Request for " << URL << " found, appending." << std::endl; std::cout << typeid(*this).name() << " Request for " << URL << " found, appending." << std::endl;
#endif #endif*/
//Add the current listener to the item already being requested //Add the current listener to the item already being requested
(*iter)->Children.push_back(this); (*iter)->Children.push_back(this);
return RequestBroker::Duplicate; return RequestBroker::Duplicate;
@@ -103,9 +103,9 @@ RequestBroker::ProcessResponse ImageRequest::Process(RequestBroker & rb)
} }
//If it's not already being requested, request it //If it's not already being requested, request it
#ifdef DEBUG /*#ifdef DEBUG
std::cout << typeid(*this).name() << " Creating new request for " << URL << std::endl; std::cout << typeid(*this).name() << " Creating new request for " << URL << std::endl;
#endif #endif*/
HTTPContext = http_async_req_start(NULL, (char *)URL.c_str(), NULL, 0, 0); HTTPContext = http_async_req_start(NULL, (char *)URL.c_str(), NULL, 0, 0);
RequestTime = time(NULL); RequestTime = time(NULL);
} }

View File

@@ -319,11 +319,17 @@ sign * GameController::GetSignAt(int x, int y)
void GameController::PlaceSave(ui::Point position) void GameController::PlaceSave(ui::Point position)
{ {
if (gameModel->GetPlaceSave()) GameSave *placeSave = gameModel->GetPlaceSave();
if (placeSave)
{ {
HistorySnapshot(); HistorySnapshot();
gameModel->GetSimulation()->Load(position.X, position.Y, gameModel->GetPlaceSave()); if (!gameModel->GetSimulation()->Load(position.X, position.Y, placeSave))
gameModel->SetPaused(gameModel->GetPlaceSave()->paused | gameModel->GetPaused()); {
gameModel->SetPaused(placeSave->paused | gameModel->GetPaused());
// if this is a clipboard and there is no author info, don't do anything
if (Client::Ref().IsAuthorsEmpty() || placeSave->authors["type"] != "clipboard")
Client::Ref().MergeStampAuthorInfo(placeSave->authors);
}
} }
} }
@@ -560,7 +566,10 @@ std::string GameController::StampRegion(ui::Point point1, ui::Point point2)
if(newSave) if(newSave)
{ {
newSave->paused = gameModel->GetPaused(); newSave->paused = gameModel->GetPaused();
return Client::Ref().AddStamp(newSave); std::string stampName = Client::Ref().AddStamp(newSave);
if (stampName.length() == 0)
new ErrorMessage("Could not create stamp", "Error serializing save file");
return stampName;
} }
else else
{ {
@@ -575,6 +584,13 @@ void GameController::CopyRegion(ui::Point point1, ui::Point point2)
newSave = gameModel->GetSimulation()->Save(point1.X, point1.Y, point2.X, point2.Y); newSave = gameModel->GetSimulation()->Save(point1.X, point1.Y, point2.X, point2.Y);
if(newSave) if(newSave)
{ {
Json::Value clipboardInfo;
clipboardInfo["type"] = "clipboard";
clipboardInfo["username"] = Client::Ref().GetAuthUser().Username;
clipboardInfo["date"] = (Json::Value::UInt64)time(NULL);
Client::Ref().SaveAuthorInfo(&clipboardInfo);
newSave->authors = clipboardInfo;
newSave->paused = gameModel->GetPaused(); newSave->paused = gameModel->GetPaused();
gameModel->SetClipboard(newSave); gameModel->SetClipboard(newSave);
} }
@@ -1203,9 +1219,20 @@ void GameController::OpenLocalSaveWindow(bool asCurrent)
} }
else if (gameModel->GetSaveFile()) else if (gameModel->GetSaveFile())
{ {
Json::Value localSaveInfo;
localSaveInfo["type"] = "localsave";
localSaveInfo["username"] = Client::Ref().GetAuthUser().Username;
localSaveInfo["title"] = gameModel->GetSaveFile()->GetName();
localSaveInfo["date"] = (Json::Value::UInt64)time(NULL);
Client::Ref().SaveAuthorInfo(&localSaveInfo);
gameSave->authors = localSaveInfo;
gameModel->SetSaveFile(&tempSave); gameModel->SetSaveFile(&tempSave);
Client::Ref().MakeDirectory(LOCAL_SAVE_DIR); Client::Ref().MakeDirectory(LOCAL_SAVE_DIR);
if (Client::Ref().WriteFile(gameSave->Serialise(), gameModel->GetSaveFile()->GetName())) std::vector<char> saveData = gameSave->Serialise();
if (saveData.size() == 0)
new ErrorMessage("Error", "Unable to serialize game data.");
else if (Client::Ref().WriteFile(gameSave->Serialise(), gameModel->GetSaveFile()->GetName()))
new ErrorMessage("Error", "Unable to write save file."); new ErrorMessage("Error", "Unable to write save file.");
else else
gameModel->SetInfoTip("Saved Successfully"); gameModel->SetInfoTip("Saved Successfully");

View File

@@ -1,25 +1,29 @@
#include "gui/interface/Engine.h"
#include "GameModel.h" #include "GameModel.h"
#include "GameView.h" #include "GameView.h"
#include "simulation/Simulation.h"
#include "simulation/Air.h"
#include "ToolClasses.h" #include "ToolClasses.h"
#include "graphics/Renderer.h"
#include "gui/interface/Point.h"
#include "Brush.h" #include "Brush.h"
#include "EllipseBrush.h" #include "EllipseBrush.h"
#include "TriangleBrush.h" #include "TriangleBrush.h"
#include "BitmapBrush.h" #include "BitmapBrush.h"
#include "client/Client.h"
#include "client/GameSave.h"
#include "client/SaveFile.h"
#include "common/tpt-minmax.h"
#include "gui/game/DecorationTool.h"
#include "QuickOptions.h" #include "QuickOptions.h"
#include "GameModelException.h" #include "GameModelException.h"
#include "Format.h" #include "Format.h"
#include "Favorite.h" #include "Favorite.h"
#include "client/Client.h"
#include "client/GameSave.h"
#include "client/SaveFile.h"
#include "common/tpt-minmax.h"
#include "graphics/Renderer.h"
#include "simulation/Air.h"
#include "simulation/Simulation.h"
#include "simulation/Snapshot.h"
#include "gui/game/DecorationTool.h"
#include "gui/interface/Engine.h"
#include "gui/interface/Point.h"
GameModel::GameModel(): GameModel::GameModel():
clipboard(NULL), clipboard(NULL),
placeSave(NULL), placeSave(NULL),
@@ -581,9 +585,6 @@ int GameModel::GetActiveMenu()
//Get an element tool from an element ID //Get an element tool from an element ID
Tool * GameModel::GetElementTool(int elementID) Tool * GameModel::GetElementTool(int elementID)
{ {
#ifdef DEBUG
std::cout << elementID << std::endl;
#endif
for(std::vector<Tool*>::iterator iter = elementTools.begin(), end = elementTools.end(); iter != end; ++iter) for(std::vector<Tool*>::iterator iter = elementTools.begin(), end = elementTools.end(); iter != end; ++iter)
{ {
if((*iter)->GetToolID() == elementID) if((*iter)->GetToolID() == elementID)
@@ -647,7 +648,28 @@ void GameModel::SetSave(SaveInfo * newSave)
sim->grav->stop_grav_async(); sim->grav->stop_grav_async();
sim->clear_sim(); sim->clear_sim();
ren->ClearAccumulation(); ren->ClearAccumulation();
sim->Load(saveData); if (!sim->Load(saveData))
{
// This save was created before logging existed
// Add in the correct info
if (saveData->authors.size() == 0)
{
saveData->authors["type"] = "save";
saveData->authors["id"] = newSave->id;
saveData->authors["username"] = newSave->userName;
saveData->authors["title"] = newSave->name;
saveData->authors["description"] = newSave->Description;
saveData->authors["published"] = newSave->Published;
saveData->authors["date"] = newSave->updatedDate;
}
// This save was probably just created, and we didn't know the ID when creating it
// Update with the proper ID
else if (saveData->authors.get("id", -1) == 0)
{
saveData->authors["id"] = newSave->id;
}
Client::Ref().OverwriteAuthorInfo(saveData->authors);
}
} }
notifySaveChanged(); notifySaveChanged();
UpdateQuickOptions(); UpdateQuickOptions();
@@ -691,7 +713,10 @@ void GameModel::SetSaveFile(SaveFile * newSave)
} }
sim->clear_sim(); sim->clear_sim();
ren->ClearAccumulation(); ren->ClearAccumulation();
sim->Load(saveData); if (!sim->Load(saveData))
{
Client::Ref().OverwriteAuthorInfo(saveData->authors);
}
} }
notifySaveChanged(); notifySaveChanged();
@@ -968,6 +993,7 @@ void GameModel::ClearSimulation()
sim->clear_sim(); sim->clear_sim();
ren->ClearAccumulation(); ren->ClearAccumulation();
Client::Ref().ClearAuthorInfo();
notifySaveChanged(); notifySaveChanged();
UpdateQuickOptions(); UpdateQuickOptions();

View File

@@ -15,6 +15,7 @@
#include "gui/search/Thumbnail.h" #include "gui/search/Thumbnail.h"
#include "simulation/SaveRenderer.h" #include "simulation/SaveRenderer.h"
#include "simulation/SimulationData.h" #include "simulation/SimulationData.h"
#include "gui/dialogues/InformationMessage.h"
#include "gui/dialogues/ConfirmPrompt.h" #include "gui/dialogues/ConfirmPrompt.h"
#include "client/SaveFile.h" #include "client/SaveFile.h"
#include "Format.h" #include "Format.h"
@@ -1487,6 +1488,15 @@ void GameView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool
case SDLK_F5: case SDLK_F5:
c->ReloadSim(); c->ReloadSim();
break; break;
#if defined(DEBUG) || defined(SNAPSHOT)
case 'a':
if (ctrl)
{
std::string authorString = Client::Ref().GetAuthorString();
new InformationMessage("Save authorship info", authorString, true);
}
break;
#endif
case 'r': case 'r':
if (ctrl) if (ctrl)
c->ReloadSim(); c->ReloadSim();

View File

@@ -110,7 +110,18 @@ void LocalSaveActivity::Save()
void LocalSaveActivity::saveWrite(std::string finalFilename) void LocalSaveActivity::saveWrite(std::string finalFilename)
{ {
Client::Ref().MakeDirectory(LOCAL_SAVE_DIR); Client::Ref().MakeDirectory(LOCAL_SAVE_DIR);
if (Client::Ref().WriteFile(save.GetGameSave()->Serialise(), finalFilename)) GameSave *gameSave = save.GetGameSave();
Json::Value localSaveInfo;
localSaveInfo["type"] = "localsave";
localSaveInfo["username"] = Client::Ref().GetAuthUser().Username;
localSaveInfo["title"] = finalFilename;
localSaveInfo["date"] = (Json::Value::UInt64)time(NULL);
Client::Ref().SaveAuthorInfo(&localSaveInfo);
gameSave->authors = localSaveInfo;
std::vector<char> saveData = gameSave->Serialise();
if (saveData.size() == 0)
new ErrorMessage("Error", "Unable to serialize game data.");
else if (Client::Ref().WriteFile(gameSave->Serialise(), finalFilename))
new ErrorMessage("Error", "Unable to write save file."); new ErrorMessage("Error", "Unable to write save file.");
else else
{ {

View File

@@ -199,7 +199,9 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, bool saveNow, ServerSaveAc
titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
AddComponent(titleLabel); AddComponent(titleLabel);
saveUploadTask = new SaveUploadTask(save); AddAuthorInfo();
saveUploadTask = new SaveUploadTask(this->save);
saveUploadTask->AddTaskListener(this); saveUploadTask->AddTaskListener(this);
saveUploadTask->Start(); saveUploadTask->Start();
} }
@@ -255,6 +257,20 @@ void ServerSaveActivity::Save()
} }
} }
void ServerSaveActivity::AddAuthorInfo()
{
Json::Value serverSaveInfo;
serverSaveInfo["type"] = "save";
serverSaveInfo["id"] = save.GetID();
serverSaveInfo["username"] = Client::Ref().GetAuthUser().Username;
serverSaveInfo["title"] = save.GetName();
serverSaveInfo["description"] = save.GetDescription();
serverSaveInfo["published"] = save.GetPublished();
serverSaveInfo["date"] = (Json::Value::UInt64)time(NULL);
Client::Ref().SaveAuthorInfo(&serverSaveInfo);
save.GetGameSave()->authors = serverSaveInfo;
}
void ServerSaveActivity::saveUpload() void ServerSaveActivity::saveUpload()
{ {
save.SetName(nameField->GetText()); save.SetName(nameField->GetText());
@@ -263,6 +279,7 @@ void ServerSaveActivity::saveUpload()
save.SetUserName(Client::Ref().GetAuthUser().Username); save.SetUserName(Client::Ref().GetAuthUser().Username);
save.SetID(0); save.SetID(0);
save.GetGameSave()->paused = pausedCheckbox->GetChecked(); save.GetGameSave()->paused = pausedCheckbox->GetChecked();
AddAuthorInfo();
if(Client::Ref().UploadSave(save) != RequestOkay) if(Client::Ref().UploadSave(save) != RequestOkay)
{ {

View File

@@ -37,6 +37,7 @@ public:
virtual void OnTick(float dt); virtual void OnTick(float dt);
virtual ~ServerSaveActivity(); virtual ~ServerSaveActivity();
protected: protected:
void AddAuthorInfo();
virtual void NotifyDone(Task * task); virtual void NotifyDone(Task * task);
VideoBuffer * thumbnail; VideoBuffer * thumbnail;
SaveInfo save; SaveInfo save;

View File

@@ -22,6 +22,7 @@
#include "gui/game/Tool.h" #include "gui/game/Tool.h"
#include "LuaScriptHelper.h" #include "LuaScriptHelper.h"
#include "client/HTTP.h" #include "client/HTTP.h"
#include "client/GameSave.h"
#include "client/SaveFile.h" #include "client/SaveFile.h"
#include "Misc.h" #include "Misc.h"
#include "Platform.h" #include "Platform.h"
@@ -1593,6 +1594,7 @@ int LuaScriptInterface::simulation_decoColor(lua_State * l)
int LuaScriptInterface::simulation_clearSim(lua_State * l) int LuaScriptInterface::simulation_clearSim(lua_State * l)
{ {
luacon_sim->clear_sim(); luacon_sim->clear_sim();
Client::Ref().ClearAuthorInfo();
return 0; return 0;
} }
@@ -1672,7 +1674,7 @@ int LuaScriptInterface::simulation_loadStamp(lua_State * l)
const char * filename = luaL_optstring(l, 1, ""); const char * filename = luaL_optstring(l, 1, "");
tempfile = Client::Ref().GetStamp(filename); tempfile = Client::Ref().GetStamp(filename);
} }
if (!tempfile && lua_isnumber(l, 1)) //Load from stamp ID if ((!tempfile || !tempfile->GetGameSave()) && lua_isnumber(l, 1)) //Load from stamp ID
{ {
i = luaL_optint(l, 1, 0); i = luaL_optint(l, 1, 0);
int stampCount = Client::Ref().GetStampsCount(); int stampCount = Client::Ref().GetStampsCount();
@@ -1687,6 +1689,12 @@ int LuaScriptInterface::simulation_loadStamp(lua_State * l)
{ {
//luacon_sim->sys_pause = (tempfile->GetGameSave()->paused | luacon_model->GetPaused())?1:0; //luacon_sim->sys_pause = (tempfile->GetGameSave()->paused | luacon_model->GetPaused())?1:0;
lua_pushinteger(l, 1); lua_pushinteger(l, 1);
if (tempfile->GetGameSave()->authors.size())
{
tempfile->GetGameSave()->authors["type"] = "luastamp";
Client::Ref().MergeStampAuthorInfo(tempfile->GetGameSave()->authors);
}
} }
else else
lua_pushnil(l); lua_pushnil(l);