diff options
-rw-r--r-- | AnvilStats/AnvilStats.cpp | 3 | ||||
-rw-r--r-- | AnvilStats/AnvilStats.vcproj | 8 | ||||
-rw-r--r-- | AnvilStats/Processor.cpp | 2 | ||||
-rw-r--r-- | AnvilStats/SpringStats.cpp | 279 | ||||
-rw-r--r-- | AnvilStats/SpringStats.h | 102 |
5 files changed, 393 insertions, 1 deletions
diff --git a/AnvilStats/AnvilStats.cpp b/AnvilStats/AnvilStats.cpp index ef4d33ca5..ae3f901dc 100644 --- a/AnvilStats/AnvilStats.cpp +++ b/AnvilStats/AnvilStats.cpp @@ -9,6 +9,7 @@ #include "BiomeMap.h"
#include "HeightMap.h"
#include "ChunkExtract.h"
+#include "SpringStats.h"
@@ -24,6 +25,7 @@ int main(int argc, char * argv[]) LOG(" 1 - biome map");
LOG(" 2 - height map");
LOG(" 3 - extract chunks");
+ LOG(" 4 - count lava- and water- springs");
LOG("\nNo method number present, aborting.");
return -1;
}
@@ -45,6 +47,7 @@ int main(int argc, char * argv[]) case 1: Factory = new cBiomeMapFactory; break;
case 2: Factory = new cHeightMapFactory; break;
case 3: Factory = new cChunkExtractFactory(WorldFolder); break;
+ case 4: Factory = new cSpringStatsFactory; break;
default:
{
LOG("Unknown method \"%s\", aborting.", argv[1]);
diff --git a/AnvilStats/AnvilStats.vcproj b/AnvilStats/AnvilStats.vcproj index 8e8af8dfa..7a7c78360 100644 --- a/AnvilStats/AnvilStats.vcproj +++ b/AnvilStats/AnvilStats.vcproj @@ -330,6 +330,14 @@ >
</File>
<File
+ RelativePath=".\SpringStats.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\SpringStats.h"
+ >
+ </File>
+ <File
RelativePath=".\Statistics.cpp"
>
</File>
diff --git a/AnvilStats/Processor.cpp b/AnvilStats/Processor.cpp index 8b506791c..07fb8be3f 100644 --- a/AnvilStats/Processor.cpp +++ b/AnvilStats/Processor.cpp @@ -507,7 +507,7 @@ void cProcessor::ProcessWorld(const AString & a_WorldFolder, cCallbackFactory & // (One more thread can be in the file-read IO block while all other threads crunch the numbers)
int NumThreads = GetNumCores() + 1;
- //*
+ /*
// Limit the number of threads in DEBUG mode to 1 for easier debugging
#ifdef _DEBUG
NumThreads = 1;
diff --git a/AnvilStats/SpringStats.cpp b/AnvilStats/SpringStats.cpp new file mode 100644 index 000000000..14ede6889 --- /dev/null +++ b/AnvilStats/SpringStats.cpp @@ -0,0 +1,279 @@ +
+// SpringStats.cpp
+
+// Implements the cSpringStats class representing a cCallback descendant that collects statistics on lava and water springs
+
+#include "Globals.h"
+#include "SpringStats.h"
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSpringStats::cStats
+
+cSpringStats::cStats::cStats(void) :
+ m_TotalChunks(0)
+{
+ memset(m_LavaSprings, 0, sizeof(m_LavaSprings));
+ memset(m_WaterSprings, 0, sizeof(m_WaterSprings));
+}
+
+
+
+
+
+void cSpringStats::cStats::Add(const cSpringStats::cStats & a_Other)
+{
+ m_TotalChunks += a_Other.m_TotalChunks;
+ for (int Biome = 0; Biome < 256; Biome++)
+ {
+ for (int Height = 0; Height < 256; Height++)
+ {
+ m_LavaSprings[Biome][Height] += a_Other.m_LavaSprings[Biome][Height];
+ m_WaterSprings[Biome][Height] += a_Other.m_WaterSprings[Biome][Height];
+ }
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSpringStats:
+
+cSpringStats::cSpringStats(void) :
+ m_AreBiomesValid(false)
+{
+}
+
+
+
+
+
+bool cSpringStats::OnNewChunk(int a_ChunkX, int a_ChunkZ)
+{
+ memset(m_BlockTypes, 0, sizeof(m_BlockTypes));
+ m_AreBiomesValid = false;
+ return false;
+}
+
+
+
+
+
+bool cSpringStats::OnBiomes(const unsigned char * a_BiomeData)
+{
+ memcpy(m_Biomes, a_BiomeData, sizeof(m_Biomes));
+ m_AreBiomesValid = true;
+ return false;
+}
+
+
+
+
+
+bool cSpringStats::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
+)
+{
+ memcpy(m_BlockTypes + ((int)a_Y) * 16 * 16 * 16, a_BlockTypes, 16 * 16 * 16);
+ memcpy(m_BlockMetas + ((int)a_Y) * 16 * 16 * 16 / 2, a_BlockMeta, 16 * 16 * 16 / 2);
+ return false;
+}
+
+
+
+
+
+bool cSpringStats::OnSectionsFinished(void)
+{
+ if (!m_AreBiomesValid)
+ {
+ return true;
+ }
+
+ // Calc the spring stats:
+ for (int y = 1; y < 255; y++)
+ {
+ int BaseY = y * 16 * 16;
+ for (int z = 1; z < 15; z++)
+ {
+ int Base = BaseY + z * 16;
+ for (int x = 1; x < 15; x++)
+ {
+ if (cChunkDef::GetNibble(m_BlockMetas, Base + x) != 0)
+ {
+ // Not a source block
+ continue;
+ }
+ switch (m_BlockTypes[Base + x])
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ {
+ TestSpring(x, y, z, m_Stats.m_WaterSprings);
+ break;
+ }
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ TestSpring(x, y, z, m_Stats.m_LavaSprings);
+ break;
+ }
+ } // switch (BlockType)
+ } // for x
+ } // for z
+ } // for y
+ m_Stats.m_TotalChunks += 1;
+ return true;
+}
+
+
+
+
+
+void cSpringStats::TestSpring(int a_RelX, int a_RelY, int a_RelZ, cSpringStats::cStats::SpringStats & a_Stats)
+{
+ static const struct
+ {
+ int x, y, z;
+ } Coords[] =
+ {
+ {-1, 0, 0},
+ { 1, 0, 0},
+ { 0, -1, 0},
+ { 0, 1, 0},
+ { 0, 0, -1},
+ { 0, 0, 1},
+ } ;
+ bool HasFluidNextToIt = false;
+ for (int i = 0; i < ARRAYCOUNT(Coords); i++)
+ {
+ switch (cChunkDef::GetBlock(m_BlockTypes, a_RelX + Coords[i].x, a_RelY + Coords[i].y, a_RelZ + Coords[i].z))
+ {
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER:
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ {
+ if (cChunkDef::GetNibble(m_BlockMetas, a_RelX + Coords[i].x, a_RelY + Coords[i].y, a_RelZ + Coords[i].z) == 0)
+ {
+ // There is another source block next to this, so this is not a spring
+ return;
+ }
+ HasFluidNextToIt = true;
+ }
+ } // switch (BlockType)
+ } // for i - Coords[]
+
+ if (!HasFluidNextToIt)
+ {
+ // Surrounded by solids on all sides, this is probably not a spring,
+ // but rather a bedrocked lake or something similar. Dont want.
+ return;
+ }
+
+ // No source blocks next to the specified block, so it is a spring. Add it to stats:
+ a_Stats[a_RelY][((unsigned char *)m_Biomes)[a_RelX + 16 * a_RelZ]] += 1;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSpringStatsFactory:
+
+cSpringStatsFactory::~cSpringStatsFactory()
+{
+ LOG("cSpringStats:");
+ LOG(" Joining results...");
+ JoinResults();
+ LOG(" Total %llu chunks went through", m_CombinedStats.m_TotalChunks);
+
+ // Save statistics:
+ LOG(" Saving statistics into files:");
+ LOG(" Springs.xls");
+ SaveTotals("Springs.xls");
+ LOG(" BiomeWaterSprings.xls");
+ SaveStatistics(m_CombinedStats.m_WaterSprings, "BiomeWaterSprings.xls");
+ LOG(" BiomeLavaSprings.xls");
+ SaveStatistics(m_CombinedStats.m_LavaSprings, "BiomeLavaSprings.xls");
+}
+
+
+
+
+
+void cSpringStatsFactory::JoinResults(void)
+{
+ for (cCallbacks::iterator itr = m_Callbacks.begin(), end = m_Callbacks.end(); itr != end; ++itr)
+ {
+ m_CombinedStats.Add(((cSpringStats *)(*itr))->GetStats());
+ } // for itr - m_Callbacks[]
+}
+
+
+
+
+
+void cSpringStatsFactory::SaveTotals(const AString & a_FileName)
+{
+ cFile f(a_FileName, cFile::fmWrite);
+ if (!f.IsOpen())
+ {
+ LOG("Cannot open file \"%s\" for writing!", a_FileName.c_str());
+ return;
+ }
+ f.Printf("Height\tWater\tLava\n");
+ for (int Height = 0; Height < 256; Height++)
+ {
+ UInt64 TotalW = 0;
+ UInt64 TotalL = 0;
+ for (int Biome = 0; Biome < 256; Biome++)
+ {
+ TotalW += m_CombinedStats.m_WaterSprings[Height][Biome];
+ TotalL += m_CombinedStats.m_LavaSprings[Height][Biome];
+ }
+ f.Printf("%d\t%llu\t%llu\n", Height, TotalW, TotalL);
+ }
+ f.Printf("\n# Chunks\t%llu", m_CombinedStats.m_TotalChunks);
+}
+
+
+
+
+
+void cSpringStatsFactory::SaveStatistics(const cSpringStats::cStats::SpringStats & a_Stats, const AString & a_FileName)
+{
+ cFile f(a_FileName, cFile::fmWrite);
+ if (!f.IsOpen())
+ {
+ LOG("Cannot open file \"%s\" for writing!", a_FileName.c_str());
+ return;
+ }
+ for (int Height = 0; Height < 256; Height++)
+ {
+ AString Line;
+ Line.reserve(2000);
+ Printf(Line, "%d\t", Height);
+ for (int Biome = 0; Biome < 256; Biome++)
+ {
+ AppendPrintf(Line, "%llu\t", a_Stats[Height][Biome]);
+ }
+ Line.append("\n");
+ f.Write(Line.c_str(), Line.size());
+ }
+}
+
+
+
+
diff --git a/AnvilStats/SpringStats.h b/AnvilStats/SpringStats.h new file mode 100644 index 000000000..292c5b82d --- /dev/null +++ b/AnvilStats/SpringStats.h @@ -0,0 +1,102 @@ +
+// SpringStats.h
+
+// Declares the cSpringStats class representing a cCallback descendant that collects statistics on lava and water springs
+
+
+
+
+
+#pragma once
+
+#include "Callback.h"
+
+
+
+
+
+class cSpringStats :
+ public cCallback
+{
+public:
+ class cStats
+ {
+ public:
+ /// Per-height, per-biome frequencies of springs
+ typedef UInt64 SpringStats[256][256];
+
+ SpringStats m_LavaSprings;
+ SpringStats m_WaterSprings;
+
+ UInt64 m_TotalChunks; ///< Total number of chunks that are fully processed through this callback(OnSectionsFinished())
+
+ cStats(void);
+ void Add(const cStats & a_Other);
+ } ;
+
+ cSpringStats(void);
+
+ const cStats & GetStats(void) const { return m_Stats; }
+
+protected:
+
+ BLOCKTYPE m_BlockTypes[16 * 16 * 256];
+ NIBBLETYPE m_BlockMetas[16 * 16 * 256 / 2];
+ char m_Biomes[16 * 16];
+ bool m_AreBiomesValid;
+
+ cStats m_Stats;
+
+ // cCallback overrides:
+ virtual bool OnNewChunk(int a_ChunkX, int a_ChunkZ) override;
+ virtual bool OnHeader(int a_FileOffset, unsigned char a_NumSectors, int a_Timestamp) override { return false; }
+ virtual bool OnCompressedDataSizePos(int a_CompressedDataSize, int a_DataOffset, char a_CompressionMethod) override { return false; }
+ virtual bool OnDecompressedData(const char * a_DecompressedNBT, int a_DataSize) override { return false; }
+ virtual bool OnRealCoords(int a_ChunkX, int a_ChunkZ) override { return false; }
+ virtual bool OnLastUpdate(Int64 a_LastUpdate) override { return false; }
+ virtual bool OnTerrainPopulated(bool a_Populated) override { return !a_Populated; } // If not populated, we don't want it!
+ virtual bool OnBiomes(const unsigned char * a_BiomeData) override;
+ virtual bool OnHeightMap(const int * a_HeightMap) override { return false; }
+ virtual bool 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
+ ) override;
+ virtual bool OnSectionsFinished(void) override;
+
+ /// Tests the specified block, if it appears to be a spring, it is added to a_Stats
+ void TestSpring(int a_RelX, int a_RelY, int a_RelZ, cStats::SpringStats & a_Stats);
+} ;
+
+
+
+
+
+class cSpringStatsFactory :
+ public cCallbackFactory
+{
+public:
+ virtual ~cSpringStatsFactory();
+
+ virtual cCallback * CreateNewCallback(void) override
+ {
+ return new cSpringStats;
+ }
+
+ cSpringStats::cStats m_CombinedStats;
+
+ void JoinResults(void);
+
+ /// Saves total per-height data (summed through biomes) for both spring types to the file
+ void SaveTotals(const AString & a_FileName);
+
+ /// Saves complete per-height, per-biome statistics for the springs to the file
+ void SaveStatistics(const cSpringStats::cStats::SpringStats & a_Stats, const AString & a_FileName);
+} ;
+
+
+
+
|