From 8e5b0c760e08eaca84b77d2ce839d8c72c8eea4a Mon Sep 17 00:00:00 2001 From: jacob1 Date: Thu, 13 Jul 2017 00:19:35 -0400 Subject: [PATCH] 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 --- src/client/Client.cpp | 87 +++++++++++++++++- src/client/Client.h | 15 +++ src/client/GameSave.cpp | 106 ++++++++++++++++++---- src/client/GameSave.h | 9 +- src/client/requestbroker/ImageRequest.cpp | 16 ++-- src/gui/game/GameController.cpp | 37 +++++++- src/gui/game/GameModel.cpp | 56 +++++++++--- src/gui/game/GameView.cpp | 10 ++ src/gui/save/LocalSaveActivity.cpp | 13 ++- src/gui/save/ServerSaveActivity.cpp | 19 +++- src/gui/save/ServerSaveActivity.h | 1 + src/lua/LuaScriptInterface.cpp | 10 +- 12 files changed, 328 insertions(+), 51 deletions(-) diff --git a/src/client/Client.cpp b/src/client/Client.cpp index 13adeafdd..dc3cdb782 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -1011,13 +1011,14 @@ RequestStatus Client::UploadSave(SaveInfo & save) lastError = "Empty game save"; return RequestFailure; } + save.SetID(0); gameData = save.GetGameSave()->Serialise(gameDataLength); if (!gameData) { - lastError = "Cannot upload game save"; + lastError = "Cannot serialize game save"; return RequestFailure; } #ifdef SNAPSHOT @@ -1141,14 +1142,29 @@ std::string Client::AddStamp(GameSave * saveData) saveID << std::setw(8) << std::setfill('0') << std::hex << lastStampTime << 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); + + 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; char * gameData = saveData->Serialise(gameDataLength); + if (gameData == NULL) + return ""; 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.close(); @@ -1997,6 +2013,73 @@ std::list * Client::AddTag(int saveID, std::string tag) 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 // Recursively go down the json to get the setting we want diff --git a/src/client/Client.h b/src/client/Client.h index 6e92b563d..47928b86f 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -85,10 +85,25 @@ private: Json::Value preferences; 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); + + // Save stealing info + Json::Value authors; + public: std::vector 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(); Client(); diff --git a/src/client/GameSave.cpp b/src/client/GameSave.cpp index 95cd0b70a..4100c5666 100644 --- a/src/client/GameSave.cpp +++ b/src/client/GameSave.cpp @@ -52,6 +52,7 @@ originalData(save.originalData) blockHeight = save.blockHeight; } particlesCount = save.particlesCount; + authors = save.authors; } GameSave::GameSave(int width, int height) @@ -73,9 +74,6 @@ GameSave::GameSave(std::vector data) expanded = false; hasOriginalData = true; originalData = data; -#ifdef DEBUG - std::cout << "Creating Collapsed save from data" << std::endl; -#endif try { Expand(); @@ -99,9 +97,6 @@ GameSave::GameSave(std::vector data) expanded = false; hasOriginalData = true; originalData = std::vector(data.begin(), data.end()); -#ifdef DEBUG - std::cout << "Creating Collapsed save from data" << std::endl; -#endif try { Expand(); @@ -153,6 +148,7 @@ void GameSave::InitData() ambientHeat = NULL; fromNewerVersion = false; hasAmbientHeat = false; + authors.clear(); } 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)) { -#ifdef DEBUG - std::cout << "Reading PSv..." << std::endl; -#endif readPSv(data, dataSize); } else if(data[0] == 'O' && data[1] == 'P' && data[2] == 'S') { -#ifdef DEBUG - std::cout << "Reading OPS..." << std::endl; -#endif if (data[3] != '1') throw ParseException(ParseException::WrongVersion, "Save format from newer version"); readOPS(data, dataSize); @@ -257,6 +247,8 @@ std::vector GameSave::Serialise() { unsigned int dataSize; char * data = Serialise(dataSize); + if (data == NULL) + return std::vector(); std::vector dataVect(data, data+dataSize); delete[] data; return dataVect; @@ -644,6 +636,20 @@ void GameSave::readOPS(char * data, int dataLength) 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 @@ -1130,6 +1136,38 @@ fin: 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) { unsigned char * d = NULL, * c = (unsigned char *)data; @@ -2277,10 +2315,14 @@ char * GameSave::serialiseOPS(unsigned int & dataLength) } bson_append_finish_array(&b); } - bson_finish(&b); -#ifdef DEBUG - bson_print(&b); -#endif + if (authors.size()) + { + bson_append_start_object(&b, "authors"); + ConvertJsonToBson(&b, authors); + bson_append_finish_object(&b); + } + if (bson_finish(&b) == BSON_ERROR) + goto fin; finalData = (unsigned char *)bson_data(&b); finalDataLen = bson_size(&b); @@ -2335,6 +2377,38 @@ fin: 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 template void GameSave::Deallocate2DArray(T ***array, int blockHeight) diff --git a/src/client/GameSave.h b/src/client/GameSave.h index 87eb8cafa..be1fa23e4 100644 --- a/src/client/GameSave.h +++ b/src/client/GameSave.h @@ -7,6 +7,7 @@ #include "Misc.h" #include "bson/BSON.h" +#include "json/json.h" #include "simulation/Sign.h" #include "simulation/Particle.h" @@ -61,7 +62,10 @@ public: //Element palette typedef std::pair PaletteItem; std::vector palette; - + + // author information + Json::Value authors; + GameSave(); GameSave(GameSave & save); GameSave(int width, int height); @@ -112,7 +116,8 @@ private: void readOPS(char * data, int dataLength); void readPSv(char * data, int dataLength); char * serialiseOPS(unsigned int & dataSize); - //serialisePSv(); + void ConvertJsonToBson(bson *b, Json::Value j); + void ConvertBsonToJson(bson_iterator *b, Json::Value *j); }; #endif diff --git a/src/client/requestbroker/ImageRequest.cpp b/src/client/requestbroker/ImageRequest.cpp index dda94f878..d0745d7a7 100644 --- a/src/client/requestbroker/ImageRequest.cpp +++ b/src/client/requestbroker/ImageRequest.cpp @@ -29,9 +29,9 @@ RequestBroker::ProcessResponse ImageRequest::Process(RequestBroker & rb) if((*iter).first == URL) { image = (*iter).second; -#ifdef DEBUG +/*#ifdef DEBUG std::cout << typeid(*this).name() << " " << URL << " found in cache" << std::endl; -#endif +#endif*/ } } @@ -74,9 +74,9 @@ RequestBroker::ProcessResponse ImageRequest::Process(RequestBroker & rb) } else { - #ifdef DEBUG +#ifdef DEBUG std::cout << typeid(*this).name() << " Request for " << URL << " failed with status " << status << std::endl; - #endif +#endif free(data); return RequestBroker::Failed; @@ -93,9 +93,9 @@ RequestBroker::ProcessResponse ImageRequest::Process(RequestBroker & rb) ImageRequest * otherReq = (ImageRequest*)(*iter); if(otherReq->URL == URL && otherReq != this) { - #ifdef DEBUG +/*#ifdef DEBUG std::cout << typeid(*this).name() << " Request for " << URL << " found, appending." << std::endl; - #endif +#endif*/ //Add the current listener to the item already being requested (*iter)->Children.push_back(this); return RequestBroker::Duplicate; @@ -103,9 +103,9 @@ RequestBroker::ProcessResponse ImageRequest::Process(RequestBroker & rb) } //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; - #endif +#endif*/ HTTPContext = http_async_req_start(NULL, (char *)URL.c_str(), NULL, 0, 0); RequestTime = time(NULL); } diff --git a/src/gui/game/GameController.cpp b/src/gui/game/GameController.cpp index 5d2357cbc..574be86b8 100644 --- a/src/gui/game/GameController.cpp +++ b/src/gui/game/GameController.cpp @@ -319,11 +319,17 @@ sign * GameController::GetSignAt(int x, int y) void GameController::PlaceSave(ui::Point position) { - if (gameModel->GetPlaceSave()) + GameSave *placeSave = gameModel->GetPlaceSave(); + if (placeSave) { HistorySnapshot(); - gameModel->GetSimulation()->Load(position.X, position.Y, gameModel->GetPlaceSave()); - gameModel->SetPaused(gameModel->GetPlaceSave()->paused | gameModel->GetPaused()); + if (!gameModel->GetSimulation()->Load(position.X, position.Y, placeSave)) + { + 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) { 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 { @@ -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); 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(); gameModel->SetClipboard(newSave); } @@ -1203,9 +1219,20 @@ void GameController::OpenLocalSaveWindow(bool asCurrent) } 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); Client::Ref().MakeDirectory(LOCAL_SAVE_DIR); - if (Client::Ref().WriteFile(gameSave->Serialise(), gameModel->GetSaveFile()->GetName())) + std::vector 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."); else gameModel->SetInfoTip("Saved Successfully"); diff --git a/src/gui/game/GameModel.cpp b/src/gui/game/GameModel.cpp index ebf6f2ffb..b7b7f0646 100644 --- a/src/gui/game/GameModel.cpp +++ b/src/gui/game/GameModel.cpp @@ -1,25 +1,29 @@ -#include "gui/interface/Engine.h" #include "GameModel.h" #include "GameView.h" -#include "simulation/Simulation.h" -#include "simulation/Air.h" #include "ToolClasses.h" -#include "graphics/Renderer.h" -#include "gui/interface/Point.h" #include "Brush.h" #include "EllipseBrush.h" #include "TriangleBrush.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 "GameModelException.h" #include "Format.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(): clipboard(NULL), placeSave(NULL), @@ -581,9 +585,6 @@ int GameModel::GetActiveMenu() //Get an element tool from an element ID Tool * GameModel::GetElementTool(int elementID) { -#ifdef DEBUG - std::cout << elementID << std::endl; -#endif for(std::vector::iterator iter = elementTools.begin(), end = elementTools.end(); iter != end; ++iter) { if((*iter)->GetToolID() == elementID) @@ -647,7 +648,28 @@ void GameModel::SetSave(SaveInfo * newSave) sim->grav->stop_grav_async(); sim->clear_sim(); 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(); UpdateQuickOptions(); @@ -691,7 +713,10 @@ void GameModel::SetSaveFile(SaveFile * newSave) } sim->clear_sim(); ren->ClearAccumulation(); - sim->Load(saveData); + if (!sim->Load(saveData)) + { + Client::Ref().OverwriteAuthorInfo(saveData->authors); + } } notifySaveChanged(); @@ -968,6 +993,7 @@ void GameModel::ClearSimulation() sim->clear_sim(); ren->ClearAccumulation(); + Client::Ref().ClearAuthorInfo(); notifySaveChanged(); UpdateQuickOptions(); diff --git a/src/gui/game/GameView.cpp b/src/gui/game/GameView.cpp index fbd18c6e1..e640c8ed0 100644 --- a/src/gui/game/GameView.cpp +++ b/src/gui/game/GameView.cpp @@ -15,6 +15,7 @@ #include "gui/search/Thumbnail.h" #include "simulation/SaveRenderer.h" #include "simulation/SimulationData.h" +#include "gui/dialogues/InformationMessage.h" #include "gui/dialogues/ConfirmPrompt.h" #include "client/SaveFile.h" #include "Format.h" @@ -1487,6 +1488,15 @@ void GameView::OnKeyPress(int key, Uint16 character, bool shift, bool ctrl, bool case SDLK_F5: c->ReloadSim(); 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': if (ctrl) c->ReloadSim(); diff --git a/src/gui/save/LocalSaveActivity.cpp b/src/gui/save/LocalSaveActivity.cpp index 9fc82811b..657537051 100644 --- a/src/gui/save/LocalSaveActivity.cpp +++ b/src/gui/save/LocalSaveActivity.cpp @@ -110,7 +110,18 @@ void LocalSaveActivity::Save() void LocalSaveActivity::saveWrite(std::string finalFilename) { 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 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."); else { diff --git a/src/gui/save/ServerSaveActivity.cpp b/src/gui/save/ServerSaveActivity.cpp index 91dd37fbc..d25bb590e 100644 --- a/src/gui/save/ServerSaveActivity.cpp +++ b/src/gui/save/ServerSaveActivity.cpp @@ -199,7 +199,9 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, bool saveNow, ServerSaveAc titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; AddComponent(titleLabel); - saveUploadTask = new SaveUploadTask(save); + AddAuthorInfo(); + + saveUploadTask = new SaveUploadTask(this->save); saveUploadTask->AddTaskListener(this); 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() { save.SetName(nameField->GetText()); @@ -263,6 +279,7 @@ void ServerSaveActivity::saveUpload() save.SetUserName(Client::Ref().GetAuthUser().Username); save.SetID(0); save.GetGameSave()->paused = pausedCheckbox->GetChecked(); + AddAuthorInfo(); if(Client::Ref().UploadSave(save) != RequestOkay) { diff --git a/src/gui/save/ServerSaveActivity.h b/src/gui/save/ServerSaveActivity.h index e90c3a2d6..3e5fb1c84 100644 --- a/src/gui/save/ServerSaveActivity.h +++ b/src/gui/save/ServerSaveActivity.h @@ -37,6 +37,7 @@ public: virtual void OnTick(float dt); virtual ~ServerSaveActivity(); protected: + void AddAuthorInfo(); virtual void NotifyDone(Task * task); VideoBuffer * thumbnail; SaveInfo save; diff --git a/src/lua/LuaScriptInterface.cpp b/src/lua/LuaScriptInterface.cpp index 74ea8fd1b..6c8d48cd2 100644 --- a/src/lua/LuaScriptInterface.cpp +++ b/src/lua/LuaScriptInterface.cpp @@ -22,6 +22,7 @@ #include "gui/game/Tool.h" #include "LuaScriptHelper.h" #include "client/HTTP.h" +#include "client/GameSave.h" #include "client/SaveFile.h" #include "Misc.h" #include "Platform.h" @@ -1593,6 +1594,7 @@ int LuaScriptInterface::simulation_decoColor(lua_State * l) int LuaScriptInterface::simulation_clearSim(lua_State * l) { luacon_sim->clear_sim(); + Client::Ref().ClearAuthorInfo(); return 0; } @@ -1672,7 +1674,7 @@ int LuaScriptInterface::simulation_loadStamp(lua_State * l) const char * filename = luaL_optstring(l, 1, ""); 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); 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; lua_pushinteger(l, 1); + + if (tempfile->GetGameSave()->authors.size()) + { + tempfile->GetGameSave()->authors["type"] = "luastamp"; + Client::Ref().MergeStampAuthorInfo(tempfile->GetGameSave()->authors); + } } else lua_pushnil(l);