Versions

Component Types by version

Version 7

  • all component types saved as strings instead of enum
  • NULL_TYPE saved as empty string
  • allows for updating and rearranging the component type enum without breaking save games

Version 6

  • undocumented
  • region files store 16 x 16 x 16 chunks
  • chunks are 32 x 32 x 32 cells

Description

.region files contain chunk data. .region filenames represent what chunks are contained within chunk file data is compressed using zlib when uncompressed its a straightforward 3 dimensional array of voxel data

.region files consist of X blocks of data, each blockSize large.

block [0] contains:

  • PakFileHeader (x 1)
  • PakFileRecord (x PakFileHeader.record_count)

block [PakFileHeader.first_block_offset] onwards is data pointed to by the PakFileRecords

To find record_idx data =

  • void * record_data_start = (PakFileHeader.first_block_offset + PakFileRecord.block_offset) * blockSize;
  • size_t record_data_length = PakFileRecord.block_length * blockSize;
static const int chunks_per_region = 32;
static const int records_per_region = chunks_per_region * chunks_per_region;
static const blockSize = 1024 * 4;

struct Voxel
{
	type_;
}

struct Chunk 
{
	Voxel [16][chunk_size_y][16];
}
 
struct PakFileHeader
{
	char tag [4];
	int version_number;
	int record_count; // = records_per_region
	int first_block_offset;
};

struct PakFileRecord
{
	short block_offset;
	short block_length;
	int file_length;
};

int CalcRecordIdFromChunkId (Int3 chunk_id)
{
	DIO_ASSERT (chunk_id.y == 0);

	int x = chunk_id.x % chunks_per_region;
	int z = chunk_id.z % chunks_per_region;
	x = (x >= 0) ? x : x + chunks_per_region;
	z = (z >= 0) ? z : z + chunks_per_region;
	int record = x + z * chunks_per_region;

	DIO_ASSERT (x >= 0 && x < chunks_per_region);
	DIO_ASSERT (z >= 0 && z < chunks_per_region);
	
	return record;
}
bool DoesRecordExist (int recordIdx) const
{
	const PakFileRecord * record = &records [recordIdx];
	return (record->block_offset != -1);
}
static const int blockSize = 1024 * 4;

char * GetRecordData (PakFileHeader & header, int record_idx)
{
	PakFileRecord * record = &records [recordIdx];
	char * data = header.first_block_offset + record->block_offset * blockSize
	
	return data;
}
int PakFileCompressed::LoadRecord (int recordIdx, /*RakNet::Time * lastModified, */unsigned char * out, int maxOut)
{
	DIO_ASSERT (recordIdx >= 0 && recordIdx < header.record_count);
	const int compressedSize = PakFileBase::LoadRecord (recordIdx, /*lastModified, */compressionBuffer, compressionBufferSize);
	const int decompressedSize = Utils::Zlib_Uncompress (compressionBuffer, compressedSize, out, maxOut);
	return decompressedSize;
}
//------------------------------------------------
int FloorDivideInt (int value, int divideBy)
{
	int result = ((value >= 0) ? value / divideBy : (value + 1) / divideBy - 1);
	return result;
}

//------------------------------------------------
void CalcRegionFilenameFromChunkId (
		Text path_to_room, 
		Int3 chunk_id, 
		char * out_filename, 
		int max_length)
{
	int x = FloorDivideInt (chunk_id.x, chunks_per_region);
	int y = 0;
	int z = FloorDivideInt (chunk_id.z, chunks_per_region);

	sprintf_s (out_filename, max_length, "%sx%d_y%d_z%d.region", path_to_room.c_str (), x, y, z);
}

prelim notes for the world hierarchy and file save

world	1

	0..* rooms (scenes)	0..*  (3 in minecraft... normal, nether, the end)

		0..* regions (for loading/saving optimisation - so no gameplay impact YET) 

			32x32 chunks (are a collection of voxels in a size suitable for streaming / updating etc)

				8 blocks (sized for network updates AND rendering)

					16x16x16 voxels


MC ------ 	chunk is 16x128x16
my game --	chunk is going to be 32x32x32

why square? because my game is going to be infinite in height

Twitch | Twitter