From cead22a206617207d8f757b57e064eea39d0b2f2 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Sat, 29 Sep 2012 13:33:45 +0000 Subject: [PATCH] AnvilStats: Overall statistics and mobspawner statistics. git-svn-id: http://mc-server.googlecode.com/svn/trunk@899 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- AnvilStats/Callback.h | 44 ++++++- AnvilStats/Globals.h | 3 + AnvilStats/Processor.cpp | 144 ++++++++++++++++++++- AnvilStats/Processor.h | 5 + AnvilStats/Statistics.cpp | 255 +++++++++++++++++++++++++++++++------- AnvilStats/Statistics.h | 77 +++++++++--- AnvilStats/Utils.cpp | 66 ++++++++++ AnvilStats/Utils.h | 46 +++++++ 8 files changed, 574 insertions(+), 66 deletions(-) diff --git a/AnvilStats/Callback.h b/AnvilStats/Callback.h index 66d6bcfae..52d2e27eb 100644 --- a/AnvilStats/Callback.h +++ b/AnvilStats/Callback.h @@ -13,6 +13,13 @@ +// fwd: +class cParsedNBT; + + + + + /** The base class for all chunk-processor callbacks, declares the interface. The processor calls each virtual function in the order they are declared here with the specified args. If the function returns true, the processor moves on to next chunk and starts calling the callbacks again from start with @@ -68,7 +75,42 @@ public: */ virtual bool OnEmptySection(unsigned char a_Y) { return false; } - // TODO: entities, tile-entities, tile-ticks + /** Called for each entity in the chunk. + Common parameters are parsed from the NBT. + The callback may parse any other param from the a_NBT and a_NBTTag parameters. + The a_NBTTag parameter points to the entity compound tag inside the Entities tag. + */ + virtual bool OnEntity( + const AString & a_EntityType, + double a_PosX, double a_PosY, double a_PosZ, + double a_SpeedX, double a_SpeedY, double a_SpeedZ, + float a_Yaw, float a_Pitch, + float a_FallDistance, + short a_FireTicksLeft, + short a_AirTicks, + char a_IsOnGround, + cParsedNBT & a_NBT, + int a_NBTTag + ) { return true; } + + /** Called for each tile entity in the chunk. + Common parameters are parsed from the NBT. + The callback may parse any other param from the a_NBT and a_NBTTag parameters. + The a_NBTTag parameter points to the tile entity compound tag inside the TileEntities tag. + */ + virtual bool OnTileEntity( + const AString & a_EntityType, + int a_PosX, int a_PosY, int a_PosZ, + cParsedNBT & a_NBT, + int a_NBTTag + ) { return true; } + + /// Called for each tile tick in the chunk + virtual bool OnTileTick( + int a_BlockType, + int a_TicksLeft, + int a_PosX, int a_PosY, int a_PosZ + ) { return true; } } ; typedef std::vector cCallbacks; diff --git a/AnvilStats/Globals.h b/AnvilStats/Globals.h index 1c7387b75..e95a9e801 100644 --- a/AnvilStats/Globals.h +++ b/AnvilStats/Globals.h @@ -15,6 +15,8 @@ // Disable some warnings that we don't care about: #pragma warning(disable:4100) + + #define _CRT_SECURE_NO_WARNINGS #define OBSOLETE __declspec(deprecated) @@ -149,6 +151,7 @@ typedef short Int16; #include #include #include +#include diff --git a/AnvilStats/Processor.cpp b/AnvilStats/Processor.cpp index 58f11e650..0d7c5d192 100644 --- a/AnvilStats/Processor.cpp +++ b/AnvilStats/Processor.cpp @@ -256,7 +256,21 @@ void cProcessor::cThread::ProcessParsedChunkData(int a_ChunkX, int a_ChunkZ, cPa { return; } - // TODO: entities, tile-entities etc. + + if (ProcessChunkEntities(a_ChunkX, a_ChunkZ, a_NBT, LevelTag)) + { + return; + } + + if (ProcessChunkTileEntities(a_ChunkX, a_ChunkZ, a_NBT, LevelTag)) + { + return; + } + + if (ProcessChunkTileTicks(a_ChunkX, a_ChunkZ, a_NBT, LevelTag)) + { + return; + } } @@ -323,6 +337,134 @@ bool cProcessor::cThread::ProcessChunkSections(int a_ChunkX, int a_ChunkZ, cPars +bool cProcessor::cThread::ProcessChunkEntities(int a_ChunkX, int a_ChunkZ, cParsedNBT & a_NBT, int a_LevelTag) +{ + int EntitiesTag = a_NBT.FindChildByName(a_LevelTag, "Entities"); + if (EntitiesTag < 0) + { + return false; + } + + for (int EntityTag = a_NBT.GetFirstChild(EntitiesTag); EntityTag > 0; EntityTag = a_NBT.GetNextSibling(EntityTag)) + { + int PosTag = a_NBT.FindChildByName(EntityTag, "Pos"); + if (PosTag < 0) + { + continue; + } + int SpeedTag = a_NBT.FindChildByName(EntityTag, "Motion"); + if (SpeedTag < 0) + { + continue; + } + int RotTag = a_NBT.FindChildByName(EntityTag, "Rotation"); + if (RotTag < 0) + { + continue; + } + double Pos[3]; + for (int i = 0, tag = a_NBT.GetFirstChild(PosTag); (i < 3) && (tag > 0); i++) + { + Pos[i] = a_NBT.GetDouble(tag); + } + double Speed[3]; + for (int i = 0, tag = a_NBT.GetFirstChild(SpeedTag); (i < 3) && (tag > 0); i++) + { + Speed[i] = a_NBT.GetDouble(tag); + } + float Rot[2]; + for (int i = 0, tag = a_NBT.GetFirstChild(RotTag); (i < 2) && (tag > 0); i++) + { + Rot[i] = a_NBT.GetFloat(tag); + } + + if (m_Callback.OnEntity( + a_NBT.GetString(a_NBT.FindChildByName(EntityTag, "id")), + Pos[0], Pos[1], Pos[2], + Speed[0], Speed[1], Speed[2], + Rot[0], Rot[1], + a_NBT.GetFloat(a_NBT.FindChildByName(EntityTag, "FallDistance")), + a_NBT.GetShort(a_NBT.FindChildByName(EntityTag, "Fire")), + a_NBT.GetShort(a_NBT.FindChildByName(EntityTag, "Air")), + a_NBT.GetByte(a_NBT.FindChildByName(EntityTag, "OnGround")), + a_NBT, EntityTag + )) + { + return true; + } + } // for EntityTag - Entities[] + return false; +} + + + + + +bool cProcessor::cThread::ProcessChunkTileEntities(int a_ChunkX, int a_ChunkZ, cParsedNBT & a_NBT, int a_LevelTag) +{ + int TileEntitiesTag = a_NBT.FindChildByName(a_LevelTag, "TileEntities"); + if (TileEntitiesTag < 0) + { + return false; + } + + for (int TileEntityTag = a_NBT.GetFirstChild(TileEntitiesTag); TileEntityTag > 0; TileEntityTag = a_NBT.GetNextSibling(TileEntityTag)) + { + if (m_Callback.OnTileEntity( + a_NBT.GetString(a_NBT.FindChildByName(TileEntityTag, "id")), + a_NBT.GetInt(a_NBT.FindChildByName(TileEntityTag, "x")), + a_NBT.GetInt(a_NBT.FindChildByName(TileEntityTag, "y")), + a_NBT.GetInt(a_NBT.FindChildByName(TileEntityTag, "z")), + a_NBT, TileEntityTag + )) + { + return true; + } + } // for EntityTag - Entities[] + return false; +} + + + + + +bool cProcessor::cThread::ProcessChunkTileTicks(int a_ChunkX, int a_ChunkZ, cParsedNBT & a_NBT, int a_LevelTag) +{ + int TileTicksTag = a_NBT.FindChildByName(a_LevelTag, "TileTicks"); + if (TileTicksTag < 0) + { + return false; + } + + for (int TileTickTag = a_NBT.GetFirstChild(TileTicksTag); TileTickTag > 0; TileTickTag = a_NBT.GetNextSibling(TileTickTag)) + { + int iTag = a_NBT.FindChildByName(TileTicksTag, "i"); + int tTag = a_NBT.FindChildByName(TileTicksTag, "t"); + int xTag = a_NBT.FindChildByName(TileTicksTag, "x"); + int yTag = a_NBT.FindChildByName(TileTicksTag, "y"); + int zTag = a_NBT.FindChildByName(TileTicksTag, "z"); + if ((iTag < 0) || (tTag < 0) || (xTag < 0) || (yTag < 0) || (zTag < 0)) + { + continue; + } + if (m_Callback.OnTileTick( + a_NBT.GetInt(iTag), + a_NBT.GetInt(iTag), + a_NBT.GetInt(iTag), + a_NBT.GetInt(iTag), + a_NBT.GetInt(iTag) + )) + { + return true; + } + } // for EntityTag - Entities[] + return false; +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cProcessor: diff --git a/AnvilStats/Processor.h b/AnvilStats/Processor.h index 4f5496bc6..b7d3f392e 100644 --- a/AnvilStats/Processor.h +++ b/AnvilStats/Processor.h @@ -39,7 +39,12 @@ class cProcessor void ProcessChunk(const char * a_FileData, int a_ChunkX, int a_ChunkZ, unsigned a_SectorStart, unsigned a_SectorSize, unsigned a_TimeStamp); void ProcessCompressedChunkData(int a_ChunkX, int a_ChunkZ, const char * a_CompressedData, int a_CompressedSize); void ProcessParsedChunkData(int a_ChunkX, int a_ChunkZ, cParsedNBT & a_NBT); + + // The following processing parts return true if they were interrupted by the callback, causing the processing of current chunk to abort bool ProcessChunkSections(int a_ChunkX, int a_ChunkZ, cParsedNBT & a_NBT, int a_LevelTag); + bool ProcessChunkEntities(int a_ChunkX, int a_ChunkZ, cParsedNBT & a_NBT, int a_LevelTag); + bool ProcessChunkTileEntities(int a_ChunkX, int a_ChunkZ, cParsedNBT & a_NBT, int a_LevelTag); + bool ProcessChunkTileTicks(int a_ChunkX, int a_ChunkZ, cParsedNBT & a_NBT, int a_LevelTag); public: cThread(cCallback & a_Callback, cProcessor & a_ParentProcessor); diff --git a/AnvilStats/Statistics.cpp b/AnvilStats/Statistics.cpp index 7856fa9b1..f786cac79 100644 --- a/AnvilStats/Statistics.cpp +++ b/AnvilStats/Statistics.cpp @@ -5,7 +5,56 @@ #include "Globals.h" #include "Statistics.h" -#include "Utils.h" +#include "../source/WorldStorage/FastNBT.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cStatistics::cStats: + +cStatistics::cStats::cStats(void) : + m_TotalChunks(0), + m_BiomeNumChunks(0), + m_BlockNumChunks(0), + m_NumEntities(0), + m_NumTileEntities(0), + m_NumTileTicks(0) +{ + memset(m_BiomeCounts, 0, sizeof(m_BiomeCounts)); + memset(m_BlockCounts, 0, sizeof(m_BlockCounts)); + memset(m_SpawnerEntity, 0, sizeof(m_SpawnerEntity)); +} + + + + + +void cStatistics::cStats::Add(const cStatistics::cStats & a_Stats) +{ + for (int i = 0; i <= 255; i++) + { + m_BiomeCounts[i] += a_Stats.m_BiomeCounts[i]; + } + for (int i = 0; i <= 255; i++) + { + for (int j = 0; j <= 255; j++) + { + m_BlockCounts[i][j] += a_Stats.m_BlockCounts[i][j]; + } + } + for (int i = 0; i < ARRAYCOUNT(m_SpawnerEntity); i++) + { + m_SpawnerEntity[i] += a_Stats.m_SpawnerEntity[i]; + } + m_BiomeNumChunks += a_Stats.m_BiomeNumChunks; + m_BlockNumChunks += a_Stats.m_BlockNumChunks; + m_TotalChunks += a_Stats.m_TotalChunks; + m_NumEntities += a_Stats.m_NumEntities; + m_NumTileEntities += a_Stats.m_NumTileEntities; + m_NumTileTicks += a_Stats.m_NumTileTicks; +} @@ -14,13 +63,8 @@ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cStatistics: -cStatistics::cStatistics(void) : - m_TotalChunks(0), - m_BiomeNumChunks(0), - m_BlockNumChunks(0) +cStatistics::cStatistics(void) { - memset(m_BiomeCounts, 0, sizeof(m_BiomeCounts)); - memset(m_BlockCounts, 0, sizeof(m_BlockCounts)); } @@ -29,7 +73,7 @@ cStatistics::cStatistics(void) : bool cStatistics::OnNewChunk(int a_ChunkX, int a_ChunkZ) { - m_TotalChunks++; + m_Stats.m_TotalChunks++; m_IsBiomesValid = false; m_IsFirstSectionInChunk = true; return false; @@ -43,9 +87,9 @@ bool cStatistics::OnBiomes(const unsigned char * a_BiomeData) { for (int i = 0; i < 16 * 16; i++) { - m_BiomeCounts[a_BiomeData[i]] += 1; + m_Stats.m_BiomeCounts[a_BiomeData[i]] += 1; } - m_BiomeNumChunks += 1; + m_Stats.m_BiomeNumChunks += 1; memcpy(m_BiomeData, a_BiomeData, sizeof(m_BiomeData)); m_IsBiomesValid = true; return false; @@ -80,12 +124,12 @@ bool cStatistics::OnSection { 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_Stats.m_BlockCounts[Biome][BlockType] += 1; } } } - m_BlockNumChunks += m_IsFirstSectionInChunk ? 1 : 0; + m_Stats.m_BlockNumChunks += m_IsFirstSectionInChunk ? 1 : 0; m_IsFirstSectionInChunk = false; return false; @@ -109,10 +153,58 @@ bool cStatistics::OnEmptySection(unsigned char a_Y) 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 + m_Stats.m_BlockCounts[Biome][0] += 16; // 16 blocks in a column, all air } } + m_Stats.m_BlockNumChunks += m_IsFirstSectionInChunk ? 1 : 0; + m_IsFirstSectionInChunk = false; + + return false; +} + + + + + +bool cStatistics::OnEntity( + const AString & a_EntityType, + double a_PosX, double a_PosY, double a_PosZ, + double a_SpeedX, double a_SpeedY, double a_SpeedZ, + float a_Yaw, float a_Pitch, + float a_FallDistance, + short a_FireTicksLeft, + short a_AirTicks, + char a_IsOnGround, + cParsedNBT & a_NBT, + int a_NBTTag +) +{ + m_Stats.m_NumEntities += 1; + + // TODO + + return false; +} + + + + + +bool cStatistics::OnTileEntity( + const AString & a_EntityType, + int a_PosX, int a_PosY, int a_PosZ, + cParsedNBT & a_NBT, + int a_NBTTag +) +{ + m_Stats.m_NumTileEntities += 1; + + if (a_EntityType == "MobSpawner") + { + OnSpawner(a_NBT, a_NBTTag); + } + return false; } @@ -120,17 +212,58 @@ bool cStatistics::OnEmptySection(unsigned char a_Y) +bool cStatistics::OnTileTick( + int a_BlockType, + int a_TicksLeft, + int a_PosX, int a_PosY, int a_PosZ +) +{ + m_Stats.m_NumTileTicks += 1; + return false; +} + + + + + +void cStatistics::OnSpawner(cParsedNBT & a_NBT, int a_TileEntityTag) +{ + int EntityIDTag = a_NBT.FindChildByName(a_TileEntityTag, "EntityId"); + if ((EntityIDTag < 0) || (a_NBT.GetType(EntityIDTag) != TAG_String)) + { + return; + } + eEntityType Ent = GetEntityType(a_NBT.GetString(EntityIDTag)); + if (Ent < ARRAYCOUNT(m_Stats.m_SpawnerEntity)) + { + m_Stats.m_SpawnerEntity[Ent] += 1; + } +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cStatisticsFactory: +cStatisticsFactory::cStatisticsFactory(void) : + m_BeginTick(clock()) +{ +} + + + + + 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); + LOG(" Total %d chunks went through", m_CombinedStats.m_TotalChunks); + LOG(" Biomes processed for %d chunks", m_CombinedStats.m_BiomeNumChunks); // Check the number of blocks processed Int64 TotalBlocks = 0; @@ -138,20 +271,24 @@ cStatisticsFactory::~cStatisticsFactory() { for (int j = 0; j < 255; j++) { - TotalBlocks += m_BlockCounts[i][j]; + TotalBlocks += m_CombinedStats.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"); + Int64 ExpTotalBlocks = (Int64)(m_CombinedStats.m_BlockNumChunks) * 16LL * 16LL * 256LL; + LOG(" BlockIDs processed for %d chunks, %lld blocks (exp %lld; %s)", m_CombinedStats.m_BlockNumChunks, TotalBlocks, ExpTotalBlocks, (TotalBlocks == ExpTotalBlocks) ? "match" : "failed"); // Save statistics: LOG(" Saving statistics into files:"); - LOG(" Biomes.txt"); + LOG(" Statistics.txt"); + SaveStatistics(); + LOG(" Biomes.xls"); SaveBiomes(); - LOG(" BlockTypes.txt"); + LOG(" BlockTypes.xls"); SaveBlockTypes(); - LOG(" BiomeBlockTypes.txt"); + LOG(" BiomeBlockTypes.xls"); SaveBiomeBlockTypes(); + LOG(" Spawners.xls"); + SaveSpawners(); } @@ -160,28 +297,9 @@ cStatisticsFactory::~cStatisticsFactory() 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; + m_CombinedStats.Add(((cStatistics *)(*itr))->GetStats()); } // for itr - m_Callbacks[] } @@ -197,7 +315,7 @@ void cStatisticsFactory::SaveBiomes(void) LOG("Cannot write to file Biomes.xls. Statistics not written."); return; } - double TotalColumns = (double)m_BiomeNumChunks * 16 * 16; // Total number of columns processed + double TotalColumns = (double)(m_CombinedStats.m_BiomeNumChunks) * 16 * 16 / 100; // Total number of columns processed; convert into percent if (TotalColumns < 1) { // Avoid division by zero @@ -206,7 +324,7 @@ void cStatisticsFactory::SaveBiomes(void) 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); + Printf(Line, "%s\t%d\t%d\t%.05f\n", GetBiomeString(i), i, m_CombinedStats.m_BiomeCounts[i], ((double)(m_CombinedStats.m_BiomeCounts[i])) / TotalColumns); f.Write(Line.c_str(), Line.length()); } } @@ -223,7 +341,7 @@ void cStatisticsFactory::SaveBlockTypes(void) 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 + double TotalBlocks = ((double)(m_CombinedStats.m_BlockNumChunks)) * 16 * 16 * 256 / 100; // Total number of blocks processed; convert into percent if (TotalBlocks < 1) { // Avoid division by zero @@ -234,13 +352,12 @@ void cStatisticsFactory::SaveBlockTypes(void) int Count = 0; for (int Biome = 0; Biome <= 255; ++Biome) { - Count += m_BlockCounts[Biome][i]; + Count += m_CombinedStats.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 } @@ -282,7 +399,7 @@ void cStatisticsFactory::SaveBiomeBlockTypes(void) Printf(Line, "%s\t%d", GetBlockTypeString(BlockType), BlockType); for (int Biome = 0; Biome <= 127; Biome++) { - AppendPrintf(Line, "\t%d", m_BlockCounts[Biome][BlockType]); + AppendPrintf(Line, "\t%d", m_CombinedStats.m_BlockCounts[Biome][BlockType]); } Line.append("\n"); f.Write(Line.c_str(), Line.length()); @@ -310,7 +427,7 @@ void cStatisticsFactory::SaveBiomeBlockTypes(void) Printf(Line, "%s\t%d", GetBlockTypeString(BlockType), BlockType); for (int Biome = 128; Biome <= 255; Biome++) { - AppendPrintf(Line, "\t%d", m_BlockCounts[Biome][BlockType]); + AppendPrintf(Line, "\t%d", m_CombinedStats.m_BlockCounts[Biome][BlockType]); } Line.append("\n"); f.Write(Line.c_str(), Line.length()); @@ -321,3 +438,45 @@ void cStatisticsFactory::SaveBiomeBlockTypes(void) + +void cStatisticsFactory::SaveStatistics(void) +{ + cFile f; + if (!f.Open("Statistics.txt", cFile::fmWrite)) + { + LOG("Cannot write to file Statistics.txt. Statistics not written."); + return; + } + + int Elapsed = (clock() - m_BeginTick) / CLOCKS_PER_SEC; + f.Printf("Time elapsed: %d seconds (%d hours, %d minutes and %d seconds)\n", Elapsed, Elapsed / 3600, (Elapsed / 60) % 60, Elapsed % 60); + f.Printf("Total chunks processed: %d\n", m_CombinedStats.m_TotalChunks); + f.Printf("Chunk processing speed: %.02f chunks per second\n", (double)(m_CombinedStats.m_TotalChunks) / Elapsed); + f.Printf("Biomes counted for %d chunks.\n", m_CombinedStats.m_BiomeNumChunks); + f.Printf("Blocktypes counted for %d chunks.\n", m_CombinedStats.m_BlockNumChunks); + f.Printf("Total blocks counted: %lld\n", (Int64)(m_CombinedStats.m_BlockNumChunks) * 16 * 16 * 256); + f.Printf("Total biomes counted: %lld\n", (Int64)(m_CombinedStats.m_BiomeNumChunks) * 16 * 16); + f.Printf("Total entities counted: %d\n", m_CombinedStats.m_NumEntities); + f.Printf("Total tile entities counted: %d\n", m_CombinedStats.m_NumTileEntities); + f.Printf("Total tile ticks counted: %d\n", m_CombinedStats.m_NumTileTicks); +} + + + + + +void cStatisticsFactory::SaveSpawners(void) +{ + cFile f; + if (!f.Open("Spawners.xls", cFile::fmWrite)) + { + LOG("Cannot write to file Spawners.xls. Statistics not written."); + return; + } + + f.Printf("Entity type\tTotal count\tCount per chunk\n"); + for (int i = 0; i < entMax; i++) + { + f.Printf("%s\t%d\t%0.4f\n", GetEntityTypeString((eEntityType)i), m_CombinedStats.m_SpawnerEntity[i], (double)(m_CombinedStats.m_SpawnerEntity[i]) / m_CombinedStats.m_BlockNumChunks); + } +} \ No newline at end of file diff --git a/AnvilStats/Statistics.h b/AnvilStats/Statistics.h index 38f69615c..8be58bf8e 100644 --- a/AnvilStats/Statistics.h +++ b/AnvilStats/Statistics.h @@ -10,6 +10,7 @@ #pragma once #include "Callback.h" +#include "Utils.h" @@ -18,20 +19,35 @@ class cStatistics : public cCallback { - friend class cStatisticsFactory; - public: + class cStats + { + public: + int m_TotalChunks; // Total number of chunks that go through this callback (OnNewChunk()) + int m_BiomeCounts[256]; + int m_BlockCounts[256][256]; // First dimension is the biome, second dimension is BlockType + int m_BiomeNumChunks; // Num chunks that have been processed for biome stats + int m_BlockNumChunks; // Num chunks that have been processed for block stats + int m_NumEntities; + int m_NumTileEntities; + int m_NumTileTicks; + + int m_SpawnerEntity[entMax + 1]; + + cStats(void); + void Add(const cStats & a_Stats); + } ; + cStatistics(void); + const cStats & GetStats(void) const { return m_Stats; } + protected: - int m_TotalChunks; // Total number of chunks that go through this callback (OnNewChunk()) - int m_BiomeCounts[256]; - int m_BlockCounts[256][256]; // First dimension is the biome, second dimension is BlockType - int m_BiomeNumChunks; // Num chunks that have been processed for biome stats - int m_BlockNumChunks; // Num chunks that have been processed for block stats - bool m_IsBiomesValid; // Set to true in OnBiomes(), reset to false in OnNewChunk(); if true, the m_BiomeData is valid for the current chunk - unsigned char m_BiomeData[16 * 16]; - bool m_IsFirstSectionInChunk; // True if there was no section in the chunk yet. Set by OnNewChunk(), reset by OnSection() + cStats m_Stats; + + bool m_IsBiomesValid; // Set to true in OnBiomes(), reset to false in OnNewChunk(); if true, the m_BiomeData is valid for the current chunk + unsigned char m_BiomeData[16 * 16]; + bool m_IsFirstSectionInChunk; // True if there was no section in the chunk yet. Set by OnNewChunk(), reset by OnSection() // cCallback overrides: virtual bool OnNewChunk(int a_ChunkX, int a_ChunkZ) override; @@ -51,7 +67,36 @@ protected: const NIBBLETYPE * a_BlockLight, const NIBBLETYPE * a_BlockSkyLight ) override; + virtual bool OnEmptySection(unsigned char a_Y) override; + + virtual bool OnEntity( + const AString & a_EntityType, + double a_PosX, double a_PosY, double a_PosZ, + double a_SpeedX, double a_SpeedY, double a_SpeedZ, + float a_Yaw, float a_Pitch, + float a_FallDistance, + short a_FireTicksLeft, + short a_AirTicks, + char a_IsOnGround, + cParsedNBT & a_NBT, + int a_NBTTag + ) override; + + virtual bool OnTileEntity( + const AString & a_EntityType, + int a_PosX, int a_PosY, int a_PosZ, + cParsedNBT & a_NBT, + int a_NBTTag + ) override; + + virtual bool OnTileTick( + int a_BlockType, + int a_TicksLeft, + int a_PosX, int a_PosY, int a_PosZ + ) override; + + void OnSpawner(cParsedNBT & a_NBT, int a_TileEntityTag); } ; @@ -62,6 +107,7 @@ class cStatisticsFactory : public cCallbackFactory { public: + cStatisticsFactory(void); virtual ~cStatisticsFactory(); virtual cCallback * CreateNewCallback(void) @@ -71,17 +117,16 @@ public: protected: // The results, combined, are stored here: - int m_TotalChunks; - int m_BiomeCounts[256]; - int m_BlockCounts[256][256]; // First dimension is the biome, second dimension is BlockType - int m_BiomeNumChunks; // Num chunks that have been processed for biome stats - int m_BlockNumChunks; // Num chunks that have been processed for block stats + cStatistics::cStats m_CombinedStats; + + clock_t m_BeginTick; void JoinResults(void); void SaveBiomes(void); void SaveBlockTypes(void); void SaveBiomeBlockTypes(void); - + void SaveStatistics(void); + void SaveSpawners(void); } ; diff --git a/AnvilStats/Utils.cpp b/AnvilStats/Utils.cpp index e6fe296e0..be1f067c0 100644 --- a/AnvilStats/Utils.cpp +++ b/AnvilStats/Utils.cpp @@ -10,6 +10,47 @@ +struct +{ + eEntityType Type; + const char * String; +} g_EntityTypes[] = +{ + {entBat, "Bat"}, + {entBlaze, "Blaze"}, + {entCaveSpider, "CaveSpider"}, + {entChicken, "Chicken"}, + {entCow, "Cow"}, + {entCreeper, "Creeper"}, + {entEnderDragon, "EnderDragon"}, + {entEnderman, "Enderman"}, + {entGhast, "Ghast"}, + {entGiant, "Giant"}, + {entLavaSlime, "LavaSlime"}, + {entMushroomCow, "MushroomCow"}, + {entOzelot, "Ozelot"}, + {entPig, "Pig"}, + {entPigZombie, "PigZombie"}, + {entSheep, "Sheep"}, + {entSilverfish, "Slverfish"}, + {entSkeleton, "Skeleton"}, + {entSlime, "Slime"}, + {entSnowMan, "SnowMan"}, + {entSpider, "Spider"}, + {entSquid, "Squid"}, + {entVillager, "Villager"}, + {entVillagerGolem, "VillagerGolem"}, + {entWitch, "Witch"}, + {entWitherBoss, "WitherBoss"}, + {entWolf, "Wolf"}, + {entZombie, "Zombie"}, + {entUnknown, "Unknown"}, +} ; + + + + + const char * GetBiomeString(unsigned char a_Biome) { static const char * BiomeNames[] = // Biome names, as equivalent to their index @@ -203,6 +244,31 @@ const char * GetBlockTypeString(unsigned char a_BlockType) +eEntityType GetEntityType(const AString & a_EntityTypeString) +{ + for (int i = 0; i < ARRAYCOUNT(g_EntityTypes); i++) + { + if (a_EntityTypeString == g_EntityTypes[i].String) + { + return g_EntityTypes[i].Type; + } + } + return entUnknown; +} + + + + + +extern const char * GetEntityTypeString(eEntityType a_EntityType) +{ + return g_EntityTypes[a_EntityType].String; +} + + + + + int GetNumCores(void) { // Get number of cores by querying the system process affinity mask (Windows-specific) diff --git a/AnvilStats/Utils.h b/AnvilStats/Utils.h index c22a9ab58..095abc99e 100644 --- a/AnvilStats/Utils.h +++ b/AnvilStats/Utils.h @@ -7,8 +7,54 @@ +#pragma once + + + + + +enum eEntityType +{ + entBat, + entBlaze, + entCaveSpider, + entChicken, + entCow, + entCreeper, + entEnderDragon, + entEnderman, + entGhast, + entGiant, + entLavaSlime, + entMushroomCow, + entOzelot, + entPig, + entPigZombie, + entSheep, + entSilverfish, + entSkeleton, + entSlime, + entSnowMan, + entSpider, + entSquid, + entVillager, + entVillagerGolem, + entWitch, + entWitherBoss, + entWolf, + entZombie, + entUnknown, + entMax = entUnknown, +} ; + + + + + extern const char * GetBiomeString(unsigned char a_Biome); extern const char * GetBlockTypeString(unsigned char a_BlockType); +extern eEntityType GetEntityType(const AString & a_EntityTypeString); +extern const char * GetEntityTypeString(eEntityType a_EntityType); extern int GetNumCores(void);