Add Simulation::frameCount, save blockair/blockairh and rng state
Saving blockair/blockairh is nice because RecalculateBlockAirMaps uses the sim rng, which means the sim rng would get advanced in Simulation::Load. Also rename RecalculateBlockAirMaps to ApproximateBlockAirMaps because that's what it is, an approximation, and it's needed only if there are no block air maps in the save. Simulation::frameCount keeps track of frames elapsed since the beginning of the simulation, zeroed at clear_sim. It overflows when it reaches the 64-bit limit, which means anything that depends on it should either handle this, or not fail catastrophically. sandcolour (the only thing that depends on it as of now) is a good example of the latter: sandcolour has a periodicity of 360 frames, which means that there is one sandcolour period that is cut short by the overflow. This is not "handled" (the period is cut short, which is detectable by users) but is not catastrophic either (it's not a big deal, and it won't ever happen unless someone hacks the save). Also restrict saves with determinism data to 98.0.
This commit is contained in:
parent
eee42b2ea3
commit
0b82796ba4
@ -33,7 +33,7 @@ constexpr int MINOR_VERSION = 0;
|
||||
constexpr int BUILD_NUM = 352;
|
||||
constexpr int SNAPSHOT_ID = @SNAPSHOT_ID@;
|
||||
constexpr int MOD_ID = @MOD_ID@;
|
||||
constexpr int FUTURE_SAVE_VERSION = 97;
|
||||
constexpr int FUTURE_SAVE_VERSION = 98;
|
||||
constexpr int FUTURE_MINOR_VERSION = 0;
|
||||
|
||||
constexpr char IDENT_RELTYPE = SNAPSHOT ? 'S' : (BETA ? 'B' : 'R');
|
||||
|
@ -20,15 +20,18 @@ static void ConvertBsonToJson(bson_iterator *b, Json::Value *j, int depth = 0);
|
||||
static void CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen);
|
||||
static void CheckBsonFieldBool(bson_iterator iter, const char *field, bool *flag);
|
||||
static void CheckBsonFieldInt(bson_iterator iter, const char *field, int *setting);
|
||||
static void CheckBsonFieldLong(bson_iterator iter, const char *field, int64_t *setting);
|
||||
static void CheckBsonFieldFloat(bson_iterator iter, const char *field, float *setting);
|
||||
|
||||
GameSave::GameSave(int width, int height)
|
||||
{
|
||||
rngState = RNG().state(); // initialize it with something sane
|
||||
setSize(width, height);
|
||||
}
|
||||
|
||||
GameSave::GameSave(const std::vector<char> &data, bool newWantAuthors)
|
||||
{
|
||||
rngState = RNG().state(); // initialize it with something sane
|
||||
wantAuthors = newWantAuthors;
|
||||
blockWidth = 0;
|
||||
blockHeight = 0;
|
||||
@ -92,6 +95,8 @@ void GameSave::setSize(int newWidth, int newHeight)
|
||||
velocityX = Plane<float>(blockWidth, blockHeight, 0.0f);
|
||||
velocityY = Plane<float>(blockWidth, blockHeight, 0.0f);
|
||||
ambientHeat = Plane<float>(blockWidth, blockHeight, 0.0f);
|
||||
blockAir = Plane<unsigned char>(blockWidth, blockHeight, 0);
|
||||
blockAirh = Plane<unsigned char>(blockWidth, blockHeight, 0);
|
||||
}
|
||||
|
||||
std::pair<bool, std::vector<char>> GameSave::Serialise() const
|
||||
@ -226,6 +231,8 @@ void GameSave::Transform(matrix2d transform, vector2d translate, vector2d transl
|
||||
Plane<float> velocityXNew(newBlockWidth, newBlockHeight, 0.0f);
|
||||
Plane<float> velocityYNew(newBlockWidth, newBlockHeight, 0.0f);
|
||||
Plane<float> ambientHeatNew(newBlockWidth, newBlockHeight, 0.0f);
|
||||
Plane<unsigned char> blockAirNew(newBlockWidth, newBlockHeight, 0);
|
||||
Plane<unsigned char> blockAirhNew(newBlockWidth, newBlockHeight, 0);
|
||||
|
||||
// Match these up with the matrices provided in GameView::OnKeyPress.
|
||||
bool patchPipeR = transform.a == 0 && transform.b == 1 && transform.c == -1 && transform.d == 0;
|
||||
@ -324,6 +331,8 @@ void GameSave::Transform(matrix2d transform, vector2d translate, vector2d transl
|
||||
velocityXNew[ny][nx] = velocityX[y][x];
|
||||
velocityYNew[ny][nx] = velocityY[y][x];
|
||||
ambientHeatNew[ny][nx] = ambientHeat[y][x];
|
||||
blockAirNew[ny][nx] = blockAir[y][x];
|
||||
blockAirhNew[ny][nx] = blockAirh[y][x];
|
||||
}
|
||||
translated = v2d_add(m2d_multiply_v2d(transform, translated), translateReal);
|
||||
|
||||
@ -337,6 +346,8 @@ void GameSave::Transform(matrix2d transform, vector2d translate, vector2d transl
|
||||
velocityX = velocityXNew;
|
||||
velocityY = velocityYNew;
|
||||
ambientHeat = ambientHeatNew;
|
||||
blockAir = blockAirNew;
|
||||
blockAirh = blockAirhNew;
|
||||
}
|
||||
|
||||
static void CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen)
|
||||
@ -384,6 +395,21 @@ static void CheckBsonFieldInt(bson_iterator iter, const char *field, int *settin
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckBsonFieldLong(bson_iterator iter, const char *field, int64_t *setting)
|
||||
{
|
||||
if (!strcmp(bson_iterator_key(&iter), field))
|
||||
{
|
||||
if (bson_iterator_type(&iter) == BSON_LONG)
|
||||
{
|
||||
*setting = bson_iterator_long(&iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void CheckBsonFieldFloat(bson_iterator iter, const char *field, float *setting)
|
||||
{
|
||||
if (!strcmp(bson_iterator_key(&iter), field))
|
||||
@ -404,9 +430,9 @@ void GameSave::readOPS(const std::vector<char> &data)
|
||||
Renderer::PopulateTables();
|
||||
|
||||
unsigned char *inputData = (unsigned char*)&data[0], *partsData = NULL, *partsPosData = NULL, *fanData = NULL, *wallData = NULL, *soapLinkData = NULL;
|
||||
unsigned char *pressData = NULL, *vxData = NULL, *vyData = NULL, *ambientData = NULL;
|
||||
unsigned char *pressData = NULL, *vxData = NULL, *vyData = NULL, *ambientData = NULL, *blockAirData = nullptr;
|
||||
unsigned int inputDataLen = data.size(), bsonDataLen = 0, partsDataLen, partsPosDataLen, fanDataLen, wallDataLen, soapLinkDataLen;
|
||||
unsigned int pressDataLen, vxDataLen, vyDataLen, ambientDataLen;
|
||||
unsigned int pressDataLen, vxDataLen, vyDataLen, ambientDataLen, blockAirDataLen;
|
||||
unsigned partsCount = 0;
|
||||
unsigned int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH;
|
||||
int savedVersion = inputData[4];
|
||||
@ -498,6 +524,7 @@ void GameSave::readOPS(const std::vector<char> &data)
|
||||
CheckBsonFieldUser(iter, "vxMap", &vxData, &vxDataLen);
|
||||
CheckBsonFieldUser(iter, "vyMap", &vyData, &vyDataLen);
|
||||
CheckBsonFieldUser(iter, "ambientMap", &ambientData, &ambientDataLen);
|
||||
CheckBsonFieldUser(iter, "blockAir", &blockAirData, &blockAirDataLen);
|
||||
CheckBsonFieldUser(iter, "fanMap", &fanData, &fanDataLen);
|
||||
CheckBsonFieldUser(iter, "soapLinks", &soapLinkData, &soapLinkDataLen);
|
||||
CheckBsonFieldBool(iter, "legacyEnable", &legacyEnable);
|
||||
@ -512,6 +539,9 @@ void GameSave::readOPS(const std::vector<char> &data)
|
||||
CheckBsonFieldFloat(iter, "ambientAirTemp", &ambientAirTemp);
|
||||
CheckBsonFieldInt(iter, "edgeMode", &edgeMode);
|
||||
CheckBsonFieldInt(iter, "pmapbits", &pmapbits);
|
||||
CheckBsonFieldLong(iter, "frameCount", reinterpret_cast<int64_t *>(&frameCount));
|
||||
CheckBsonFieldLong(iter, "rngState0", reinterpret_cast<int64_t *>(&rngState[0]));
|
||||
CheckBsonFieldLong(iter, "rngState1", reinterpret_cast<int64_t *>(&rngState[1]));
|
||||
if (!strcmp(bson_iterator_key(&iter), "signs"))
|
||||
{
|
||||
if (bson_iterator_type(&iter)==BSON_ARRAY)
|
||||
@ -840,6 +870,22 @@ void GameSave::readOPS(const std::vector<char> &data)
|
||||
hasAmbientHeat = true;
|
||||
}
|
||||
|
||||
if (blockAirData)
|
||||
{
|
||||
if (blockW * blockH * 2 > blockAirDataLen)
|
||||
throw ParseException(ParseException::Corrupt, "Not enough block air data");
|
||||
auto blockAirhData = blockAirData + blockW * blockH;
|
||||
for (unsigned int x = 0; x < blockW; x++)
|
||||
{
|
||||
for (unsigned int y = 0; y < blockH; y++)
|
||||
{
|
||||
blockAir[blockY + y][blockX + x] = blockAirData[y * blockW + x];
|
||||
blockAirh[blockY + y][blockX + x] = blockAirhData[y * blockW + x];
|
||||
}
|
||||
}
|
||||
hasBlockAirMaps = true;
|
||||
}
|
||||
|
||||
//Read particle data
|
||||
if (partsData && partsPosData)
|
||||
{
|
||||
@ -1933,13 +1979,16 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
|
||||
std::vector<unsigned char> vxData(blockWidth*blockHeight*2);
|
||||
std::vector<unsigned char> vyData(blockWidth*blockHeight*2);
|
||||
std::vector<unsigned char> ambientData(blockWidth*blockHeight*2, 0);
|
||||
std::vector<unsigned char> blockAirData(blockWidth * blockHeight * 2);
|
||||
auto *blockAirhData = &blockAirData[blockWidth * blockHeight];
|
||||
unsigned int wallDataLen = blockWidth*blockHeight, fanDataLen = 0, pressDataLen = 0, vxDataLen = 0, vyDataLen = 0, ambientDataLen = 0;
|
||||
|
||||
for (x = blockX; x < blockX+blockW; x++)
|
||||
{
|
||||
for (y = blockY; y < blockY+blockH; y++)
|
||||
{
|
||||
wallData[(y-blockY)*blockW+(x-blockX)] = blockMap[y][x];
|
||||
auto rowMajorIndex = (y - blockY) * blockW + (x - blockX);
|
||||
wallData[rowMajorIndex] = blockMap[y][x];
|
||||
if (blockMap[y][x])
|
||||
hasWallData = true;
|
||||
|
||||
@ -1957,6 +2006,9 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
|
||||
|
||||
vyData[vyDataLen++] = (unsigned char)((int)(velY*128)&0xFF);
|
||||
vyData[vyDataLen++] = (unsigned char)((int)(velY*128)>>8);
|
||||
|
||||
blockAirData[rowMajorIndex] = blockAir[y][x];
|
||||
blockAirhData[rowMajorIndex] = blockAirh[y][x];
|
||||
}
|
||||
|
||||
if (hasAmbientHeat)
|
||||
@ -1983,6 +2035,7 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
|
||||
}
|
||||
}
|
||||
}
|
||||
auto blockAirDataLen = blockAirData.size();
|
||||
|
||||
//Index positions of all particles, using linked lists
|
||||
//partsPosFirstMap is pmap for the first particle in each position
|
||||
@ -2491,6 +2544,14 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
|
||||
bson_append_binary(&b, "ambientMap", (char)BSON_BIN_USER, (const char*)&ambientData[0], ambientDataLen);
|
||||
if (soapLinkDataLen)
|
||||
bson_append_binary(&b, "soapLinks", (char)BSON_BIN_USER, (const char *)&soapLinkData[0], soapLinkDataLen);
|
||||
if (ensureDeterminism)
|
||||
{
|
||||
bson_append_binary(&b, "blockAir", (char)BSON_BIN_USER, (const char *)&blockAirData[0], blockAirDataLen);
|
||||
bson_append_long(&b, "frameCount", int64_t(frameCount));
|
||||
bson_append_long(&b, "rngState0", int64_t(rngState[0]));
|
||||
bson_append_long(&b, "rngState1", int64_t(rngState[1]));
|
||||
RESTRICTVERSION(98, 0);
|
||||
}
|
||||
unsigned int signsCount = 0;
|
||||
for (size_t i = 0; i < signs.size(); i++)
|
||||
{
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "Misc.h"
|
||||
#include "SimulationConfig.h"
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <json/json.h>
|
||||
|
||||
struct sign;
|
||||
@ -93,6 +94,10 @@ public:
|
||||
int minorVersion = 0;
|
||||
bool hasPressure = false;
|
||||
bool hasAmbientHeat = false;
|
||||
bool hasBlockAirMaps = false; // only written by readOPS, never read
|
||||
bool ensureDeterminism = false; // only read by serializeOPS, never written
|
||||
std::array<uint64_t, 2> rngState;
|
||||
uint64_t frameCount = 0;
|
||||
|
||||
//Simulation data
|
||||
int particlesCount = 0;
|
||||
@ -104,6 +109,8 @@ public:
|
||||
Plane<float> velocityX;
|
||||
Plane<float> velocityY;
|
||||
Plane<float> ambientHeat;
|
||||
Plane<unsigned char> blockAir;
|
||||
Plane<unsigned char> blockAirh;
|
||||
|
||||
//Simulation Options
|
||||
bool waterEEnabled = false;
|
||||
|
@ -17,6 +17,16 @@ public:
|
||||
|
||||
RNG();
|
||||
void seed(unsigned int sd);
|
||||
|
||||
void state(std::array<uint64_t, 2> ns)
|
||||
{
|
||||
s = ns;
|
||||
}
|
||||
|
||||
std::array<uint64_t, 2> state() const
|
||||
{
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
// Please only use this on the main thread and never for simulation stuff.
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "simulation/Simulation.h"
|
||||
#include "simulation/ToolClasses.h"
|
||||
#include "simulation/SaveRenderer.h"
|
||||
#include "simulation/Snapshot.h"
|
||||
|
||||
#include "gui/interface/Window.h"
|
||||
#include "gui/interface/Engine.h"
|
||||
|
@ -356,7 +356,7 @@ void Air::Invert()
|
||||
}
|
||||
|
||||
// called when loading saves / stamps to ensure nothing "leaks" the first frame
|
||||
void Air::RecalculateBlockAirMaps()
|
||||
void Air::ApproximateBlockAirMaps()
|
||||
{
|
||||
for (int i = 0; i <= sim.parts_lastActiveIndex; i++)
|
||||
{
|
||||
|
@ -32,6 +32,6 @@ public:
|
||||
void Clear();
|
||||
void ClearAirH();
|
||||
void Invert();
|
||||
void RecalculateBlockAirMaps();
|
||||
void ApproximateBlockAirMaps();
|
||||
Air(Simulation & sim);
|
||||
};
|
||||
|
@ -22,6 +22,8 @@ std::unique_ptr<Snapshot> Simulation::CreateSnapshot()
|
||||
snap->AmbientHeat .insert (snap->AmbientHeat .begin(), &hv [0][0] , &hv [0][0] + NCELL);
|
||||
snap->BlockMap .insert (snap->BlockMap .begin(), &bmap[0][0] , &bmap[0][0] + NCELL);
|
||||
snap->ElecMap .insert (snap->ElecMap .begin(), &emap[0][0] , &emap[0][0] + NCELL);
|
||||
snap->BlockAir .insert (snap->BlockAir .begin(), &air->bmap_blockair[0][0] , &air->bmap_blockair[0][0] + NCELL);
|
||||
snap->BlockAirH .insert (snap->BlockAirH .begin(), &air->bmap_blockairh[0][0], &air->bmap_blockairh[0][0] + NCELL);
|
||||
snap->FanVelocityX .insert (snap->FanVelocityX .begin(), &fvx [0][0] , &fvx [0][0] + NCELL);
|
||||
snap->FanVelocityY .insert (snap->FanVelocityY .begin(), &fvy [0][0] , &fvy [0][0] + NCELL);
|
||||
snap->GravVelocityX .insert (snap->GravVelocityX .begin(), &gravx [0] , &gravx [0] + NCELL);
|
||||
@ -35,6 +37,8 @@ std::unique_ptr<Snapshot> Simulation::CreateSnapshot()
|
||||
snap->stickmen .push_back(player2);
|
||||
snap->stickmen .push_back(player);
|
||||
snap->signs = signs;
|
||||
snap->FrameCount = frameCount;
|
||||
snap->RngState = rng.state();
|
||||
return snap;
|
||||
}
|
||||
|
||||
@ -53,6 +57,8 @@ void Simulation::Restore(const Snapshot &snap)
|
||||
std::copy(snap.AmbientHeat .begin(), snap.AmbientHeat .end(), &hv[0][0] );
|
||||
std::copy(snap.BlockMap .begin(), snap.BlockMap .end(), &bmap[0][0] );
|
||||
std::copy(snap.ElecMap .begin(), snap.ElecMap .end(), &emap[0][0] );
|
||||
std::copy(snap.BlockAir .begin(), snap.BlockAir .end(), &air->bmap_blockair[0][0] );
|
||||
std::copy(snap.BlockAirH .begin(), snap.BlockAirH .end(), &air->bmap_blockairh[0][0]);
|
||||
std::copy(snap.FanVelocityX .begin(), snap.FanVelocityX .end(), &fvx[0][0] );
|
||||
std::copy(snap.FanVelocityY .begin(), snap.FanVelocityY .end(), &fvy[0][0] );
|
||||
if (grav->IsEnabled())
|
||||
@ -70,8 +76,9 @@ void Simulation::Restore(const Snapshot &snap)
|
||||
player = snap.stickmen[snap.stickmen.size() - 1];
|
||||
player2 = snap.stickmen[snap.stickmen.size() - 2];
|
||||
signs = snap.signs;
|
||||
frameCount = snap.FrameCount;
|
||||
rng.state(snap.RngState);
|
||||
parts_lastActiveIndex = NPART - 1;
|
||||
air->RecalculateBlockAirMaps();
|
||||
RecalcFreeParticles(false);
|
||||
gravWallChanged = true;
|
||||
}
|
||||
|
@ -64,6 +64,8 @@ int Simulation::Load(const GameSave * originalSave, bool includePressure, int fu
|
||||
}
|
||||
|
||||
RecalcFreeParticles(false);
|
||||
frameCount = save->frameCount;
|
||||
rng.state(save->rngState);
|
||||
|
||||
auto &possiblyCarriesType = Particle::PossiblyCarriesType();
|
||||
auto &properties = Particle::GetProperties();
|
||||
@ -325,12 +327,20 @@ int Simulation::Load(const GameSave * originalSave, bool includePressure, int fu
|
||||
}
|
||||
if (save->hasAmbientHeat)
|
||||
hv[saveBlockY+blockY][saveBlockX+blockX] = save->ambientHeat[saveBlockY][saveBlockX];
|
||||
if (save->hasBlockAirMaps)
|
||||
{
|
||||
air->bmap_blockair[saveBlockY+blockY][saveBlockX+blockX] = save->blockAir[saveBlockY][saveBlockX];
|
||||
air->bmap_blockairh[saveBlockY+blockY][saveBlockX+blockX] = save->blockAirh[saveBlockY][saveBlockX];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gravWallChanged = true;
|
||||
air->RecalculateBlockAirMaps();
|
||||
if (!save->hasBlockAirMaps)
|
||||
{
|
||||
air->ApproximateBlockAirMaps();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -371,6 +381,8 @@ GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int full
|
||||
GameSave * newSave = new GameSave(blockW, blockH);
|
||||
auto &possiblyCarriesType = Particle::PossiblyCarriesType();
|
||||
auto &properties = Particle::GetProperties();
|
||||
newSave->frameCount = frameCount;
|
||||
newSave->rngState = rng.state();
|
||||
|
||||
int storedParts = 0;
|
||||
int elementCount[PT_NUM];
|
||||
@ -473,6 +485,8 @@ GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int full
|
||||
newSave->velocityX[saveBlockY][saveBlockX] = vx[saveBlockY+blockY][saveBlockX+blockX];
|
||||
newSave->velocityY[saveBlockY][saveBlockX] = vy[saveBlockY+blockY][saveBlockX+blockX];
|
||||
newSave->ambientHeat[saveBlockY][saveBlockX] = hv[saveBlockY+blockY][saveBlockX+blockX];
|
||||
newSave->blockAir[saveBlockY][saveBlockX] = air->bmap_blockair[saveBlockY+blockY][saveBlockX+blockX];
|
||||
newSave->blockAirh[saveBlockY][saveBlockX] = air->bmap_blockairh[saveBlockY+blockY][saveBlockX+blockX];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -481,6 +495,10 @@ GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int full
|
||||
newSave->hasPressure = true;
|
||||
newSave->hasAmbientHeat = true;
|
||||
}
|
||||
if (true) // TODO: tie to an option maybe?
|
||||
{
|
||||
newSave->ensureDeterminism = true;
|
||||
}
|
||||
|
||||
newSave->stkm.rocketBoots1 = player.rocketBoots;
|
||||
newSave->stkm.rocketBoots2 = player2.rocketBoots;
|
||||
@ -1074,6 +1092,7 @@ int Simulation::parts_avg(int ci, int ni,int t)
|
||||
|
||||
void Simulation::clear_sim(void)
|
||||
{
|
||||
frameCount = 0;
|
||||
debug_nextToUpdate = 0;
|
||||
debug_mostRecentlyUpdated = -1;
|
||||
emp_decor = 0;
|
||||
@ -2124,9 +2143,10 @@ int Simulation::create_part(int p, int x, int y, int t, int v)
|
||||
if((elements[t].Properties & TYPE_PART) && pretty_powder)
|
||||
{
|
||||
int colr, colg, colb;
|
||||
colr = PIXR(elements[t].Colour) + int(sandcolour * 1.3) + rng.between(-20, 20) + rng.between(-15, 15);
|
||||
colg = PIXG(elements[t].Colour) + int(sandcolour * 1.3) + rng.between(-20, 20) + rng.between(-15, 15);
|
||||
colb = PIXB(elements[t].Colour) + int(sandcolour * 1.3) + rng.between(-20, 20) + rng.between(-15, 15);
|
||||
auto sandcolourToUse = p == -2 ? sandcolour_interface : sandcolour;
|
||||
colr = PIXR(elements[t].Colour) + int(sandcolourToUse * 1.3) + rng.between(-20, 20) + rng.between(-15, 15);
|
||||
colg = PIXG(elements[t].Colour) + int(sandcolourToUse * 1.3) + rng.between(-20, 20) + rng.between(-15, 15);
|
||||
colb = PIXB(elements[t].Colour) + int(sandcolourToUse * 1.3) + rng.between(-20, 20) + rng.between(-15, 15);
|
||||
colr = colr>255 ? 255 : (colr<0 ? 0 : colr);
|
||||
colg = colg>255 ? 255 : (colg<0 ? 0 : colg);
|
||||
colb = colb>255 ? 255 : (colb<0 ? 0 : colb);
|
||||
@ -3818,8 +3838,9 @@ void Simulation::BeforeSim()
|
||||
if (elementRecount)
|
||||
std::fill(elementCount, elementCount+PT_NUM, 0);
|
||||
}
|
||||
sandcolour = (int)(20.0f*sin((float)sandcolour_frame*(TPT_PI_FLT/180.0f)));
|
||||
sandcolour_interface = (int)(20.0f*sin((float)sandcolour_frame*(TPT_PI_FLT/180.0f)));
|
||||
sandcolour_frame = (sandcolour_frame+1)%360;
|
||||
sandcolour = (int)(20.0f*sin((float)(frameCount)*(TPT_PI_FLT/180.0f)));
|
||||
|
||||
if (gravWallChanged)
|
||||
{
|
||||
@ -3990,6 +4011,8 @@ void Simulation::AfterSim()
|
||||
Element_EMP_Trigger(this, emp_trigger_count);
|
||||
emp_trigger_count = 0;
|
||||
}
|
||||
|
||||
frameCount += 1;
|
||||
}
|
||||
|
||||
Simulation::~Simulation()
|
||||
|
@ -115,8 +115,10 @@ public:
|
||||
int framerender;
|
||||
int pretty_powder;
|
||||
int sandcolour;
|
||||
int sandcolour_interface;
|
||||
int sandcolour_frame;
|
||||
int deco_space;
|
||||
uint64_t frameCount;
|
||||
|
||||
int Load(const GameSave * save, bool includePressure);
|
||||
int Load(const GameSave * save, bool includePressure, int x, int y);
|
||||
|
43
src/simulation/Snapshot.cpp
Normal file
43
src/simulation/Snapshot.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include "Snapshot.h"
|
||||
|
||||
uint32_t Snapshot::Hash() const
|
||||
{
|
||||
// http://www.isthe.com/chongo/tech/comp/fnv/
|
||||
auto hash = UINT32_C(2166136261);
|
||||
auto take = [&hash](const uint8_t *data, size_t size) {
|
||||
for (auto i = 0U; i < size; ++i)
|
||||
{
|
||||
hash ^= data[i];
|
||||
hash *= UINT32_C(16777619);
|
||||
}
|
||||
};
|
||||
auto takeThing = [&take](auto &thing) {
|
||||
take(reinterpret_cast<const uint8_t *>(&thing), sizeof(thing));
|
||||
};
|
||||
auto takeVector = [&take](auto &vec) {
|
||||
take(reinterpret_cast<const uint8_t *>(vec.data()), vec.size() * sizeof(vec[0]));
|
||||
};
|
||||
takeVector(AirPressure);
|
||||
takeVector(AirVelocityX);
|
||||
takeVector(AirVelocityY);
|
||||
takeVector(AmbientHeat);
|
||||
takeVector(Particles);
|
||||
takeVector(GravVelocityX);
|
||||
takeVector(GravVelocityY);
|
||||
takeVector(GravValue);
|
||||
takeVector(GravMap);
|
||||
takeVector(BlockMap);
|
||||
takeVector(ElecMap);
|
||||
takeVector(BlockAir);
|
||||
takeVector(BlockAirH);
|
||||
takeVector(FanVelocityX);
|
||||
takeVector(FanVelocityY);
|
||||
takeVector(PortalParticles);
|
||||
takeVector(WirelessData);
|
||||
takeVector(stickmen);
|
||||
takeThing(FrameCount);
|
||||
takeThing(RngState[0]);
|
||||
takeThing(RngState[1]);
|
||||
// signs and Authors are excluded on purpose, as they aren't POD and don't have much effect on the simulation.
|
||||
return hash;
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
#include "Sign.h"
|
||||
#include "Stickman.h"
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <json/json.h>
|
||||
|
||||
class Snapshot
|
||||
@ -22,6 +23,8 @@ public:
|
||||
|
||||
std::vector<unsigned char> BlockMap;
|
||||
std::vector<unsigned char> ElecMap;
|
||||
std::vector<unsigned char> BlockAir;
|
||||
std::vector<unsigned char> BlockAirH;
|
||||
|
||||
std::vector<float> FanVelocityX;
|
||||
std::vector<float> FanVelocityY;
|
||||
@ -32,32 +35,12 @@ public:
|
||||
std::vector<playerst> stickmen;
|
||||
std::vector<sign> signs;
|
||||
|
||||
uint64_t FrameCount;
|
||||
std::array<uint64_t, 2> RngState;
|
||||
|
||||
uint32_t Hash() const;
|
||||
|
||||
Json::Value Authors;
|
||||
|
||||
Snapshot() :
|
||||
AirPressure(),
|
||||
AirVelocityX(),
|
||||
AirVelocityY(),
|
||||
AmbientHeat(),
|
||||
Particles(),
|
||||
GravVelocityX(),
|
||||
GravVelocityY(),
|
||||
GravValue(),
|
||||
GravMap(),
|
||||
BlockMap(),
|
||||
ElecMap(),
|
||||
FanVelocityX(),
|
||||
FanVelocityY(),
|
||||
PortalParticles(),
|
||||
WirelessData(),
|
||||
stickmen(),
|
||||
signs()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual ~Snapshot()
|
||||
{
|
||||
|
||||
}
|
||||
virtual ~Snapshot() = default;
|
||||
};
|
||||
|
@ -14,7 +14,7 @@
|
||||
// size" even if their sizes weren't derived from compile-time constants, as they'd still
|
||||
// be the same size throughout the life of a Simulation, and thus any Snapshot created from it.
|
||||
// * Fields of dynamic size, whose sizes may be different between Snapshots. These are, fortunately,
|
||||
// the minority: Particles, signs and Authors.
|
||||
// the minority: Particles, signs, etc.
|
||||
// * Each field in Snapshot has a mirror set of fields in SnapshotDelta. Fields of static size
|
||||
// have mirror fields whose type is HunkVector, templated by the item type of the
|
||||
// corresponding field; these fields are handled in a uniform manner. Fields of dynamic size are
|
||||
@ -34,7 +34,7 @@
|
||||
// * ApplyHunkVector<false> is the B = A + d operation, which takes a field of a SnapshotDelta and
|
||||
// the corresponding field of an "older" Snapshot, and fills the latter with the "new" values.
|
||||
// * This difference type is intended for fields of static size. This covers all fields in Snapshot
|
||||
// except for Particles, signs, and Authors.
|
||||
// except for Particles, signs, Authors, FrameCount, and RngState.
|
||||
// * A SingleDiff is, unsurprisingly enough, a single Diff, with an accompanying bool that signifies
|
||||
// whether the Diff does in fact hold the "old" value of a field in the "old" Snapshot and the "new"
|
||||
// value of the same field in the "new" Snapshot. If this bool is false, the data in the fields
|
||||
@ -43,7 +43,8 @@
|
||||
// * FillSingleDiff is the d = B - A operation, while ApplySingleDiff<false> and ApplySingleDiff<true>
|
||||
// are the A = B - d and B = A + d operations. These are self-explanatory.
|
||||
// * This difference type is intended for fields of dynamic size whose data doesn't change often and
|
||||
// doesn't consume too much memory. This covers the Snapshot fields signs and Authors.
|
||||
// doesn't consume too much memory. This covers the Snapshot fields signs and Authors, FrameCount,
|
||||
// and RngState.
|
||||
// * This leaves Snapshot::Particles. This field mirrors Simulation::parts, which is actually also
|
||||
// a field of static size, but since most of the time most of this array is empty, it doesn't make
|
||||
// sense to store all of it in a Snapshot (unlike Air::hv, which can be fairly chaotic (i.e. may have
|
||||
@ -212,11 +213,15 @@ std::unique_ptr<SnapshotDelta> SnapshotDelta::FromSnapshots(const Snapshot &oldS
|
||||
FillHunkVector(oldSnap.GravMap , newSnap.GravMap , delta.GravMap );
|
||||
FillHunkVector(oldSnap.BlockMap , newSnap.BlockMap , delta.BlockMap );
|
||||
FillHunkVector(oldSnap.ElecMap , newSnap.ElecMap , delta.ElecMap );
|
||||
FillHunkVector(oldSnap.BlockAir , newSnap.BlockAir , delta.BlockAir );
|
||||
FillHunkVector(oldSnap.BlockAirH , newSnap.BlockAirH , delta.BlockAirH );
|
||||
FillHunkVector(oldSnap.FanVelocityX , newSnap.FanVelocityX , delta.FanVelocityX );
|
||||
FillHunkVector(oldSnap.FanVelocityY , newSnap.FanVelocityY , delta.FanVelocityY );
|
||||
FillHunkVector(oldSnap.WirelessData , newSnap.WirelessData , delta.WirelessData );
|
||||
FillSingleDiff(oldSnap.signs , newSnap.signs , delta.signs );
|
||||
FillSingleDiff(oldSnap.Authors , newSnap.Authors , delta.Authors );
|
||||
FillSingleDiff(oldSnap.FrameCount , newSnap.FrameCount , delta.FrameCount );
|
||||
FillSingleDiff(oldSnap.RngState , newSnap.RngState , delta.RngState );
|
||||
FillHunkVectorPtr(reinterpret_cast<const uint32_t *>(&oldSnap.PortalParticles[0]), reinterpret_cast<const uint32_t *>(&newSnap.PortalParticles[0]), delta.PortalParticles, newSnap.PortalParticles.size() * ParticleUint32Count);
|
||||
FillHunkVectorPtr(reinterpret_cast<const uint32_t *>(&oldSnap.stickmen[0]) , reinterpret_cast<const uint32_t *>(&newSnap.stickmen[0] ), delta.stickmen , newSnap.stickmen .size() * playerstUint32Count);
|
||||
|
||||
@ -245,11 +250,15 @@ std::unique_ptr<Snapshot> SnapshotDelta::Forward(const Snapshot &oldSnap)
|
||||
ApplyHunkVector<false>(GravMap , newSnap.GravMap );
|
||||
ApplyHunkVector<false>(BlockMap , newSnap.BlockMap );
|
||||
ApplyHunkVector<false>(ElecMap , newSnap.ElecMap );
|
||||
ApplyHunkVector<false>(BlockAir , newSnap.BlockAir );
|
||||
ApplyHunkVector<false>(BlockAirH , newSnap.BlockAirH );
|
||||
ApplyHunkVector<false>(FanVelocityX , newSnap.FanVelocityX );
|
||||
ApplyHunkVector<false>(FanVelocityY , newSnap.FanVelocityY );
|
||||
ApplyHunkVector<false>(WirelessData , newSnap.WirelessData );
|
||||
ApplySingleDiff<false>(signs , newSnap.signs );
|
||||
ApplySingleDiff<false>(Authors , newSnap.Authors );
|
||||
ApplySingleDiff<false>(FrameCount , newSnap.FrameCount );
|
||||
ApplySingleDiff<false>(RngState , newSnap.RngState );
|
||||
ApplyHunkVectorPtr<false>(PortalParticles, reinterpret_cast<uint32_t *>(&newSnap.PortalParticles[0]));
|
||||
ApplyHunkVectorPtr<false>(stickmen , reinterpret_cast<uint32_t *>(&newSnap.stickmen[0] ));
|
||||
|
||||
@ -276,11 +285,15 @@ std::unique_ptr<Snapshot> SnapshotDelta::Restore(const Snapshot &newSnap)
|
||||
ApplyHunkVector<true>(GravMap , oldSnap.GravMap );
|
||||
ApplyHunkVector<true>(BlockMap , oldSnap.BlockMap );
|
||||
ApplyHunkVector<true>(ElecMap , oldSnap.ElecMap );
|
||||
ApplyHunkVector<true>(BlockAir , oldSnap.BlockAir );
|
||||
ApplyHunkVector<true>(BlockAirH , oldSnap.BlockAirH );
|
||||
ApplyHunkVector<true>(FanVelocityX , oldSnap.FanVelocityX );
|
||||
ApplyHunkVector<true>(FanVelocityY , oldSnap.FanVelocityY );
|
||||
ApplyHunkVector<true>(WirelessData , oldSnap.WirelessData );
|
||||
ApplySingleDiff<true>(signs , oldSnap.signs );
|
||||
ApplySingleDiff<true>(Authors , oldSnap.Authors );
|
||||
ApplySingleDiff<true>(FrameCount , oldSnap.FrameCount );
|
||||
ApplySingleDiff<true>(RngState , oldSnap.RngState );
|
||||
ApplyHunkVectorPtr<true>(PortalParticles, reinterpret_cast<uint32_t *>(&oldSnap.PortalParticles[0]));
|
||||
ApplyHunkVectorPtr<true>(stickmen , reinterpret_cast<uint32_t *>(&oldSnap.stickmen[0] ));
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Snapshot.h"
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include "Snapshot.h"
|
||||
|
||||
struct SnapshotDelta
|
||||
{
|
||||
@ -58,6 +56,8 @@ struct SnapshotDelta
|
||||
|
||||
HunkVector<unsigned char> BlockMap;
|
||||
HunkVector<unsigned char> ElecMap;
|
||||
HunkVector<unsigned char> BlockAir;
|
||||
HunkVector<unsigned char> BlockAirH;
|
||||
|
||||
HunkVector<float> FanVelocityX;
|
||||
HunkVector<float> FanVelocityY;
|
||||
@ -67,6 +67,8 @@ struct SnapshotDelta
|
||||
HunkVector<int> WirelessData;
|
||||
HunkVector<uint32_t> stickmen;
|
||||
SingleDiff<std::vector<sign>> signs;
|
||||
SingleDiff<uint64_t> FrameCount;
|
||||
SingleDiff<std::array<uint64_t, 2>> RngState;
|
||||
|
||||
SingleDiff<Json::Value> Authors;
|
||||
|
||||
|
@ -21,6 +21,7 @@ powder_files += files(
|
||||
'Editing.cpp',
|
||||
'SimTool.cpp',
|
||||
'ToolClasses.cpp',
|
||||
'Snapshot.cpp',
|
||||
'SnapshotDelta.cpp',
|
||||
)
|
||||
render_files += files(
|
||||
|
Reference in New Issue
Block a user