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:
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -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=""..\..\zlib-1.2.7""
|
||||
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=""..\..\zlib-1.2.7""
|
||||
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=""..\..\zlib-1.2.7""
|
||||
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>
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
// Globals.cpp
|
||||
|
||||
// This file is used for precompiled header generation in MSVC environments
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user