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:
Tamás Bálint Misius 2023-04-10 12:46:48 +02:00
parent eee42b2ea3
commit 0b82796ba4
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
15 changed files with 197 additions and 44 deletions

View File

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

View File

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

View File

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

View File

@ -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.

View File

@ -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"

View File

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

View File

@ -32,6 +32,6 @@ public:
void Clear();
void ClearAirH();
void Invert();
void RecalculateBlockAirMaps();
void ApproximateBlockAirMaps();
Air(Simulation & sim);
};

View File

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

View File

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

View File

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

View 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;
}

View File

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

View File

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

View File

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

View File

@ -21,6 +21,7 @@ powder_files += files(
'Editing.cpp',
'SimTool.cpp',
'ToolClasses.cpp',
'Snapshot.cpp',
'SnapshotDelta.cpp',
)
render_files += files(