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:
parent
d1c95742dd
commit
66e73a2d68
@ -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);
|
cCSLock Lock(m_CSChunks);
|
||||||
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
|
cChunkPtr Chunk = GetChunkNoGen(a_Coords);
|
||||||
if ((Chunk == nullptr) || !Chunk->IsValid())
|
if ((Chunk == nullptr) || !Chunk->IsValid())
|
||||||
{
|
{
|
||||||
|
// The chunk is not present
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Chunk->GetAllData(a_Callback);
|
Chunk->GetAllData(a_Callback);
|
||||||
|
@ -108,7 +108,9 @@ public:
|
|||||||
const cChunkDef::BlockNibbles & a_SkyLight
|
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 */
|
/** Copies the chunk's blocktypes into a_Blocks; returns true if successful */
|
||||||
bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks);
|
bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks);
|
||||||
@ -439,7 +441,7 @@ private:
|
|||||||
|
|
||||||
typedef std::list<cChunkStay *> cChunkStays;
|
typedef std::list<cChunkStay *> cChunkStays;
|
||||||
|
|
||||||
cCriticalSection m_CSChunks;
|
mutable cCriticalSection m_CSChunks;
|
||||||
|
|
||||||
/** A map of chunk coordinates to chunk pointers
|
/** A map of chunk coordinates to chunk pointers
|
||||||
Uses a map (as opposed to unordered_map) because sorted maps are apparently faster */
|
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
|
// Deprecated in favor of the vector version
|
||||||
cChunkPtr GetChunkNoGen(int a_ChunkX, int a_ChunkZ)
|
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 */
|
/** Constructs a chunk, returning it. Doesn't load, doesn't generate */
|
||||||
|
@ -241,7 +241,7 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set<cCli
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Query and prepare chunk data:
|
// Query and prepare chunk data:
|
||||||
if (!m_World.GetChunkData(a_ChunkX, a_ChunkZ, *this))
|
if (!m_World.GetChunkData({a_ChunkX, a_ChunkZ}, *this))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -336,7 +336,7 @@ void cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ)
|
|||||||
for (int x = 0; x < 3; x++)
|
for (int x = 0; x < 3; x++)
|
||||||
{
|
{
|
||||||
Reader.m_ReadingChunkX = 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 z
|
||||||
} // for x
|
} // for x
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -234,7 +234,9 @@ public:
|
|||||||
const cChunkDef::BlockNibbles & a_SkyLight
|
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 */
|
/** Gets the chunk's blocks, only the block types */
|
||||||
bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes);
|
bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,139 +1,27 @@
|
|||||||
|
|
||||||
// NBTChunkSerializer.h
|
// NBTChunkSerializer.h
|
||||||
|
|
||||||
// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../ChunkDataCallback.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// fwd:
|
// fwd:
|
||||||
class cFastNBTWriter;
|
class cFastNBTWriter;
|
||||||
class cEntity;
|
class cWorld;
|
||||||
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 cNBTChunkSerializer :
|
/** Saves the chunk data into a NBT format, used by the Anvil storage.
|
||||||
public cChunkDataCopyCollector
|
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:
|
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);
|
||||||
|
};
|
||||||
|
@ -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)
|
bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer)
|
||||||
{
|
{
|
||||||
a_Writer.BeginCompound("Level");
|
if (!NBTChunkSerializer::serialize(*m_World, a_Chunk, a_Writer))
|
||||||
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))
|
|
||||||
{
|
{
|
||||||
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;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user