AnvilStats: moved into the Tools folder

git-svn-id: http://mc-server.googlecode.com/svn/trunk@1658 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
madmaxoft@gmail.com
2013-07-06 19:56:03 +00:00
parent b2214a56c4
commit 8f18510dec
22 changed files with 30 additions and 30 deletions
+69
View File
@@ -0,0 +1,69 @@
// AnvilStats.cpp
// Implements the main app entrypoint
#include "Globals.h"
#include "Processor.h"
#include "Statistics.h"
#include "BiomeMap.h"
#include "HeightMap.h"
#include "ChunkExtract.h"
#include "SpringStats.h"
int main(int argc, char * argv[])
{
if (argc < 2)
{
LOG("Usage: %s <method number> [<world folder>]", argv[0]);
LOG("Available methods:");
LOG(" 0 - statistics");
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;
}
AString WorldFolder;
if (argc > 2)
{
WorldFolder = argv[2];
}
else
{
WorldFolder = "." + cFile::PathSeparator;
}
cCallbackFactory * Factory = NULL;
switch (atol(argv[1]))
{
case 0: Factory = new cStatisticsFactory; break;
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]);
return -2;
}
}
cProcessor Processor;
Processor.ProcessWorld(WorldFolder, *Factory);
LOG("Processing finished");
delete Factory;
LOG("Done");
}
+34
View File
@@ -0,0 +1,34 @@
Microsoft Visual Studio Solution File, Format Version 10.00
# Visual C++ Express 2008
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AnvilStats", "AnvilStats.vcproj", "{CF996A5E-0A86-4004-9710-682B06B5AEBA}"
ProjectSection(ProjectDependencies) = postProject
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA} = {EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\..\VC2008\zlib.vcproj", "{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release profiled|Win32 = Release profiled|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Debug|Win32.ActiveCfg = Debug|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Debug|Win32.Build.0 = Debug|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release profiled|Win32.ActiveCfg = Release profiled|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release profiled|Win32.Build.0 = Release profiled|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release|Win32.ActiveCfg = Release|Win32
{CF996A5E-0A86-4004-9710-682B06B5AEBA}.Release|Win32.Build.0 = Release|Win32
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Debug|Win32.ActiveCfg = Debug|Win32
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Debug|Win32.Build.0 = Debug|Win32
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release profiled|Win32.ActiveCfg = Release profiled|Win32
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release profiled|Win32.Build.0 = Release profiled|Win32
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release|Win32.ActiveCfg = Release|Win32
{EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
+27
View File
@@ -0,0 +1,27 @@
// AnvilStats.txt
// A Readme for the project
/*
AnvilStats
==========
This is a project for measuring various metrics throughout an Anvil world, presumably created by a vanilla MC.
It works by parsing the MCA files in the path specified as its param (or current directory, if no params) and
feeding each decompressed chunk into the statistics-gathering callback function.
Possible usage:
- count the per-chunk density of specific blocks
- count the per-chunk density of dungeons, by measuring the number of zombie/skeleton/regularspider spawners
- count the per-chunk-per-biome density of trees, by measuring the number of dirt-log vertical transitions, correlating to biome data
This project is Windows-only, although it shouldn't be too difficult to make it portable.
Because this project uses NBT extensively, it runs much faster in Release mode.
*/
+452
View File
@@ -0,0 +1,452 @@
<?xml version="1.0" encoding="windows-1250"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9,00"
Name="AnvilStats"
ProjectGUID="{CF996A5E-0A86-4004-9710-682B06B5AEBA}"
RootNamespace="AnvilStats"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="&quot;..\..\zlib-1.2.7&quot;"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="Globals.h"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
StackReserveSize="16777216"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="&quot;..\..\zlib-1.2.7&quot;"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="Globals.h"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
StackReserveSize="16777216"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release profiled|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="2"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="&quot;..\..\zlib-1.2.7&quot;"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="2"
PrecompiledHeaderThrough="Globals.h"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="ws2_32.lib"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
StackReserveSize="16777216"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
Profile="true"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
>
<File
RelativePath=".\AnvilStats.cpp"
>
</File>
<File
RelativePath=".\BiomeMap.cpp"
>
</File>
<File
RelativePath=".\BiomeMap.h"
>
</File>
<File
RelativePath=".\Callback.h"
>
</File>
<File
RelativePath=".\ChunkExtract.cpp"
>
</File>
<File
RelativePath=".\ChunkExtract.h"
>
</File>
<File
RelativePath=".\Globals.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
<FileConfiguration
Name="Release profiled|Win32"
>
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"
/>
</FileConfiguration>
</File>
<File
RelativePath=".\Globals.h"
>
</File>
<File
RelativePath=".\HeightMap.cpp"
>
</File>
<File
RelativePath=".\HeightMap.h"
>
</File>
<File
RelativePath=".\Processor.cpp"
>
</File>
<File
RelativePath=".\Processor.h"
>
</File>
<File
RelativePath=".\SpringStats.cpp"
>
</File>
<File
RelativePath=".\SpringStats.h"
>
</File>
<File
RelativePath=".\Statistics.cpp"
>
</File>
<File
RelativePath=".\Statistics.h"
>
</File>
<File
RelativePath=".\Utils.cpp"
>
</File>
<File
RelativePath=".\Utils.h"
>
</File>
</Filter>
<Filter
Name="shared"
>
<File
RelativePath="..\..\source\OSSupport\CriticalSection.cpp"
>
</File>
<File
RelativePath="..\..\source\OSSupport\CriticalSection.h"
>
</File>
<File
RelativePath="..\..\source\Endianness.h"
>
</File>
<File
RelativePath="..\..\source\OSSupport\Event.cpp"
>
</File>
<File
RelativePath="..\..\source\OSSupport\Event.h"
>
</File>
<File
RelativePath="..\..\source\WorldStorage\FastNBT.cpp"
>
<FileConfiguration
Name="Debug|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="NBT_RESERVE_SIZE=10000"
/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="NBT_RESERVE_SIZE=10000"
/>
</FileConfiguration>
<FileConfiguration
Name="Release profiled|Win32"
>
<Tool
Name="VCCLCompilerTool"
PreprocessorDefinitions="NBT_RESERVE_SIZE=10000"
/>
</FileConfiguration>
</File>
<File
RelativePath="..\..\source\WorldStorage\FastNBT.h"
>
</File>
<File
RelativePath="..\..\source\OSSupport\File.cpp"
>
</File>
<File
RelativePath="..\..\source\OSSupport\File.h"
>
</File>
<File
RelativePath="..\..\source\OSSupport\GZipFile.cpp"
>
</File>
<File
RelativePath="..\..\source\OSSupport\GZipFile.h"
>
</File>
<File
RelativePath="..\..\source\OSSupport\IsThread.cpp"
>
</File>
<File
RelativePath="..\..\source\OSSupport\IsThread.h"
>
</File>
<File
RelativePath="..\..\source\StringUtils.cpp"
>
</File>
<File
RelativePath="..\..\source\StringUtils.h"
>
</File>
</Filter>
<File
RelativePath=".\AnvilStats.txt"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>
+172
View File
@@ -0,0 +1,172 @@
// BiomeMap.cpp
// Implements the cBiomeMap class representing a cCallback descendant that draws a map of biomes for the world
#include "Globals.h"
#include "BiomeMap.h"
static const int g_BiomePalette[] =
{
// ARGB:
0xff0000ff, /* Ocean */
0xff00cf3f, /* Plains */
0xffffff00, /* Desert */
0xff7f7f7f, /* Extreme Hills */
0xff00cf00, /* Forest */
0xff007f3f, /* Taiga */
0xff3f7f00, /* Swampland */
0xff003fff, /* River */
0xff7f0000, /* Hell */
0xff007fff, /* Sky */
0xff3f3fff, /* Frozen Ocean */
0xff3f3fff, /* Frozen River */
0xff7fffcf, /* Ice Plains */
0xff3fcf7f, /* Ice Mountains */
0xffcf00cf, /* Mushroom Island */
0xff7f00ff, /* Mushroom Island Shore */
0xffffff3f, /* Beach */
0xffcfcf00, /* Desert Hills */
0xff00cf3f, /* Forest Hills */
0xff006f1f, /* Taiga Hills */
0xff7f8f7f, /* Extreme Hills Edge */
0xff004f00, /* Jungle */
0xff003f00, /* Jungle Hills */
} ;
static const unsigned char g_BMPHeader[] =
{
0x42, 0x4D, 0x36, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
} ;
cBiomeMap::cBiomeMap(void) :
m_CurrentRegionX(0),
m_CurrentRegionZ(0),
m_IsCurrentRegionValid(false)
{
}
void cBiomeMap::Finish(void)
{
if (m_IsCurrentRegionValid)
{
StartNewRegion(0, 0);
}
}
bool cBiomeMap::OnNewChunk(int a_ChunkX, int a_ChunkZ)
{
int RegionX = (a_ChunkX < 0) ? (a_ChunkX - 31) / 32 : a_ChunkX / 32;
int RegionZ = (a_ChunkZ < 0) ? (a_ChunkZ - 31) / 32 : a_ChunkZ / 32;
if ((RegionX != m_CurrentRegionX) || (RegionZ != m_CurrentRegionZ))
{
if (m_IsCurrentRegionValid)
{
StartNewRegion(RegionX, RegionZ);
}
m_CurrentRegionX = RegionX;
m_CurrentRegionZ = RegionZ;
}
m_IsCurrentRegionValid = true;
m_CurrentChunkX = a_ChunkX;
m_CurrentChunkZ = a_ChunkZ;
m_CurrentChunkOffX = m_CurrentChunkX - m_CurrentRegionX * 32;
m_CurrentChunkOffZ = m_CurrentChunkZ - m_CurrentRegionZ * 32;
return false;
}
bool cBiomeMap::OnBiomes(const unsigned char * a_BiomeData)
{
ASSERT(m_CurrentChunkOffX >= 0);
ASSERT(m_CurrentChunkOffX < 32);
ASSERT(m_CurrentChunkOffZ >= 0);
ASSERT(m_CurrentChunkOffZ < 32);
char * BaseBiomes = m_Biomes + m_CurrentChunkOffZ * 16 * 512 + m_CurrentChunkOffX * 16;
for (int z = 0; z < 16; z++)
{
char * Row = BaseBiomes + z * 512;
memcpy(Row, a_BiomeData + z * 16, 16);
} // for z
return true;
}
void cBiomeMap::StartNewRegion(int a_RegionX, int a_RegionZ)
{
AString FileName;
Printf(FileName, "Biomes.%d.%d.bmp", m_CurrentRegionX, m_CurrentRegionZ);
cFile f;
if (!f.Open(FileName, cFile::fmWrite))
{
LOG("Cannot open file \"%s\" for writing the biome map. Data for this region lost.", FileName.c_str());
}
else
{
f.Write(g_BMPHeader, sizeof(g_BMPHeader));
for (int z = 0; z < 512; z++)
{
int RowData[512];
unsigned char * BiomeRow = (unsigned char *)m_Biomes + z * 512;
for (int x = 0; x < 512; x++)
{
RowData[x] = g_BiomePalette[BiomeRow[x]];
}
f.Write(RowData, sizeof(RowData));
} // for z
}
memset(m_Biomes, 0, sizeof(m_Biomes));
m_CurrentRegionX = a_RegionX;
m_CurrentRegionZ = a_RegionZ;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBiomeMapFactory:
cBiomeMapFactory::~cBiomeMapFactory()
{
// Force all threads to save their last regions:
for (cCallbacks::iterator itr = m_Callbacks.begin(), end = m_Callbacks.end(); itr != end; ++itr)
{
((cBiomeMap *)(*itr))->Finish();
}
// TODO: Join all the files into one giant image file
}
+69
View File
@@ -0,0 +1,69 @@
// BiomeMap.h
// Interfaces to the cBiomeMap class representing a cCallback descendant that draws a map of biomes for the world
#pragma once
#include "Callback.h"
class cBiomeMap :
public cCallback
{
public:
cBiomeMap(void);
/// Saves the last region that it was processing
void Finish(void);
protected:
int m_CurrentChunkX; // Absolute chunk coords
int m_CurrentChunkZ;
int m_CurrentChunkOffX; // Chunk offset from the start of the region
int m_CurrentChunkOffZ;
int m_CurrentRegionX;
int m_CurrentRegionZ;
bool m_IsCurrentRegionValid;
char m_Biomes[16 * 32 * 16 * 32]; // Biome map of the entire current region [x + 16 * 32 * z]
// 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;
void StartNewRegion(int a_RegionX, int a_RegionZ);
} ;
class cBiomeMapFactory :
public cCallbackFactory
{
public:
virtual ~cBiomeMapFactory();
virtual cCallback * CreateNewCallback(void) override
{
return new cBiomeMap;
}
} ;
+165
View File
@@ -0,0 +1,165 @@
// Callback.h
// Interfaces to the cCallback base class used as the base class for all statistical callbacks
#pragma once
// 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
the new chunk data.
So if a statistics collector doesn't need data decompression at all, it can stop the processor from doing so early-enough
and still get meaningful data.
A callback is guaranteed to run in a single thread and always the same thread.
A callback is guaranteed to run on all chunks in a region and one region is guaranteed to be handled by only callback.
*/
class cCallback abstract
{
public:
virtual ~cCallback() {} // Force a virtual destructor in each descendant
/// Called to inform the stats module of the chunk coords for newly processing chunk
virtual bool OnNewChunk(int a_ChunkX, int a_ChunkZ) = 0;
/// Called to inform about the chunk's data offset in the file (chunk mini-header), the number of sectors it uses and the timestamp field value
virtual bool OnHeader(int a_FileOffset, unsigned char a_NumSectors, int a_Timestamp) { return true; }
/// Called to inform of the compressed chunk data size and position in the file (offset from file start to the actual data)
virtual bool OnCompressedDataSizePos(int a_CompressedDataSize, int a_DataOffset, char a_CompressionMethod) { return true; }
/// Just in case you wanted to process the NBT yourself ;)
virtual bool OnDecompressedData(const char * a_DecompressedNBT, int a_DataSize) { return true; }
/// The chunk's NBT should specify chunk coords, these are sent here:
virtual bool OnRealCoords(int a_ChunkX, int a_ChunkZ) { return true; }
/// The chunk contains a LastUpdate value specifying the last tick in which it was saved.
virtual bool OnLastUpdate(Int64 a_LastUpdate) { return true; }
virtual bool OnTerrainPopulated(bool a_Populated) { return true; }
virtual bool OnBiomes(const unsigned char * a_BiomeData) { return true; }
/** Called when a heightmap for the chunk is read from the file.
Note that the heightmap is given in big-endian ints, so if you want it, you need to ntohl() it first!
*/
virtual bool OnHeightMap(const int * a_HeightMapBE) { return true; }
/** If there is data for the section, this callback is called; otherwise OnEmptySection() is called instead.
All OnSection() callbacks are called first, and only then all the remaining sections are reported in OnEmptySection().
*/
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
) { return true; }
/** If there is no data for a section, this callback is called; otherwise OnSection() is called instead.
OnEmptySection() callbacks are called after all OnSection() callbacks.
*/
virtual bool OnEmptySection(unsigned char a_Y) { return false; }
/** Called after all sections have been processed via either OnSection() or OnEmptySection().
*/
virtual bool OnSectionsFinished(void) { return true; }
/** 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<cCallback *> cCallbacks;
/** The base class for a factory that creates callback objects for separate threads.
The processor creates a callback for each thread on which it runs using this factory.
The factory is guaranteed to be called from a single thread.
The factory keeps track of all the callbacks that it has created and deletes them when destructed
*/
class cCallbackFactory
{
public:
virtual ~cCallbackFactory()
{
for (cCallbacks::iterator itr = m_Callbacks.begin(), end = m_Callbacks.end(); itr != end; ++itr)
{
delete *itr;
}
}
/// Descendants override this method to return the correct callback type
virtual cCallback * CreateNewCallback(void) = 0;
/// cProcessor uses this method to request a new callback
cCallback * GetNewCallback(void)
{
cCallback * Callback = CreateNewCallback();
if (Callback != NULL)
{
m_Callbacks.push_back(Callback);
}
return Callback;
}
protected:
cCallbacks m_Callbacks;
} ;
+104
View File
@@ -0,0 +1,104 @@
// ChunkExtract.cpp
// Implements the cChunkExtract class representing a cCallback descendant that extracts raw chunk data into separate .chunk files
#include "Globals.h"
#include "ChunkExtract.h"
#include "../../source/OSSupport/GZipFile.h"
cChunkExtract::cChunkExtract(const AString & iWorldFolder) :
mWorldFolder(iWorldFolder)
{
}
bool cChunkExtract::OnNewChunk(int a_ChunkX, int a_ChunkZ)
{
int AnvilX = (a_ChunkX - ((a_ChunkX > 0) ? 0 : 31)) / 32;
int AnvilZ = (a_ChunkZ - ((a_ChunkZ > 0) ? 0 : 31)) / 32;
if ((AnvilX != mCurAnvilX) || (AnvilZ != mCurAnvilZ))
{
OpenAnvilFile(AnvilX, AnvilZ);
}
mCurChunkX = a_ChunkX;
mCurChunkZ = a_ChunkZ;
return false;
}
bool cChunkExtract::OnCompressedDataSizePos(int a_CompressedDataSize, int a_DataOffset, char a_CompressionMethod)
{
if (!mAnvilFile.IsOpen())
{
return true;
}
cFile ChunkFile;
AString ChunkPath = Printf("%d.%d.zchunk", mCurChunkX, mCurChunkZ);
if (!ChunkFile.Open(ChunkPath, cFile::fmWrite))
{
LOG("Cannot open zchunk file \"%s\" for writing. Chunk [%d, %d] skipped.", ChunkPath.c_str(), mCurChunkX, mCurChunkZ);
return false;
}
// Copy data from mAnvilFile to ChunkFile:
mAnvilFile.Seek(a_DataOffset);
for (int BytesToCopy = a_CompressedDataSize; BytesToCopy > 0; )
{
char Buffer[64000];
int NumBytes = std::min(BytesToCopy, (int)sizeof(Buffer));
int BytesRead = mAnvilFile.Read(Buffer, NumBytes);
if (BytesRead != NumBytes)
{
LOG("Cannot copy chunk data, chunk [%d, %d] is probably corrupted. Skipping chunk.", mCurChunkX, mCurChunkZ);
return false;
}
ChunkFile.Write(Buffer, BytesRead);
BytesToCopy -= BytesRead;
} // for BytesToCopy
return false;
}
bool cChunkExtract::OnDecompressedData(const char * a_DecompressedNBT, int a_DataSize)
{
ASSERT(mAnvilFile.IsOpen()); // If it weren't, the OnCompressedDataSizePos would've prevented this from running
AString FileName = Printf("%d.%d.gzchunk", mCurChunkX, mCurChunkZ);
cGZipFile GZipChunk;
if (!GZipChunk.Open(FileName, cGZipFile::fmWrite))
{
LOG("Cannot open gzchunk file \"%s\" for writing. Chunk [%d, %d] skipped.", FileName.c_str(), mCurChunkX, mCurChunkZ);
return true;
}
GZipChunk.Write(a_DecompressedNBT, a_DataSize);
return true;
}
void cChunkExtract::OpenAnvilFile(int a_AnvilX, int a_AnvilZ)
{
mAnvilFile.Close();
AString FileName = Printf("%s/r.%d.%d.mca", mWorldFolder.c_str(), a_AnvilX, a_AnvilZ);
if (!mAnvilFile.Open(FileName, cFile::fmRead))
{
LOG("Cannot open Anvil file \"%s\" for reading", FileName.c_str());
}
mCurAnvilX = a_AnvilX;
mCurAnvilZ = a_AnvilZ;
}
+66
View File
@@ -0,0 +1,66 @@
// ChunkExtract.h
// Declares the cChunkExtract class representing a cCallback descendant that extracts raw chunk data into separate .chunk files
#pragma once
#include "Callback.h"
class cChunkExtract :
public cCallback
{
public:
cChunkExtract(const AString & iWorldFolder);
protected:
AString mWorldFolder;
cFile mAnvilFile;
int mCurAnvilX; // X-coord of mAnvilFile, in Anvil-coords (1 Anvil-coord = 32 chunks)
int mCurAnvilZ; // Z-coord of mAnvilFile, -"-
int mCurChunkX; // X-coord of the chunk being processed
int mCurChunkZ; // Z-coord of the chunk being processed
/// Opens new anvil file into mAnvilFile, sets mCurAnvilX and mCurAnvilZ
void OpenAnvilFile(int a_AnvilX, int a_AnvilZ);
// 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;
virtual bool OnDecompressedData(const char * a_DecompressedNBT, int a_DataSize) override;
} ;
class cChunkExtractFactory :
public cCallbackFactory
{
public:
cChunkExtractFactory(const AString & iWorldFolder) :
mWorldFolder(iWorldFolder)
{
}
virtual cCallback * CreateNewCallback(void) override
{
return new cChunkExtract(mWorldFolder);
}
protected:
AString mWorldFolder;
} ;
+10
View File
@@ -0,0 +1,10 @@
// Globals.cpp
// This file is used for precompiled header generation in MSVC environments
#include "Globals.h"
+228
View File
@@ -0,0 +1,228 @@
// Globals.h
// This file gets included from every module in the project, so that global symbols may be introduced easily
// Also used for precompiled header generation in MSVC environments
// Compiler-dependent stuff:
#if defined(_MSC_VER)
// MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether
#pragma warning(disable:4481)
// Disable some warnings that we don't care about:
#pragma warning(disable:4100)
#define _CRT_SECURE_NO_WARNINGS
#define OBSOLETE __declspec(deprecated)
// No alignment needed in MSVC
#define ALIGN_8
#define ALIGN_16
#elif defined(__GNUC__)
// TODO: Can GCC explicitly mark classes as abstract (no instances can be created)?
#define abstract
// TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class)
#define override
#define OBSOLETE __attribute__((deprecated))
#define ALIGN_8 __attribute__((aligned(8)))
#define ALIGN_16 __attribute__((aligned(16)))
// Some portability macros :)
#define stricmp strcasecmp
#else
#error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler"
/*
// Copy and uncomment this into another #elif section based on your compiler identification
// Explicitly mark classes as abstract (no instances can be created)
#define abstract
// Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class)
#define override
// Mark functions as obsolete, so that their usage results in a compile-time warning
#define OBSOLETE
// Mark types / variables for alignment. Do the platforms need it?
#define ALIGN_8
#define ALIGN_16
*/
#endif
// Integral types with predefined sizes:
typedef long long Int64;
typedef int Int32;
typedef short Int16;
typedef unsigned long long UInt64;
typedef unsigned int UInt32;
typedef unsigned short UInt16;
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for any class that shouldn't allow copying itself
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
TypeName(const TypeName &); \
void operator=(const TypeName &)
// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc
#define UNUSED(X) (void)(X)
// OS-dependent stuff:
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <winsock2.h>
// Windows SDK defines min and max macros, messing up with our std::min and std::max usage
#undef min
#undef max
// Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant
#ifdef GetFreeSpace
#undef GetFreeSpace
#endif // GetFreeSpace
#else
#include <sys/types.h>
#include <sys/stat.h> // for mkdir
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <dirent.h>
#include <errno.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
#endif
#define FILE_IO_PREFIX ""
// CRT stuff:
#include <assert.h>
#include <stdio.h>
#include <math.h>
#include <stdarg.h>
// STL stuff:
#include <vector>
#include <list>
#include <deque>
#include <string>
#include <map>
#include <algorithm>
#include <memory>
#include <ctime>
// Common headers (part 1, without macros):
#include "../../source/StringUtils.h"
#include "../../source/OSSupport/CriticalSection.h"
#include "../../source/OSSupport/Semaphore.h"
#include "../../source/OSSupport/Event.h"
#include "../../source/OSSupport/IsThread.h"
#include "../../source/OSSupport/File.h"
// Common definitions:
#define LOG(x,...) printf(x "\n", __VA_ARGS__)
#define LOGERROR LOG
#define LOGWARNING LOG
#define LOGINFO LOG
#define LOGWARN LOG
/// Evaluates to the number of elements in an array (compile-time!)
#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X)))
/// Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)" )
#define KiB * 1024
/// Allows arithmetic expressions like "32 MiB" (but consider using parenthesis around it, "(32 MiB)" )
#define MiB * 1024 * 1024
/// Faster than (int)floorf((float)x / (float)div)
#define FAST_FLOOR_DIV( x, div ) ( (x) < 0 ? (((int)x / div) - 1) : ((int)x / div) )
// Own version of assert() that writes failed assertions to the log for review
#ifdef _DEBUG
#define ASSERT( x ) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), assert(0), 0 ) )
#else
#define ASSERT(x) ((void)0)
#endif
// Pretty much the same as ASSERT() but stays in Release builds
#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) )
/// A generic interface used mainly in ForEach() functions
template <typename Type> class cItemCallback
{
public:
/// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating
virtual bool Item(Type * a_Type) = 0;
} ;
// Common headers (part 2, with macros):
#include "../../source/ChunkDef.h"
#include "../../source/BlockID.h"
+265
View File
@@ -0,0 +1,265 @@
// HeightMap.cpp
// Implements the cHeightMap class representing a cCallback descendant that draws a B&W map of heights for the world
#include "Globals.h"
#include "HeightMap.h"
static const unsigned char g_BMPHeader[] =
{
0x42, 0x4D, 0x36, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
} ;
cHeightMap::cHeightMap(void) :
m_CurrentRegionX(0),
m_CurrentRegionZ(0),
m_IsCurrentRegionValid(false)
{
}
void cHeightMap::Finish(void)
{
if (m_IsCurrentRegionValid)
{
StartNewRegion(0, 0);
}
}
bool cHeightMap::OnNewChunk(int a_ChunkX, int a_ChunkZ)
{
int RegionX = (a_ChunkX < 0) ? (a_ChunkX - 31) / 32 : a_ChunkX / 32;
int RegionZ = (a_ChunkZ < 0) ? (a_ChunkZ - 31) / 32 : a_ChunkZ / 32;
if ((RegionX != m_CurrentRegionX) || (RegionZ != m_CurrentRegionZ))
{
if (m_IsCurrentRegionValid)
{
StartNewRegion(RegionX, RegionZ);
}
m_CurrentRegionX = RegionX;
m_CurrentRegionZ = RegionZ;
}
m_IsCurrentRegionValid = true;
m_CurrentChunkX = a_ChunkX;
m_CurrentChunkZ = a_ChunkZ;
m_CurrentChunkOffX = m_CurrentChunkX - m_CurrentRegionX * 32;
m_CurrentChunkOffZ = m_CurrentChunkZ - m_CurrentRegionZ * 32;
memset(m_BlockTypes, 0, sizeof(m_BlockTypes));
return false;
}
bool cHeightMap::OnHeightMap(const int * a_HeightMapBE)
{
ASSERT(m_CurrentChunkOffX >= 0);
ASSERT(m_CurrentChunkOffX < 32);
ASSERT(m_CurrentChunkOffZ >= 0);
ASSERT(m_CurrentChunkOffZ < 32);
int * BaseHeight = m_Height + m_CurrentChunkOffZ * 16 * 512 + m_CurrentChunkOffX * 16;
for (int z = 0; z < 16; z++)
{
int * Row = BaseHeight + z * 512;
for (int x = 0; x < 16; x++)
{
Row[x] = ntohl(a_HeightMapBE[z * 16 + x]);
}
} // for z
return false; // Still want blockdata to remove trees from the heightmap
}
bool cHeightMap::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
)
{
// Copy the section data into the appropriate place in the internal buffer
memcpy(m_BlockTypes + a_Y * 16 * 16 * 16, a_BlockTypes, 16 * 16 * 16);
return false;
}
bool cHeightMap::OnSectionsFinished(void)
{
// Remove trees from the heightmap:
for (int z = 0; z < 16; z++)
{
for (int x = 0; x < 16; x++)
{
for (int y = m_Height[512 * (16 * m_CurrentChunkOffZ + z) + 16 * m_CurrentChunkOffX + x]; y >= 0; y--)
{
if (IsGround(m_BlockTypes[256 * y + 16 * z + x]))
{
m_Height[512 * (16 * m_CurrentChunkOffZ + z) + 16 * m_CurrentChunkOffX + x] = y;
break; // for y
}
} // for y
} // for x
} // for z
return true;
}
void cHeightMap::StartNewRegion(int a_RegionX, int a_RegionZ)
{
AString FileName;
Printf(FileName, "Height.%d.%d.bmp", m_CurrentRegionX, m_CurrentRegionZ);
cFile f;
if (!f.Open(FileName, cFile::fmWrite))
{
LOG("Cannot open file \"%s\" for writing the height map. Data for this region lost.", FileName.c_str());
}
else
{
f.Write(g_BMPHeader, sizeof(g_BMPHeader));
for (int z = 0; z < 512; z++)
{
int RowData[512];
int * HeightRow = m_Height + z * 512;
for (int x = 0; x < 512; x++)
{
RowData[x] = std::max(std::min(HeightRow[x], 255), 0) * 0x010101;
}
f.Write(RowData, sizeof(RowData));
} // for z
}
memset(m_Height, 0, sizeof(m_Height));
m_CurrentRegionX = a_RegionX;
m_CurrentRegionZ = a_RegionZ;
}
bool cHeightMap::IsGround(BLOCKTYPE a_BlockType)
{
// Name all blocks that are NOT ground, return false for them:
switch (a_BlockType)
{
case E_BLOCK_AIR:
case E_BLOCK_BED:
case E_BLOCK_BREWING_STAND:
case E_BLOCK_BROWN_MUSHROOM:
case E_BLOCK_CACTUS:
case E_BLOCK_CAKE:
case E_BLOCK_CARROTS:
case E_BLOCK_CAULDRON:
case E_BLOCK_CHEST:
case E_BLOCK_COBBLESTONE_WALL:
case E_BLOCK_COBWEB:
case E_BLOCK_COCOA_POD:
case E_BLOCK_CROPS:
case E_BLOCK_DEAD_BUSH:
case E_BLOCK_DETECTOR_RAIL:
case E_BLOCK_DIRT:
case E_BLOCK_DRAGON_EGG:
case E_BLOCK_END_PORTAL:
case E_BLOCK_ENDER_CHEST:
case E_BLOCK_FENCE:
case E_BLOCK_FENCE_GATE:
case E_BLOCK_FIRE:
case E_BLOCK_FLOWER_POT:
case E_BLOCK_HEAD:
case E_BLOCK_IRON_BARS:
case E_BLOCK_LADDER:
case E_BLOCK_LAVA:
case E_BLOCK_LEAVES:
case E_BLOCK_LEVER:
case E_BLOCK_LILY_PAD:
case E_BLOCK_LOG: // NOTE: This block is actually solid, but we don't want it because it's the thing that trees are made of, and we're getting rid of trees
case E_BLOCK_MELON:
case E_BLOCK_MELON_STEM:
case E_BLOCK_NETHER_BRICK_FENCE:
case E_BLOCK_NETHER_PORTAL:
case E_BLOCK_POWERED_RAIL:
case E_BLOCK_PUMPKIN:
case E_BLOCK_PUMPKIN_STEM:
case E_BLOCK_RAIL:
case E_BLOCK_RED_ROSE:
case E_BLOCK_RED_MUSHROOM:
case E_BLOCK_REDSTONE_REPEATER_OFF:
case E_BLOCK_REDSTONE_REPEATER_ON:
case E_BLOCK_REDSTONE_TORCH_OFF:
case E_BLOCK_REDSTONE_TORCH_ON:
case E_BLOCK_REDSTONE_WIRE:
case E_BLOCK_REEDS:
case E_BLOCK_SAPLING:
case E_BLOCK_SIGN_POST:
case E_BLOCK_SNOW:
case E_BLOCK_STATIONARY_LAVA:
case E_BLOCK_STATIONARY_WATER:
case E_BLOCK_STONE_BUTTON:
case E_BLOCK_STONE_PRESSURE_PLATE:
case E_BLOCK_TALL_GRASS:
case E_BLOCK_TORCH:
case E_BLOCK_TRIPWIRE:
case E_BLOCK_TRIPWIRE_HOOK:
case E_BLOCK_VINES:
case E_BLOCK_WALLSIGN:
case E_BLOCK_WATER:
case E_BLOCK_WOODEN_BUTTON:
case E_BLOCK_WOODEN_PRESSURE_PLATE:
case E_BLOCK_YELLOW_FLOWER:
{
return false;
}
}
return true;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cHeightMapFactory:
cHeightMapFactory::~cHeightMapFactory()
{
// Force all threads to save their last regions:
for (cCallbacks::iterator itr = m_Callbacks.begin(), end = m_Callbacks.end(); itr != end; ++itr)
{
((cHeightMap *)(*itr))->Finish();
}
// TODO: Join all the files into one giant image file
}
+81
View File
@@ -0,0 +1,81 @@
// HeightMap.h
// Declares the cHeightMap class representing a cCallback descendant that draws a B&W map of heights for the world
#pragma once
#include "Callback.h"
class cHeightMap :
public cCallback
{
public:
cHeightMap(void);
void Finish(void);
protected:
int m_CurrentChunkX; // Absolute chunk coords
int m_CurrentChunkZ;
int m_CurrentChunkOffX; // Chunk offset from the start of the region
int m_CurrentChunkOffZ;
int m_CurrentRegionX;
int m_CurrentRegionZ;
bool m_IsCurrentRegionValid;
int m_Height[16 * 32 * 16 * 32]; ///< Height-map of the entire current region [x + 16 * 32 * z]
BLOCKTYPE m_BlockTypes[16 * 16 * 256]; ///< Block data of the currently processed chunk (between OnSection() and OnSectionsFinished() )
// 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) { return false; }
virtual bool OnHeightMap(const int * a_HeightMapBE) override;
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;
void StartNewRegion(int a_RegionX, int a_RegionZ);
static bool IsGround(BLOCKTYPE a_BlockType);
} ;
class cHeightMapFactory :
public cCallbackFactory
{
public:
virtual ~cHeightMapFactory();
virtual cCallback * CreateNewCallback(void) override
{
return new cHeightMap;
}
} ;
+579
View File
@@ -0,0 +1,579 @@
// Processor.cpp
// Implements the cProcessor class representing the overall processor engine that manages threads, calls callbacks etc.
#include "Globals.h"
#include "Processor.h"
#include "Callback.h"
#include "../../source/WorldStorage/FastNBT.h"
#include "zlib.h"
#include "Utils.h"
const int CHUNK_INFLATE_MAX = 1 MiB;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProcessor::cThread:
cProcessor::cThread::cThread(cCallback & a_Callback, cProcessor & a_ParentProcessor) :
super("cProcessor::cThread"),
m_Callback(a_Callback),
m_ParentProcessor(a_ParentProcessor)
{
super::Start();
}
void cProcessor::cThread::Execute(void)
{
LOG("Started a new thread: %d", cIsThread::GetCurrentID());
m_ParentProcessor.m_ThreadsHaveStarted.Set();
for (;;)
{
AString FileName = m_ParentProcessor.GetOneFileName();
if (FileName.empty())
{
// All done, terminate the thread
break;
}
ProcessFile(FileName);
} // for-ever
LOG("Thread %d terminated", cIsThread::GetCurrentID());
}
void cProcessor::cThread::ProcessFile(const AString & a_FileName)
{
LOG("Processing file \"%s\"", a_FileName.c_str());
size_t idx = a_FileName.rfind("r.");
if (idx == AString::npos)
{
LOG("Cannot parse filename \"%s\", skipping file.", a_FileName.c_str());
return;
}
int RegionX = 0, RegionZ = 0;
if (sscanf_s(a_FileName.c_str() + idx, "r.%d.%d.mca", &RegionX, &RegionZ) != 2)
{
LOG("Cannot parse filename \"%s\" into coords, skipping file.", a_FileName.c_str());
return;
}
cFile f;
if (!f.Open(a_FileName, cFile::fmRead))
{
LOG("Cannot open file \"%s\", skipping file.", a_FileName.c_str());
return;
}
AString FileContents;
f.ReadRestOfFile(FileContents);
if (FileContents.size() < sizeof(8 KiB))
{
LOG("Cannot read header in file \"%s\", skipping file.", a_FileName.c_str());
return;
}
ProcessFileData(FileContents.data(), FileContents.size(), RegionX * 32, RegionZ * 32);
}
void cProcessor::cThread::ProcessFileData(const char * a_FileData, size_t a_Size, int a_ChunkBaseX, int a_ChunkBaseZ)
{
int Header[2048];
int * HeaderPtr = (int *)a_FileData;
for (int i = 0; i < ARRAYCOUNT(Header); i++)
{
Header[i] = ntohl(HeaderPtr[i]);
}
for (int i = 0; i < 1024; i++)
{
unsigned Location = Header[i];
unsigned Timestamp = Header[i + 1024];
if (
((Location == 0) && (Timestamp == 0)) || // Official docs' "not present"
(Location >> 8 < 2) || // Logical - no chunk can start inside the header
((Location & 0xff) == 0) || // Logical - no chunk can be zero bytes
((Location >> 8) * 4096 > a_Size) // Logical - no chunk can start at beyond the file end
)
{
// Chunk not present in the file
continue;
}
int ChunkX = a_ChunkBaseX + (i % 32);
int ChunkZ = a_ChunkBaseZ + (i / 32);
if (m_Callback.OnNewChunk(ChunkX, ChunkZ))
{
continue;
}
ProcessChunk(a_FileData, ChunkX, ChunkZ, Location >> 8, Location & 0xff, Timestamp);
} // for i - chunk index
}
void cProcessor::cThread::ProcessChunk(const char * a_FileData, int a_ChunkX, int a_ChunkZ, unsigned a_SectorStart, unsigned a_SectorSize, unsigned a_TimeStamp)
{
if (m_Callback.OnHeader(a_SectorStart * 4096, a_SectorSize, a_TimeStamp))
{
return;
}
const char * ChunkStart = a_FileData + a_SectorStart * 4096;
int ByteSize = ntohl(*(int *)ChunkStart);
char CompressionMethod = ChunkStart[4];
if (m_Callback.OnCompressedDataSizePos(ByteSize, a_SectorStart * 4096 + 5, CompressionMethod))
{
return;
}
ProcessCompressedChunkData(a_ChunkX, a_ChunkZ, ChunkStart + 5, ByteSize);
}
void cProcessor::cThread::ProcessCompressedChunkData(int a_ChunkX, int a_ChunkZ, const char * a_CompressedData, int a_CompressedSize)
{
char Decompressed[CHUNK_INFLATE_MAX];
z_stream strm;
strm.zalloc = (alloc_func)NULL;
strm.zfree = (free_func)NULL;
strm.opaque = NULL;
inflateInit(&strm);
strm.next_out = (Bytef *)Decompressed;
strm.avail_out = sizeof(Decompressed);
strm.next_in = (Bytef *)a_CompressedData;
strm.avail_in = a_CompressedSize;
int res = inflate(&strm, Z_FINISH);
inflateEnd(&strm);
if (res != Z_STREAM_END)
{
LOG("Decompression failed, skipping chunk [%d, %d]", a_ChunkX, a_ChunkZ);
return;
}
if (m_Callback.OnDecompressedData(Decompressed, strm.total_out))
{
return;
}
// Parse the NBT data:
cParsedNBT NBT(Decompressed, strm.total_out);
if (!NBT.IsValid())
{
LOG("NBT Parsing failed, skipping chunk [%d, %d]", a_ChunkX, a_ChunkZ);
return;
}
ProcessParsedChunkData(a_ChunkX, a_ChunkZ, NBT);
}
void cProcessor::cThread::ProcessParsedChunkData(int a_ChunkX, int a_ChunkZ, cParsedNBT & a_NBT)
{
int LevelTag = a_NBT.FindChildByName(0, "Level");
if (LevelTag < 0)
{
LOG("Bad logical structure of the NBT, skipping chunk [%d, %d].", a_ChunkX, a_ChunkZ);
return;
}
int XPosTag = a_NBT.FindChildByName(LevelTag, "xPos");
int ZPosTag = a_NBT.FindChildByName(LevelTag, "zPos");
if ((XPosTag < 0) || (ZPosTag < 0))
{
LOG("Pos tags missing in NTB, skipping chunk [%d, %d].", a_ChunkX, a_ChunkZ);
return;
}
if (m_Callback.OnRealCoords(a_NBT.GetInt(XPosTag), a_NBT.GetInt(ZPosTag)))
{
return;
}
int LastUpdateTag = a_NBT.FindChildByName(LevelTag, "LastUpdate");
if (LastUpdateTag > 0)
{
if (m_Callback.OnLastUpdate(a_NBT.GetLong(LastUpdateTag)))
{
return;
}
}
int TerrainPopulatedTag = a_NBT.FindChildByName(LevelTag, "TerrainPopulated");
bool TerrainPopulated = (TerrainPopulatedTag < 0) ? false : (a_NBT.GetByte(TerrainPopulatedTag) != 0);
if (m_Callback.OnTerrainPopulated(TerrainPopulated))
{
return;
}
int BiomesTag = a_NBT.FindChildByName(LevelTag, "Biomes");
if (BiomesTag > 0)
{
if (m_Callback.OnBiomes((const unsigned char *)(a_NBT.GetData(BiomesTag))))
{
return;
}
}
int HeightMapTag = a_NBT.FindChildByName(LevelTag, "HeightMap");
if (HeightMapTag > 0)
{
if (m_Callback.OnHeightMap((const int *)(a_NBT.GetData(HeightMapTag))))
{
return;
}
}
if (ProcessChunkSections(a_ChunkX, a_ChunkZ, a_NBT, LevelTag))
{
return;
}
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;
}
}
bool cProcessor::cThread::ProcessChunkSections(int a_ChunkX, int a_ChunkZ, cParsedNBT & a_NBT, int a_LevelTag)
{
int Sections = a_NBT.FindChildByName(a_LevelTag, "Sections");
if (Sections < 0)
{
return false;
}
bool SectionProcessed[16];
memset(SectionProcessed, 0, sizeof(SectionProcessed));
for (int Tag = a_NBT.GetFirstChild(Sections); Tag > 0; Tag = a_NBT.GetNextSibling(Tag))
{
int YTag = a_NBT.FindChildByName(Tag, "Y");
int BlocksTag = a_NBT.FindChildByName(Tag, "Blocks");
int AddTag = a_NBT.FindChildByName(Tag, "Add");
int DataTag = a_NBT.FindChildByName(Tag, "Data");
int BlockLightTag = a_NBT.FindChildByName(Tag, "BlockLightTag");
int SkyLightTag = a_NBT.FindChildByName(Tag, "SkyLight");
if ((YTag < 0) || (BlocksTag < 0) || (DataTag < 0))
{
continue;
}
unsigned char SectionY = a_NBT.GetByte(YTag);
if (SectionY >= 16)
{
LOG("WARNING: Section Y >= 16 (%d), high world, wtf? Skipping section!", SectionY);
continue;
}
if (m_Callback.OnSection(
SectionY,
(const BLOCKTYPE *) (a_NBT.GetData(BlocksTag)),
(AddTag > 0) ? (const NIBBLETYPE *)(a_NBT.GetData(AddTag)) : NULL,
(const NIBBLETYPE *)(a_NBT.GetData(DataTag)),
(BlockLightTag > 0) ? (const NIBBLETYPE *)(a_NBT.GetData(BlockLightTag)) : NULL,
(BlockLightTag > 0) ? (const NIBBLETYPE *)(a_NBT.GetData(BlockLightTag)) : NULL
))
{
return true;
}
SectionProcessed[SectionY] = true;
} // for Tag - Sections[]
// Call the callback for empty sections:
for (unsigned char y = 0; y < 16; y++)
{
if (!SectionProcessed[y])
{
if (m_Callback.OnEmptySection(y))
{
return true;
}
}
}
if (m_Callback.OnSectionsFinished())
{
return true;
}
return false;
}
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:
cProcessor::cProcessor(void) :
m_IsShuttingDown(false)
{
}
cProcessor::~cProcessor()
{
}
void cProcessor::ProcessWorld(const AString & a_WorldFolder, cCallbackFactory & a_CallbackFactory)
{
PopulateFileQueue(a_WorldFolder);
if (m_FileQueue.empty())
{
LOG("No files to process, exitting.");
return;
}
// Start as many threads as there are cores, plus one:
// (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;
#endif // _DEBUG
//*/
for (int i = 0; i < NumThreads; i++)
{
cCallback * Callback = a_CallbackFactory.GetNewCallback();
m_Threads.push_back(new cThread(*Callback, *this));
}
// Wait for the first thread to start processing:
m_ThreadsHaveStarted.Wait();
// Wait for all threads to finish
// simply by calling each thread's destructor sequentially
LOG("Waiting for threads to finish");
for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr)
{
delete *itr;
} // for itr - m_Threads[]
LOG("Processor finished");
}
void cProcessor::PopulateFileQueue(const AString & a_WorldFolder)
{
LOG("Processing world in \"%s\"...", a_WorldFolder.c_str());
AString Path = a_WorldFolder;
if (!Path.empty() && (Path[Path.length() - 1] != cFile::PathSeparator))
{
Path.push_back(cFile::PathSeparator);
}
AStringList AllFiles = GetDirectoryContents(Path.c_str());
for (AStringList::iterator itr = AllFiles.begin(), end = AllFiles.end(); itr != end; ++itr)
{
if (itr->rfind(".mca") != itr->length() - 4)
{
// Not a .mca file
continue;
}
m_FileQueue.push_back(Path + *itr);
} // for itr - AllFiles[]
}
AString cProcessor::GetOneFileName(void)
{
cCSLock Lock(m_CS);
if (m_FileQueue.empty())
{
return "";
}
AString res = m_FileQueue.back();
m_FileQueue.pop_back();
return res;
}
+77
View File
@@ -0,0 +1,77 @@
// Processor.h
// Interfaces to the cProcessor class representing the overall processor engine that manages threads, calls callbacks etc.
#pragma once
// fwd:
class cCallback;
class cCallbackFactory;
class cParsedNBT;
class cProcessor
{
class cThread :
public cIsThread
{
typedef cIsThread super;
cCallback & m_Callback;
cProcessor & m_ParentProcessor;
// cIsThread override:
virtual void Execute(void) override;
void ProcessFile(const AString & a_FileName);
void ProcessFileData(const char * a_FileData, size_t a_Size, int a_ChunkBaseX, int a_ChunkBaseZ);
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);
} ;
typedef std::vector<cThread *> cThreads;
public:
cProcessor(void);
~cProcessor();
void ProcessWorld(const AString & a_WorldFolder, cCallbackFactory & a_CallbackFactory);
protected:
bool m_IsShuttingDown; // If true, the threads should stop ASAP
cCriticalSection m_CS;
AStringList m_FileQueue;
cThreads m_Threads;
cEvent m_ThreadsHaveStarted; // This is signalled by each thread to notify the parent thread that it can start waiting for those threads
void PopulateFileQueue(const AString & a_WorldFolder);
AString GetOneFileName(void);
} ;
+279
View File
@@ -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());
}
}
+102
View File
@@ -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);
} ;
+523
View File
@@ -0,0 +1,523 @@
// Statistics.cpp
// Implements the various statistics-collecting classes
#include "Globals.h"
#include "Statistics.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),
m_MinChunkX(0x7fffffff),
m_MaxChunkX(0x80000000),
m_MinChunkZ(0x7fffffff),
m_MaxChunkZ(0x80000000)
{
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;
UpdateCoordsRange(a_Stats.m_MinChunkX, a_Stats.m_MinChunkZ);
UpdateCoordsRange(a_Stats.m_MinChunkX, a_Stats.m_MinChunkZ);
}
void cStatistics::cStats::UpdateCoordsRange(int a_ChunkX, int a_ChunkZ)
{
if (a_ChunkX < m_MinChunkX)
{
m_MinChunkX = a_ChunkX;
}
if (a_ChunkX > m_MaxChunkX)
{
m_MaxChunkX = a_ChunkX;
}
if (a_ChunkZ < m_MinChunkZ)
{
m_MinChunkZ = a_ChunkZ;
}
if (a_ChunkZ > m_MaxChunkZ)
{
m_MaxChunkZ = a_ChunkZ;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cStatistics:
cStatistics::cStatistics(void)
{
}
bool cStatistics::OnNewChunk(int a_ChunkX, int a_ChunkZ)
{
m_Stats.m_TotalChunks++;
m_Stats.UpdateCoordsRange(a_ChunkX, a_ChunkZ);
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_Stats.m_BiomeCounts[a_BiomeData[i]] += 1;
}
m_Stats.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_Stats.m_BlockCounts[Biome][BlockType] += 1;
}
}
}
m_Stats.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_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;
}
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 %llu chunks went through", m_CombinedStats.m_TotalChunks);
LOG(" Biomes processed for %llu chunks", m_CombinedStats.m_BiomeNumChunks);
// Check the number of blocks processed
UInt64 TotalBlocks = 0;
for (int i = 0; i <= 255; i++)
{
for (int j = 0; j < 255; j++)
{
TotalBlocks += m_CombinedStats.m_BlockCounts[i][j];
}
}
UInt64 ExpTotalBlocks = m_CombinedStats.m_BlockNumChunks * 16LL * 16LL * 256LL;
LOG(" BlockIDs processed for %llu chunks, %llu blocks (exp %llu; %s)", m_CombinedStats.m_BlockNumChunks, TotalBlocks, ExpTotalBlocks, (TotalBlocks == ExpTotalBlocks) ? "match" : "failed");
// Save statistics:
LOG(" Saving statistics into files:");
LOG(" Statistics.txt");
SaveStatistics();
LOG(" Biomes.xls");
SaveBiomes();
LOG(" BlockTypes.xls");
SaveBlockTypes();
LOG(" BiomeBlockTypes.xls");
SaveBiomeBlockTypes();
LOG(" Spawners.xls");
SaveSpawners();
}
void cStatisticsFactory::JoinResults(void)
{
for (cCallbacks::iterator itr = m_Callbacks.begin(), end = m_Callbacks.end(); itr != end; ++itr)
{
m_CombinedStats.Add(((cStatistics *)(*itr))->GetStats());
} // 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_CombinedStats.m_BiomeNumChunks) * 16 * 16 / 100; // Total number of columns processed; convert into percent
if (TotalColumns < 1)
{
// Avoid division by zero
TotalColumns = 1;
}
for (int i = 0; i <= 255; i++)
{
AString Line;
Printf(Line, "%s\t%d\t%llu\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());
}
}
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_CombinedStats.m_BlockNumChunks)) * 16 * 16 * 256 / 100; // Total number of blocks processed; convert into percent
if (TotalBlocks < 1)
{
// Avoid division by zero
TotalBlocks = 1;
}
for (int i = 0; i <= 255; i++)
{
UInt64 Count = 0;
for (int Biome = 0; Biome <= 255; ++Biome)
{
Count += m_CombinedStats.m_BlockCounts[Biome][i];
}
AString Line;
Printf(Line, "%s\t%d\t%llu\t%.08f\n", GetBlockTypeString(i), i, Count, ((double)Count) / TotalBlocks);
f.Write(Line.c_str(), Line.length());
}
}
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%llu", m_CombinedStats.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%llu", m_CombinedStats.m_BlockCounts[Biome][BlockType]);
}
Line.append("\n");
f.Write(Line.c_str(), Line.length());
}
}
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: %llu\n", m_CombinedStats.m_TotalChunks);
if (Elapsed > 0)
{
f.Printf("Chunk processing speed: %.02f chunks per second\n", (double)(m_CombinedStats.m_TotalChunks) / Elapsed);
}
f.Printf("Biomes counted for %llu chunks.\n", m_CombinedStats.m_BiomeNumChunks);
f.Printf("Blocktypes counted for %llu chunks.\n", m_CombinedStats.m_BlockNumChunks);
f.Printf("Total blocks counted: %llu\n", m_CombinedStats.m_BlockNumChunks * 16 * 16 * 256);
f.Printf("Total biomes counted: %llu\n", m_CombinedStats.m_BiomeNumChunks * 16 * 16);
f.Printf("Total entities counted: %llu\n", m_CombinedStats.m_NumEntities);
f.Printf("Total tile entities counted: %llu\n", m_CombinedStats.m_NumTileEntities);
f.Printf("Total tile ticks counted: %llu\n", m_CombinedStats.m_NumTileTicks);
f.Printf("Chunk coord ranges:\n");
f.Printf("\tX: %d .. %d\n", m_CombinedStats.m_MinChunkX, m_CombinedStats.m_MaxChunkX);
f.Printf("\tZ: %d .. %d\n", m_CombinedStats.m_MinChunkZ, m_CombinedStats.m_MaxChunkZ);
}
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%llu\t%0.4f\n", GetEntityTypeString((eEntityType)i), m_CombinedStats.m_SpawnerEntity[i], (double)(m_CombinedStats.m_SpawnerEntity[i]) / m_CombinedStats.m_BlockNumChunks);
}
}
+138
View File
@@ -0,0 +1,138 @@
// Statistics.h
// Interfaces to the cStatistics class representing a statistics-collecting callback
#pragma once
#include "Callback.h"
#include "Utils.h"
class cStatistics :
public cCallback
{
public:
class cStats
{
public:
UInt64 m_TotalChunks; // Total number of chunks that go through this callback (OnNewChunk())
UInt64 m_BiomeCounts[256];
UInt64 m_BlockCounts[256][256]; // First dimension is the biome, second dimension is BlockType
UInt64 m_BiomeNumChunks; // Num chunks that have been processed for biome stats
UInt64 m_BlockNumChunks; // Num chunks that have been processed for block stats
UInt64 m_NumEntities;
UInt64 m_NumTileEntities;
UInt64 m_NumTileTicks;
int m_MinChunkX, m_MaxChunkX; // X coords range
int m_MinChunkZ, m_MaxChunkZ; // Z coords range
Int64 m;
UInt64 m_SpawnerEntity[entMax + 1];
cStats(void);
void Add(const cStats & a_Stats);
void UpdateCoordsRange(int a_ChunkX, int a_ChunkZ);
} ;
cStatistics(void);
const cStats & GetStats(void) const { return m_Stats; }
protected:
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;
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 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);
} ;
class cStatisticsFactory :
public cCallbackFactory
{
public:
cStatisticsFactory(void);
virtual ~cStatisticsFactory();
virtual cCallback * CreateNewCallback(void)
{
return new cStatistics;
}
protected:
// The results, combined, are stored here:
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);
} ;
+291
View File
@@ -0,0 +1,291 @@
// Utils.cpp
// Implements utility functions
#include "Globals.h"
#include "Utils.h"
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
{
"Ocean",
"Plains",
"Desert",
"Extreme Hills",
"Forest",
"Taiga",
"Swampland",
"River",
"Hell",
"Sky",
"Frozen Ocean",
"Frozen River",
"Ice Plains",
"Ice Mountains",
"Mushroom Island",
"Mushroom Island Shore",
"Beach",
"Desert Hills",
"Forest Hills",
"Taiga Hills",
"Extreme Hills Edge",
"Jungle",
"Jungle Hills",
} ;
return (a_Biome < ARRAYCOUNT(BiomeNames)) ? BiomeNames[a_Biome] : "";
}
const char * GetBlockTypeString(unsigned char a_BlockType)
{
static const char * BlockTypeNames[] = // Block type names, as equivalent to their index
{
"air",
"stone",
"grass",
"dirt",
"cobblestone",
"planks",
"sapling",
"bedrock",
"water",
"stillwater",
"lava",
"stilllava",
"sand",
"gravel",
"goldore",
"ironore",
"coalore",
"log",
"leaves",
"sponge",
"glass",
"lapisore",
"lapisblock",
"dispenser",
"sandstone",
"noteblock",
"bedblock",
"poweredrail",
"detectorrail",
"stickypiston",
"cobweb",
"tallgrass",
"deadbush",
"piston",
"pistonhead",
"wool",
"pistonmovedblock",
"flower",
"rose",
"brownmushroom",
"redmushroom",
"goldblock",
"ironblock",
"doubleslab",
"slab",
"brickblock",
"tnt",
"bookcase",
"mossycobblestone",
"obsidian",
"torch",
"fire",
"mobspawner",
"woodstairs",
"chest",
"redstonedust",
"diamondore",
"diamondblock",
"workbench",
"crops",
"soil",
"furnace",
"litfurnace",
"signblock",
"wooddoorblock",
"ladder",
"tracks",
"cobblestonestairs",
"wallsign",
"lever",
"stoneplate",
"irondoorblock",
"woodplate",
"redstoneore",
"redstoneorealt",
"redstonetorchoff",
"redstonetorchon",
"button",
"snow",
"ice",
"snowblock",
"cactus",
"clayblock",
"reedblock",
"jukebox",
"fence",
"pumpkin",
"netherrack",
"soulsand",
"glowstone",
"portal",
"jack-o-lantern",
"cakeblock",
"repeateroff",
"repeateron",
"lockedchest",
"trapdoor",
"silverfishblock",
"stonebricks",
"hugebrownmushroom",
"hugeredmushroom",
"ironbars",
"glasspane",
"melon",
"pumpkinstem",
"melonstem",
"vines",
"fencegate",
"brickstairs",
"stonebrickstairs",
"mycelium",
"lilypad",
"netherbrick",
"netherbrickfence",
"netherbrickstairs",
"netherwartblock",
"enchantmenttable",
"brewingstandblock",
"cauldronblock",
"endportal",
"endportalframe",
"endstone",
"dragonegg",
"redstonelampoff",
"redstonelampon",
"woodendoubleslab",
"woodenslab",
"cocoapod",
"sandstonestairs", /* 128 */
"Emerald Ore",
"Ender Chest",
"Tripwire Hook",
"Tripwire",
"Block of Emerald",
"Spruce Wood Stairs",
"Birch Wood Stairs",
"Jungle Wood Stairs",
"Command Block",
"Beacon",
"Cobblestone Wall",
"Flower Pot",
"Carrots",
"Potatoes",
"Wooden Button",
"Head",
} ;
return (a_BlockType < ARRAYCOUNT(BlockTypeNames)) ? BlockTypeNames[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)
DWORD Affinity, ProcAffinity;
GetProcessAffinityMask(GetCurrentProcess(), &ProcAffinity, &Affinity);
int NumCores = 0;
while (Affinity > 0)
{
if ((Affinity & 1) == 1)
{
++NumCores;
}
Affinity >>= 1;
} // while (Affinity > 0)
return NumCores;
}
+61
View File
@@ -0,0 +1,61 @@
// Utils.h
// Interfaces to utility functions
#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);
+70
View File
@@ -0,0 +1,70 @@
@echo off
::
:: Profiling using a MSVC standalone profiler
::
:: See http://www.codeproject.com/Articles/144643/Profiling-of-C-Applications-in-Visual-Studio-for-F for details
::
set pt="C:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Performance Tools"
set appdir="Release profiled"
set app="Release profiled\AnvilStats.exe"
set args="0 c:\Games\MLG\world\region"
:: outputdir is relative to appdir!
set outputdir=Profiling
set output=profile.vsp
::Create the output directory, if it didn't exist
mkdir %outputdir%
:: Start the profiler
%pt%\vsperfcmd /start:sample /output:%outputdir%\%output%
if errorlevel 1 goto haderror
:: Launch the application via the profiler
%pt%\vsperfcmd /launch:%app% /args:%args%
if errorlevel 1 goto haderror
:: Shut down the profiler (this command waits, until the application is terminated)
%pt%\vsperfcmd /shutdown
if errorlevel 1 goto haderror
:: cd to outputdir, so that the reports are generated there
cd %outputdir%
:: generate the report files (.csv)
%pt%\vsperfreport /summary:all %output% /symbolpath:"srv*C:\Programovani\Symbols*http://msdl.microsoft.com/download/symbols"
if errorlevel 1 goto haderror
goto finished
:haderror
echo An error was encountered
pause
:finished