Move palette code to GameSave

Because Simulation shouldn't need to know about palettes.
This commit is contained in:
Tamás Bálint Misius 2023-12-09 15:35:26 +01:00
parent 2d7b40b9e5
commit cc27126f27
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
9 changed files with 115 additions and 96 deletions

View File

@ -47,6 +47,83 @@ GameSave::GameSave(const std::vector<char> &data, bool newWantAuthors)
}
}
void GameSave::MapPalette()
{
int partMap[PT_NUM];
for(int i = 0; i < PT_NUM; i++)
{
partMap[i] = i;
}
auto &sd = SimulationData::CRef();
auto &elements = sd.elements;
if(palette.size())
{
for(int i = 0; i < PT_NUM; i++)
{
partMap[i] = 0;
}
for(auto &pi : palette)
{
if (pi.second > 0 && pi.second < PT_NUM)
{
int myId = 0;
for (int i = 0; i < PT_NUM; i++)
{
if (elements[i].Enabled && elements[i].Identifier == pi.first)
{
myId = i;
}
}
if (myId)
{
partMap[pi.second] = myId;
}
else
{
missingElements.identifiers.insert(pi);
}
}
}
}
auto paletteLookup = [this, &partMap](int type) {
if (type > 0 && type < PT_NUM)
{
auto carriedType = partMap[type];
if (!carriedType) // type is not 0 so this shouldn't be 0 either
{
missingElements.ids.insert(type);
}
type = carriedType;
}
return type;
};
unsigned int pmapmask = (1<<pmapbits)-1;
auto &possiblyCarriesType = Particle::PossiblyCarriesType();
auto &properties = Particle::GetProperties();
for (int n = 0; n < NPART && n < particlesCount; n++)
{
Particle &tempPart = particles[n];
if (tempPart.type <= 0 || tempPart.type >= PT_NUM)
{
continue;
}
tempPart.type = paletteLookup(tempPart.type);
for (auto index : possiblyCarriesType)
{
if (elements[tempPart.type].CarriesTypeIn & (1U << index))
{
auto *prop = reinterpret_cast<int *>(reinterpret_cast<char *>(&tempPart) + properties[index].Offset);
auto carriedType = *prop & int(pmapmask);
auto extra = *prop >> pmapbits;
carriedType = paletteLookup(carriedType);
*prop = PMAP(extra, carriedType);
}
}
}
}
void GameSave::Expand(const std::vector<char> &data)
{
try
@ -68,6 +145,7 @@ void GameSave::Expand(const std::vector<char> &data)
std::cerr << "Got Magic number '" << data[0] << data[1] << data[2] << "'" << std::endl;
throw ParseException(ParseException::Corrupt, "Invalid save format");
}
MapPalette();
}
else
{
@ -1980,6 +2058,9 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
std::vector<unsigned> partsSaveIndex(NPART);
unsigned int partsCount = 0;
std::fill(&partsSaveIndex[0], &partsSaveIndex[0] + NPART, 0);
auto &sd = SimulationData::CRef();
auto &elements = sd.elements;
std::set<int> paletteSet;
for (auto pos : partS.OriginRect().Range<TOP_TO_BOTTOM, LEFT_TO_RIGHT>())
{
//Find the first particle in this position
@ -1997,6 +2078,16 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
//Store saved particle index+1 for this partsptr index (0 means not saved)
partsSaveIndex[i] = (partsCount++) + 1;
paletteSet.insert(particles[i].type);
for (auto index : possiblyCarriesType)
{
if (elements[particles[i].type].CarriesTypeIn & (1U << index))
{
auto *prop = reinterpret_cast<const int *>(reinterpret_cast<const char *>(&particles[i]) + properties[index].Offset);
paletteSet.insert(TYP(*prop));
}
}
//Type (required)
partsData[partsDataLen++] = particles[i].type;
@ -2250,6 +2341,12 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
}
}
std::vector<PaletteItem> paletteData;
for (int ID : paletteSet)
{
paletteData.push_back(GameSave::PaletteItem(elements[ID].Identifier, ID));
}
unsigned int soapLinkDataLen = 0;
std::vector<unsigned char> soapLinkData(3*soapCount);
if (soapCount)
@ -2381,10 +2478,10 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
{
bson_append_binary(&b, "parts", (char)BSON_BIN_USER, (const char *)&partsData[0], partsDataLen);
if (palette.size())
if (paletteData.size())
{
bson_append_start_array(&b, "palette");
for(auto iter = palette.begin(), end = palette.end(); iter != end; ++iter)
for(auto iter = paletteData.begin(), end = paletteData.end(); iter != end; ++iter)
{
bson_append_int(&b, (*iter).first.c_str(), (*iter).second);
}

View File

@ -5,6 +5,7 @@
#include "common/Version.h"
#include "simulation/Sign.h"
#include "simulation/Particle.h"
#include "simulation/MissingElements.h"
#include "Misc.h"
#include "SimulationConfig.h"
#include <vector>
@ -62,6 +63,8 @@ class GameSave
void readPSv(const std::vector<char> &data);
std::pair<bool, std::vector<char>> serialiseOPS() const;
void MapPalette();
public:
Vec2<int> blockSize = { 0, 0 };
bool fromNewerVersion = false;
@ -101,6 +104,8 @@ public:
int edgeMode = 0;
bool wantAuthors = true;
MissingElements missingElements;
//Signs
std::vector<sign> signs;
StkmData stkm;

View File

@ -29,7 +29,7 @@ ThumbnailRendererTask::~ThumbnailRendererTask()
bool ThumbnailRendererTask::doWork()
{
std::tie(thumbnail, std::ignore) = SaveRenderer::Ref().Render(save.get(), decorations, fire);
thumbnail = SaveRenderer::Ref().Render(save.get(), decorations, fire);
if (thumbnail)
{
thumbnail->ResizeToFit(size, true);

View File

@ -1964,7 +1964,7 @@ void GameView::NotifyTransformedPlaceSaveChanged(GameModel *sender)
{
if (sender->GetTransformedPlaceSave())
{
std::tie(placeSaveThumb, std::ignore) = SaveRenderer::Ref().Render(sender->GetTransformedPlaceSave(), true, true, sender->GetRenderer());
placeSaveThumb = SaveRenderer::Ref().Render(sender->GetTransformedPlaceSave(), true, true, sender->GetRenderer());
selectMode = PlaceSave;
selectPoint2 = mousePosition;
}

View File

@ -4,6 +4,7 @@
#include "client/Client.h"
#include "client/SaveInfo.h"
#include "client/GameSave.h"
#include "client/http/AddCommentRequest.h"
#include "client/http/ReportSaveRequest.h"
@ -570,7 +571,8 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender)
if(save->GetGameSave())
{
std::tie(savePreview, missingElements) = SaveRenderer::Ref().Render(save->GetGameSave(), false, true);
missingElements = save->GetGameSave()->missingElements;
savePreview = SaveRenderer::Ref().Render(save->GetGameSave(), false, true);
if (savePreview)
savePreview->ResizeToFit(RES / 2, true);
missingElementsButton->Visible = missingElements.identifiers.size() || missingElements.ids.size();

View File

@ -18,7 +18,7 @@ SaveRenderer::SaveRenderer()
SaveRenderer::~SaveRenderer() = default;
std::pair<std::unique_ptr<VideoBuffer>, MissingElements> SaveRenderer::Render(const GameSave *save, bool decorations, bool fire, Renderer *renderModeSource)
std::unique_ptr<VideoBuffer> SaveRenderer::Render(const GameSave *save, bool decorations, bool fire, Renderer *renderModeSource)
{
// this function usually runs on a thread different from where element info in SimulationData may be written, so we acquire a read-only lock on it
auto &sd = SimulationData::CRef();
@ -35,7 +35,7 @@ std::pair<std::unique_ptr<VideoBuffer>, MissingElements> SaveRenderer::Render(co
sim->clear_sim();
auto missingElementTypes = sim->Load(save, true, { 0, 0 });
sim->Load(save, true, { 0, 0 });
ren->decorations_enable = true;
ren->blackDecorations = !decorations;
ren->ClearAccumulation();
@ -59,5 +59,5 @@ std::pair<std::unique_ptr<VideoBuffer>, MissingElements> SaveRenderer::Render(co
auto tempThumb = std::make_unique<VideoBuffer>(save->blockSize * CELL);
tempThumb->BlendImage(ren->Data(), 0xFF, ren->Size().OriginRect());
return { std::move(tempThumb), missingElementTypes };
return tempThumb;
}

View File

@ -5,7 +5,6 @@
#include <vector>
#include "common/ExplicitSingleton.h"
#include "common/String.h"
#include "MissingElements.h"
class GameSave;
class VideoBuffer;
@ -21,5 +20,5 @@ class SaveRenderer: public ExplicitSingleton<SaveRenderer>
public:
SaveRenderer();
~SaveRenderer();
std::pair<std::unique_ptr<VideoBuffer>, MissingElements> Render(const 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);
};

View File

@ -22,67 +22,15 @@ static float remainder_p(float x, float y)
return std::fmod(x, y) + (x>=0 ? 0 : y);
}
MissingElements Simulation::Load(const GameSave *save, bool includePressure, Vec2<int> blockP) // block coordinates
void Simulation::Load(const GameSave *save, bool includePressure, Vec2<int> blockP) // block coordinates
{
MissingElements missingElements;
auto partP = blockP * CELL;
unsigned int pmapmask = (1<<save->pmapbits)-1;
int partMap[PT_NUM];
for(int i = 0; i < PT_NUM; i++)
{
partMap[i] = i;
}
auto &sd = SimulationData::CRef();
auto &elements = sd.elements;
if(save->palette.size())
{
for(int i = 0; i < PT_NUM; i++)
{
partMap[i] = 0;
}
for(auto &pi : save->palette)
{
if (pi.second > 0 && pi.second < PT_NUM)
{
int myId = 0;
for (int i = 0; i < PT_NUM; i++)
{
if (elements[i].Enabled && elements[i].Identifier == pi.first)
{
myId = i;
}
}
if (myId)
{
partMap[pi.second] = myId;
}
else
{
missingElements.identifiers.insert(pi);
}
}
}
}
auto paletteLookup = [&partMap, &missingElements](int type) {
if (type > 0 && type < PT_NUM)
{
auto carriedType = partMap[type];
if (!carriedType) // type is not 0 so this shouldn't be 0 either
{
missingElements.ids.insert(type);
}
type = carriedType;
}
return type;
};
RecalcFreeParticles(false);
auto &possiblyCarriesType = Particle::PossiblyCarriesType();
auto &properties = Particle::GetProperties();
std::map<unsigned int, unsigned int> soapList;
for (int n = 0; n < NPART && n < save->particlesCount; n++)
{
@ -97,26 +45,12 @@ MissingElements Simulation::Load(const GameSave *save, bool includePressure, Vec
int x = int(tempPart.x + 0.5f);
int y = int(tempPart.y + 0.5f);
// Check various scenarios where we are unable to spawn the element, and set type to 0 to block spawning later
if (!InBounds(x, y))
{
continue;
}
tempPart.type = paletteLookup(tempPart.type);
for (auto index : possiblyCarriesType)
{
if (elements[tempPart.type].CarriesTypeIn & (1U << index))
{
auto *prop = reinterpret_cast<int *>(reinterpret_cast<char *>(&tempPart) + properties[index].Offset);
auto carriedType = *prop & int(pmapmask);
auto extra = *prop >> save->pmapbits;
carriedType = paletteLookup(carriedType);
*prop = PMAP(extra, carriedType);
}
}
// Ensure we can spawn this element
if ((player.spwn == 1 && tempPart.type==PT_STKM) || (player2.spwn == 1 && tempPart.type==PT_STKM2))
{
@ -339,8 +273,6 @@ MissingElements Simulation::Load(const GameSave *save, bool includePressure, Vec
{
air->ApproximateBlockAirMaps();
}
return missingElements;
}
std::unique_ptr<GameSave> Simulation::Save(bool includePressure, Rect<int> partR) // particle coordinates
@ -349,8 +281,6 @@ std::unique_ptr<GameSave> Simulation::Save(bool includePressure, Rect<int> partR
auto blockP = blockR.TopLeft;
auto newSave = std::make_unique<GameSave>(blockR.Size());
auto &possiblyCarriesType = Particle::PossiblyCarriesType();
auto &properties = Particle::GetProperties();
newSave->frameCount = frameCount;
newSave->rngState = rng.state();
@ -360,7 +290,6 @@ std::unique_ptr<GameSave> Simulation::Save(bool includePressure, Rect<int> partR
// Map of soap particles loaded into this save, old ID -> new ID
// Now stores all particles, not just SOAP (but still only used for soap)
std::map<unsigned int, unsigned int> particleMap;
std::set<int> paletteSet;
auto &sd = SimulationData::CRef();
auto &elements = sd.elements;
@ -381,22 +310,10 @@ std::unique_ptr<GameSave> Simulation::Save(bool includePressure, Rect<int> partR
storedParts++;
elementCount[tempPart.type]++;
paletteSet.insert(tempPart.type);
for (auto index : possiblyCarriesType)
{
if (elements[tempPart.type].CarriesTypeIn & (1U << index))
{
auto *prop = reinterpret_cast<const int *>(reinterpret_cast<const char *>(&tempPart) + properties[index].Offset);
paletteSet.insert(TYP(*prop));
}
}
}
}
}
for (int ID : paletteSet)
newSave->palette.push_back(GameSave::PaletteItem(elements[ID].Identifier, ID));
if (storedParts && elementCount[PT_SOAP])
{
// fix SOAP links using particleMap, a map of old particle ID -> new particle ID

View File

@ -11,7 +11,6 @@
#include "common/tpt-rand.h"
#include "Element.h"
#include "SimulationConfig.h"
#include "MissingElements.h"
#include <cstring>
#include <cstddef>
#include <vector>
@ -114,7 +113,7 @@ public:
uint64_t frameCount;
bool ensureDeterminism;
MissingElements Load(const GameSave *save, bool includePressure, Vec2<int> blockP); // block coordinates
void Load(const GameSave *save, bool includePressure, Vec2<int> blockP); // block coordinates
std::unique_ptr<GameSave> Save(bool includePressure, Rect<int> partR); // particle coordinates
void SaveSimOptions(GameSave &gameSave);
SimulationSample GetSample(int x, int y);