// ChunkDef.h // Interfaces to helper types for chunk definitions. Most modules want to include this instead of cChunk.h #pragma once #include "Vector3.h" #include "BiomeDef.h" // Used to smoothly convert to new axis ordering. One will be removed when deemed stable. #define AXIS_ORDER_YZX 1 // Original (1.1-) #define AXIS_ORDER_XZY 2 // New (1.2+) #define AXIS_ORDER AXIS_ORDER_XZY // fwd class cBlockEntity; class cEntity; class cClientHandle; class cBlockEntity; typedef std::list cEntityList; typedef std::list cBlockEntityList; // tolua_begin /// The datatype used by blockdata typedef unsigned char BLOCKTYPE; /// The datatype used by nibbledata (meta, light, skylight) typedef unsigned char NIBBLETYPE; /// The type used by the heightmap typedef unsigned char HEIGHTTYPE; // tolua_end /// Constants used throughout the code, useful typedefs and utility functions class cChunkDef { public: // Chunk dimensions: static const int Width = 16; static const int Height = 256; static const int NumBlocks = Width * Height * Width; /// If the data is collected into a single buffer, how large it needs to be: static const int BlockDataSize = cChunkDef::NumBlocks * 2 + (cChunkDef::NumBlocks / 2); // 2.5 * numblocks /// The type used for any heightmap operations and storage; idx = x + Width * z; Height points to the highest non-air block in the column typedef HEIGHTTYPE HeightMap[Width * Width]; /** The type used for any biomemap operations and storage inside MCServer, using MCServer biomes (need not correspond to client representation!) idx = x + Width * z // Need to verify this with the protocol spec, currently unknown! */ typedef EMCSBiome BiomeMap[Width * Width]; /// The type used for block type operations and storage, AXIS_ORDER ordering typedef BLOCKTYPE BlockTypes[NumBlocks]; /// The type used for block data in nibble format, AXIS_ORDER ordering typedef NIBBLETYPE BlockNibbles[NumBlocks / 2]; /** The storage wrapper used for compressed blockdata residing in RAMz */ typedef std::vector COMPRESSED_BLOCKTYPE; /** The storage wrapper used for compressed nibbledata residing in RAMz */ typedef std::vector COMPRESSED_NIBBLETYPE; /// Converts absolute block coords into relative (chunk + block) coords: inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ) { UNUSED(a_Y); BlockToChunk(a_X, a_Z, a_ChunkX, a_ChunkZ); a_X = a_X - a_ChunkX * Width; a_Z = a_Z - a_ChunkZ * Width; } /// Converts absolute block coords to chunk coords: inline static void BlockToChunk(int a_X, int a_Z, int & a_ChunkX, int & a_ChunkZ) { a_ChunkX = a_X / Width; if ((a_X < 0) && (a_X % Width != 0)) { a_ChunkX--; } a_ChunkZ = a_Z / cChunkDef::Width; if ((a_Z < 0) && (a_Z % Width != 0)) { a_ChunkZ--; } } inline static int MakeIndex(int x, int y, int z) { if ( (x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1) ) { return MakeIndexNoCheck(x, y, z); } LOGERROR("cChunkDef::MakeIndex(): coords out of range: {%d, %d, %d}; returning fake index 0", x, y, z); ASSERT(!"cChunkDef::MakeIndex(): coords out of chunk range!"); return 0; } inline static int MakeIndexNoCheck(int x, int y, int z) { #if AXIS_ORDER == AXIS_ORDER_XZY // For some reason, NOT using the Horner schema is faster. Weird. return x + (z * cChunkDef::Width) + (y * cChunkDef::Width * cChunkDef::Width); // 1.2 uses XZY #elif AXIS_ORDER == AXIS_ORDER_YZX return y + (z * cChunkDef::Width) + (x * cChunkDef::Height * cChunkDef::Width); // 1.1 uses YZX #endif } inline static Vector3i IndexToCoordinate( unsigned int index) { #if AXIS_ORDER == AXIS_ORDER_XZY return Vector3i( // 1.2 index % cChunkDef::Width, // X index / (cChunkDef::Width * cChunkDef::Width), // Y (index / cChunkDef::Width) % cChunkDef::Width // Z ); #elif AXIS_ORDER == AXIS_ORDER_YZX return Vector3i( // 1.1 index / (cChunkDef::Height * cChunkDef::Width), // X index % cChunkDef::Height, // Y (index / cChunkDef::Height) % cChunkDef::Width // Z ); #endif } inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z, BLOCKTYPE a_Type) { ASSERT((a_X >= 0) && (a_X < Width)); ASSERT((a_Y >= 0) && (a_Y < Height)); ASSERT((a_Z >= 0) && (a_Z < Width)); a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)] = a_Type; } inline static void SetBlock(BLOCKTYPE * a_BlockTypes, int a_Index, BLOCKTYPE a_Type) { ASSERT((a_Index >= 0) && (a_Index <= NumBlocks)); a_BlockTypes[a_Index] = a_Type; } inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_X, int a_Y, int a_Z) { ASSERT((a_X >= 0) && (a_X < Width)); ASSERT((a_Y >= 0) && (a_Y < Height)); ASSERT((a_Z >= 0) && (a_Z < Width)); return a_BlockTypes[MakeIndexNoCheck(a_X, a_Y, a_Z)]; } inline static BLOCKTYPE GetBlock(const BLOCKTYPE * a_BlockTypes, int a_Idx) { ASSERT((a_Idx >= 0) && (a_Idx < NumBlocks)); return a_BlockTypes[a_Idx]; } inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z) { ASSERT((a_X >= 0) && (a_X < Width)); ASSERT((a_Z >= 0) && (a_Z < Width)); return a_HeightMap[a_X + Width * a_Z]; } inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height) { ASSERT((a_X >= 0) && (a_X < Width)); ASSERT((a_Z >= 0) && (a_Z < Width)); a_HeightMap[a_X + Width * a_Z] = a_Height; } inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z) { ASSERT((a_X >= 0) && (a_X < Width)); ASSERT((a_Z >= 0) && (a_Z < Width)); return a_BiomeMap[a_X + Width * a_Z]; } inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome) { ASSERT((a_X >= 0) && (a_X < Width)); ASSERT((a_Z >= 0) && (a_Z < Width)); a_BiomeMap[a_X + Width * a_Z] = a_Biome; } static NIBBLETYPE GetNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int a_BlockIdx, bool a_IsSkyLightNibble = false) { if ((a_BlockIdx > -1) && (a_BlockIdx < NumBlocks)) { if (static_cast(a_BlockIdx / 2) >= a_Buffer.size()) { return (a_IsSkyLightNibble ? 0xff : 0); } return (a_Buffer[static_cast(a_BlockIdx / 2)] >> ((a_BlockIdx & 1) * 4)) & 0x0f; } ASSERT(!"cChunkDef::GetNibble(): index out of chunk range!"); return 0; } static NIBBLETYPE GetNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int x, int y, int z, bool a_IsSkyLightNibble = false) { if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1)) { size_t Index = static_cast(MakeIndexNoCheck(x, y, z)); if ((Index / 2) >= a_Buffer.size()) { return (a_IsSkyLightNibble ? 0xff : 0); } return ExpandNibble(a_Buffer, Index); } ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!"); return 0; } static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int x, int y, int z) { if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1)) { int Index = MakeIndexNoCheck(x, y, z); return (a_Buffer[static_cast(Index / 2)] >> ((Index & 1) * 4)) & 0x0f; } ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!"); return 0; } static void SetNibble(COMPRESSED_NIBBLETYPE & a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble) { if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks)) { ASSERT(!"cChunkDef::SetNibble(): index out of range!"); return; } if (static_cast(a_BlockIdx / 2) >= a_Buffer.size()) { a_Buffer.resize(static_cast((a_BlockIdx / 2) + 1)); } a_Buffer[static_cast(a_BlockIdx / 2)] = PackNibble(a_Buffer, static_cast(a_BlockIdx), a_Nibble); } static void SetNibble(COMPRESSED_NIBBLETYPE & a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble) { if ( (x >= Width) || (x < 0) || (y >= Height) || (y < 0) || (z >= Width) || (z < 0) ) { ASSERT(!"cChunkDef::SetNibble(): index out of range!"); return; } size_t Index = static_cast(MakeIndexNoCheck(x, y, z)); if ((Index / 2) >= a_Buffer.size()) { a_Buffer.resize(((Index / 2) + 1)); } a_Buffer[(Index / 2)] = PackNibble(a_Buffer, Index, a_Nibble); } private: inline static NIBBLETYPE PackNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, size_t a_Index, NIBBLETYPE a_Nibble) { return static_cast( (a_Buffer[a_Index / 2] & (0xf0 >> ((a_Index & 1) * 4))) | // The untouched nibble ((a_Nibble & 0x0f) << ((a_Index & 1) * 4)) // The nibble being set ); } inline static NIBBLETYPE ExpandNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, size_t a_Index) { return (a_Buffer[a_Index / 2] >> ((a_Index & 1) * 4)) & 0x0f; } } ; /** Interface class used for comparing clients of two chunks. Used primarily for entity moving while both chunks are locked. */ class cClientDiffCallback { public: virtual ~cClientDiffCallback() {} /// Called for clients that are in Chunk1 and not in Chunk2, virtual void Removed(cClientHandle * a_Client) = 0; /// Called for clients that are in Chunk2 and not in Chunk1. virtual void Added(cClientHandle * a_Client) = 0; } ; struct sSetBlock { int x, y, z; int ChunkX, ChunkZ; BLOCKTYPE BlockType; NIBBLETYPE BlockMeta; sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // absolute block position sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) : x(a_X), y(a_Y), z(a_Z), ChunkX(a_ChunkX), ChunkZ(a_ChunkZ), BlockType(a_BlockType), BlockMeta(a_BlockMeta) {} }; typedef std::list sSetBlockList; typedef std::vector sSetBlockVector; class cChunkCoords { public: int m_ChunkX; int m_ChunkZ; cChunkCoords(int a_ChunkX, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ) {} bool operator == (const cChunkCoords & a_Other) const { return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkZ == a_Other.m_ChunkZ)); } } ; typedef std::list cChunkCoordsList; typedef std::vector cChunkCoordsVector; class cChunkCoordsWithBool { public: int m_ChunkX; int m_ChunkZ; bool m_ForceGenerate; cChunkCoordsWithBool(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ), m_ForceGenerate(a_ForceGenerate){} bool operator == (const cChunkCoordsWithBool & a_Other) const { return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkZ == a_Other.m_ChunkZ) && (m_ForceGenerate == a_Other.m_ForceGenerate)); } }; typedef std::list cChunkCoordsWithBoolList; /// Interface class used as a callback for operations that involve chunk coords class cChunkCoordCallback { public: virtual ~cChunkCoordCallback() {} virtual void Call(int a_ChunkX, int a_ChunkZ) = 0; } ; /** Generic template that can store any kind of data together with a triplet of 3 coords*/ template class cCoordWithData { public: int x; int y; int z; X Data; cCoordWithData(int a_X, int a_Y, int a_Z) : x(a_X), y(a_Y), z(a_Z), Data() { } cCoordWithData(int a_X, int a_Y, int a_Z, const X & a_Data) : x(a_X), y(a_Y), z(a_Z), Data(a_Data) { } } ; typedef cCoordWithData cCoordWithInt; typedef cCoordWithData cCoordWithBlock; typedef std::list cCoordWithIntList; typedef std::vector cCoordWithIntVector; /** Generic template that can store two types of any kind of data together with a triplet of 3 coords */ template class cCoordWithDoubleData { public: int x; int y; int z; X Data; Z DataTwo; cCoordWithDoubleData(int a_X, int a_Y, int a_Z) : x(a_X), y(a_Y), z(a_Z) { } cCoordWithDoubleData(int a_X, int a_Y, int a_Z, const X & a_Data, const Z & a_DataTwo) : x(a_X), y(a_Y), z(a_Z), Data(a_Data), DataTwo(a_DataTwo) { } }; typedef cCoordWithDoubleData cCoordWithBlockAndBool; typedef std::vector cCoordWithBlockAndBoolVector;