summaryrefslogblamecommitdiffstats
path: root/src/BlockArea.cpp
blob: 53d82a10696d46c60812f7118cb797b4adb46e2b (plain) (tree)
1
2
3
4
5
6
7
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030


                



                                                                                                  




                                                                                                                                                      
                               
                                
                   
                      
                                      



 








                                                              


                                                                                                                        

                                                                                                                   
                                                     
                         
                                                             
                                                               



                                                       
                                                      

 

                           











                                                                               





                                                                                                                                   
                                                                    
                                                                                                                                     
                                 










                                         
                                                          
                         
                                                                                                                       

                              



                                      





 
                                                        
                         
                                                                                                                     



                                      



                                              







                                                  
                                                        
                         
                                                                                                                     



                                      



                                              







                                                  
                                                     
                         
                                                                                                                  






                                        
                                     


                                        



                                      

                       
 










                                               
 








                                              



                                                      


                               
 








                                                          



                                                      










                                           
                                                            
                         
                                                                                                                         




                                                                         



                                              






         
                                                           
                         
                                                                                                                        
 
                                                                                 

                                        



                                      



                                      



                                              






         



















                                                                                                                           
                                                     
                         
                                                                                                                  

                                                                                
                                                                               

                                        



                                      


         




                                              



 
                                                                                


                              



                                















                         


















                                                                

                            



                                                            





                                                     

                              







                                                                               



                                                        





                                                                                                                                                
 

































                                                             



                                                                       

                                              





 








                                                                 
                                                                         
 
                                                        





 
                                                       
 
                                                           





 

                                                                       
                                                      





 

                                                     
                                                         





 







































                                                                                                     
                                                                                                                                                                                            
 





                                                        
 



                                  
 





                                                                                                                   
                                                            
                                   
 




                                                                                                   
 
                            
                                                                                                           



                             
 






                    
                                                                                                                













                                             
                                                                                                                                            













                                       
                                                                                                                                          

                                                                                                                

                                                                     
 
                                                                                                                





 
                                                                                                                     











                                                            






                                                                               
 
                       

                                                                     
                                            















                                                                                                 








                                                                                                                    






















                                                                                                   


                                                            


                           
                                                                             
                               
                                           





















                                                                     




 
 







                                                                                                               


                                                      


                                                                                                                                    
                                                     



                                                                                           
 















                                                                                                               




                                                 
 



                                                   
                 





















                                                                                                                           


                 



                                                       























                                                                                                                 
                               
         











                                                                                              

         



                                                          







                                                                                                               


                                                            
 
                                                                             
 

                         
                                                                                                      
         
            
         
                                                                                                     






         








                                                                                                            








                                                                                                                                             
 
                                            

                                         
                                                       





                                                      
                                                       





                                                      
                                                       





                                                       
                                                       



                                                             





                                                                                                                                 





 
                                                                                                                        










                                                                                                                                             
 



























                                                                                                                                                      





                                                                                                                                            





 
















                                                                                    











                                                                                                      





                                                  
                        






























                                                                                                                                    
                        
































                                                                                                                                    
                        


















                                                                                                                                    
 











                                              















                                                                                    






                                                                                      
 





                                                                                  
 
                                                                                                     


                                                                
         

                                                  

                                     
                                                          
                         
                                                                                              







                                                                                                                           

                                                
 
















                                                                                          
                                      



















                                                                                      
 
                                                                                                     


                                                                

                             
                                                  
                 

                                                          
                         
                                                                                              







                                                                                                                          

                                                
 
















                                                                                          
                                      












                                                                                 
 







                                                                                                     


                                          
         
                                                
                 
                                                          










                                                                                                                      
















                                                                         





















                                                                                                     

                                 
                                        
         
                                                  
                 
                                                          










                                                                                                                      
















                                                                         





















                                                                                                     


                                          
         
                                                  
                 
                                                        










                                                                                                                      
















                                                                         









                                      

                                                                      
                 

                                                          

                                             
                                                                  
                                 
                                                                                                                                      



                                                  
                                                        


                            

                                                                        
                 

                                                          

                                             
                                                                  
                                 
                                                                                                                                      



                                                  
                                                        
         

















                                                                                          
                                      









                                     

                                                                      
                 

                                                          

                                             
                                                                  
                                 
                                                                                                                                      



                                                  
                                                        


                            

                                                                        
                 

                                                          

                                             
                                                                  
                                 
                                                                                                                                      



                                                  
                                                        
         

















                                                                                          
                                      







                                     

                                 

                            
                                                  


                                                       
                                                                  





                                                                                                                             
 

                            
                                                  


                                                       
                                                                  





                                                                                                                             
















                                                                         







                                     

                                 



                                               
                                                          
                         
                                                                  





                                                                                                                             
 



                                               
                                                          
                         
                                                                  





                                                                                                                             
















                                                                         







                                     

                                 

                            
                                                  
                 
                                                          







                                                                                                                             
 

                            
                                                  
                 
                                                          







                                                                                                                             
















                                                                         







                                                                                           





                                                     
         














                                                                                                                                   
         







                                                                                              
                                                                                                          





























































                                                                                                       
                                    












                                                                                  
                                                                                                    





























































                                                                                                                          
                                                                                                                           








                                                                                                                             
                                    






                                                                                                         
                                    






                                                                                                         


















                                                                                                                                          







                                                                                                                                    
                                                                                                                                  








                                                                                                                                 
                                    







                                                                         
 
                                    













                                                                         






























                                                                                    


























































                                                                                                     


































































                                                                                                                                                                          


                                        
                                    


                               
                                    


                               
                                    


                               
                                       


                                  



                                       








                                                                                
                                                             
                                                        
 
                                         

                                                                          
                                            



                                     
                                         

                                                                           
                                            

                                              
                                               


                                     
                                         

                                                                           
                                            

                                              
                                               
                                              
                                               


                                     
                                            

                                                                              
                                               

                                              
                                               
                                              
                                               
                                              
                                               


                                     















                                                                       
                                              









                                                                   
                                  
                            
                                  
                            
                                  
 
                                                                         





 
















































                                                                                                                        

                                                                                                           
                               












                                                                                                              
                                                                                                            







                                                                                                   
                               












                                                                                                      
                                                                                                          






 
                                                                                                                         
 

















                                                                                                      





 
                                                                                                                                             

























                                                                                                                           


                                                        
                                                                                


                                                                 
                                          
         

                                                                       
                 
                                                                                 
                                                         
                                                          














                                                                                                                                               


                                                        
                                                                                


                                                               
                                          
         

                                                                       
                 
                                                                                 
                                                         
                                                          









                                                                        


 





                                                                       




                                                   
















                                                         




















                                                                                                                                            



 

 
                          

                                                                                                                                                                             
                                                               




                                                                                                               
 











                                                                                                             
 












                                                                                               
                              
                                      
 










                                                                                             
                              
                                    
 










                                                                                             
                              
                                    
 










                                                                                          
                              
                                 
 










                                                                                                 
                              












                                                                                                
                              
                                       
 










                                                                                                   
                              
                                          
 










                                                                                          
                              
                                 








                                                                                                             
                                 
 























                                                                          


 
 










































































































































































































































































































































































                                                                                                                                                                

// BlockArea.cpp

// NOTE: compile.sh checks for this file in order to determine if this is the Cuberite folder.
// Please modify compile.sh if you want to rename or remove this file.
// This file was chosen arbitrarily and it's a good enough indicator we're in the Cuberite folder.

// Implements the cBlockArea object representing an area of block data that can be queried from cWorld and then accessed again without further queries
// The object also supports writing the blockdata back into cWorld, even into other coords

#include "Globals.h"
#include "BlockArea.h"
#include "OSSupport/GZipFile.h"
#include "Blocks/BlockHandler.h"
#include "Cuboid.h"
#include "ChunkData.h"
#include "BlockEntities/BlockEntity.h"




// Disable MSVC warnings: "conditional expression is constant"
#ifdef _MSC_VER
	#pragma warning(push)
	#pragma warning(disable:4127)
#endif





typedef void (CombinatorFunc)(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta);

/** Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function
This wild construct allows us to pass a function argument and still have it inlined by the compiler. */
template <bool MetasValid, CombinatorFunc Combinator>
void InternalMergeBlocks(
	BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes,
	NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas,
	int a_SizeX, int a_SizeY, int a_SizeZ,
	int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ,
	int a_DstOffX, int a_DstOffY, int a_DstOffZ,
	int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,
	int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ
)
{
	UNUSED(a_SrcSizeY);
	UNUSED(a_DstSizeY);
	for (int y = 0; y < a_SizeY; y++)
	{
		int SrcBaseY = (y + a_SrcOffY) * a_SrcSizeX * a_SrcSizeZ;
		int DstBaseY = (y + a_DstOffY) * a_DstSizeX * a_DstSizeZ;
		for (int z = 0; z < a_SizeZ; z++)
		{
			int SrcBaseZ = SrcBaseY + (z + a_SrcOffZ) * a_SrcSizeX;
			int DstBaseZ = DstBaseY + (z + a_DstOffZ) * a_DstSizeX;
			int SrcIdx = SrcBaseZ + a_SrcOffX;
			int DstIdx = DstBaseZ + a_DstOffX;
			for (int x = 0; x < a_SizeX; x++)
			{
				if (MetasValid)
				{
					Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]);
				}
				else
				{
					NIBBLETYPE FakeDestMeta = 0;
					Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], FakeDestMeta, static_cast<NIBBLETYPE>(0));
				}
				++DstIdx;
				++SrcIdx;
			}  // for x
		}  // for z
	}  // for y
}





/** Combinator used for cBlockArea::msOverwrite merging */
template <bool MetaValid>
void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
	a_DstType = a_SrcType;
	if (MetaValid)
	{
		a_DstMeta = a_SrcMeta;
	}
}





/** Combinator used for cBlockArea::msFillAir merging */
template <bool MetaValid>
void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
	if (a_DstType == E_BLOCK_AIR)
	{
		a_DstType = a_SrcType;
		if (MetaValid)
		{
			a_DstMeta = a_SrcMeta;
		}
	}
	// "else" is the default, already in place
}





/** Combinator used for cBlockArea::msImprint merging */
template <bool MetaValid>
void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
	if (a_SrcType != E_BLOCK_AIR)
	{
		a_DstType = a_SrcType;
		if (MetaValid)
		{
			a_DstMeta = a_SrcMeta;
		}
	}
	// "else" is the default, already in place
}





/** Combinator used for cBlockArea::msLake merging */
template <bool MetaValid>
void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
	// Sponge is the NOP block
	if (a_SrcType == E_BLOCK_SPONGE)
	{
		return;
	}

	// Air is always hollowed out
	if (a_SrcType == E_BLOCK_AIR)
	{
		a_DstType = E_BLOCK_AIR;
		if (MetaValid)
		{
			a_DstMeta = 0;
		}
		return;
	}

	// Water and lava are never overwritten
	switch (a_DstType)
	{
		case E_BLOCK_WATER:
		case E_BLOCK_STATIONARY_WATER:
		case E_BLOCK_LAVA:
		case E_BLOCK_STATIONARY_LAVA:
		{
			return;
		}
	}

	// Water and lava always overwrite
	switch (a_SrcType)
	{
		case E_BLOCK_WATER:
		case E_BLOCK_STATIONARY_WATER:
		case E_BLOCK_LAVA:
		case E_BLOCK_STATIONARY_LAVA:
		{
			a_DstType = a_SrcType;
			if (MetaValid)
			{
				a_DstMeta = a_SrcMeta;
			}
			return;
		}
	}

	if (a_SrcType == E_BLOCK_STONE)
	{
		switch (a_DstType)
		{
			case E_BLOCK_DIRT:
			case E_BLOCK_GRASS:
			case E_BLOCK_MYCELIUM:
			{
				a_DstType = E_BLOCK_STONE;
				if (MetaValid)
				{
					a_DstMeta = 0;
				}
				return;
			}
		}
	}
	// Everything else is left as it is
}





/** Combinator used for cBlockArea::msSpongePrint merging */
template <bool MetaValid>
void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
	// Sponge overwrites nothing, everything else overwrites anything
	if (a_SrcType != E_BLOCK_SPONGE)
	{
		a_DstType = a_SrcType;
		if (MetaValid)
		{
			a_DstMeta = a_SrcMeta;
		}
	}
}





/** Combinator used for cBlockArea::msDifference merging */
template <bool MetaValid>
void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
	if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta)))
	{
		a_DstType = E_BLOCK_AIR;
		if (MetaValid)
		{
			a_DstMeta = 0;
		}
	}
	else
	{
		a_DstType = a_SrcType;
		if (MetaValid)
		{
			a_DstMeta = a_SrcMeta;
		}
	}
}





/** Combinator used for cBlockArea::msSimpleCompare merging */
template <bool MetaValid>
void MergeCombinatorSimpleCompare(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
	if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta)))
	{
		// The blocktypes are the same, and the blockmetas are not present or are the same
		a_DstType = E_BLOCK_AIR;
	}
	else
	{
		// The blocktypes or blockmetas differ
		a_DstType = E_BLOCK_STONE;
	}
}





/** Combinator used for cBlockArea::msMask merging */
template <bool MetaValid>
void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
	// If the blocks are the same, keep the dest; otherwise replace with air
	if ((a_SrcType != a_DstType) || !MetaValid || (a_SrcMeta != a_DstMeta))
	{
		a_DstType = E_BLOCK_AIR;
		if (MetaValid)
		{
			a_DstMeta = 0;
		}
	}
}

// Re-enable previously disabled MSVC warnings
#ifdef _MSC_VER
	#pragma warning(pop)
#endif





////////////////////////////////////////////////////////////////////////////////
// cBlockArea:

cBlockArea::cBlockArea(void) :
	m_BlockTypes(nullptr),
	m_BlockMetas(nullptr),
	m_BlockLight(nullptr),
	m_BlockSkyLight(nullptr)
{
}





cBlockArea::~cBlockArea()
{
	Clear();
}





bool cBlockArea::IsValidDataTypeCombination(int a_DataTypes)
{
	// BlockEntities require that BlockTypes be present, too
	if ((a_DataTypes & baBlockEntities) != 0)
	{
		if ((a_DataTypes & baTypes) == 0)
		{
			return false;
		}
	}

	// All other combinations are considered valid
	return true;
}





void cBlockArea::Clear(void)
{
	delete[] m_BlockTypes;    m_BlockTypes    = nullptr;
	delete[] m_BlockMetas;    m_BlockMetas    = nullptr;
	delete[] m_BlockLight;    m_BlockLight    = nullptr;
	delete[] m_BlockSkyLight; m_BlockSkyLight = nullptr;
	if (m_BlockEntities != nullptr)
	{
		ClearBlockEntities(*m_BlockEntities);
		m_BlockEntities.reset();
	}
	m_BlockEntities.reset();
	m_Origin.Set(0, 0, 0);
	m_Size.Set(0, 0, 0);
}





void cBlockArea::Create(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
{
	ASSERT(a_SizeX > 0);
	ASSERT(a_SizeY > 0);
	ASSERT(a_SizeZ > 0);
	ASSERT(IsValidDataTypeCombination(a_DataTypes));

	// Warn if the height is too much, but proceed with the creation:
	if (a_SizeY > cChunkDef::Height)
	{
		LOGWARNING("Creating a cBlockArea with height larger than world height (%d). Continuing, but the area may misbehave.", a_SizeY);
	}

	Clear();
	int BlockCount = a_SizeX * a_SizeY * a_SizeZ;
	if ((a_DataTypes & baTypes) != 0)
	{
		m_BlockTypes = new BLOCKTYPE[BlockCount];
		for (int i = 0; i < BlockCount; i++)
		{
			m_BlockTypes[i] = E_BLOCK_AIR;
		}
	}
	if ((a_DataTypes & baMetas) != 0)
	{
		m_BlockMetas = new NIBBLETYPE[BlockCount];
		for (int i = 0; i < BlockCount; i++)
		{
			m_BlockMetas[i] = 0;
		}
	}
	if ((a_DataTypes & baLight) != 0)
	{
		m_BlockLight = new NIBBLETYPE[BlockCount];
		for (int i = 0; i < BlockCount; i++)
		{
			m_BlockLight[i] = 0;
		}
	}
	if ((a_DataTypes & baSkyLight) != 0)
	{
		m_BlockSkyLight = new NIBBLETYPE[BlockCount];
		for (int i = 0; i < BlockCount; i++)
		{
			m_BlockSkyLight[i] = 0x0f;
		}
	}
	if ((a_DataTypes & baBlockEntities) != 0)
	{
		m_BlockEntities = cpp14::make_unique<cBlockEntities>();
	}
	m_Size.Set(a_SizeX, a_SizeY, a_SizeZ);
	m_Origin.Set(0, 0, 0);
}





void cBlockArea::Create(const Vector3i & a_Size, int a_DataTypes)
{
	Create(a_Size.x, a_Size.y, a_Size.z, a_DataTypes);
}





void cBlockArea::SetWEOffset(int a_OffsetX, int a_OffsetY, int a_OffsetZ)
{
	m_WEOffset.Set(a_OffsetX, a_OffsetY, a_OffsetZ);
}





void cBlockArea::SetWEOffset(const Vector3i & a_Offset)
{
	m_WEOffset.Set(a_Offset.x, a_Offset.y, a_Offset.z);
}





void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ)
{
	m_Origin.Set(a_OriginX, a_OriginY, a_OriginZ);
}





void cBlockArea::SetOrigin(const Vector3i & a_Origin)
{
	m_Origin.Set(a_Origin.x, a_Origin.y, a_Origin.z);
}





bool cBlockArea::IsValidRelCoords(int a_RelX, int a_RelY, int a_RelZ) const
{
	return (
		(a_RelX >= 0) && (a_RelX < m_Size.x) &&
		(a_RelY >= 0) && (a_RelY < m_Size.y) &&
		(a_RelZ >= 0) && (a_RelZ < m_Size.z)
	);
}





bool cBlockArea::IsValidRelCoords(const Vector3i & a_RelCoords) const
{
	return IsValidRelCoords(a_RelCoords.x, a_RelCoords.y, a_RelCoords.z);
}





bool cBlockArea::IsValidCoords(int a_BlockX, int a_BlockY, int a_BlockZ) const
{
	return IsValidRelCoords(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z);
}





bool cBlockArea::IsValidCoords(const Vector3i & a_Coords) const
{
	return IsValidRelCoords(a_Coords - m_Origin);
}





bool cBlockArea::Read(cForEachChunkProvider & a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes)
{
	ASSERT(IsValidDataTypeCombination(a_DataTypes));
	ASSERT(cChunkDef::IsValidHeight(a_MinBlockY));
	ASSERT(cChunkDef::IsValidHeight(a_MaxBlockY));
	ASSERT(a_MinBlockX <= a_MaxBlockX);
	ASSERT(a_MinBlockY <= a_MaxBlockX);
	ASSERT(a_MinBlockZ <= a_MaxBlockZ);

	// Include the Max coords:
	a_MaxBlockX += 1;
	a_MaxBlockY += 1;
	a_MaxBlockZ += 1;

	// Allocate the needed memory:
	Clear();
	if (!SetSize(a_MaxBlockX - a_MinBlockX, a_MaxBlockY - a_MinBlockY, a_MaxBlockZ - a_MinBlockZ, a_DataTypes))
	{
		return false;
	}
	m_Origin.Set(a_MinBlockX, a_MinBlockY, a_MinBlockZ);
	cChunkReader Reader(*this);

	// Convert block coords to chunks coords:
	int MinChunkX, MaxChunkX;
	int MinChunkZ, MaxChunkZ;
	cChunkDef::AbsoluteToRelative(a_MinBlockX, a_MinBlockY, a_MinBlockZ, MinChunkX, MinChunkZ);
	cChunkDef::AbsoluteToRelative(a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ, MaxChunkX, MaxChunkZ);

	// Query block data:
	if (!a_ForEachChunkProvider.ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader))
	{
		Clear();
		return false;
	}

	return true;
}





bool cBlockArea::Read(cForEachChunkProvider & a_ForEachChunkProvider, const cCuboid & a_Bounds, int a_DataTypes)
{
	return Read(
		a_ForEachChunkProvider,
		a_Bounds.p1.x, a_Bounds.p2.x,
		a_Bounds.p1.y, a_Bounds.p2.y,
		a_Bounds.p1.z, a_Bounds.p2.z,
		a_DataTypes
	);
}





bool cBlockArea::Read(cForEachChunkProvider & a_ForEachChunkProvider, const Vector3i & a_Point1, const Vector3i & a_Point2, int a_DataTypes)
{
	return Read(
		a_ForEachChunkProvider,
		a_Point1.x, a_Point2.x,
		a_Point1.y, a_Point2.y,
		a_Point1.z, a_Point2.z,
		a_DataTypes
	);
}





bool cBlockArea::Write(cForEachChunkProvider & a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
{
	ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes);  // Are you requesting only the data that I have?
	ASSERT(cChunkDef::IsValidHeight(a_MinBlockY));
	ASSERT(cChunkDef::IsValidHeight(a_MinBlockY + m_Size.y - 1));

	return a_ForEachChunkProvider.WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
}





bool cBlockArea::Write(cForEachChunkProvider & a_ForEachChunkProvider, const Vector3i & a_MinCoords, int a_DataTypes)
{
	return Write(
		a_ForEachChunkProvider,
		a_MinCoords.x, a_MinCoords.y, a_MinCoords.z,
		a_DataTypes
	);
}





void cBlockArea::CopyTo(cBlockArea & a_Into) const
{
	if (&a_Into == this)
	{
		LOGWARNING("Trying to copy a cBlockArea into self, ignoring.");
		return;
	}

	a_Into.Clear();
	a_Into.SetSize(m_Size.x, m_Size.y, m_Size.z, GetDataTypes());
	a_Into.m_Origin = m_Origin;
	size_t BlockCount = GetBlockCount();
	if (HasBlockTypes())
	{
		memcpy(a_Into.m_BlockTypes, m_BlockTypes, BlockCount * sizeof(BLOCKTYPE));
	}
	if (HasBlockMetas())
	{
		memcpy(a_Into.m_BlockMetas, m_BlockMetas, BlockCount * sizeof(NIBBLETYPE));
	}
	if (HasBlockLights())
	{
		memcpy(a_Into.m_BlockLight, m_BlockLight, BlockCount * sizeof(NIBBLETYPE));
	}
	if (HasBlockSkyLights())
	{
		memcpy(a_Into.m_BlockSkyLight, m_BlockSkyLight, BlockCount * sizeof(NIBBLETYPE));
	}
	if (HasBlockEntities())
	{
		ClearBlockEntities(*(a_Into.m_BlockEntities));
		for (const auto & keyPair: *m_BlockEntities)
		{
			const auto & pos = keyPair.second->GetPos();
			a_Into.m_BlockEntities->insert({keyPair.first, keyPair.second->Clone(pos.x, pos.y, pos.z)});
		}
	}
}





void cBlockArea::CopyFrom(const cBlockArea & a_From)
{
	a_From.CopyTo(*this);
}





void cBlockArea::DumpToRawFile(const AString & a_FileName)
{
	cFile f;
	if (!f.Open(a_FileName, cFile::fmWrite))
	{
		LOGWARNING("cBlockArea: Cannot open file \"%s\" for raw dump", a_FileName.c_str());
		return;
	}
	UInt32 SizeX = ntohl(static_cast<UInt32>(m_Size.x));
	UInt32 SizeY = ntohl(static_cast<UInt32>(m_Size.y));
	UInt32 SizeZ = ntohl(static_cast<UInt32>(m_Size.z));
	f.Write(&SizeX, 4);
	f.Write(&SizeY, 4);
	f.Write(&SizeZ, 4);
	unsigned char DataTypes = static_cast<unsigned char>(GetDataTypes());
	f.Write(&DataTypes, 1);
	size_t NumBlocks = GetBlockCount();
	if (HasBlockTypes())
	{
		f.Write(m_BlockTypes, NumBlocks * sizeof(BLOCKTYPE));
	}
	if (HasBlockMetas())
	{
		f.Write(m_BlockMetas, NumBlocks);
	}
	if (HasBlockLights())
	{
		f.Write(m_BlockLight, NumBlocks);
	}
	if (HasBlockSkyLights())
	{
		f.Write(m_BlockSkyLight, NumBlocks);
	}
}
















void cBlockArea::Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
{
	if (
		(a_AddMinX + a_SubMaxX >= m_Size.x) ||
		(a_AddMinY + a_SubMaxY >= m_Size.y) ||
		(a_AddMinZ + a_SubMaxZ >= m_Size.z)
	)
	{
		LOGWARNING("cBlockArea:Crop called with more croping than the dimensions: %d x %d x %d with cropping %d, %d and %d",
			m_Size.x, m_Size.y, m_Size.z,
			a_AddMinX + a_SubMaxX, a_AddMinY + a_SubMaxY, a_AddMinZ + a_SubMaxZ
		);
		return;
	}

	if (HasBlockTypes())
	{
		CropBlockTypes(a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
	}
	if (HasBlockMetas())
	{
		CropNibbles(m_BlockMetas, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
	}
	if (HasBlockLights())
	{
		CropNibbles(m_BlockLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
	}
	if (HasBlockSkyLights())
	{
		CropNibbles(m_BlockSkyLight, a_AddMinX, a_SubMaxX, a_AddMinY, a_SubMaxY, a_AddMinZ, a_SubMaxZ);
	}
	if (HasBlockEntities())
	{
		auto maxX = m_Size.x - a_SubMaxX;
		auto maxY = m_Size.y - a_SubMaxY;
		auto maxZ = m_Size.z - a_SubMaxZ;

		// Move and crop block Entities:
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto posX = be->GetPosX();
			auto posY = be->GetPosY();
			auto posZ = be->GetPosZ();
			if (
				(posX < a_AddMinX) || (posX >= maxX) ||
				(posY < a_AddMinY) || (posY >= maxY) ||
				(posZ < a_AddMinZ) || (posZ >= maxZ)
			)
			{
				// The block entity is out of new coord range, remove it:
				delete be;
			}
			else
			{
				// The block entity is within the new coords, recalculate its coords to match the new area:
				posX -= a_AddMinX;
				posY -= a_AddMinY;
				posZ -= a_AddMinZ;
				be->SetPos(posX, posY, posZ);
				m_BlockEntities->insert({MakeIndex(posX, posY, posZ), std::move(be)});
			}
		}
	}

	m_Origin.Move(a_AddMinX, a_AddMinY, a_AddMinZ);
	m_Size.x -= a_AddMinX + a_SubMaxX;
	m_Size.y -= a_AddMinY + a_SubMaxY;
	m_Size.z -= a_AddMinZ + a_SubMaxZ;
}





void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
{
	if (HasBlockTypes())
	{
		ExpandBlockTypes(a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
	}
	if (HasBlockMetas())
	{
		ExpandNibbles(m_BlockMetas, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
	}
	if (HasBlockLights())
	{
		ExpandNibbles(m_BlockLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
	}
	if (HasBlockSkyLights())
	{
		ExpandNibbles(m_BlockSkyLight, a_SubMinX, a_AddMaxX, a_SubMinY, a_AddMaxY, a_SubMinZ, a_AddMaxZ);
	}
	if (HasBlockEntities())
	{
		// Move block entities:
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto posX = be->GetPosX() + a_SubMinX;
			auto posY = be->GetPosY() + a_SubMinY;
			auto posZ = be->GetPosZ() + a_SubMinZ;
			be->SetPos(posX, posY, posZ);
			m_BlockEntities->insert({MakeIndex(posX, posY, posZ), std::move(be)});
		}
	}

	m_Origin.Move(-a_SubMinX, -a_SubMinY, -a_SubMinZ);
	m_Size.x += a_SubMinX + a_AddMaxX;
	m_Size.y += a_SubMinY + a_AddMaxY;
	m_Size.z += a_SubMinZ + a_AddMaxZ;
}





void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy)
{

	const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas();
	NIBBLETYPE * DstMetas = m_BlockMetas;

	bool IsDummyMetas = ((SrcMetas == nullptr) || (DstMetas == nullptr));

	if (IsDummyMetas)
	{
		MergeByStrategy<false>(a_Src, a_RelX, a_RelY, a_RelZ, a_Strategy, SrcMetas, DstMetas);
	}
	else
	{
		MergeByStrategy<true>(a_Src, a_RelX, a_RelY, a_RelZ, a_Strategy, SrcMetas, DstMetas);
	}
}





void cBlockArea::Merge(const cBlockArea & a_Src, const Vector3i & a_RelMinCoords, eMergeStrategy a_Strategy)
{
	Merge(a_Src, a_RelMinCoords.x, a_RelMinCoords.y, a_RelMinCoords.z, a_Strategy);
}





void cBlockArea::Fill(int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight)
{
	if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
	{
		LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
			__FUNCTION__, a_DataTypes, GetDataTypes()
		);
		a_DataTypes = a_DataTypes & GetDataTypes();
	}

	size_t BlockCount = GetBlockCount();
	if ((a_DataTypes & baTypes) != 0)
	{
		for (size_t i = 0; i < BlockCount; i++)
		{
			m_BlockTypes[i] = a_BlockType;
		}
	}
	if ((a_DataTypes & baMetas) != 0)
	{
		for (size_t i = 0; i < BlockCount; i++)
		{
			m_BlockMetas[i] = a_BlockMeta;
		}
	}
	if ((a_DataTypes & baLight) != 0)
	{
		for (size_t i = 0; i < BlockCount; i++)
		{
			m_BlockLight[i] = a_BlockLight;
		}
	}
	if ((a_DataTypes & baSkyLight) != 0)
	{
		for (size_t i = 0; i < BlockCount; i++)
		{
			m_BlockSkyLight[i] = a_BlockSkyLight;
		}
	}

	// If the area contains block entities, remove those not matching and replace with whatever block entity block was filled
	if (HasBlockEntities() && ((a_DataTypes & baTypes) != 0))
	{
		RescanBlockEntities();
	}
}





void cBlockArea::FillRelCuboid(int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ,
	int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
	NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
)
{
	if ((a_DataTypes & GetDataTypes()) != a_DataTypes)
	{
		LOGWARNING("%s: requested datatypes that are not present in the BlockArea object, trimming those away (req 0x%x, stor 0x%x)",
			__FUNCTION__, a_DataTypes, GetDataTypes()
		);
		a_DataTypes = a_DataTypes & GetDataTypes();
	}

	if ((a_DataTypes & baTypes) != 0)
	{
		for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
		{
			m_BlockTypes[MakeIndex(x, y, z)] = a_BlockType;
		}  // for x, z, y
	}
	if ((a_DataTypes & baMetas) != 0)
	{
		for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
		{
			m_BlockMetas[MakeIndex(x, y, z)] = a_BlockMeta;
		}  // for x, z, y
	}
	if ((a_DataTypes & baLight) != 0)
	{
		for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
		{
			m_BlockLight[MakeIndex(x, y, z)] = a_BlockLight;
		}  // for x, z, y
	}
	if ((a_DataTypes & baSkyLight) != 0)
	{
		for (int y = a_MinRelY; y <= a_MaxRelY; y++) for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) for (int x = a_MinRelX; x <= a_MaxRelX; x++)
		{
			m_BlockSkyLight[MakeIndex(x, y, z)] = a_BlockSkyLight;
		}  // for x, z, y
	}

	// If the area contains block entities, remove those in the affected cuboid and replace with whatever block entity block was filled:
	if (HasBlockEntities() && ((a_DataTypes & baTypes) != 0))
	{
		RescanBlockEntities();
	}
}





void cBlockArea::FillRelCuboid(const cCuboid & a_RelCuboid,
	int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
	NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
)
{
	FillRelCuboid(
		a_RelCuboid.p1.x, a_RelCuboid.p2.x,
		a_RelCuboid.p1.y, a_RelCuboid.p2.y,
		a_RelCuboid.p1.z, a_RelCuboid.p2.z,
		a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight
	);
}





void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int a_RelY2, int a_RelZ2,
	int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
	NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
)
{
	// Bresenham-3D algorithm for drawing lines:
	int dx = abs(a_RelX2 - a_RelX1);
	int dy = abs(a_RelY2 - a_RelY1);
	int dz = abs(a_RelZ2 - a_RelZ1);
	int sx = (a_RelX1 < a_RelX2) ? 1 : -1;
	int sy = (a_RelY1 < a_RelY2) ? 1 : -1;
	int sz = (a_RelZ1 < a_RelZ2) ? 1 : -1;

	if (dx >= std::max(dy, dz))  // x dominant
	{
		int yd = dy - dx / 2;
		int zd = dz - dx / 2;

		for (;;)
		{
			RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);

			if (a_RelX1 == a_RelX2)
			{
				break;
			}

			if (yd >= 0)  // move along y
			{
				a_RelY1 += sy;
				yd -= dx;
			}

			if (zd >= 0)  // move along z
			{
				a_RelZ1 += sz;
				zd -= dx;
			}

			// move along x
			a_RelX1  += sx;
			yd += dy;
			zd += dz;
		}
	}
	else if (dy >= std::max(dx, dz))  // y dominant
	{
		int xd = dx - dy / 2;
		int zd = dz - dy / 2;

		for (;;)
		{
			RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);

			if (a_RelY1 == a_RelY2)
			{
				break;
			}

			if (xd >= 0)  // move along x
			{
				a_RelX1 += sx;
				xd -= dy;
			}

			if (zd >= 0)  // move along z
			{
				a_RelZ1 += sz;
				zd -= dy;
			}

			// move along y
			a_RelY1 += sy;
			xd += dx;
			zd += dz;
		}
	}
	else
	{
		// z dominant
		ASSERT(dz >= std::max(dx, dy));
		int xd = dx - dz / 2;
		int yd = dy - dz / 2;

		for (;;)
		{
			RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);

			if (a_RelZ1 == a_RelZ2)
			{
				break;
			}

			if (xd >= 0)  // move along x
			{
				a_RelX1 += sx;
				xd -= dz;
			}

			if (yd >= 0)  // move along y
			{
				a_RelY1 += sy;
				yd -= dz;
			}

			// move along z
			a_RelZ1 += sz;
			xd += dx;
			yd += dy;
		}
	}  // if (which dimension is dominant)
}





void cBlockArea::RelLine(const Vector3i & a_Point1, const Vector3i & a_Point2,
	int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
	NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
)
{
	RelLine(
		a_Point1.x, a_Point1.y, a_Point1.z,
		a_Point2.x, a_Point2.y, a_Point2.z,
		a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight
	);
}





void cBlockArea::RotateCCW(void)
{
	if (!HasBlockTypes())
	{
		LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
		return;
	}

	if (!HasBlockMetas())
	{
		// There are no blockmetas to rotate, just use the NoMeta function
		RotateCCWNoMeta();
		return;
	}

	// We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
	BLOCKTYPE * NewTypes = new BLOCKTYPE[GetBlockCount()];
	NIBBLETYPE * NewMetas = new NIBBLETYPE[GetBlockCount()];
	for (int x = 0; x < m_Size.x; x++)
	{
		int NewZ = m_Size.x - x - 1;
		for (int z = 0; z < m_Size.z; z++)
		{
			int NewX = z;
			for (int y = 0; y < m_Size.y; y++)
			{
				int NewIdx = NewX + NewZ * m_Size.z + y * m_Size.x * m_Size.z;
				int OldIdx = MakeIndex(x, y, z);
				NewTypes[NewIdx] = m_BlockTypes[OldIdx];
				NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCCW(m_BlockMetas[OldIdx]);
			}  // for y
		}  // for z
	}  // for x
	std::swap(m_BlockTypes, NewTypes);
	std::swap(m_BlockMetas, NewMetas);
	delete[] NewTypes;   NewTypes = nullptr;
	delete[] NewMetas;   NewMetas = nullptr;

	// Rotate the BlockEntities:
	if (HasBlockEntities())
	{
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto newX = be->GetPosZ();
			auto newY = be->GetPosY();
			auto newZ = m_Size.x - be->GetPosX() - 1;
			auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
			be->SetPos(newX, newY, newZ);
			m_BlockEntities->insert({newIdx, std::move(be)});
		}
	}

	std::swap(m_Size.x, m_Size.z);
}





void cBlockArea::RotateCW(void)
{
	if (!HasBlockTypes())
	{
		LOGWARNING("cBlockArea: Cannot rotate blockmeta without blocktypes!");
		return;
	}

	if (!HasBlockMetas())
	{
		// There are no blockmetas to rotate, just use the NoMeta function
		RotateCWNoMeta();
		return;
	}

	// We are guaranteed that both blocktypes and blockmetas exist; rotate both at the same time:
	BLOCKTYPE * NewTypes = new BLOCKTYPE[GetBlockCount()];
	NIBBLETYPE * NewMetas = new NIBBLETYPE[GetBlockCount()];
	for (int x = 0; x < m_Size.x; x++)
	{
		int NewZ = x;
		for (int z = 0; z < m_Size.z; z++)
		{
			int NewX = m_Size.z - z - 1;
			for (int y = 0; y < m_Size.y; y++)
			{
				int NewIdx = NewX + NewZ * m_Size.z + y * m_Size.x * m_Size.z;
				int OldIdx = MakeIndex(x, y, z);
				NewTypes[NewIdx] = m_BlockTypes[OldIdx];
				NewMetas[NewIdx] = BlockHandler(m_BlockTypes[OldIdx])->MetaRotateCW(m_BlockMetas[OldIdx]);
			}  // for y
		}  // for z
	}  // for x
	std::swap(m_BlockTypes, NewTypes);
	std::swap(m_BlockMetas, NewMetas);
	delete[] NewTypes;   NewTypes = nullptr;
	delete[] NewMetas;   NewMetas = nullptr;

	// Rotate the BlockEntities:
	if (HasBlockEntities())
	{
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto newX = m_Size.z - be->GetPosZ() - 1;
			auto newY = be->GetPosY();
			auto newZ = be->GetPosX();
			auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
			be->SetPos(newX, newY, newZ);
			m_BlockEntities->insert({newIdx, std::move(be)});
		}
	}

	std::swap(m_Size.x, m_Size.z);
}





void cBlockArea::MirrorXY(void)
{
	if (!HasBlockTypes())
	{
		LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
		return;
	}

	if (!HasBlockMetas())
	{
		// There are no blockmetas to mirror, just use the NoMeta function
		MirrorXYNoMeta();
		return;
	}

	// We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
	int HalfZ = m_Size.z / 2;
	int MaxZ = m_Size.z - 1;
	for (int y = 0; y < m_Size.y; y++)
	{
		for (int z = 0; z <= HalfZ; z++)
		{
			for (int x = 0; x < m_Size.x; x++)
			{
				int Idx1 = MakeIndex(x, y, z);
				int Idx2 = MakeIndex(x, y, MaxZ - z);
				std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
				NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXY(m_BlockMetas[Idx1]);
				NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXY(m_BlockMetas[Idx2]);
				m_BlockMetas[Idx1] = Meta2;
				m_BlockMetas[Idx2] = Meta1;
			}  // for x
		}  // for z
	}  // for y

	// Mirror the BlockEntities:
	if (HasBlockEntities())
	{
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto newX = be->GetPosX();
			auto newY = be->GetPosY();
			auto newZ = MaxZ - be->GetPosZ();
			auto newIdx = MakeIndex(newX, newY, newZ);
			be->SetPos(newX, newY, newZ);
			m_BlockEntities->insert({newIdx, std::move(be)});
		}
	}
}





void cBlockArea::MirrorXZ(void)
{
	if (!HasBlockTypes())
	{
		LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
		return;
	}

	if (!HasBlockMetas())
	{
		// There are no blockmetas to mirror, just use the NoMeta function
		MirrorXZNoMeta();
		return;
	}

	// We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
	int HalfY = m_Size.y / 2;
	int MaxY = m_Size.y - 1;
	for (int y = 0; y <= HalfY; y++)
	{
		for (int z = 0; z < m_Size.z; z++)
		{
			for (int x = 0; x < m_Size.x; x++)
			{
				int Idx1 = MakeIndex(x, y, z);
				int Idx2 = MakeIndex(x, MaxY - y, z);
				std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
				NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorXZ(m_BlockMetas[Idx1]);
				NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorXZ(m_BlockMetas[Idx2]);
				m_BlockMetas[Idx1] = Meta2;
				m_BlockMetas[Idx2] = Meta1;
			}  // for x
		}  // for z
	}  // for y

	// Mirror the BlockEntities:
	if (HasBlockEntities())
	{
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto newX = be->GetPosX();
			auto newY = MaxY - be->GetPosY();
			auto newZ = be->GetPosZ();
			auto newIdx = MakeIndex(newX, newY, newZ);
			be->SetPos(newX, newY, newZ);
			m_BlockEntities->insert({newIdx, std::move(be)});
		}
	}
}





void cBlockArea::MirrorYZ(void)
{
	if (!HasBlockTypes())
	{
		LOGWARNING("cBlockArea: Cannot mirror meta without blocktypes!");
		return;
	}

	if (!HasBlockMetas())
	{
		// There are no blockmetas to mirror, just use the NoMeta function
		MirrorYZNoMeta();
		return;
	}

	// We are guaranteed that both blocktypes and blockmetas exist; mirror both at the same time:
	int HalfX = m_Size.x / 2;
	int MaxX = m_Size.x - 1;
	for (int y = 0; y < m_Size.y; y++)
	{
		for (int z = 0; z < m_Size.z; z++)
		{
			for (int x = 0; x <= HalfX; x++)
			{
				int Idx1 = MakeIndex(x, y, z);
				int Idx2 = MakeIndex(MaxX - x, y, z);
				std::swap(m_BlockTypes[Idx1], m_BlockTypes[Idx2]);
				NIBBLETYPE Meta1 = BlockHandler(m_BlockTypes[Idx2])->MetaMirrorYZ(m_BlockMetas[Idx1]);
				NIBBLETYPE Meta2 = BlockHandler(m_BlockTypes[Idx1])->MetaMirrorYZ(m_BlockMetas[Idx2]);
				m_BlockMetas[Idx1] = Meta2;
				m_BlockMetas[Idx2] = Meta1;
			}  // for x
		}  // for z
	}  // for y

	// Mirror the BlockEntities:
	if (HasBlockEntities())
	{
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto newX = MaxX - be->GetPosX();
			auto newY = be->GetPosY();
			auto newZ = be->GetPosZ();
			auto newIdx = MakeIndex(newX, newY, newZ);
			be->SetPos(newX, newY, newZ);
			m_BlockEntities->insert({newIdx, std::move(be)});
		}
	}
}





void cBlockArea::RotateCCWNoMeta(void)
{
	if (HasBlockTypes())
	{
		BLOCKTYPE * NewTypes = new BLOCKTYPE[GetBlockCount()];
		for (int x = 0; x < m_Size.x; x++)
		{
			int NewZ = m_Size.x - x - 1;
			for (int z = 0; z < m_Size.z; z++)
			{
				int NewX = z;
				for (int y = 0; y < m_Size.y; y++)
				{
					NewTypes[NewX + NewZ * m_Size.z + y * m_Size.x * m_Size.z] = m_BlockTypes[MakeIndex(x, y, z)];
				}  // for y
			}  // for z
		}  // for x
		std::swap(m_BlockTypes, NewTypes);
		delete[] NewTypes;   NewTypes = nullptr;
	}
	if (HasBlockMetas())
	{
		NIBBLETYPE * NewMetas = new NIBBLETYPE[GetBlockCount()];
		for (int x = 0; x < m_Size.x; x++)
		{
			int NewZ = m_Size.x - x - 1;
			for (int z = 0; z < m_Size.z; z++)
			{
				int NewX = z;
				for (int y = 0; y < m_Size.y; y++)
				{
					NewMetas[NewX + NewZ * m_Size.z + y * m_Size.x * m_Size.z] = m_BlockMetas[MakeIndex(x, y, z)];
				}  // for y
			}  // for z
		}  // for x
		std::swap(m_BlockMetas, NewMetas);
		delete[] NewMetas;   NewMetas = nullptr;
	}

	// Rotate the BlockEntities:
	if (HasBlockEntities())
	{
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto newX = be->GetPosZ();
			auto newY = be->GetPosY();
			auto newZ = m_Size.x - be->GetPosX() - 1;
			auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
			be->SetPos(newX, newY, newZ);
			m_BlockEntities->insert({newIdx, std::move(be)});
		}
	}

	std::swap(m_Size.x, m_Size.z);
}





void cBlockArea::RotateCWNoMeta(void)
{
	if (HasBlockTypes())
	{
		BLOCKTYPE * NewTypes = new BLOCKTYPE[GetBlockCount()];
		for (int z = 0; z < m_Size.z; z++)
		{
			int NewX = m_Size.z - z - 1;
			for (int x = 0; x < m_Size.x; x++)
			{
				int NewZ = x;
				for (int y = 0; y < m_Size.y; y++)
				{
					NewTypes[NewX + NewZ * m_Size.z + y * m_Size.x * m_Size.z] = m_BlockTypes[MakeIndex(x, y, z)];
				}  // for y
			}  // for x
		}  // for z
		std::swap(m_BlockTypes, NewTypes);
		delete[] NewTypes;   NewTypes = nullptr;
	}
	if (HasBlockMetas())
	{
		NIBBLETYPE * NewMetas = new NIBBLETYPE[GetBlockCount()];
		for (int z = 0; z < m_Size.z; z++)
		{
			int NewX = m_Size.z - z - 1;
			for (int x = 0; x < m_Size.x; x++)
			{
				int NewZ = x;
				for (int y = 0; y < m_Size.y; y++)
				{
					NewMetas[NewX + NewZ * m_Size.z + y * m_Size.x * m_Size.z] = m_BlockMetas[MakeIndex(x, y, z)];
				}  // for y
			}  // for x
		}  // for z
		std::swap(m_BlockMetas, NewMetas);
		delete[] NewMetas;   NewMetas = nullptr;
	}

	// Rotate the BlockEntities:
	if (HasBlockEntities())
	{
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto newX = m_Size.z - be->GetPosZ() - 1;
			auto newY = be->GetPosY();
			auto newZ = be->GetPosX();
			auto newIdx = newX + newZ * m_Size.z + newY * m_Size.x * m_Size.z;
			be->SetPos(newX, newY, newZ);
			m_BlockEntities->insert({newIdx, std::move(be)});
		}
	}

	std::swap(m_Size.x, m_Size.z);
}





void cBlockArea::MirrorXYNoMeta(void)
{
	int HalfZ = m_Size.z / 2;
	int MaxZ = m_Size.z - 1;
	if (HasBlockTypes())
	{
		for (int y = 0; y < m_Size.y; y++)
		{
			for (int z = 0; z < HalfZ; z++)
			{
				for (int x = 0; x < m_Size.x; x++)
				{
					std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, y, MaxZ - z)]);
				}  // for x
			}  // for z
		}  // for y
	}  // if (HasBlockTypes)

	if (HasBlockMetas())
	{
		for (int y = 0; y < m_Size.y; y++)
		{
			for (int z = 0; z < HalfZ; z++)
			{
				for (int x = 0; x < m_Size.x; x++)
				{
					std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, y, MaxZ - z)]);
				}  // for x
			}  // for z
		}  // for y
	}  // if (HasBlockMetas)

	// Mirror the BlockEntities:
	if (HasBlockEntities())
	{
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto newX = be->GetPosX();
			auto newY = be->GetPosY();
			auto newZ = MaxZ - be->GetPosZ();
			auto newIdx = MakeIndex(newX, newY, newZ);
			be->SetPos(newX, newY, newZ);
			m_BlockEntities->insert({newIdx, std::move(be)});
		}
	}
}





void cBlockArea::MirrorXZNoMeta(void)
{
	int HalfY = m_Size.y / 2;
	int MaxY = m_Size.y - 1;
	if (HasBlockTypes())
	{
		for (int y = 0; y < HalfY; y++)
		{
			for (int z = 0; z < m_Size.z; z++)
			{
				for (int x = 0; x < m_Size.x; x++)
				{
					std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(x, MaxY - y, z)]);
				}  // for x
			}  // for z
		}  // for y
	}  // if (HasBlockTypes)

	if (HasBlockMetas())
	{
		for (int y = 0; y < HalfY; y++)
		{
			for (int z = 0; z < m_Size.z; z++)
			{
				for (int x = 0; x < m_Size.x; x++)
				{
					std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(x, MaxY - y, z)]);
				}  // for x
			}  // for z
		}  // for y
	}  // if (HasBlockMetas)

	// Mirror the BlockEntities:
	if (HasBlockEntities())
	{
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto newX = be->GetPosX();
			auto newY = MaxY - be->GetPosY();
			auto newZ = be->GetPosZ();
			auto newIdx = MakeIndex(newX, newY, newZ);
			be->SetPos(newX, newY, newZ);
			m_BlockEntities->insert({newIdx, std::move(be)});
		}
	}
}





void cBlockArea::MirrorYZNoMeta(void)
{
	int HalfX = m_Size.x / 2;
	int MaxX = m_Size.x - 1;
	if (HasBlockTypes())
	{
		for (int y = 0; y < m_Size.y; y++)
		{
			for (int z = 0; z < m_Size.z; z++)
			{
				for (int x = 0; x < HalfX; x++)
				{
					std::swap(m_BlockTypes[MakeIndex(x, y, z)], m_BlockTypes[MakeIndex(MaxX - x, y, z)]);
				}  // for x
			}  // for z
		}  // for y
	}  // if (HasBlockTypes)

	if (HasBlockMetas())
	{
		for (int y = 0; y < m_Size.y; y++)
		{
			for (int z = 0; z < m_Size.z; z++)
			{
				for (int x = 0; x < HalfX; x++)
				{
					std::swap(m_BlockMetas[MakeIndex(x, y, z)], m_BlockMetas[MakeIndex(MaxX - x, y, z)]);
				}  // for x
			}  // for z
		}  // for y
	}  // if (HasBlockMetas)

	// Mirror the BlockEntities:
	if (HasBlockEntities())
	{
		cBlockEntities oldBE;
		std::swap(oldBE, *m_BlockEntities);
		for (const auto & keyPair: oldBE)
		{
			auto & be = keyPair.second;
			auto newX = MaxX - be->GetPosX();
			auto newY = be->GetPosY();
			auto newZ = be->GetPosZ();
			auto newIdx = MakeIndex(newX, newY, newZ);
			be->SetPos(newX, newY, newZ);
			m_BlockEntities->insert({newIdx, std::move(be)});
		}
	}
}





void cBlockArea::SetRelBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
{
	ASSERT(m_BlockTypes != nullptr);
	auto idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
	m_BlockTypes[idx] = a_BlockType;

	// Update the block entities, if appropriate:
	if (HasBlockEntities())
	{
		auto itr = m_BlockEntities->find(idx);
		if (itr != m_BlockEntities->end())
		{
			if (itr->second->GetBlockType() == a_BlockType)
			{
				// The block entity is for the same block type, keep the current one
				return;
			}
			m_BlockEntities->erase(itr);
		}
		if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
		{
			NIBBLETYPE meta = HasBlockMetas() ? m_BlockMetas[idx] : 0;
			m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(a_BlockType, meta, a_RelX, a_RelY, a_RelZ)});
		}
	}
}





void cBlockArea::SetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
{
	SetRelBlockType(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_BlockType);
}





void cBlockArea::SetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockMeta)
{
	SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockMeta, m_BlockMetas);
}





void cBlockArea::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta)
{
	SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta, m_BlockMetas);
}





void cBlockArea::SetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockLight)
{
	SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockLight, m_BlockLight);
}





void cBlockArea::SetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockLight)
{
	SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockLight, m_BlockLight);
}





void cBlockArea::SetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_BlockSkyLight)
{
	SetRelNibble(a_RelX, a_RelY, a_RelZ, a_BlockSkyLight, m_BlockSkyLight);
}





void cBlockArea::SetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockSkyLight)
{
	SetNibble(a_BlockX, a_BlockY, a_BlockZ, a_BlockSkyLight, m_BlockSkyLight);
}





BLOCKTYPE cBlockArea::GetRelBlockType(int a_RelX, int a_RelY, int a_RelZ) const
{
	if (m_BlockTypes == nullptr)
	{
		LOGWARNING("cBlockArea: BlockTypes have not been read!");
		return E_BLOCK_AIR;
	}
	return m_BlockTypes[MakeIndex(a_RelX, a_RelY, a_RelZ)];
}





BLOCKTYPE cBlockArea::GetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ) const
{
	return GetRelBlockType(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z);
}





NIBBLETYPE cBlockArea::GetRelBlockMeta(int a_RelX, int a_RelY, int a_RelZ) const
{
	return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockMetas);
}





NIBBLETYPE cBlockArea::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ) const
{
	return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockMetas);
}





NIBBLETYPE cBlockArea::GetRelBlockLight(int a_RelX, int a_RelY, int a_RelZ) const
{
	return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockLight);
}





NIBBLETYPE cBlockArea::GetBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
{
	return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockLight);
}





NIBBLETYPE cBlockArea::GetRelBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ) const
{
	return GetRelNibble(a_RelX, a_RelY, a_RelZ, m_BlockSkyLight);
}





NIBBLETYPE cBlockArea::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ) const
{
	return GetNibble(a_BlockX, a_BlockY, a_BlockZ, m_BlockSkyLight);
}





void cBlockArea::SetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
	SetRelBlockTypeMeta(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_BlockType, a_BlockMeta);
}





void cBlockArea::SetRelBlockTypeMeta(int a_RelX,   int a_RelY,   int a_RelZ,   BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
	int idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
	if (m_BlockTypes == nullptr)
	{
		LOGWARNING("%s: BlockTypes not available but requested to be written to.", __FUNCTION__);
	}
	else
	{
		m_BlockTypes[idx] = a_BlockType;
	}
	if (m_BlockMetas == nullptr)
	{
		LOGWARNING("%s: BlockMetas not available but requested to be written to.", __FUNCTION__);
	}
	else
	{
		m_BlockMetas[idx] = a_BlockMeta;
	}

	// Update the block entities, if appropriate:
	if (HasBlockEntities())
	{
		auto itr = m_BlockEntities->find(idx);
		if (itr != m_BlockEntities->end())
		{
			if (itr->second->GetBlockType() == a_BlockType)
			{
				// The block entity is for the same block type, keep the current one
				return;
			}
			m_BlockEntities->erase(itr);
		}
		if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
		{
			m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, a_RelX, a_RelY, a_RelZ)});
		}
	}
}





void cBlockArea::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
{
	return GetRelBlockTypeMeta(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_BlockType, a_BlockMeta);
}





void cBlockArea::GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const
{
	int idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
	if (m_BlockTypes == nullptr)
	{
		LOGWARNING("cBlockArea: BlockTypes have not been read!");
		a_BlockType = E_BLOCK_AIR;
	}
	else
	{
		a_BlockType = m_BlockTypes[idx];
	}

	if (m_BlockMetas == nullptr)
	{
		LOGWARNING("cBlockArea: BlockMetas have not been read!");
		a_BlockMeta = 0;
	}
	else
	{
		a_BlockMeta = m_BlockMetas[idx];
	}
}





size_t cBlockArea::CountNonAirBlocks(void) const
{
	// Check if blocktypes are valid:
	if (m_BlockTypes == nullptr)
	{
		LOGWARNING("%s: BlockTypes have not been read!", __FUNCTION__);
		return 0;
	}

	// Count the blocks:
	size_t res = 0;
	for (int y = 0; y < m_Size.y; y++)
	{
		for (int z = 0; z < m_Size.z; z++)
		{
			for (int x = 0; x < m_Size.x; x++)
			{
				if (m_BlockTypes[MakeIndex(x, y, z)] != E_BLOCK_AIR)
				{
					++res;
				}
			}  // for x
		}  // for z
	}  // for y
	return res;
}





size_t cBlockArea::CountSpecificBlocks(BLOCKTYPE a_BlockType) const
{
	// If blocktypes are not valid, log a warning and return zero occurences:
	if (m_BlockTypes == nullptr)
	{
		LOGWARNING("%s: BlockTypes not available!", __FUNCTION__);
		return 0;
	}

	// Count the blocks:
	size_t num = GetBlockCount();
	size_t res = 0;
	for (size_t i = 0; i < num; i++)
	{
		if (m_BlockTypes[i] == a_BlockType)
		{
			res++;
		}
	}
	return res;
}





size_t cBlockArea::CountSpecificBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) const
{
	// If blocktypes are not valid, log a warning and return zero occurences:
	if (m_BlockTypes == nullptr)
	{
		LOGWARNING("%s: BlockTypes not available!", __FUNCTION__);
		return 0;
	}

	// If blockmetas are not valid, log a warning and count only blocktypes:
	if (m_BlockMetas == nullptr)
	{
		LOGWARNING("%s: BlockMetas not available, comparing blocktypes only!", __FUNCTION__);
		return CountSpecificBlocks(a_BlockType);
	}

	// Count the blocks:
	size_t num = GetBlockCount();
	size_t res = 0;
	for (size_t i = 0; i < num; i++)
	{
		if ((m_BlockTypes[i] == a_BlockType) && (m_BlockMetas[i] == a_BlockMeta))
		{
			res++;
		}
	}
	return res;
}





void cBlockArea::GetNonAirCropRelCoords(int & a_MinRelX, int & a_MinRelY, int & a_MinRelZ, int & a_MaxRelX, int & a_MaxRelY, int & a_MaxRelZ, BLOCKTYPE a_IgnoreBlockType)
{
	// Check if blocktypes are valid:
	if (m_BlockTypes == nullptr)
	{
		LOGWARNING("%s: BlockTypes have not been read!", __FUNCTION__);
		a_MinRelX = 1;
		a_MaxRelX = 0;
		return;
	}

	// Walk all the blocks and find the min and max coords for the non-ignored ones:
	int MaxX = 0, MinX = m_Size.x - 1;
	int MaxY = 0, MinY = m_Size.y - 1;
	int MaxZ = 0, MinZ = m_Size.z - 1;
	for (int y = 0; y < m_Size.y; y++)
	{
		for (int z = 0; z < m_Size.z; z++)
		{
			for (int x = 0; x < m_Size.x; x++)
			{
				if (m_BlockTypes[MakeIndex(x, y, z)] == a_IgnoreBlockType)
				{
					continue;
				}
				// The block is not ignored, update any coords that need updating:
				if (x < MinX)
				{
					MinX = x;
				}
				if (x > MaxX)
				{
					MaxX = x;
				}
				if (y < MinY)
				{
					MinY = y;
				}
				if (y > MaxY)
				{
					MaxY = y;
				}
				if (z < MinZ)
				{
					MinZ = z;
				}
				if (z > MaxZ)
				{
					MaxZ = z;
				}
			}  // for x
		}  // for z
	}  // for y

	// Assign to the output:
	a_MinRelX = MinX;
	a_MinRelY = MinY;
	a_MinRelZ = MinZ;
	a_MaxRelX = MaxX;
	a_MaxRelY = MaxY;
	a_MaxRelZ = MaxZ;
}





int cBlockArea::GetDataTypes(void) const
{
	int res = 0;
	if (m_BlockTypes != nullptr)
	{
		res |= baTypes;
	}
	if (m_BlockMetas != nullptr)
	{
		res |= baMetas;
	}
	if (m_BlockLight != nullptr)
	{
		res |= baLight;
	}
	if (m_BlockSkyLight != nullptr)
	{
		res |= baSkyLight;
	}
	if (m_BlockEntities != nullptr)
	{
		res |= baBlockEntities;
	}
	return res;
}





bool cBlockArea::SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
{
	ASSERT(m_BlockTypes == nullptr);  // Has been cleared
	ASSERT(IsValidDataTypeCombination(a_DataTypes));

	if ((a_DataTypes & baTypes) != 0)
	{
		m_BlockTypes = new BLOCKTYPE[a_SizeX * a_SizeY * a_SizeZ];
		if (m_BlockTypes == nullptr)
		{
			return false;
		}
	}
	if ((a_DataTypes & baMetas) != 0)
	{
		m_BlockMetas = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
		if (m_BlockMetas == nullptr)
		{
			delete[] m_BlockTypes;
			m_BlockTypes = nullptr;
			return false;
		}
	}
	if ((a_DataTypes & baLight) != 0)
	{
		m_BlockLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
		if (m_BlockLight == nullptr)
		{
			delete[] m_BlockMetas;
			m_BlockMetas = nullptr;
			delete[] m_BlockTypes;
			m_BlockTypes = nullptr;
			return false;
		}
	}
	if ((a_DataTypes & baSkyLight) != 0)
	{
		m_BlockSkyLight = new NIBBLETYPE[a_SizeX * a_SizeY * a_SizeZ];
		if (m_BlockSkyLight == nullptr)
		{
			delete[] m_BlockLight;
			m_BlockLight = nullptr;
			delete[] m_BlockMetas;
			m_BlockMetas = nullptr;
			delete[] m_BlockTypes;
			m_BlockTypes = nullptr;
			return false;
		}
	}
	if ((a_DataTypes & baBlockEntities) != 0)
	{
		m_BlockEntities = cpp14::make_unique<cBlockEntities>();
		if (m_BlockEntities == nullptr)
		{
			delete[] m_BlockSkyLight;
			m_BlockSkyLight = nullptr;
			delete[] m_BlockLight;
			m_BlockLight = nullptr;
			delete[] m_BlockMetas;
			m_BlockMetas = nullptr;
			delete[] m_BlockTypes;
			m_BlockTypes = nullptr;
			return false;
		}
	}
	m_Size.Set(a_SizeX, a_SizeY, a_SizeZ);
	return true;
}





int cBlockArea::MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const
{
	ASSERT(a_RelX >= 0);
	ASSERT(a_RelX < m_Size.x);
	ASSERT(a_RelY >= 0);
	ASSERT(a_RelY < m_Size.y);
	ASSERT(a_RelZ >= 0);
	ASSERT(a_RelZ < m_Size.z);

	return a_RelX + a_RelZ * m_Size.x + a_RelY * m_Size.x * m_Size.z;
}





bool cBlockArea::DoWithBlockEntityRelAt(int a_RelX, int a_RelY, int a_RelZ, cItemCallback<cBlockEntity> & a_Callback)
{
	ASSERT(IsValidRelCoords(a_RelX, a_RelY, a_RelZ));
	if (!HasBlockEntities())
	{
		return false;
	}
	auto idx = MakeIndex(a_RelX, a_RelY, a_RelZ);
	auto itr = m_BlockEntities->find(idx);
	if (itr == m_BlockEntities->end())
	{
		return false;
	}
	return a_Callback.Item(itr->second);
}





bool cBlockArea::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cItemCallback<cBlockEntity> & a_Callback)
{
	return DoWithBlockEntityRelAt(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_Callback);
}





bool cBlockArea::ForEachBlockEntity(cItemCallback<cBlockEntity> & a_Callback)
{
	if (!HasBlockEntities())
	{
		return true;
	}
	for (auto & keyPair: *m_BlockEntities)
	{
		if (a_Callback.Item(keyPair.second))
		{
			return false;
		}
	}
	return true;
}





void cBlockArea::SetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
{
	if (a_Array == nullptr)
	{
		LOGWARNING("cBlockArea: datatype has not been read!");
		return;
	}
	a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)] = a_Value;
}





void cBlockArea::SetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Value, NIBBLETYPE * a_Array)
{
	SetRelNibble(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_Value, a_Array);
}





NIBBLETYPE cBlockArea::GetRelNibble(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE * a_Array) const
{
	if (a_Array == nullptr)
	{
		LOGWARNING("cBlockArea: datatype has not been read!");
		return 16;
	}
	return a_Array[MakeIndex(a_RelX, a_RelY, a_RelZ)];
}





NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE * a_Array) const
{
	return GetRelNibble(a_BlockX - m_Origin.x, a_BlockY - m_Origin.y, a_BlockZ - m_Origin.z, a_Array);
}






void cBlockArea::CropBlockTypes(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
{
	int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
	int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
	int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
	BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[NewSizeX * NewSizeY * NewSizeZ];
	int idx = 0;
	for (int y = 0; y < NewSizeY; y++)
	{
		for (int z = 0; z < NewSizeZ; z++)
		{
			for (int x = 0; x < NewSizeX; x++)
			{
				int OldIndex = MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ);
				NewBlockTypes[idx++] = m_BlockTypes[OldIndex];
			}  // for x
		}  // for z
	}  // for y
	delete m_BlockTypes;
	m_BlockTypes = NewBlockTypes;
}





void cBlockArea::CropNibbles(NIBBLEARRAY & a_Array, int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ)
{
	int NewSizeX = GetSizeX() - a_AddMinX - a_SubMaxX;
	int NewSizeY = GetSizeY() - a_AddMinY - a_SubMaxY;
	int NewSizeZ = GetSizeZ() - a_AddMinZ - a_SubMaxZ;
	NIBBLETYPE * NewNibbles = new NIBBLETYPE[NewSizeX * NewSizeY * NewSizeZ];
	int idx = 0;
	for (int y = 0; y < NewSizeY; y++)
	{
		for (int z = 0; z < NewSizeZ; z++)
		{
			for (int x = 0; x < NewSizeX; x++)
			{
				NewNibbles[idx++] = a_Array[MakeIndex(x + a_AddMinX, y + a_AddMinY, z + a_AddMinZ)];
			}  // for x
		}  // for z
	}  // for y
	delete a_Array;
	a_Array = NewNibbles;
}





void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
{
	int NewSizeX = m_Size.x + a_SubMinX + a_AddMaxX;
	int NewSizeY = m_Size.y + a_SubMinY + a_AddMaxY;
	int NewSizeZ = m_Size.z + a_SubMinZ + a_AddMaxZ;
	size_t BlockCount = static_cast<size_t>(NewSizeX * NewSizeY * NewSizeZ);
	BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[BlockCount];
	memset(NewBlockTypes, 0, BlockCount * sizeof(BLOCKTYPE));
	int OldIndex = 0;
	for (int y = 0; y < m_Size.y; y++)
	{
		int IndexBaseY = (y + a_SubMinY) * m_Size.x * m_Size.z;
		for (int z = 0; z < m_Size.z; z++)
		{
			int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_Size.x;
			int idx = IndexBaseZ + a_SubMinX;
			for (int x = 0; x < m_Size.x; x++)
			{
				NewBlockTypes[idx++] = m_BlockTypes[OldIndex++];
			}  // for x
		}  // for z
	}  // for y
	delete m_BlockTypes;
	m_BlockTypes = NewBlockTypes;
}





void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ)
{
	int NewSizeX = m_Size.x + a_SubMinX + a_AddMaxX;
	int NewSizeY = m_Size.y + a_SubMinY + a_AddMaxY;
	int NewSizeZ = m_Size.z + a_SubMinZ + a_AddMaxZ;
	size_t BlockCount = static_cast<size_t>(NewSizeX * NewSizeY * NewSizeZ);
	NIBBLETYPE * NewNibbles = new NIBBLETYPE[BlockCount];
	memset(NewNibbles, 0, BlockCount * sizeof(NIBBLETYPE));
	int OldIndex = 0;
	for (int y = 0; y < m_Size.y; y++)
	{
		int IndexBaseY = (y + a_SubMinY) * m_Size.x * m_Size.z;
		for (int z = 0; z < m_Size.z; z++)
		{
			int IndexBaseZ = IndexBaseY + (z + a_SubMinZ) * m_Size.x;
			int idx = IndexBaseZ + a_SubMinX;
			for (int x = 0; x < m_Size.x; x++)
			{
				NewNibbles[idx++] = a_Array[OldIndex++];
			}  // for x
		}  // for z
	}  // for y
	delete a_Array;
	a_Array = NewNibbles;
}





void cBlockArea::RelSetData(
	int a_RelX, int a_RelY, int a_RelZ,
	int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
	NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
)
{
	if (!IsValidCoords(a_RelX, a_RelY, a_RelZ))
	{
		return;
	}

	int Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
	if ((a_DataTypes & baTypes) != 0)
	{
		m_BlockTypes[Index] = a_BlockType;
	}
	if ((a_DataTypes & baMetas) != 0)
	{
		m_BlockMetas[Index] = a_BlockMeta;
	}
	if ((a_DataTypes & baLight) != 0)
	{
		m_BlockLight[Index] = a_BlockLight;
	}
	if ((a_DataTypes & baSkyLight) != 0)
	{
		m_BlockSkyLight[Index] = a_BlockSkyLight;
	}

	// Update the block entities, if appropriate:
	if (HasBlockEntities())
	{
		auto itr = m_BlockEntities->find(Index);
		if (itr != m_BlockEntities->end())
		{
			if (itr->second->GetBlockType() == a_BlockType)
			{
				// The block entity is for the same block type, keep the current one
				return;
			}
			// The block entity is for a different block type, remove it:
			m_BlockEntities->erase(itr);
		}
		if (cBlockEntity::IsBlockEntityBlockType(a_BlockType))
		{
			// The block type should have a block entity attached to it, create an empty one:
			m_BlockEntities->insert({Index, cBlockEntity::CreateByBlockType(a_BlockType, a_BlockMeta, a_RelX, a_RelY, a_RelZ)});
		}
	}
}





template <bool MetasValid>
void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas)
{
	// Block types are compulsory, block metas are optional
	if (!HasBlockTypes() || !a_Src.HasBlockTypes())
	{
		LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__);
		return;
	}

	// Dst is *this, Src is a_Src
	int SrcOffX = std::max(0, -a_RelX);  // Offset in Src where to start reading
	int DstOffX = std::max(0,  a_RelX);  // Offset in Dst where to start writing
	int SizeX   = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX);  // How many blocks to copy

	int SrcOffY = std::max(0, -a_RelY);  // Offset in Src where to start reading
	int DstOffY = std::max(0,  a_RelY);  // Offset in Dst where to start writing
	int SizeY   = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY);  // How many blocks to copy

	int SrcOffZ = std::max(0, -a_RelZ);  // Offset in Src where to start reading
	int DstOffZ = std::max(0,  a_RelZ);  // Offset in Dst where to start writing
	int SizeZ   = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ);  // How many blocks to copy

	switch (a_Strategy)
	{
		case cBlockArea::msOverwrite:
		{
			InternalMergeBlocks<MetasValid, MergeCombinatorOverwrite<MetasValid> >(
				m_BlockTypes, a_Src.GetBlockTypes(),
				DstMetas, SrcMetas,
				SizeX, SizeY, SizeZ,
				SrcOffX, SrcOffY, SrcOffZ,
				DstOffX, DstOffY, DstOffZ,
				a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
				m_Size.x, m_Size.y, m_Size.z
			);
			break;
		}  // case msOverwrite

		case cBlockArea::msFillAir:
		{
			InternalMergeBlocks<MetasValid, MergeCombinatorFillAir<MetasValid> >(
				m_BlockTypes, a_Src.GetBlockTypes(),
				DstMetas, SrcMetas,
				SizeX, SizeY, SizeZ,
				SrcOffX, SrcOffY, SrcOffZ,
				DstOffX, DstOffY, DstOffZ,
				a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
				m_Size.x, m_Size.y, m_Size.z
			);
			break;
		}  // case msFillAir

		case cBlockArea::msImprint:
		{
			InternalMergeBlocks<MetasValid, MergeCombinatorImprint<MetasValid> >(
				m_BlockTypes, a_Src.GetBlockTypes(),
				DstMetas, SrcMetas,
				SizeX, SizeY, SizeZ,
				SrcOffX, SrcOffY, SrcOffZ,
				DstOffX, DstOffY, DstOffZ,
				a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
				m_Size.x, m_Size.y, m_Size.z
			);
			break;
		}  // case msImprint

		case cBlockArea::msLake:
		{
			InternalMergeBlocks<MetasValid, MergeCombinatorLake<MetasValid> >(
				m_BlockTypes, a_Src.GetBlockTypes(),
				DstMetas, SrcMetas,
				SizeX, SizeY, SizeZ,
				SrcOffX, SrcOffY, SrcOffZ,
				DstOffX, DstOffY, DstOffZ,
				a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
				m_Size.x, m_Size.y, m_Size.z
			);
			break;
		}  // case msLake

		case cBlockArea::msSpongePrint:
		{
			InternalMergeBlocks<MetasValid, MergeCombinatorSpongePrint<MetasValid> >(
				m_BlockTypes, a_Src.GetBlockTypes(),
				DstMetas, SrcMetas,
				SizeX, SizeY, SizeZ,
				SrcOffX, SrcOffY, SrcOffZ,
				DstOffX, DstOffY, DstOffZ,
				a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
				m_Size.x, m_Size.y, m_Size.z
			);
			break;
		}  // case msSpongePrint

		case cBlockArea::msDifference:
		{
			InternalMergeBlocks<MetasValid, MergeCombinatorDifference<MetasValid> >(
				m_BlockTypes, a_Src.GetBlockTypes(),
				DstMetas, SrcMetas,
				SizeX, SizeY, SizeZ,
				SrcOffX, SrcOffY, SrcOffZ,
				DstOffX, DstOffY, DstOffZ,
				a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
				m_Size.x, m_Size.y, m_Size.z
			);
			break;
		}  // case msDifference

		case cBlockArea::msSimpleCompare:
		{
			InternalMergeBlocks<MetasValid, MergeCombinatorSimpleCompare<MetasValid> >(
				m_BlockTypes, a_Src.GetBlockTypes(),
				DstMetas, SrcMetas,
				SizeX, SizeY, SizeZ,
				SrcOffX, SrcOffY, SrcOffZ,
				DstOffX, DstOffY, DstOffZ,
				a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
				m_Size.x, m_Size.y, m_Size.z
			);
			break;
		}  // case msSimpleCompare

		case cBlockArea::msMask:
		{
			InternalMergeBlocks<MetasValid, MergeCombinatorMask<MetasValid> >(
				m_BlockTypes, a_Src.GetBlockTypes(),
				DstMetas, SrcMetas,
				SizeX, SizeY, SizeZ,
				SrcOffX, SrcOffY, SrcOffZ,
				DstOffX, DstOffY, DstOffZ,
				a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
				m_Size.x, m_Size.y, m_Size.z
			);
			break;
		}  // case msMask

		#ifndef __clang__  // Clang complains about a default case in a switch with all cases covered
		default:
		{
			LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
			ASSERT(!"Unknown block area merge strategy");
			return;
		}
		#endif
	}  // switch (a_Strategy)

	if (HasBlockEntities())
	{
		if (a_Src.HasBlockEntities())
		{
			MergeBlockEntities(a_RelX, a_RelY, a_RelZ, a_Src);
		}
		else
		{
			RescanBlockEntities();
		}
	}
}





void cBlockArea::ClearBlockEntities(cBlockEntities & a_BlockEntities)
{
	for (auto & keyPair: a_BlockEntities)
	{
		delete keyPair.second;
	}
	a_BlockEntities.clear();
}





void cBlockArea::MergeBlockEntities(int a_RelX, int a_RelY, int a_RelZ, const cBlockArea & a_Src)
{
	// Only supported with both BlockEntities and BlockTypes (caller should check):
	ASSERT(HasBlockTypes());
	ASSERT(HasBlockEntities());
	ASSERT(a_Src.HasBlockTypes());
	ASSERT(a_Src.HasBlockEntities());

	// Remove block entities that no longer match the block at their coords:
	RemoveNonMatchingBlockEntities();

	// Clone BEs from a_Src wherever a BE is missing:
	for (int y = 0; y < m_Size.y; ++y) for (int z = 0; z < m_Size.z; ++z) for (int x = 0; x < m_Size.x; ++x)
	{
		auto idx = MakeIndex(x, y, z);
		auto type = m_BlockTypes[idx];
		if (!cBlockEntity::IsBlockEntityBlockType(type))
		{
			continue;
		}

		// This block should have a block entity, check that there is one:
		auto itr = m_BlockEntities->find(idx);
		if (itr != m_BlockEntities->end())
		{
			// There is one already
			continue;
		}

		// Copy a BE from a_Src, if it exists there:
		auto srcX = x + a_RelX;
		auto srcY = y + a_RelY;
		auto srcZ = z + a_RelZ;
		if (a_Src.IsValidRelCoords(srcX, srcY, srcZ))
		{
			auto srcIdx = a_Src.MakeIndex(srcX, srcY, srcZ);
			auto itrSrc = a_Src.m_BlockEntities->find(srcIdx);
			if (itrSrc == a_Src.m_BlockEntities->end())
			{
				m_BlockEntities->insert({idx, itrSrc->second->Clone(x, y, z)});
				continue;
			}
		}
		// No BE found in a_Src, insert a new empty one:
		NIBBLETYPE meta = HasBlockMetas() ? m_BlockMetas[idx] : 0;
		m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(type, meta, x, y, z)});
	}  // for x, z, y
}





void cBlockArea::RescanBlockEntities(void)
{
	// Only supported with both BlockEntities and BlockTypes
	if (!HasBlockEntities() || !HasBlockTypes())
	{
		return;
	}

	// Remove block entities that no longer match the block at their coords:
	RemoveNonMatchingBlockEntities();

	// Add block entities for all block types that should have a BE assigned to them:
	for (int y = 0; y < m_Size.y; ++y) for (int z = 0; z < m_Size.z; ++z) for (int x = 0; x < m_Size.x; ++x)
	{
		auto idx = MakeIndex(x, y, z);
		auto type = m_BlockTypes[idx];
		if (!cBlockEntity::IsBlockEntityBlockType(type))
		{
			continue;
		}
		// This block should have a block entity, check that there is one:
		auto itr = m_BlockEntities->find(idx);
		if (itr != m_BlockEntities->end())
		{
			continue;
		}
		// Create a new BE for this block:
		NIBBLETYPE meta = HasBlockMetas() ? m_BlockMetas[idx] : 0;
		m_BlockEntities->insert({idx, cBlockEntity::CreateByBlockType(type, meta, x, y, z)});
	}  // for x, z, y
}





void cBlockArea::RemoveNonMatchingBlockEntities(void)
{
	// Only supported with both BlockEntities and BlockTypes:
	ASSERT(HasBlockTypes());
	ASSERT(HasBlockEntities());

	cBlockEntities oldBE;
	std::swap(oldBE, *m_BlockEntities);
	for (auto & keyPair: oldBE)
	{
		auto type = m_BlockTypes[keyPair.first];
		if (type == keyPair.second->GetBlockType())
		{
			m_BlockEntities->insert({keyPair.first, std::move(keyPair.second)});
		}
		else
		{
			delete keyPair.second;
		}
	}
}





////////////////////////////////////////////////////////////////////////////////
// cBlockArea::cChunkReader:

cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) :
	m_Area(a_Area),
	m_AreaBounds(cCuboid(a_Area.GetOrigin(), a_Area.GetOrigin() + a_Area.GetSize())),
	m_Origin(a_Area.m_Origin.x, a_Area.m_Origin.y, a_Area.m_Origin.z),
	m_CurrentChunkX(0),
	m_CurrentChunkZ(0)
{
}





void cBlockArea::cChunkReader::CopyNibbles(NIBBLETYPE * a_AreaDst, const NIBBLETYPE * a_ChunkSrc)
{
	int SizeY = m_Area.m_Size.y;
	int MinY = m_Origin.y;

	// SizeX, SizeZ are the dmensions of the block data to copy from the current chunk (size of the geometric union)
	// OffX, OffZ are the offsets of the current chunk data from the area origin
	// BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
	int SizeX = cChunkDef::Width;
	int SizeZ = cChunkDef::Width;
	int OffX, OffZ;
	int BaseX, BaseZ;
	OffX = m_CurrentChunkX * cChunkDef::Width - m_Origin.x;
	if (OffX < 0)
	{
		BaseX = -OffX;
		SizeX += OffX;  // SizeX is decreased, OffX is negative
		OffX = 0;
	}
	else
	{
		BaseX = 0;
	}
	OffZ = m_CurrentChunkZ * cChunkDef::Width - m_Origin.z;
	if (OffZ < 0)
	{
		BaseZ = -OffZ;
		SizeZ += OffZ;  // SizeZ is decreased, OffZ is negative
		OffZ = 0;
	}
	else
	{
		BaseZ = 0;
	}
	// If the chunk extends beyond the area in the X or Z axis, cut off the Size:
	if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_Origin.x + m_Area.m_Size.x)
	{
		SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_Origin.x + m_Area.m_Size.x);
	}
	if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_Origin.z + m_Area.m_Size.z)
	{
		SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
	}

	for (int y = 0; y < SizeY; y++)
	{
		int ChunkY = MinY + y;
		int AreaY = y;
		for (int z = 0; z < SizeZ; z++)
		{
			int ChunkZ = BaseZ + z;
			int AreaZ = OffZ + z;
			for (int x = 0; x < SizeX; x++)
			{
				int ChunkX = BaseX + x;
				int AreaX = OffX + x;
				a_AreaDst[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = cChunkDef::GetNibble(a_ChunkSrc, ChunkX, ChunkY, ChunkZ);
			}  // for x
		}  // for z
	}  // for y
}





bool cBlockArea::cChunkReader::Coords(int a_ChunkX, int a_ChunkZ)
{
	m_CurrentChunkX = a_ChunkX;
	m_CurrentChunkZ = a_ChunkZ;
	return true;
}





void cBlockArea::cChunkReader::ChunkData(const cChunkData & a_BlockBuffer)
{
	int SizeY = m_Area.m_Size.y;
	int MinY = m_Origin.y;

	// SizeX, SizeZ are the dimensions of the block data to copy from the current chunk (size of the geometric union)
	// OffX, OffZ are the offsets of the current chunk data from the area origin
	// BaseX, BaseZ are the offsets of the area data within the current chunk from the chunk borders
	int SizeX = cChunkDef::Width;
	int SizeZ = cChunkDef::Width;
	int OffX, OffZ;
	int BaseX, BaseZ;
	OffX = m_CurrentChunkX * cChunkDef::Width - m_Origin.x;
	if (OffX < 0)
	{
		BaseX = -OffX;
		SizeX += OffX;  // SizeX is decreased, OffX is negative
		OffX = 0;
	}
	else
	{
		BaseX = 0;
	}
	OffZ = m_CurrentChunkZ * cChunkDef::Width - m_Origin.z;
	if (OffZ < 0)
	{
		BaseZ = -OffZ;
		SizeZ += OffZ;  // SizeZ is decreased, OffZ is negative
		OffZ = 0;
	}
	else
	{
		BaseZ = 0;
	}
	// If the chunk extends beyond the area in the X or Z axis, cut off the Size:
	if ((m_CurrentChunkX + 1) * cChunkDef::Width > m_Origin.x + m_Area.m_Size.x)
	{
		SizeX -= (m_CurrentChunkX + 1) * cChunkDef::Width - (m_Origin.x + m_Area.m_Size.x);
	}
	if ((m_CurrentChunkZ + 1) * cChunkDef::Width > m_Origin.z + m_Area.m_Size.z)
	{
		SizeZ -= (m_CurrentChunkZ + 1) * cChunkDef::Width - (m_Origin.z + m_Area.m_Size.z);
	}

	// Copy the blocktypes:
	if (m_Area.m_BlockTypes != nullptr)
	{
		for (int y = 0; y < SizeY; y++)
		{
			int InChunkY = MinY + y;
			int AreaY = y;
			for (int z = 0; z < SizeZ; z++)
			{
				int InChunkZ = BaseZ + z;
				int AreaZ = OffZ + z;
				for (int x = 0; x < SizeX; x++)
				{
					int InChunkX = BaseX + x;
					int AreaX = OffX + x;
					m_Area.m_BlockTypes[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlock(InChunkX, InChunkY, InChunkZ);
				}  // for x
			}  // for z
		}  // for y
	}

	// Copy the block metas:
	if (m_Area.m_BlockMetas != nullptr)
	{
		for (int y = 0; y < SizeY; y++)
		{
			int InChunkY = MinY + y;
			int AreaY = y;
			for (int z = 0; z < SizeZ; z++)
			{
				int InChunkZ = BaseZ + z;
				int AreaZ = OffZ + z;
				for (int x = 0; x < SizeX; x++)
				{
					int InChunkX = BaseX + x;
					int AreaX = OffX + x;
					m_Area.m_BlockMetas[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetMeta(InChunkX, InChunkY, InChunkZ);
				}  // for x
			}  // for z
		}  // for y
	}

	// Copy the blocklight:
	if (m_Area.m_BlockLight != nullptr)
	{
		for (int y = 0; y < SizeY; y++)
		{
			int InChunkY = MinY + y;
			int AreaY = y;
			for (int z = 0; z < SizeZ; z++)
			{
				int InChunkZ = BaseZ + z;
				int AreaZ = OffZ + z;
				for (int x = 0; x < SizeX; x++)
				{
					int InChunkX = BaseX + x;
					int AreaX = OffX + x;
					m_Area.m_BlockLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetBlockLight(InChunkX, InChunkY, InChunkZ);
				}  // for x
			}  // for z
		}  // for y
	}

	// Copy the skylight:
	if (m_Area.m_BlockSkyLight != nullptr)
	{
		for (int y = 0; y < SizeY; y++)
		{
			int InChunkY = MinY + y;
			int AreaY = y;
			for (int z = 0; z < SizeZ; z++)
			{
				int InChunkZ = BaseZ + z;
				int AreaZ = OffZ + z;
				for (int x = 0; x < SizeX; x++)
				{
					int InChunkX = BaseX + x;
					int AreaX = OffX + x;
					m_Area.m_BlockSkyLight[m_Area.MakeIndex(AreaX, AreaY, AreaZ)] = a_BlockBuffer.GetSkyLight(InChunkX, InChunkY, InChunkZ);
				}  // for x
			}  // for z
		}  // for y
	}
}




void cBlockArea::cChunkReader::BlockEntity(cBlockEntity * a_BlockEntity)
{
	if (!m_Area.HasBlockEntities())
	{
		return;
	}
	if (!m_AreaBounds.IsInside(a_BlockEntity->GetPos()))
	{
		return;
	}
	auto areaX = a_BlockEntity->GetPosX() - m_Area.m_Origin.x;
	auto areaY = a_BlockEntity->GetPosY() - m_Area.m_Origin.y;
	auto areaZ = a_BlockEntity->GetPosZ() - m_Area.m_Origin.z;
	int Idx = cChunkDef::MakeIndex(areaX, areaY, areaZ);
	m_Area.m_BlockEntities->insert({Idx, a_BlockEntity->Clone(areaX, areaY, areaZ)});
}