1
0
Fork 0

NBTChunkSerializer: Cleaned up interface.

Removed dependency on cChunkDataCallback.
Moved all the serializing code into a worker class.
Changed the serialization into a single-call action.
This commit is contained in:
Mattes D 2019-09-24 14:20:50 +02:00
parent d1c95742dd
commit 66e73a2d68
9 changed files with 1119 additions and 1170 deletions

View File

@ -444,12 +444,18 @@ void cChunkMap::ChunkLighted(
bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback)
bool cChunkMap::GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callback)
{
if (!a_Callback.Coords(a_Coords.m_ChunkX, a_Coords.m_ChunkZ))
{
// The callback doesn't want the data
return false;
}
cCSLock Lock(m_CSChunks);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(a_Coords);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
// The chunk is not present
return false;
}
Chunk->GetAllData(a_Callback);

View File

@ -108,7 +108,9 @@ public:
const cChunkDef::BlockNibbles & a_SkyLight
);
bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback);
/** Calls the callback with the chunk's data, if available (with ChunkCS locked).
Returns true if the chunk was reported successfully, false if not (chunk not present or callback failed). */
bool GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callback);
/** Copies the chunk's blocktypes into a_Blocks; returns true if successful */
bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks);
@ -439,7 +441,7 @@ private:
typedef std::list<cChunkStay *> cChunkStays;
cCriticalSection m_CSChunks;
mutable cCriticalSection m_CSChunks;
/** A map of chunk coordinates to chunk pointers
Uses a map (as opposed to unordered_map) because sorted maps are apparently faster */
@ -468,7 +470,7 @@ private:
// Deprecated in favor of the vector version
cChunkPtr GetChunkNoGen(int a_ChunkX, int a_ChunkZ)
{
return GetChunkNoGen(cChunkCoords(a_ChunkX, a_ChunkZ));
return GetChunkNoGen({a_ChunkX, a_ChunkZ});
}
/** Constructs a chunk, returning it. Doesn't load, doesn't generate */

View File

@ -241,7 +241,7 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set<cCli
}
// Query and prepare chunk data:
if (!m_World.GetChunkData(a_ChunkX, a_ChunkZ, *this))
if (!m_World.GetChunkData({a_ChunkX, a_ChunkZ}, *this))
{
return;
}

View File

@ -336,7 +336,7 @@ void cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ)
for (int x = 0; x < 3; x++)
{
Reader.m_ReadingChunkX = x;
VERIFY(m_World.GetChunkData(a_ChunkX + x - 1, a_ChunkZ + z - 1, Reader));
VERIFY(m_World.GetChunkData({a_ChunkX + x - 1, a_ChunkZ + z - 1}, Reader));
} // for z
} // for x

View File

@ -2636,9 +2636,9 @@ void cWorld::ChunkLighted(
bool cWorld::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback)
bool cWorld::GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callback) const
{
return m_ChunkMap->GetChunkData(a_ChunkX, a_ChunkZ, a_Callback);
return m_ChunkMap->GetChunkData(a_Coords, a_Callback);
}

View File

@ -234,7 +234,9 @@ public:
const cChunkDef::BlockNibbles & a_SkyLight
);
bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback);
/** Calls the callback with the chunk's data, if available (with ChunkCS locked).
Returns true if the chunk was reported successfully, false if not (chunk not present or callback failed). */
bool GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callback) const;
/** Gets the chunk's blocks, only the block types */
bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes);

File diff suppressed because it is too large Load Diff

View File

@ -1,139 +1,27 @@
// NBTChunkSerializer.h
// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil
#pragma once
#include "../ChunkDataCallback.h"
// fwd:
class cFastNBTWriter;
class cEntity;
class cBlockEntity;
class cBoat;
class cBeaconEntity;
class cBedEntity;
class cBrewingstandEntity;
class cChestEntity;
class cCommandBlockEntity;
class cDispenserEntity;
class cDropperEntity;
class cEnderCrystal;
class cFurnaceEntity;
class cHopperEntity;
class cJukeboxEntity;
class cNoteEntity;
class cSignEntity;
class cMobHeadEntity;
class cMobSpawnerEntity;
class cFlowerPotEntity;
class cFallingBlock;
class cMinecart;
class cMinecartWithChest;
class cMonster;
class cPickup;
class cItemGrid;
class cProjectileEntity;
class cTNTEntity;
class cExpOrb;
class cHangingEntity;
class cItemFrame;
class cLeashKnot;
class cPainting;
class cWorld;
class cNBTChunkSerializer :
public cChunkDataCopyCollector
/** Saves the chunk data into a NBT format, used by the Anvil storage.
The Writer is expected to be set up so that the serializer can write the chunk's top level "Level" NBT tag immediately.
Provides a single static entry point that does all the work, through a hidden worker class in the CPP file. */
class NBTChunkSerializer
{
public:
cChunkDef::BiomeMap m_Biomes;
unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width];
int m_VanillaHeightMap[cChunkDef::Width * cChunkDef::Width];
bool m_BiomesAreValid;
cNBTChunkSerializer(cFastNBTWriter & a_Writer);
/** Close NBT tags that we've opened */
void Finish(void);
bool IsLightValid(void) const { return m_IsLightValid; }
protected:
/* From cChunkDataCopyCollector we inherit:
- cChunkData m_Data */
cFastNBTWriter & m_Writer;
bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed.
bool m_HasHadEntity; // True if any Entity has already been received and processed
bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed
bool m_IsLightValid; // True if the chunk lighting is valid
/** Writes an item into the writer, if slot >= 0, adds the Slot tag. The compound is named as requested. */
void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = "");
/** Writes an item grid into the writer; begins the stored slot numbers with a_BeginSlotNum. Note that it doesn't begin nor end the list tag */
void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0);
// Block entities:
void AddBasicTileEntity (cBlockEntity * a_Entity, const char * a_EntityTypeID);
void AddBeaconEntity (cBeaconEntity * a_Entity);
void AddBedEntity (cBedEntity * a_Entity);
void AddBrewingstandEntity(cBrewingstandEntity * a_Brewingstand);
void AddChestEntity (cChestEntity * a_Entity, BLOCKTYPE a_ChestType);
void AddDispenserEntity (cDispenserEntity * a_Entity);
void AddDropperEntity (cDropperEntity * a_Entity);
void AddFurnaceEntity (cFurnaceEntity * a_Furnace);
void AddHopperEntity (cHopperEntity * a_Entity);
void AddJukeboxEntity (cJukeboxEntity * a_Jukebox);
void AddMobSpawnerEntity (cMobSpawnerEntity * a_MobSpawner);
void AddNoteEntity (cNoteEntity * a_Note);
void AddSignEntity (cSignEntity * a_Sign);
void AddMobHeadEntity (cMobHeadEntity * a_MobHead);
void AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock);
void AddFlowerPotEntity (cFlowerPotEntity * a_FlowerPot);
// Entities:
void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName);
void AddBoatEntity (cBoat * a_Boat);
void AddEnderCrystalEntity(cEnderCrystal * a_EnderCrystal);
void AddFallingBlockEntity(cFallingBlock * a_FallingBlock);
void AddMinecartEntity (cMinecart * a_Minecart);
void AddMonsterEntity (cMonster * a_Monster);
void AddPickupEntity (cPickup * a_Pickup);
void AddProjectileEntity (cProjectileEntity * a_Projectile);
void AddHangingEntity (cHangingEntity * a_Hanging);
void AddTNTEntity (cTNTEntity * a_TNT);
void AddExpOrbEntity (cExpOrb * a_ExpOrb);
void AddItemFrameEntity (cItemFrame * a_ItemFrame);
void AddLeashKnotEntity (cLeashKnot * a_LeashKnot);
void AddPaintingEntity (cPainting * a_Painting);
void AddMinecartChestContents(cMinecartWithChest * a_Minecart);
// cChunkDataSeparateCollector overrides:
virtual void LightIsValid(bool a_IsLightValid) override;
virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) override;
virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override;
virtual void Entity(cEntity * a_Entity) override;
virtual void BlockEntity(cBlockEntity * a_Entity) override;
} ; // class cNBTChunkSerializer
/** Serializes the chunk into the specified writer.
Returns true on success, false on failure (chunk not present etc.) */
static bool serialize(const cWorld & aWorld, cChunkCoords aCoords, cFastNBTWriter & aWriter);
};

View File

@ -491,68 +491,11 @@ void cWSSAnvil::CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString &
bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer)
{
a_Writer.BeginCompound("Level");
a_Writer.AddInt("xPos", a_Chunk.m_ChunkX);
a_Writer.AddInt("zPos", a_Chunk.m_ChunkZ);
cNBTChunkSerializer Serializer(a_Writer);
if (!m_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer))
if (!NBTChunkSerializer::serialize(*m_World, a_Chunk, a_Writer))
{
LOGWARNING("Cannot get chunk [%d, %d] data for NBT saving", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
LOGWARNING("Failed to save chunk %s.", a_Chunk.ToString());
return false;
}
Serializer.Finish(); // Close NBT tags
// Save biomes, both MCS (IntArray) and MC-vanilla (ByteArray):
if (Serializer.m_BiomesAreValid)
{
a_Writer.AddByteArray("Biomes", reinterpret_cast<const char *>(Serializer.m_VanillaBiomes), ARRAYCOUNT(Serializer.m_VanillaBiomes));
a_Writer.AddIntArray ("MCSBiomes", reinterpret_cast<const int *>(Serializer.m_Biomes), ARRAYCOUNT(Serializer.m_Biomes));
}
// Save heightmap (Vanilla require this):
a_Writer.AddIntArray("HeightMap", reinterpret_cast<const int *>(Serializer.m_VanillaHeightMap), ARRAYCOUNT(Serializer.m_VanillaHeightMap));
// Save blockdata:
a_Writer.BeginList("Sections", TAG_Compound);
for (size_t Y = 0; Y != cChunkData::NumSections; ++Y)
{
auto Section = Serializer.m_Data.GetSection(Y);
if (Section == nullptr)
{
continue;
}
a_Writer.BeginCompound("");
a_Writer.AddByteArray("Blocks", reinterpret_cast<const char *>(Section->m_BlockTypes), ARRAYCOUNT(Section->m_BlockTypes));
a_Writer.AddByteArray("Data", reinterpret_cast<const char *>(Section->m_BlockMetas), ARRAYCOUNT(Section->m_BlockMetas));
#ifdef DEBUG_SKYLIGHT
a_Writer.AddByteArray("BlockLight", reinterpret_cast<const char *>(Section->m_BlockSkyLight), ARRAYCOUNT(Section->m_BlockSkyLight));
#else
a_Writer.AddByteArray("BlockLight", reinterpret_cast<const char *>(Section->m_BlockLight), ARRAYCOUNT(Section->m_BlockLight));
#endif
a_Writer.AddByteArray("SkyLight", reinterpret_cast<const char *>(Section->m_BlockSkyLight), ARRAYCOUNT(Section->m_BlockSkyLight));
a_Writer.AddByte("Y", static_cast<unsigned char>(Y));
a_Writer.EndCompound();
}
a_Writer.EndList(); // "Sections"
// Store the information that the lighting is valid.
// For compatibility reason, the default is "invalid" (missing) - this means older data is re-lighted upon loading.
if (Serializer.IsLightValid())
{
a_Writer.AddByte("MCSIsLightValid", 1);
}
// Save the world age to the chunk data. Required by vanilla and mcedit.
a_Writer.AddLong("LastUpdate", m_World->GetWorldAge());
// Store the flag that the chunk has all the ores, trees, dungeons etc. MCS chunks are always complete.
a_Writer.AddByte("TerrainPopulated", 1);
a_Writer.EndCompound(); // "Level"
return true;
}