// Statistics.cpp // Implements the various statistics-collecting classes #include "Globals.h" #include "Statistics.h" #include "Utils.h" /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cStatistics: cStatistics::cStatistics(void) : m_TotalChunks(0), m_BiomeNumChunks(0), m_BlockNumChunks(0) { memset(m_BiomeCounts, 0, sizeof(m_BiomeCounts)); memset(m_BlockCounts, 0, sizeof(m_BlockCounts)); } bool cStatistics::OnNewChunk(int a_ChunkX, int a_ChunkZ) { m_TotalChunks++; m_IsBiomesValid = false; m_IsFirstSectionInChunk = true; return false; } bool cStatistics::OnBiomes(const unsigned char * a_BiomeData) { for (int i = 0; i < 16 * 16; i++) { m_BiomeCounts[a_BiomeData[i]] += 1; } m_BiomeNumChunks += 1; memcpy(m_BiomeData, a_BiomeData, sizeof(m_BiomeData)); m_IsBiomesValid = true; return false; } bool cStatistics::OnSection ( unsigned char a_Y, const BLOCKTYPE * a_BlockTypes, const NIBBLETYPE * a_BlockAdditional, const NIBBLETYPE * a_BlockMeta, const NIBBLETYPE * a_BlockLight, const NIBBLETYPE * a_BlockSkyLight ) { if (!m_IsBiomesValid) { // The current biome data is not valid, we don't have the means for sorting the BlockTypes into per-biome arrays return true; } for (int y = 0; y < 16; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { unsigned char Biome = m_BiomeData[x + 16 * z]; // Cannot use cChunkDef, different datatype unsigned char BlockType = cChunkDef::GetBlock(a_BlockTypes, x, y, z); m_BlockCounts[Biome][BlockType] += 1; } } } m_BlockNumChunks += m_IsFirstSectionInChunk ? 1 : 0; m_IsFirstSectionInChunk = false; return false; } bool cStatistics::OnEmptySection(unsigned char a_Y) { if (!m_IsBiomesValid) { // The current biome data is not valid, we don't have the means for sorting the BlockTypes into per-biome arrays return true; } // Add air to all columns: for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { unsigned char Biome = m_BiomeData[x + 16 * z]; // Cannot use cChunkDef, different datatype m_BlockCounts[Biome][0] += 16; // 16 blocks in a column, all air } } return false; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cStatisticsFactory: cStatisticsFactory::~cStatisticsFactory() { // Join the results together: LOG("cStatistics:"); LOG(" Joining results..."); JoinResults(); LOG(" Total %d chunks went through", m_TotalChunks); LOG(" Biomes processed for %d chunks", m_BiomeNumChunks); // Check the number of blocks processed Int64 TotalBlocks = 0; for (int i = 0; i <= 255; i++) { for (int j = 0; j < 255; j++) { TotalBlocks += m_BlockCounts[i][j]; } } Int64 ExpTotalBlocks = (Int64)m_BlockNumChunks * 16LL * 16LL * 256LL; LOG(" BlockIDs processed for %d chunks, %lld blocks (exp %lld; %s)", m_BlockNumChunks, TotalBlocks, ExpTotalBlocks, (TotalBlocks == ExpTotalBlocks) ? "match" : "failed"); // Save statistics: LOG(" Saving statistics into files:"); LOG(" Biomes.txt"); SaveBiomes(); LOG(" BlockTypes.txt"); SaveBlockTypes(); LOG(" BiomeBlockTypes.txt"); SaveBiomeBlockTypes(); } void cStatisticsFactory::JoinResults(void) { m_BiomeNumChunks = 0; m_BlockNumChunks = 0; m_TotalChunks = 0; memset(m_BiomeCounts, 0, sizeof(m_BiomeCounts)); memset(m_BlockCounts, 0, sizeof(m_BlockCounts)); for (cCallbacks::iterator itr = m_Callbacks.begin(), end = m_Callbacks.end(); itr != end; ++itr) { cStatistics * stats = (cStatistics *)(*itr); for (int i = 0; i <= 255; i++) { m_BiomeCounts[i] += stats->m_BiomeCounts[i]; } for (int i = 0; i <= 255; i++) { for (int j = 0; j <= 255; j++) { m_BlockCounts[i][j] += stats->m_BlockCounts[i][j]; } } m_BiomeNumChunks += stats->m_BiomeNumChunks; m_BlockNumChunks += stats->m_BlockNumChunks; m_TotalChunks += stats->m_TotalChunks; } // for itr - m_Callbacks[] } void cStatisticsFactory::SaveBiomes(void) { cFile f; if (!f.Open("Biomes.xls", cFile::fmWrite)) { LOG("Cannot write to file Biomes.xls. Statistics not written."); return; } double TotalColumns = (double)m_BiomeNumChunks * 16 * 16; // Total number of columns processed if (TotalColumns < 1) { // Avoid division by zero TotalColumns = 1; } for (int i = 0; i <= 255; i++) { AString Line; Printf(Line, "%s\t%d\t%d\t%.05f\n", GetBiomeString(i), i, m_BiomeCounts[i], ((double)m_BiomeCounts[i]) / TotalColumns); f.Write(Line.c_str(), Line.length()); } } void cStatisticsFactory::SaveBlockTypes(void) { cFile f; if (!f.Open("BlockTypes.xls", cFile::fmWrite)) { LOG("Cannot write to file Biomes.xls. Statistics not written."); return; } double TotalBlocks = ((double)m_BlockNumChunks) * 16 * 16 * 256 / 100; // Total number of blocks processed if (TotalBlocks < 1) { // Avoid division by zero TotalBlocks = 1; } for (int i = 0; i <= 255; i++) { int Count = 0; for (int Biome = 0; Biome <= 255; ++Biome) { Count += m_BlockCounts[Biome][i]; } AString Line; Printf(Line, "%s\t%d\t%d\t%.08f\n", GetBlockTypeString(i), i, Count, ((double)Count) / TotalBlocks); f.Write(Line.c_str(), Line.length()); } // TODO } void cStatisticsFactory::SaveBiomeBlockTypes(void) { // Export as two tables: biomes 0-127 and 128-255, because OpenOffice doesn't support more than 256 columns cFile f; if (!f.Open("BiomeBlockTypes.xls", cFile::fmWrite)) { LOG("Cannot write to file BiomeBlockTypes.xls. Statistics not written."); return; } AString FileHeader("Biomes 0-127:\n"); f.Write(FileHeader.c_str(), FileHeader.length()); AString Header("BlockType\tBlockType"); for (int Biome = 0; Biome <= 127; Biome++) { const char * BiomeName = GetBiomeString(Biome); if ((BiomeName != NULL) && (BiomeName[0] != 0)) { AppendPrintf(Header, "\t%s (%d)", BiomeName, Biome); } else { AppendPrintf(Header, "\t%d", Biome); } } Header.append("\n"); f.Write(Header.c_str(), Header.length()); for (int BlockType = 0; BlockType <= 255; BlockType++) { AString Line; Printf(Line, "%s\t%d", GetBlockTypeString(BlockType), BlockType); for (int Biome = 0; Biome <= 127; Biome++) { AppendPrintf(Line, "\t%d", m_BlockCounts[Biome][BlockType]); } Line.append("\n"); f.Write(Line.c_str(), Line.length()); } Header.assign("\n\nBiomes 127-255:\nBlockType\tBlockType"); for (int Biome = 0; Biome <= 127; Biome++) { const char * BiomeName = GetBiomeString(Biome); if ((BiomeName != NULL) && (BiomeName[0] != 0)) { AppendPrintf(Header, "\t%s (%d)", BiomeName, Biome); } else { AppendPrintf(Header, "\t%d", Biome); } } Header.append("\n"); f.Write(Header.c_str(), Header.length()); for (int BlockType = 0; BlockType <= 255; BlockType++) { AString Line; Printf(Line, "%s\t%d", GetBlockTypeString(BlockType), BlockType); for (int Biome = 128; Biome <= 255; Biome++) { AppendPrintf(Line, "\t%d", m_BlockCounts[Biome][BlockType]); } Line.append("\n"); f.Write(Line.c_str(), Line.length()); } }