Permalink
Browse files

"Save from a newer version" is now just a warning (OPS format never c…

…hanges)

Also, actual save errors now prevent you from clicking "Open" (which allowed you to vote and do other stuff even though the save was never loaded)
  • Loading branch information...
jacob1 committed Oct 2, 2015
1 parent 9c44fc6 commit b184c78cff8c2b315fac47269edcfd48a7223f31
@@ -51,6 +51,7 @@ originalData(save.originalData)
blockHeight = save.blockHeight;
}
particlesCount = save.particlesCount;
fromNewerVersion = false;
}
GameSave::GameSave(int width, int height)
@@ -63,6 +64,7 @@ GameSave::GameSave(int width, int height)
fanVelYPtr = NULL;
particles = NULL;
fromNewerVersion = false;
hasOriginalData = false;
expanded = true;
setSize(width, height);
@@ -81,6 +83,7 @@ GameSave::GameSave(std::vector<char> data)
fanVelYPtr = NULL;
particles = NULL;
fromNewerVersion = false;
expanded = false;
hasOriginalData = true;
originalData = data;
@@ -113,6 +116,7 @@ GameSave::GameSave(std::vector<unsigned char> data)
fanVelYPtr = NULL;
particles = NULL;
fromNewerVersion = false;
expanded = false;
hasOriginalData = true;
originalData = std::vector<char>(data.begin(), data.end());
@@ -145,6 +149,7 @@ GameSave::GameSave(char * data, int dataSize)
fanVelYPtr = NULL;
particles = NULL;
fromNewerVersion = false;
expanded = false;
hasOriginalData = true;
originalData = std::vector<char>(data, data+dataSize);
@@ -246,6 +251,8 @@ void GameSave::read(char * data, int dataSize)
#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);
}
else
@@ -456,15 +463,16 @@ void GameSave::readOPS(char * data, int dataLength)
fullH = blockH*CELL;
//From newer version
if(savedVersion > SAVE_VERSION)
throw ParseException(ParseException::WrongVersion, "Save from newer version");
if (savedVersion > SAVE_VERSION)
fromNewerVersion = true;
//throw ParseException(ParseException::WrongVersion, "Save from newer version");
//Incompatible cell size
if(inputData[5] > CELL)
if (inputData[5] > CELL)
throw ParseException(ParseException::InvalidDimensions, "Incorrect CELL size");
//Too large/off screen
if(blockX+blockW > XRES/CELL || blockY+blockH > YRES/CELL)
if (blockX+blockW > XRES/CELL || blockY+blockH > YRES/CELL)
throw ParseException(ParseException::InvalidDimensions, "Save too large");
setSize(blockW, blockH);
@@ -476,11 +484,11 @@ void GameSave::readOPS(char * data, int dataLength)
//Check for overflows, don't load saves larger than 200MB
unsigned int toAlloc = bsonDataLen+1;
if(toAlloc > 209715200 || !toAlloc)
if (toAlloc > 209715200 || !toAlloc)
throw ParseException(ParseException::InvalidDimensions, "Save data too large, refusing");
bsonData = (unsigned char*)malloc(toAlloc);
if(!bsonData)
if (!bsonData)
throw ParseException(ParseException::InternalError, "Unable to allocate memory");
//Make sure bsonData is null terminated, since all string functions need null terminated strings
@@ -21,14 +21,15 @@ struct ParseException: public std::exception {
{
return message.c_str();
}
~ParseException() throw() {};
~ParseException() throw() {}
};
class GameSave
{
public:
int blockWidth, blockHeight;
bool fromNewerVersion;
//Simulation data
//int ** particleMap;
@@ -7,6 +7,7 @@
PreviewModel::PreviewModel():
doOpen(false),
canOpen(true),
save(NULL),
saveData(NULL),
saveComments(NULL),
@@ -92,6 +93,11 @@ bool PreviewModel::GetDoOpen()
return doOpen;
}
bool PreviewModel::GetCanOpen()
{
return canOpen;
}
SaveInfo * PreviewModel::GetSave()
{
return save;
@@ -169,11 +175,15 @@ void PreviewModel::OnResponseReady(void * object, int identifier)
commentsTotal = save->Comments;
try
{
save->SetGameSave(new GameSave(*saveData));
GameSave *gameSave = new GameSave(*saveData);
if (gameSave->fromNewerVersion)
new ErrorMessage("This save is from a newer version", "Please update TPT in game or at http://powdertoy.co.uk");
save->SetGameSave(gameSave);
}
catch(ParseException &e)
{
new ErrorMessage("Error", e.what());
canOpen = false;
}
notifySaveChanged();
notifyCommentsPageChanged();
@@ -16,6 +16,7 @@ using namespace std;
class PreviewView;
class PreviewModel: RequestListener {
bool doOpen;
bool canOpen;
vector<PreviewView*> observers;
SaveInfo * save;
std::vector<unsigned char> * saveData;
@@ -52,6 +53,7 @@ class PreviewModel: RequestListener {
void UpdateSave(int saveID, int saveDate);
void SetFavourite(bool favourite);
bool GetDoOpen();
bool GetCanOpen();
void SetDoOpen(bool doOpen);
void Update();
virtual void OnResponseReady(void * object, int identifier);
@@ -465,6 +465,8 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender)
savePreview->Height *= scaleFactor;
}
}
else if (!sender->GetCanOpen())
openButton->Enabled = false;
}
else
{
@@ -474,6 +476,8 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender)
authorDateLabel->SetText("");
saveDescriptionLabel->SetText("");
favButton->Enabled = false;
if (!sender->GetCanOpen())
openButton->Enabled = false;
}
}

4 comments on commit b184c78

@jacksonmj

This comment has been minimized.

Member

jacksonmj replied Oct 2, 2015

I'm not sure that's a good idea.

@jacob1

This comment has been minimized.

Member

jacob1 replied Oct 2, 2015

):

It still gives you a large warning when you try to open a save, telling you how to update. If the OPS format actually changes, it will prevent you from opening a save in that case (but the format never does ...)

Anyway, my mod has let you open saves from newer versions for a long time. It doesn't really have any problems. I think this commit is useful for future proofing reasons.

@jacksonmj

This comment has been minimized.

Member

jacksonmj replied Oct 2, 2015

Imagine a new property has been added, which is saved as 4 bytes. http://tpt.io/~1863283 contains a line of DMND with this new property set to 0x01000000. Opening it in the old version causes half the DMND particles to turn into DUST (there was no DUST present when saved). (New property value is interpreted as a particle by the old version: 01 = element type DUST, 0000 = no fields, 00 = room temperature)

Opening new saves in older versions can also cause problems due to property conversions (the "//Particle specific parsing:" bit). When opening a save from a new version, the old version will simulate it according to the old rules (e.g. LIFE ctype created by CRAY is stored in tmp2). This, or missing elements, may mean that the save does not work correctly in old versions.
However, the brokenness may not be immediately obvious, so someone might then save it from the old version thinking that, despite the warning, it looks fine. It will then open without warning in old versions, but not work correctly. Also, not all property conversions are safe to apply twice, so saving a new save using an old version may break the save in new versions too.

A more robust idea would perhaps be to calculate a "minimum compatible version" when saving. This would be the most recent of:

  • the version when the OPS field descriptor flags used by the save were added (or if you want to be less fine grained, the version in which the last new flag was added)
  • the last version in which any other problematic changes to the OPS format were made (e.g. more field descriptor bytes)
  • for each element used in the save, the version when that element last had incompatible changes made to it (property conversions, or major behaviour changes)
@jacob1

This comment has been minimized.

Member

jacob1 replied Oct 2, 2015

Those sound like good ideas. I always thought about a flag like that for the minimum version that can open a save, but I just figured it probably wouldn't be needed. But if we had the flag check all of those things, that would be really nice. My mod used to do things like that, it would scan the save for mod elements before uploading and prevent it from being published if it found any. Shouldn't be that hard to do in tpt++.

I'll try working on that this weekend. Not sure if I will be able to due to homework though.

I mostly just wanted this in case of a future 92.0, since getting a new version to be released may take who knows how long ...

Please sign in to comment.