From 0b82796ba43fcffb072bd562e6c72a18a2bd0c64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tam=C3=A1s=20B=C3=A1lint=20Misius?= Date: Mon, 10 Apr 2023 12:46:48 +0200 Subject: [PATCH] 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. --- src/Config.template.h | 2 +- src/client/GameSave.cpp | 67 ++++++++++++++++++++++++++++++-- src/client/GameSave.h | 7 ++++ src/common/tpt-rand.h | 10 +++++ src/lua/LuaScriptInterface.cpp | 1 + src/simulation/Air.cpp | 2 +- src/simulation/Air.h | 2 +- src/simulation/Editing.cpp | 9 ++++- src/simulation/Simulation.cpp | 33 +++++++++++++--- src/simulation/Simulation.h | 2 + src/simulation/Snapshot.cpp | 43 ++++++++++++++++++++ src/simulation/Snapshot.h | 35 +++++------------ src/simulation/SnapshotDelta.cpp | 19 +++++++-- src/simulation/SnapshotDelta.h | 8 ++-- src/simulation/meson.build | 1 + 15 files changed, 197 insertions(+), 44 deletions(-) create mode 100644 src/simulation/Snapshot.cpp diff --git a/src/Config.template.h b/src/Config.template.h index c7f1fa2fb..fa98ff459 100644 --- a/src/Config.template.h +++ b/src/Config.template.h @@ -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'); diff --git a/src/client/GameSave.cpp b/src/client/GameSave.cpp index 7a08040ee..135be5978 100644 --- a/src/client/GameSave.cpp +++ b/src/client/GameSave.cpp @@ -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 &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(blockWidth, blockHeight, 0.0f); velocityY = Plane(blockWidth, blockHeight, 0.0f); ambientHeat = Plane(blockWidth, blockHeight, 0.0f); + blockAir = Plane(blockWidth, blockHeight, 0); + blockAirh = Plane(blockWidth, blockHeight, 0); } std::pair> GameSave::Serialise() const @@ -226,6 +231,8 @@ void GameSave::Transform(matrix2d transform, vector2d translate, vector2d transl Plane velocityXNew(newBlockWidth, newBlockHeight, 0.0f); Plane velocityYNew(newBlockWidth, newBlockHeight, 0.0f); Plane ambientHeatNew(newBlockWidth, newBlockHeight, 0.0f); + Plane blockAirNew(newBlockWidth, newBlockHeight, 0); + Plane 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 &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 &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 &data) CheckBsonFieldFloat(iter, "ambientAirTemp", &ambientAirTemp); CheckBsonFieldInt(iter, "edgeMode", &edgeMode); CheckBsonFieldInt(iter, "pmapbits", &pmapbits); + CheckBsonFieldLong(iter, "frameCount", reinterpret_cast(&frameCount)); + CheckBsonFieldLong(iter, "rngState0", reinterpret_cast(&rngState[0])); + CheckBsonFieldLong(iter, "rngState1", reinterpret_cast(&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 &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> GameSave::serialiseOPS() const std::vector vxData(blockWidth*blockHeight*2); std::vector vyData(blockWidth*blockHeight*2); std::vector ambientData(blockWidth*blockHeight*2, 0); + std::vector 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> 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> 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> 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++) { diff --git a/src/client/GameSave.h b/src/client/GameSave.h index 7f60154b5..5fd7b310f 100644 --- a/src/client/GameSave.h +++ b/src/client/GameSave.h @@ -6,6 +6,7 @@ #include "Misc.h" #include "SimulationConfig.h" #include +#include #include 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 rngState; + uint64_t frameCount = 0; //Simulation data int particlesCount = 0; @@ -104,6 +109,8 @@ public: Plane velocityX; Plane velocityY; Plane ambientHeat; + Plane blockAir; + Plane blockAirh; //Simulation Options bool waterEEnabled = false; diff --git a/src/common/tpt-rand.h b/src/common/tpt-rand.h index 508521def..cc2b15da1 100644 --- a/src/common/tpt-rand.h +++ b/src/common/tpt-rand.h @@ -17,6 +17,16 @@ public: RNG(); void seed(unsigned int sd); + + void state(std::array ns) + { + s = ns; + } + + std::array state() const + { + return s; + } }; // Please only use this on the main thread and never for simulation stuff. diff --git a/src/lua/LuaScriptInterface.cpp b/src/lua/LuaScriptInterface.cpp index 8d0b365c5..01af521a9 100644 --- a/src/lua/LuaScriptInterface.cpp +++ b/src/lua/LuaScriptInterface.cpp @@ -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" diff --git a/src/simulation/Air.cpp b/src/simulation/Air.cpp index 13409f1e0..30d55d468 100644 --- a/src/simulation/Air.cpp +++ b/src/simulation/Air.cpp @@ -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++) { diff --git a/src/simulation/Air.h b/src/simulation/Air.h index 79f626f8d..be1f02fab 100644 --- a/src/simulation/Air.h +++ b/src/simulation/Air.h @@ -32,6 +32,6 @@ public: void Clear(); void ClearAirH(); void Invert(); - void RecalculateBlockAirMaps(); + void ApproximateBlockAirMaps(); Air(Simulation & sim); }; diff --git a/src/simulation/Editing.cpp b/src/simulation/Editing.cpp index c8c343b86..6a3be9e0f 100644 --- a/src/simulation/Editing.cpp +++ b/src/simulation/Editing.cpp @@ -22,6 +22,8 @@ std::unique_ptr 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 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; } diff --git a/src/simulation/Simulation.cpp b/src/simulation/Simulation.cpp index 70a013eba..0d980e88b 100644 --- a/src/simulation/Simulation.cpp +++ b/src/simulation/Simulation.cpp @@ -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() diff --git a/src/simulation/Simulation.h b/src/simulation/Simulation.h index 98944b97d..5f275b7c7 100644 --- a/src/simulation/Simulation.h +++ b/src/simulation/Simulation.h @@ -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); diff --git a/src/simulation/Snapshot.cpp b/src/simulation/Snapshot.cpp new file mode 100644 index 000000000..d4d70c72a --- /dev/null +++ b/src/simulation/Snapshot.cpp @@ -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(&thing), sizeof(thing)); + }; + auto takeVector = [&take](auto &vec) { + take(reinterpret_cast(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; +} diff --git a/src/simulation/Snapshot.h b/src/simulation/Snapshot.h index fa716df8f..f42d958f3 100644 --- a/src/simulation/Snapshot.h +++ b/src/simulation/Snapshot.h @@ -3,6 +3,7 @@ #include "Sign.h" #include "Stickman.h" #include +#include #include class Snapshot @@ -22,6 +23,8 @@ public: std::vector BlockMap; std::vector ElecMap; + std::vector BlockAir; + std::vector BlockAirH; std::vector FanVelocityX; std::vector FanVelocityY; @@ -32,32 +35,12 @@ public: std::vector stickmen; std::vector signs; + uint64_t FrameCount; + std::array 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; }; diff --git a/src/simulation/SnapshotDelta.cpp b/src/simulation/SnapshotDelta.cpp index 88a5938f5..b1e33c562 100644 --- a/src/simulation/SnapshotDelta.cpp +++ b/src/simulation/SnapshotDelta.cpp @@ -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 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 and ApplySingleDiff // 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::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(&oldSnap.PortalParticles[0]), reinterpret_cast(&newSnap.PortalParticles[0]), delta.PortalParticles, newSnap.PortalParticles.size() * ParticleUint32Count); FillHunkVectorPtr(reinterpret_cast(&oldSnap.stickmen[0]) , reinterpret_cast(&newSnap.stickmen[0] ), delta.stickmen , newSnap.stickmen .size() * playerstUint32Count); @@ -245,11 +250,15 @@ std::unique_ptr SnapshotDelta::Forward(const Snapshot &oldSnap) ApplyHunkVector(GravMap , newSnap.GravMap ); ApplyHunkVector(BlockMap , newSnap.BlockMap ); ApplyHunkVector(ElecMap , newSnap.ElecMap ); + ApplyHunkVector(BlockAir , newSnap.BlockAir ); + ApplyHunkVector(BlockAirH , newSnap.BlockAirH ); ApplyHunkVector(FanVelocityX , newSnap.FanVelocityX ); ApplyHunkVector(FanVelocityY , newSnap.FanVelocityY ); ApplyHunkVector(WirelessData , newSnap.WirelessData ); ApplySingleDiff(signs , newSnap.signs ); ApplySingleDiff(Authors , newSnap.Authors ); + ApplySingleDiff(FrameCount , newSnap.FrameCount ); + ApplySingleDiff(RngState , newSnap.RngState ); ApplyHunkVectorPtr(PortalParticles, reinterpret_cast(&newSnap.PortalParticles[0])); ApplyHunkVectorPtr(stickmen , reinterpret_cast(&newSnap.stickmen[0] )); @@ -276,11 +285,15 @@ std::unique_ptr SnapshotDelta::Restore(const Snapshot &newSnap) ApplyHunkVector(GravMap , oldSnap.GravMap ); ApplyHunkVector(BlockMap , oldSnap.BlockMap ); ApplyHunkVector(ElecMap , oldSnap.ElecMap ); + ApplyHunkVector(BlockAir , oldSnap.BlockAir ); + ApplyHunkVector(BlockAirH , oldSnap.BlockAirH ); ApplyHunkVector(FanVelocityX , oldSnap.FanVelocityX ); ApplyHunkVector(FanVelocityY , oldSnap.FanVelocityY ); ApplyHunkVector(WirelessData , oldSnap.WirelessData ); ApplySingleDiff(signs , oldSnap.signs ); ApplySingleDiff(Authors , oldSnap.Authors ); + ApplySingleDiff(FrameCount , oldSnap.FrameCount ); + ApplySingleDiff(RngState , oldSnap.RngState ); ApplyHunkVectorPtr(PortalParticles, reinterpret_cast(&oldSnap.PortalParticles[0])); ApplyHunkVectorPtr(stickmen , reinterpret_cast(&oldSnap.stickmen[0] )); diff --git a/src/simulation/SnapshotDelta.h b/src/simulation/SnapshotDelta.h index 1ceb9397b..16d28cad1 100644 --- a/src/simulation/SnapshotDelta.h +++ b/src/simulation/SnapshotDelta.h @@ -1,9 +1,7 @@ #pragma once - -#include "Snapshot.h" - #include #include +#include "Snapshot.h" struct SnapshotDelta { @@ -58,6 +56,8 @@ struct SnapshotDelta HunkVector BlockMap; HunkVector ElecMap; + HunkVector BlockAir; + HunkVector BlockAirH; HunkVector FanVelocityX; HunkVector FanVelocityY; @@ -67,6 +67,8 @@ struct SnapshotDelta HunkVector WirelessData; HunkVector stickmen; SingleDiff> signs; + SingleDiff FrameCount; + SingleDiff> RngState; SingleDiff Authors; diff --git a/src/simulation/meson.build b/src/simulation/meson.build index e06026dd1..76cb360d5 100644 --- a/src/simulation/meson.build +++ b/src/simulation/meson.build @@ -21,6 +21,7 @@ powder_files += files( 'Editing.cpp', 'SimTool.cpp', 'ToolClasses.cpp', + 'Snapshot.cpp', 'SnapshotDelta.cpp', ) render_files += files(