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";
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<std::string> * 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

View File

@ -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<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();
Client();

View File

@ -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<char> 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<unsigned char> data)
expanded = false;
hasOriginalData = true;
originalData = std::vector<char>(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<char> GameSave::Serialise()
{
unsigned int dataSize;
char * data = Serialise(dataSize);
if (data == NULL)
return std::vector<char>();
std::vector<char> 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 <typename T>
void GameSave::Deallocate2DArray(T ***array, int blockHeight)

View File

@ -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<std::string, int> PaletteItem;
std::vector<PaletteItem> 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

View File

@ -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);
}

View File

@ -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<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.");
else
gameModel->SetInfoTip("Saved Successfully");

View File

@ -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<Tool*>::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();

View File

@ -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();

View File

@ -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<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.");
else
{

View File

@ -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)
{

View File

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

View File

@ -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);