From 53e22b11857fed62e2313d6d84d90f88ed412ffb Mon Sep 17 00:00:00 2001 From: Alexander Harkness Date: Mon, 29 Jul 2013 12:13:03 +0100 Subject: Changed everyting to Unix line endings. --- Tools/AnvilStats/AnvilStats.cpp | 138 +- Tools/AnvilStats/AnvilStats.sln | 68 +- Tools/AnvilStats/AnvilStats.txt | 54 +- Tools/AnvilStats/AnvilStats.vcproj | 904 ++-- Tools/AnvilStats/BiomeMap.cpp | 344 +- Tools/AnvilStats/BiomeMap.h | 138 +- Tools/AnvilStats/Callback.h | 330 +- Tools/AnvilStats/ChunkExtract.cpp | 206 +- Tools/AnvilStats/ChunkExtract.h | 132 +- Tools/AnvilStats/Globals.cpp | 20 +- Tools/AnvilStats/Globals.h | 456 +- Tools/AnvilStats/HeightMap.cpp | 530 +-- Tools/AnvilStats/HeightMap.h | 162 +- Tools/AnvilStats/Processor.cpp | 1158 ++--- Tools/AnvilStats/Processor.h | 154 +- Tools/AnvilStats/SpringStats.cpp | 558 +-- Tools/AnvilStats/SpringStats.h | 204 +- Tools/AnvilStats/Statistics.cpp | 1046 ++--- Tools/AnvilStats/Statistics.h | 276 +- Tools/AnvilStats/Utils.cpp | 582 +-- Tools/AnvilStats/Utils.h | 122 +- Tools/AnvilStats/profile_run.cmd | 140 +- Tools/BiomeVisualiser/BiomeCache.cpp | 666 +-- Tools/BiomeVisualiser/BiomeCache.h | 192 +- Tools/BiomeVisualiser/BiomeRenderer.cpp | 294 +- Tools/BiomeVisualiser/BiomeRenderer.h | 100 +- Tools/BiomeVisualiser/BiomeSource.h | 74 +- Tools/BiomeVisualiser/BiomeViewWnd.cpp | 380 +- Tools/BiomeVisualiser/BiomeViewWnd.h | 92 +- Tools/BiomeVisualiser/BiomeVisualiser.cpp | 104 +- Tools/BiomeVisualiser/BiomeVisualiser.h | 62 +- Tools/BiomeVisualiser/BiomeVisualiser.sln | 46 +- Tools/BiomeVisualiser/BiomeVisualiser.vcproj | 966 ++-- Tools/BiomeVisualiser/GeneratorBiomeSource.h | 84 +- Tools/BiomeVisualiser/Pixmap.cpp | 240 +- Tools/BiomeVisualiser/Pixmap.h | 78 +- Tools/BiomeVisualiser/Timer.h | 80 +- Tools/BiomeVisualiser/WndProcThunk.h | 286 +- Tools/BiomeVisualiser/profile_run.cmd | 140 +- Tools/BlockZapper/BlockZapper.cpp | 194 +- Tools/BlockZapper/BlockZapper.sln | 68 +- Tools/BlockZapper/BlockZapper.txt | 38 +- Tools/BlockZapper/BlockZapper.vcproj | 626 +-- Tools/BlockZapper/Globals.cpp | 20 +- Tools/BlockZapper/Globals.h | 28 +- Tools/BlockZapper/Regions.cpp | 334 +- Tools/BlockZapper/Regions.h | 116 +- Tools/BlockZapper/Zapper.cpp | 880 ++-- Tools/BlockZapper/Zapper.h | 160 +- Tools/MemDumpAnalysis/Globals.cpp | 20 +- Tools/MemDumpAnalysis/Globals.h | 70 +- Tools/MemDumpAnalysis/MemDumpAnalysis.cpp | 642 +-- Tools/MemDumpAnalysis/MemDumpAnalysis.sln | 40 +- Tools/MemDumpAnalysis/MemDumpAnalysis.vcproj | 878 ++-- Tools/MemDumpAnalysis/MemDumpAnalysis.vcproj.user | 62 +- Tools/MemDumpAnalysis/targetver.h | 50 +- Tools/ProtoProxy/Connection.cpp | 5102 ++++++++++----------- Tools/ProtoProxy/Connection.h | 412 +- Tools/ProtoProxy/Globals.cpp | 20 +- Tools/ProtoProxy/Globals.h | 440 +- Tools/ProtoProxy/ProtoProxy.cpp | 64 +- Tools/ProtoProxy/ProtoProxy.sln | 58 +- Tools/ProtoProxy/ProtoProxy.txt | 62 +- Tools/ProtoProxy/ProtoProxy.vcproj | 534 +-- Tools/ProtoProxy/Server.cpp | 164 +- Tools/ProtoProxy/Server.h | 76 +- Tools/ToLuaDoxy/Globals.cpp | 20 +- Tools/ToLuaDoxy/Globals.h | 202 +- Tools/ToLuaDoxy/ToLuaDoxy.cpp | 444 +- Tools/ToLuaDoxy/ToLuaDoxy.sln | 40 +- Tools/ToLuaDoxy/ToLuaDoxy.vcproj | 442 +- 71 files changed, 11956 insertions(+), 11956 deletions(-) (limited to 'Tools') diff --git a/Tools/AnvilStats/AnvilStats.cpp b/Tools/AnvilStats/AnvilStats.cpp index ae3f901dc..f0b9dd7e6 100644 --- a/Tools/AnvilStats/AnvilStats.cpp +++ b/Tools/AnvilStats/AnvilStats.cpp @@ -1,69 +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 []", 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"); -} - - - - + +// 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 []", 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"); +} + + + + diff --git a/Tools/AnvilStats/AnvilStats.sln b/Tools/AnvilStats/AnvilStats.sln index 886a7a8c8..6e2481d84 100644 --- a/Tools/AnvilStats/AnvilStats.sln +++ b/Tools/AnvilStats/AnvilStats.sln @@ -1,34 +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 + +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 diff --git a/Tools/AnvilStats/AnvilStats.txt b/Tools/AnvilStats/AnvilStats.txt index 07c518e4c..19aa4f324 100644 --- a/Tools/AnvilStats/AnvilStats.txt +++ b/Tools/AnvilStats/AnvilStats.txt @@ -1,27 +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. - - -*/ - - - + +// 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. + + +*/ + + + diff --git a/Tools/AnvilStats/AnvilStats.vcproj b/Tools/AnvilStats/AnvilStats.vcproj index 1726cbfbf..ed4ffa9a5 100644 --- a/Tools/AnvilStats/AnvilStats.vcproj +++ b/Tools/AnvilStats/AnvilStats.vcproj @@ -1,452 +1,452 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/AnvilStats/BiomeMap.cpp b/Tools/AnvilStats/BiomeMap.cpp index eca235c5f..6505299ba 100644 --- a/Tools/AnvilStats/BiomeMap.cpp +++ b/Tools/AnvilStats/BiomeMap.cpp @@ -1,172 +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 -} - - - - + +// 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 +} + + + + diff --git a/Tools/AnvilStats/BiomeMap.h b/Tools/AnvilStats/BiomeMap.h index f0d306c04..c590a3c63 100644 --- a/Tools/AnvilStats/BiomeMap.h +++ b/Tools/AnvilStats/BiomeMap.h @@ -1,69 +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; - } - -} ; - - - - + +// 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; + } + +} ; + + + + diff --git a/Tools/AnvilStats/Callback.h b/Tools/AnvilStats/Callback.h index 92d394d0e..83b330651 100644 --- a/Tools/AnvilStats/Callback.h +++ b/Tools/AnvilStats/Callback.h @@ -1,165 +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 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; -} ; - - - - + +// 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 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; +} ; + + + + diff --git a/Tools/AnvilStats/ChunkExtract.cpp b/Tools/AnvilStats/ChunkExtract.cpp index 08517a58d..71e804e73 100644 --- a/Tools/AnvilStats/ChunkExtract.cpp +++ b/Tools/AnvilStats/ChunkExtract.cpp @@ -1,104 +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; + +// 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; } \ No newline at end of file diff --git a/Tools/AnvilStats/ChunkExtract.h b/Tools/AnvilStats/ChunkExtract.h index 5e0ed8a9a..767523354 100644 --- a/Tools/AnvilStats/ChunkExtract.h +++ b/Tools/AnvilStats/ChunkExtract.h @@ -1,66 +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; -} ; - - - - + +// 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; +} ; + + + + diff --git a/Tools/AnvilStats/Globals.cpp b/Tools/AnvilStats/Globals.cpp index 2c60fd698..13c6ae709 100644 --- a/Tools/AnvilStats/Globals.cpp +++ b/Tools/AnvilStats/Globals.cpp @@ -1,10 +1,10 @@ - -// Globals.cpp - -// This file is used for precompiled header generation in MSVC environments - -#include "Globals.h" - - - - + +// Globals.cpp + +// This file is used for precompiled header generation in MSVC environments + +#include "Globals.h" + + + + diff --git a/Tools/AnvilStats/Globals.h b/Tools/AnvilStats/Globals.h index b9d37e029..db5d3c635 100644 --- a/Tools/AnvilStats/Globals.h +++ b/Tools/AnvilStats/Globals.h @@ -1,228 +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 - #include - - // 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 - #include // for mkdir - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - #include - #include - #include - #include -#endif - - - - - -#define FILE_IO_PREFIX "" - - - - - -// CRT stuff: -#include -#include -#include -#include - - - - - -// STL stuff: -#include -#include -#include -#include -#include -#include -#include -#include - - - - - -// 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 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" - - - - + +// 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 + #include + + // 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 + #include // for mkdir + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include +#endif + + + + + +#define FILE_IO_PREFIX "" + + + + + +// CRT stuff: +#include +#include +#include +#include + + + + + +// STL stuff: +#include +#include +#include +#include +#include +#include +#include +#include + + + + + +// 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 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" + + + + diff --git a/Tools/AnvilStats/HeightMap.cpp b/Tools/AnvilStats/HeightMap.cpp index 9f52c2109..662ddf493 100644 --- a/Tools/AnvilStats/HeightMap.cpp +++ b/Tools/AnvilStats/HeightMap.cpp @@ -1,265 +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 -} - - - - + +// 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 +} + + + + diff --git a/Tools/AnvilStats/HeightMap.h b/Tools/AnvilStats/HeightMap.h index 4f9e702d5..c0e71cbc1 100644 --- a/Tools/AnvilStats/HeightMap.h +++ b/Tools/AnvilStats/HeightMap.h @@ -1,81 +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; - } - -} ; - - - - + +// 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; + } + +} ; + + + + diff --git a/Tools/AnvilStats/Processor.cpp b/Tools/AnvilStats/Processor.cpp index d6c793182..e7f7eb21d 100644 --- a/Tools/AnvilStats/Processor.cpp +++ b/Tools/AnvilStats/Processor.cpp @@ -1,579 +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; -} - - - - + +// 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; +} + + + + diff --git a/Tools/AnvilStats/Processor.h b/Tools/AnvilStats/Processor.h index b7d3f392e..72fea3081 100644 --- a/Tools/AnvilStats/Processor.h +++ b/Tools/AnvilStats/Processor.h @@ -1,77 +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 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); -} ; - - - - + +// 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 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); +} ; + + + + diff --git a/Tools/AnvilStats/SpringStats.cpp b/Tools/AnvilStats/SpringStats.cpp index 14ede6889..637cf20b6 100644 --- a/Tools/AnvilStats/SpringStats.cpp +++ b/Tools/AnvilStats/SpringStats.cpp @@ -1,279 +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()); - } -} - - - - + +// SpringStats.cpp + +// Implements the cSpringStats class representing a cCallback descendant that collects statistics on lava and water springs + +#include "Globals.h" +#include "SpringStats.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSpringStats::cStats + +cSpringStats::cStats::cStats(void) : + m_TotalChunks(0) +{ + memset(m_LavaSprings, 0, sizeof(m_LavaSprings)); + memset(m_WaterSprings, 0, sizeof(m_WaterSprings)); +} + + + + + +void cSpringStats::cStats::Add(const cSpringStats::cStats & a_Other) +{ + m_TotalChunks += a_Other.m_TotalChunks; + for (int Biome = 0; Biome < 256; Biome++) + { + for (int Height = 0; Height < 256; Height++) + { + m_LavaSprings[Biome][Height] += a_Other.m_LavaSprings[Biome][Height]; + m_WaterSprings[Biome][Height] += a_Other.m_WaterSprings[Biome][Height]; + } + } +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSpringStats: + +cSpringStats::cSpringStats(void) : + m_AreBiomesValid(false) +{ +} + + + + + +bool cSpringStats::OnNewChunk(int a_ChunkX, int a_ChunkZ) +{ + memset(m_BlockTypes, 0, sizeof(m_BlockTypes)); + m_AreBiomesValid = false; + return false; +} + + + + + +bool cSpringStats::OnBiomes(const unsigned char * a_BiomeData) +{ + memcpy(m_Biomes, a_BiomeData, sizeof(m_Biomes)); + m_AreBiomesValid = true; + return false; +} + + + + + +bool cSpringStats::OnSection( + unsigned char a_Y, + const BLOCKTYPE * a_BlockTypes, + const NIBBLETYPE * a_BlockAdditional, + const NIBBLETYPE * a_BlockMeta, + const NIBBLETYPE * a_BlockLight, + const NIBBLETYPE * a_BlockSkyLight +) +{ + memcpy(m_BlockTypes + ((int)a_Y) * 16 * 16 * 16, a_BlockTypes, 16 * 16 * 16); + memcpy(m_BlockMetas + ((int)a_Y) * 16 * 16 * 16 / 2, a_BlockMeta, 16 * 16 * 16 / 2); + return false; +} + + + + + +bool cSpringStats::OnSectionsFinished(void) +{ + if (!m_AreBiomesValid) + { + return true; + } + + // Calc the spring stats: + for (int y = 1; y < 255; y++) + { + int BaseY = y * 16 * 16; + for (int z = 1; z < 15; z++) + { + int Base = BaseY + z * 16; + for (int x = 1; x < 15; x++) + { + if (cChunkDef::GetNibble(m_BlockMetas, Base + x) != 0) + { + // Not a source block + continue; + } + switch (m_BlockTypes[Base + x]) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + { + TestSpring(x, y, z, m_Stats.m_WaterSprings); + break; + } + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + TestSpring(x, y, z, m_Stats.m_LavaSprings); + break; + } + } // switch (BlockType) + } // for x + } // for z + } // for y + m_Stats.m_TotalChunks += 1; + return true; +} + + + + + +void cSpringStats::TestSpring(int a_RelX, int a_RelY, int a_RelZ, cSpringStats::cStats::SpringStats & a_Stats) +{ + static const struct + { + int x, y, z; + } Coords[] = + { + {-1, 0, 0}, + { 1, 0, 0}, + { 0, -1, 0}, + { 0, 1, 0}, + { 0, 0, -1}, + { 0, 0, 1}, + } ; + bool HasFluidNextToIt = false; + for (int i = 0; i < ARRAYCOUNT(Coords); i++) + { + switch (cChunkDef::GetBlock(m_BlockTypes, a_RelX + Coords[i].x, a_RelY + Coords[i].y, a_RelZ + Coords[i].z)) + { + case E_BLOCK_WATER: + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_LAVA: + case E_BLOCK_STATIONARY_LAVA: + { + if (cChunkDef::GetNibble(m_BlockMetas, a_RelX + Coords[i].x, a_RelY + Coords[i].y, a_RelZ + Coords[i].z) == 0) + { + // There is another source block next to this, so this is not a spring + return; + } + HasFluidNextToIt = true; + } + } // switch (BlockType) + } // for i - Coords[] + + if (!HasFluidNextToIt) + { + // Surrounded by solids on all sides, this is probably not a spring, + // but rather a bedrocked lake or something similar. Dont want. + return; + } + + // No source blocks next to the specified block, so it is a spring. Add it to stats: + a_Stats[a_RelY][((unsigned char *)m_Biomes)[a_RelX + 16 * a_RelZ]] += 1; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSpringStatsFactory: + +cSpringStatsFactory::~cSpringStatsFactory() +{ + LOG("cSpringStats:"); + LOG(" Joining results..."); + JoinResults(); + LOG(" Total %llu chunks went through", m_CombinedStats.m_TotalChunks); + + // Save statistics: + LOG(" Saving statistics into files:"); + LOG(" Springs.xls"); + SaveTotals("Springs.xls"); + LOG(" BiomeWaterSprings.xls"); + SaveStatistics(m_CombinedStats.m_WaterSprings, "BiomeWaterSprings.xls"); + LOG(" BiomeLavaSprings.xls"); + SaveStatistics(m_CombinedStats.m_LavaSprings, "BiomeLavaSprings.xls"); +} + + + + + +void cSpringStatsFactory::JoinResults(void) +{ + for (cCallbacks::iterator itr = m_Callbacks.begin(), end = m_Callbacks.end(); itr != end; ++itr) + { + m_CombinedStats.Add(((cSpringStats *)(*itr))->GetStats()); + } // for itr - m_Callbacks[] +} + + + + + +void cSpringStatsFactory::SaveTotals(const AString & a_FileName) +{ + cFile f(a_FileName, cFile::fmWrite); + if (!f.IsOpen()) + { + LOG("Cannot open file \"%s\" for writing!", a_FileName.c_str()); + return; + } + f.Printf("Height\tWater\tLava\n"); + for (int Height = 0; Height < 256; Height++) + { + UInt64 TotalW = 0; + UInt64 TotalL = 0; + for (int Biome = 0; Biome < 256; Biome++) + { + TotalW += m_CombinedStats.m_WaterSprings[Height][Biome]; + TotalL += m_CombinedStats.m_LavaSprings[Height][Biome]; + } + f.Printf("%d\t%llu\t%llu\n", Height, TotalW, TotalL); + } + f.Printf("\n# Chunks\t%llu", m_CombinedStats.m_TotalChunks); +} + + + + + +void cSpringStatsFactory::SaveStatistics(const cSpringStats::cStats::SpringStats & a_Stats, const AString & a_FileName) +{ + cFile f(a_FileName, cFile::fmWrite); + if (!f.IsOpen()) + { + LOG("Cannot open file \"%s\" for writing!", a_FileName.c_str()); + return; + } + for (int Height = 0; Height < 256; Height++) + { + AString Line; + Line.reserve(2000); + Printf(Line, "%d\t", Height); + for (int Biome = 0; Biome < 256; Biome++) + { + AppendPrintf(Line, "%llu\t", a_Stats[Height][Biome]); + } + Line.append("\n"); + f.Write(Line.c_str(), Line.size()); + } +} + + + + diff --git a/Tools/AnvilStats/SpringStats.h b/Tools/AnvilStats/SpringStats.h index 292c5b82d..43b9f00d5 100644 --- a/Tools/AnvilStats/SpringStats.h +++ b/Tools/AnvilStats/SpringStats.h @@ -1,102 +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); -} ; - - - - + +// 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); +} ; + + + + diff --git a/Tools/AnvilStats/Statistics.cpp b/Tools/AnvilStats/Statistics.cpp index 2f30e158a..b5b3cb176 100644 --- a/Tools/AnvilStats/Statistics.cpp +++ b/Tools/AnvilStats/Statistics.cpp @@ -1,523 +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); - } -} - - - - + +// 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); + } +} + + + + diff --git a/Tools/AnvilStats/Statistics.h b/Tools/AnvilStats/Statistics.h index 2c0a86cc1..53e353f22 100644 --- a/Tools/AnvilStats/Statistics.h +++ b/Tools/AnvilStats/Statistics.h @@ -1,138 +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); -} ; - - - - + +// 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); +} ; + + + + diff --git a/Tools/AnvilStats/Utils.cpp b/Tools/AnvilStats/Utils.cpp index be1f067c0..baa87bd69 100644 --- a/Tools/AnvilStats/Utils.cpp +++ b/Tools/AnvilStats/Utils.cpp @@ -1,291 +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; -} - - - - + +// 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; +} + + + + diff --git a/Tools/AnvilStats/Utils.h b/Tools/AnvilStats/Utils.h index 095abc99e..ab2d7166c 100644 --- a/Tools/AnvilStats/Utils.h +++ b/Tools/AnvilStats/Utils.h @@ -1,61 +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); - - - + +// 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); + + + diff --git a/Tools/AnvilStats/profile_run.cmd b/Tools/AnvilStats/profile_run.cmd index 2a9285614..0edb6f23d 100644 --- a/Tools/AnvilStats/profile_run.cmd +++ b/Tools/AnvilStats/profile_run.cmd @@ -1,70 +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 +@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 diff --git a/Tools/BiomeVisualiser/BiomeCache.cpp b/Tools/BiomeVisualiser/BiomeCache.cpp index 12133b8b6..b3c308422 100644 --- a/Tools/BiomeVisualiser/BiomeCache.cpp +++ b/Tools/BiomeVisualiser/BiomeCache.cpp @@ -1,333 +1,333 @@ - -// BiomeCache.cpp - -// Implements the cBiomeCache class representing a biome source that caches data from the underlying biome source - -#include "Globals.h" -#include "BiomeCache.h" -#include "Timer.h" - - - - - -static int GetNumCores(void) -{ - // Get number of cores by querying the system process affinity mask - 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; -} - - - - - -cBiomeCache::cBiomeCache(void) : - m_Source(NULL), - m_BaseX(-100000), - m_BaseZ(-100000), - m_Available(NULL), - m_IsTerminatingThreads(false) -{ - int NumThreads = GetNumCores(); - NumThreads--; // One core should be left for the system to run on ;) - for (int i = NumThreads; i > 0; i--) - { - cThread * Thread = new cThread(*this); - m_Threads.push_back(Thread); - Thread->Start(); - } -} - - - - -cBiomeCache::~cBiomeCache() -{ - m_IsTerminatingThreads = true; - for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr) - { - m_evtQueued.Set(); - } - for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr) - { - delete *itr; - } - m_Threads.clear(); - - SetSource(NULL); -} - - - - - -cBiomeSource::eAvailability cBiomeCache::GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) -{ - if (m_Source == NULL) - { - return baNever; - } - - // Look up using the cache: - int x = a_ChunkX - m_BaseX; - int z = a_ChunkZ - m_BaseZ; - if ((x < 0) || (x >= m_Width) || (z < 0) || (z >= m_Height)) - { - // Outside the cached region - return baNever; - } - - cCSLock Lock(m_CS); - cItem * Item = m_Available[x + m_Width * z]; - if (Item == NULL) - { - // Item hasn't been processed yet - return baLater; - } - if (Item->m_IsValid) - { - memcpy(a_Biomes, Item->m_Biomes, sizeof(a_Biomes)); - return baNow; - } - - // Item has been processed, but the underlying source refused to give the data to us - return baNever; -} - - - - - -void cBiomeCache::HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) -{ - cTimer Timer("Cache: HintViewArea"); - - if ( - (a_MinChunkX == m_BaseX) && - (a_MaxChunkX == m_BaseX + m_Width - 1) && - (a_MinChunkZ == m_BaseZ) && - (a_MaxChunkZ == m_BaseZ + m_Height - 1) - ) - { - // The same set of parameters, bail out - return; - } - - if (m_Source != NULL) - { - m_Source->HintViewArea(a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ); - } - - int NewWidth = a_MaxChunkX - a_MinChunkX + 1; - int NewHeight = a_MaxChunkZ - a_MinChunkZ + 1; - - // Make a new empty cache table: - pItem * NewAvailable = new pItem[NewWidth * NewHeight]; - for (int i = NewWidth * NewHeight - 1; i >= 0; --i) - { - NewAvailable[i] = NULL; - } - - // Move the common contents of the old table into the new table: - cCSLock Lock(m_CS); - for (int z = 0; z < NewHeight; z++) - { - int OldZ = z + a_MinChunkZ - m_BaseZ; - if ((OldZ < 0) || (OldZ >= m_Height)) - { - continue; - } - for (int x = 0; x < NewWidth; x++) - { - int OldX = x + a_MinChunkX - m_BaseX; - if ((OldX < 0) || (OldX >= m_Width)) - { - continue; - } - NewAvailable[x + NewWidth * z] = m_Available[OldX + m_Width * OldZ]; - m_Available[OldX + m_Width * OldZ] = NULL; - } // for x - } // for z - - // All items that aren't common go into the pool: - for (int idx = 0, z = 0; z < m_Height; z++) - { - for (int x = 0; x < m_Width; ++x, ++idx) - { - if (m_Available[idx] != NULL) - { - m_Pool.push_back(m_Available[idx]); - m_Available[idx] = NULL; - } - } - } - - // Replace the cache table: - delete m_Available; - m_Available = NewAvailable; - m_Width = NewWidth; - m_Height = NewHeight; - m_BaseX = a_MinChunkX; - m_BaseZ = a_MinChunkZ; - - // Remove all items outside the coords: - FilterOutItems(m_Queue, a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ); - - // Queue all items from inside the coords into m_Queue: - for (int z = 0; z < NewHeight; z++) - { - for (int x = 0; x < NewWidth; x++) - { - if (m_Available[x + m_Width * z] != NULL) - { - // Already calculated, skip - continue; - } - - if (m_Pool.empty()) - { - m_Pool.push_back(new cItem(x + a_MinChunkX, z + a_MinChunkZ)); - } - ASSERT(!m_Pool.empty()); - m_Pool.back()->m_ChunkX = x + a_MinChunkX; - m_Pool.back()->m_ChunkZ = z + a_MinChunkZ; - m_Queue.push_back(m_Pool.back()); - m_Pool.pop_back(); - m_evtQueued.Set(); - } // for x - } // for z -} - - - - - -void cBiomeCache::SetSource(cBiomeSource * a_Source) -{ - // TODO: Stop all threads, so that they don't use the source anymore! - - delete m_Source; - m_Source = a_Source; - - // Invalidate cache contents: - cCSLock Lock(m_CS); - m_BaseX = -10000; - m_BaseZ = -10000; - m_Pool.splice(m_Pool.end(), m_Queue); -} - - - - - -void cBiomeCache::FilterOutItems(cItems & a_Items, int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) -{ - for (cItems::iterator itr = a_Items.begin(); itr != a_Items.end();) - { - if ( - ((*itr)->m_ChunkX < a_MinChunkX) || - ((*itr)->m_ChunkX > a_MaxChunkX) || - ((*itr)->m_ChunkX < a_MinChunkX) || - ((*itr)->m_ChunkX > a_MaxChunkX) - ) - { - m_Pool.push_back(*itr); - itr = a_Items.erase(itr); - } - else - { - ++itr; - } - } -} - - - - - -void cBiomeCache::thrProcessQueueItem(void) -{ - cItem * Item = NULL; - { - cCSLock Lock(m_CS); - if (m_Queue.empty()) - { - cCSUnlock Unlock(Lock); - m_evtQueued.Wait(); - } - if (m_IsTerminatingThreads || m_Queue.empty()) - { - // We've been woken up only to die / spurious wakeup - return; - } - Item = m_Queue.back(); - m_Queue.pop_back(); - } - - // Process the item: - Item->m_IsValid = (m_Source->GetBiome(Item->m_ChunkX, Item->m_ChunkZ, Item->m_Biomes) == baNow); - - // Store result: - cCSLock Lock(m_CS); - int x = Item->m_ChunkX - m_BaseX; - int z = Item->m_ChunkZ - m_BaseZ; - if ((x < 0) || (x >= m_Width) || (z < 0) || (z >= m_Height)) - { - // The cache rectangle has changed under our fingers, drop this chunk - return; - } - m_Available[x + m_Width * z] = Item; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBiomeCache::cItem: - -cBiomeCache::cItem::cItem(int a_ChunkX, int a_ChunkZ) : - m_ChunkX(a_ChunkX), - m_ChunkZ(a_ChunkZ) -{ -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBiomeCache::cThread: - -cBiomeCache::cThread::cThread(cBiomeCache & a_Parent) : - super("Biome cache thread"), - m_Parent(a_Parent) -{ -} - - - - - -void cBiomeCache::cThread::Execute(void) -{ - while (!m_ShouldTerminate && !m_Parent.m_IsTerminatingThreads) - { - m_Parent.thrProcessQueueItem(); - } -} - - - - + +// BiomeCache.cpp + +// Implements the cBiomeCache class representing a biome source that caches data from the underlying biome source + +#include "Globals.h" +#include "BiomeCache.h" +#include "Timer.h" + + + + + +static int GetNumCores(void) +{ + // Get number of cores by querying the system process affinity mask + 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; +} + + + + + +cBiomeCache::cBiomeCache(void) : + m_Source(NULL), + m_BaseX(-100000), + m_BaseZ(-100000), + m_Available(NULL), + m_IsTerminatingThreads(false) +{ + int NumThreads = GetNumCores(); + NumThreads--; // One core should be left for the system to run on ;) + for (int i = NumThreads; i > 0; i--) + { + cThread * Thread = new cThread(*this); + m_Threads.push_back(Thread); + Thread->Start(); + } +} + + + + +cBiomeCache::~cBiomeCache() +{ + m_IsTerminatingThreads = true; + for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr) + { + m_evtQueued.Set(); + } + for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr) + { + delete *itr; + } + m_Threads.clear(); + + SetSource(NULL); +} + + + + + +cBiomeSource::eAvailability cBiomeCache::GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) +{ + if (m_Source == NULL) + { + return baNever; + } + + // Look up using the cache: + int x = a_ChunkX - m_BaseX; + int z = a_ChunkZ - m_BaseZ; + if ((x < 0) || (x >= m_Width) || (z < 0) || (z >= m_Height)) + { + // Outside the cached region + return baNever; + } + + cCSLock Lock(m_CS); + cItem * Item = m_Available[x + m_Width * z]; + if (Item == NULL) + { + // Item hasn't been processed yet + return baLater; + } + if (Item->m_IsValid) + { + memcpy(a_Biomes, Item->m_Biomes, sizeof(a_Biomes)); + return baNow; + } + + // Item has been processed, but the underlying source refused to give the data to us + return baNever; +} + + + + + +void cBiomeCache::HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) +{ + cTimer Timer("Cache: HintViewArea"); + + if ( + (a_MinChunkX == m_BaseX) && + (a_MaxChunkX == m_BaseX + m_Width - 1) && + (a_MinChunkZ == m_BaseZ) && + (a_MaxChunkZ == m_BaseZ + m_Height - 1) + ) + { + // The same set of parameters, bail out + return; + } + + if (m_Source != NULL) + { + m_Source->HintViewArea(a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ); + } + + int NewWidth = a_MaxChunkX - a_MinChunkX + 1; + int NewHeight = a_MaxChunkZ - a_MinChunkZ + 1; + + // Make a new empty cache table: + pItem * NewAvailable = new pItem[NewWidth * NewHeight]; + for (int i = NewWidth * NewHeight - 1; i >= 0; --i) + { + NewAvailable[i] = NULL; + } + + // Move the common contents of the old table into the new table: + cCSLock Lock(m_CS); + for (int z = 0; z < NewHeight; z++) + { + int OldZ = z + a_MinChunkZ - m_BaseZ; + if ((OldZ < 0) || (OldZ >= m_Height)) + { + continue; + } + for (int x = 0; x < NewWidth; x++) + { + int OldX = x + a_MinChunkX - m_BaseX; + if ((OldX < 0) || (OldX >= m_Width)) + { + continue; + } + NewAvailable[x + NewWidth * z] = m_Available[OldX + m_Width * OldZ]; + m_Available[OldX + m_Width * OldZ] = NULL; + } // for x + } // for z + + // All items that aren't common go into the pool: + for (int idx = 0, z = 0; z < m_Height; z++) + { + for (int x = 0; x < m_Width; ++x, ++idx) + { + if (m_Available[idx] != NULL) + { + m_Pool.push_back(m_Available[idx]); + m_Available[idx] = NULL; + } + } + } + + // Replace the cache table: + delete m_Available; + m_Available = NewAvailable; + m_Width = NewWidth; + m_Height = NewHeight; + m_BaseX = a_MinChunkX; + m_BaseZ = a_MinChunkZ; + + // Remove all items outside the coords: + FilterOutItems(m_Queue, a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ); + + // Queue all items from inside the coords into m_Queue: + for (int z = 0; z < NewHeight; z++) + { + for (int x = 0; x < NewWidth; x++) + { + if (m_Available[x + m_Width * z] != NULL) + { + // Already calculated, skip + continue; + } + + if (m_Pool.empty()) + { + m_Pool.push_back(new cItem(x + a_MinChunkX, z + a_MinChunkZ)); + } + ASSERT(!m_Pool.empty()); + m_Pool.back()->m_ChunkX = x + a_MinChunkX; + m_Pool.back()->m_ChunkZ = z + a_MinChunkZ; + m_Queue.push_back(m_Pool.back()); + m_Pool.pop_back(); + m_evtQueued.Set(); + } // for x + } // for z +} + + + + + +void cBiomeCache::SetSource(cBiomeSource * a_Source) +{ + // TODO: Stop all threads, so that they don't use the source anymore! + + delete m_Source; + m_Source = a_Source; + + // Invalidate cache contents: + cCSLock Lock(m_CS); + m_BaseX = -10000; + m_BaseZ = -10000; + m_Pool.splice(m_Pool.end(), m_Queue); +} + + + + + +void cBiomeCache::FilterOutItems(cItems & a_Items, int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) +{ + for (cItems::iterator itr = a_Items.begin(); itr != a_Items.end();) + { + if ( + ((*itr)->m_ChunkX < a_MinChunkX) || + ((*itr)->m_ChunkX > a_MaxChunkX) || + ((*itr)->m_ChunkX < a_MinChunkX) || + ((*itr)->m_ChunkX > a_MaxChunkX) + ) + { + m_Pool.push_back(*itr); + itr = a_Items.erase(itr); + } + else + { + ++itr; + } + } +} + + + + + +void cBiomeCache::thrProcessQueueItem(void) +{ + cItem * Item = NULL; + { + cCSLock Lock(m_CS); + if (m_Queue.empty()) + { + cCSUnlock Unlock(Lock); + m_evtQueued.Wait(); + } + if (m_IsTerminatingThreads || m_Queue.empty()) + { + // We've been woken up only to die / spurious wakeup + return; + } + Item = m_Queue.back(); + m_Queue.pop_back(); + } + + // Process the item: + Item->m_IsValid = (m_Source->GetBiome(Item->m_ChunkX, Item->m_ChunkZ, Item->m_Biomes) == baNow); + + // Store result: + cCSLock Lock(m_CS); + int x = Item->m_ChunkX - m_BaseX; + int z = Item->m_ChunkZ - m_BaseZ; + if ((x < 0) || (x >= m_Width) || (z < 0) || (z >= m_Height)) + { + // The cache rectangle has changed under our fingers, drop this chunk + return; + } + m_Available[x + m_Width * z] = Item; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBiomeCache::cItem: + +cBiomeCache::cItem::cItem(int a_ChunkX, int a_ChunkZ) : + m_ChunkX(a_ChunkX), + m_ChunkZ(a_ChunkZ) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cBiomeCache::cThread: + +cBiomeCache::cThread::cThread(cBiomeCache & a_Parent) : + super("Biome cache thread"), + m_Parent(a_Parent) +{ +} + + + + + +void cBiomeCache::cThread::Execute(void) +{ + while (!m_ShouldTerminate && !m_Parent.m_IsTerminatingThreads) + { + m_Parent.thrProcessQueueItem(); + } +} + + + + diff --git a/Tools/BiomeVisualiser/BiomeCache.h b/Tools/BiomeVisualiser/BiomeCache.h index 86602a19d..bf1503d4c 100644 --- a/Tools/BiomeVisualiser/BiomeCache.h +++ b/Tools/BiomeVisualiser/BiomeCache.h @@ -1,96 +1,96 @@ - -// BiomeCache.h - -// Declares the cBiomeCache class representing a biome source that caches data from the underlying biome source - -/* -This cache works a bit differently than regular caches. -It first receives the hint of area that it will need to provide. -The Cache uses several threads to request biomes from the underlying source to fill that area. -While the area is being filled, requests for biomes may already come, such requests are answered with baLater if no data yet. -*/ - -#pragma once - - - - - -#include "BiomeSource.h" -#include "../source/OSSupport/IsThread.h" - - - - - -class cBiomeCache : - public cBiomeSource -{ -public: - cBiomeCache(void); - ~cBiomeCache(); - - // cBiomeSource overrides: - virtual eAvailability GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) override; - virtual void HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) override; - - void SetSource(cBiomeSource * a_Source); // Takes ownership of the source ptr - -protected: - class cItem - { - public: - cItem(int a_ChunkX, int a_ChunkZ); - - int m_ChunkX; - int m_ChunkZ; - bool m_IsValid; - cChunkDef::BiomeMap m_Biomes; - } ; - - typedef cItem * pItem; - typedef std::list cItems; - - class cThread : - public cIsThread - { - typedef cIsThread super; - - public: - cThread(cBiomeCache & a_Parent); - - // cIsThread overrides: - virtual void Execute(void) override; - - protected: - cBiomeCache & m_Parent; - } ; - - typedef std::list cThreads; - - cBiomeSource * m_Source; - - cCriticalSection m_CS; - int m_BaseX; ///< MinChunkX for the m_Available rectangle - int m_BaseZ; ///< MinChunkZ for the m_Available rectangle - int m_Width; ///< Width of the m_Available rectangle - int m_Height; ///< Height of the m_Available rectangle - pItem * m_Available; ///< Items that have already been processed (baNow or baNever), [x + m_Width * z] - cItems m_Queue; ///< Items that are queued for processing (baLater) - cItems m_Pool; ///< Items that are not needed anymore, can be reused for other coords - - cEvent m_evtQueued; // Triggerred when an item is added to m_Queue - - cThreads m_Threads; // Threads that update the cache. - bool m_IsTerminatingThreads; // Set to true to indicate to all threads that they should exit - - /// Removes from a_Items all items that are outside of the given coords, moves those into m_Pool - void FilterOutItems(cItems & a_Items, int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ); - - /// Processes one item from m_Queue into m_Available. Blocks if m_Queue is empty; respects m_IsTerminatingThreads - void thrProcessQueueItem(void); -} ; - - - - + +// BiomeCache.h + +// Declares the cBiomeCache class representing a biome source that caches data from the underlying biome source + +/* +This cache works a bit differently than regular caches. +It first receives the hint of area that it will need to provide. +The Cache uses several threads to request biomes from the underlying source to fill that area. +While the area is being filled, requests for biomes may already come, such requests are answered with baLater if no data yet. +*/ + +#pragma once + + + + + +#include "BiomeSource.h" +#include "../source/OSSupport/IsThread.h" + + + + + +class cBiomeCache : + public cBiomeSource +{ +public: + cBiomeCache(void); + ~cBiomeCache(); + + // cBiomeSource overrides: + virtual eAvailability GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) override; + virtual void HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) override; + + void SetSource(cBiomeSource * a_Source); // Takes ownership of the source ptr + +protected: + class cItem + { + public: + cItem(int a_ChunkX, int a_ChunkZ); + + int m_ChunkX; + int m_ChunkZ; + bool m_IsValid; + cChunkDef::BiomeMap m_Biomes; + } ; + + typedef cItem * pItem; + typedef std::list cItems; + + class cThread : + public cIsThread + { + typedef cIsThread super; + + public: + cThread(cBiomeCache & a_Parent); + + // cIsThread overrides: + virtual void Execute(void) override; + + protected: + cBiomeCache & m_Parent; + } ; + + typedef std::list cThreads; + + cBiomeSource * m_Source; + + cCriticalSection m_CS; + int m_BaseX; ///< MinChunkX for the m_Available rectangle + int m_BaseZ; ///< MinChunkZ for the m_Available rectangle + int m_Width; ///< Width of the m_Available rectangle + int m_Height; ///< Height of the m_Available rectangle + pItem * m_Available; ///< Items that have already been processed (baNow or baNever), [x + m_Width * z] + cItems m_Queue; ///< Items that are queued for processing (baLater) + cItems m_Pool; ///< Items that are not needed anymore, can be reused for other coords + + cEvent m_evtQueued; // Triggerred when an item is added to m_Queue + + cThreads m_Threads; // Threads that update the cache. + bool m_IsTerminatingThreads; // Set to true to indicate to all threads that they should exit + + /// Removes from a_Items all items that are outside of the given coords, moves those into m_Pool + void FilterOutItems(cItems & a_Items, int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ); + + /// Processes one item from m_Queue into m_Available. Blocks if m_Queue is empty; respects m_IsTerminatingThreads + void thrProcessQueueItem(void); +} ; + + + + diff --git a/Tools/BiomeVisualiser/BiomeRenderer.cpp b/Tools/BiomeVisualiser/BiomeRenderer.cpp index 7c4302d70..98548179c 100644 --- a/Tools/BiomeVisualiser/BiomeRenderer.cpp +++ b/Tools/BiomeVisualiser/BiomeRenderer.cpp @@ -1,147 +1,147 @@ - -// BiomeRenderer.cpp - -// Implements the cBiomeRenderer class representing the rendering engine - -#include "Globals.h" -#include "BiomeRenderer.h" -#include "Pixmap.h" -#include "Timer.h" - - - - - -cBiomeRenderer::cBiomeRenderer(void) : - m_OriginX(160), - m_OriginY(160), - m_Zoom(1) -{ -} - - - - -void cBiomeRenderer::SetSource(cBiomeSource * a_Source) -{ - m_Cache.SetSource(a_Source); -} - - - - - -bool cBiomeRenderer::Render(cPixmap & a_Pixmap) -{ - cTimer Timer("cBiomeRenderer::Render"); - - int Wid = a_Pixmap.GetWidth(); - int Hei = a_Pixmap.GetHeight(); - - // Hint the approximate view area to the biome source so that it can adjust its caches: - int MinBlockX = ( - m_OriginX) / m_Zoom; - int MaxBlockX = (Wid - m_OriginX) / m_Zoom; - int MinBlockZ = ( - m_OriginY) / m_Zoom; - int MaxBlockZ = (Hei - m_OriginY) / m_Zoom; - m_Cache.HintViewArea(MinBlockX / 16 - 1, MaxBlockX / 16 + 1, MinBlockZ / 16 - 1, MaxBlockZ / 16 + 1); - - // Hold one current chunk of biome data: - int CurChunkX = -10000; - int CurChunkZ = -10000; - cChunkDef::BiomeMap CurBiomes; - - bool res = false; - - for (int y = 0; y < Hei; y++) - { - int BlockZ = (y - m_OriginY) / m_Zoom; - int ChunkZ = (BlockZ >= 0) ? (BlockZ / 16) : ((BlockZ + 1) / 16 - 1); - int RelZ = BlockZ - ChunkZ * 16; - for (int x = 0; x < Wid; x++) - { - int BlockX = (x - m_OriginX) / m_Zoom; - int ChunkX = (BlockX >= 0) ? (BlockX / 16) : ((BlockX + 1) / 16 - 1); - int RelX = BlockX - ChunkX * 16; - if ((ChunkZ != CurChunkZ) || (ChunkX != CurChunkX)) - { - CurChunkX = ChunkX; - CurChunkZ = ChunkZ; - switch (m_Cache.GetBiome(CurChunkX, CurChunkZ, CurBiomes)) - { - case cBiomeSource::baLater: - { - res = true; - // fallthrough: - } - case cBiomeSource::baNever: - { - for (int i = 0; i < ARRAYCOUNT(CurBiomes); i++) - { - CurBiomes[i] = (EMCSBiome)-1; - } - break; - } - } // switch (Biome availability) - } - EMCSBiome Biome = cChunkDef::GetBiome(CurBiomes, RelX, RelZ); - a_Pixmap.SetPixel(x, y, GetBiomeColor(Biome)); - } // for x - } // for y - return res; -} - - - - - -int cBiomeRenderer::GetBiomeColor(EMCSBiome a_Biome) -{ - if ((a_Biome < 0) || (a_Biome > biMaxBiome)) - { - return 0xcfcfcf; // LtGray for unknown biomes - } - - static int BiomeColor[] = - { - // RGB: - 0x0000ff, /* Ocean */ - 0x00cf3f, /* Plains */ - 0xffff00, /* Desert */ - 0x7f7f7f, /* Extreme Hills */ - 0x00cf00, /* Forest */ - 0x007f3f, /* Taiga */ - 0x3f7f00, /* Swampland */ - 0x003fff, /* River */ - 0x7f0000, /* Hell */ - 0x007fff, /* Sky */ - 0x3f3fff, /* Frozen Ocean */ - 0x3f3fff, /* Frozen River */ - 0x7fffcf, /* Ice Plains */ - 0x3fcf7f, /* Ice Mountains */ - 0xcf00cf, /* Mushroom Island */ - 0x7f00ff, /* Mushroom Island Shore */ - 0xffff3f, /* Beach */ - 0xcfcf00, /* Desert Hills */ - 0x00cf3f, /* Forest Hills */ - 0x006f1f, /* Taiga Hills */ - 0x7f8f7f, /* Extreme Hills Edge */ - 0x004f00, /* Jungle */ - 0x003f00, /* Jungle Hills */ - } ; - - return BiomeColor[a_Biome]; -} - - - - - -void cBiomeRenderer::MoveViewBy(int a_OffsX, int a_OffsY) -{ - m_OriginX += a_OffsX; - m_OriginY += a_OffsY; -} - - - - + +// BiomeRenderer.cpp + +// Implements the cBiomeRenderer class representing the rendering engine + +#include "Globals.h" +#include "BiomeRenderer.h" +#include "Pixmap.h" +#include "Timer.h" + + + + + +cBiomeRenderer::cBiomeRenderer(void) : + m_OriginX(160), + m_OriginY(160), + m_Zoom(1) +{ +} + + + + +void cBiomeRenderer::SetSource(cBiomeSource * a_Source) +{ + m_Cache.SetSource(a_Source); +} + + + + + +bool cBiomeRenderer::Render(cPixmap & a_Pixmap) +{ + cTimer Timer("cBiomeRenderer::Render"); + + int Wid = a_Pixmap.GetWidth(); + int Hei = a_Pixmap.GetHeight(); + + // Hint the approximate view area to the biome source so that it can adjust its caches: + int MinBlockX = ( - m_OriginX) / m_Zoom; + int MaxBlockX = (Wid - m_OriginX) / m_Zoom; + int MinBlockZ = ( - m_OriginY) / m_Zoom; + int MaxBlockZ = (Hei - m_OriginY) / m_Zoom; + m_Cache.HintViewArea(MinBlockX / 16 - 1, MaxBlockX / 16 + 1, MinBlockZ / 16 - 1, MaxBlockZ / 16 + 1); + + // Hold one current chunk of biome data: + int CurChunkX = -10000; + int CurChunkZ = -10000; + cChunkDef::BiomeMap CurBiomes; + + bool res = false; + + for (int y = 0; y < Hei; y++) + { + int BlockZ = (y - m_OriginY) / m_Zoom; + int ChunkZ = (BlockZ >= 0) ? (BlockZ / 16) : ((BlockZ + 1) / 16 - 1); + int RelZ = BlockZ - ChunkZ * 16; + for (int x = 0; x < Wid; x++) + { + int BlockX = (x - m_OriginX) / m_Zoom; + int ChunkX = (BlockX >= 0) ? (BlockX / 16) : ((BlockX + 1) / 16 - 1); + int RelX = BlockX - ChunkX * 16; + if ((ChunkZ != CurChunkZ) || (ChunkX != CurChunkX)) + { + CurChunkX = ChunkX; + CurChunkZ = ChunkZ; + switch (m_Cache.GetBiome(CurChunkX, CurChunkZ, CurBiomes)) + { + case cBiomeSource::baLater: + { + res = true; + // fallthrough: + } + case cBiomeSource::baNever: + { + for (int i = 0; i < ARRAYCOUNT(CurBiomes); i++) + { + CurBiomes[i] = (EMCSBiome)-1; + } + break; + } + } // switch (Biome availability) + } + EMCSBiome Biome = cChunkDef::GetBiome(CurBiomes, RelX, RelZ); + a_Pixmap.SetPixel(x, y, GetBiomeColor(Biome)); + } // for x + } // for y + return res; +} + + + + + +int cBiomeRenderer::GetBiomeColor(EMCSBiome a_Biome) +{ + if ((a_Biome < 0) || (a_Biome > biMaxBiome)) + { + return 0xcfcfcf; // LtGray for unknown biomes + } + + static int BiomeColor[] = + { + // RGB: + 0x0000ff, /* Ocean */ + 0x00cf3f, /* Plains */ + 0xffff00, /* Desert */ + 0x7f7f7f, /* Extreme Hills */ + 0x00cf00, /* Forest */ + 0x007f3f, /* Taiga */ + 0x3f7f00, /* Swampland */ + 0x003fff, /* River */ + 0x7f0000, /* Hell */ + 0x007fff, /* Sky */ + 0x3f3fff, /* Frozen Ocean */ + 0x3f3fff, /* Frozen River */ + 0x7fffcf, /* Ice Plains */ + 0x3fcf7f, /* Ice Mountains */ + 0xcf00cf, /* Mushroom Island */ + 0x7f00ff, /* Mushroom Island Shore */ + 0xffff3f, /* Beach */ + 0xcfcf00, /* Desert Hills */ + 0x00cf3f, /* Forest Hills */ + 0x006f1f, /* Taiga Hills */ + 0x7f8f7f, /* Extreme Hills Edge */ + 0x004f00, /* Jungle */ + 0x003f00, /* Jungle Hills */ + } ; + + return BiomeColor[a_Biome]; +} + + + + + +void cBiomeRenderer::MoveViewBy(int a_OffsX, int a_OffsY) +{ + m_OriginX += a_OffsX; + m_OriginY += a_OffsY; +} + + + + diff --git a/Tools/BiomeVisualiser/BiomeRenderer.h b/Tools/BiomeVisualiser/BiomeRenderer.h index 369c0f114..2590e0406 100644 --- a/Tools/BiomeVisualiser/BiomeRenderer.h +++ b/Tools/BiomeVisualiser/BiomeRenderer.h @@ -1,50 +1,50 @@ - -// BiomeRenderer.h - -// Declares the cBiomeRenderer class representing the rendering engine - - - - - -#pragma once - -#include "BiomeCache.h" - - - - - -// fwd: Pixmap.h -class cPixmap; - - - - - -class cBiomeRenderer -{ -public: - cBiomeRenderer(void); - - void SetSource(cBiomeSource * a_Source); // Takes ownership of the source - - /// Renders the biomes into the given pixmap. Returns true if some biome data was missing and can be retrieved later - bool Render(cPixmap & a_Pixmap); - - /// Returns the RGB color value for the specified biome - int GetBiomeColor(EMCSBiome a_Biome); - - void MoveViewBy(int a_OffsX, int a_OffsY); - -protected: - cBiomeCache m_Cache; - - int m_OriginX; - int m_OriginY; - int m_Zoom; -} ; - - - - + +// BiomeRenderer.h + +// Declares the cBiomeRenderer class representing the rendering engine + + + + + +#pragma once + +#include "BiomeCache.h" + + + + + +// fwd: Pixmap.h +class cPixmap; + + + + + +class cBiomeRenderer +{ +public: + cBiomeRenderer(void); + + void SetSource(cBiomeSource * a_Source); // Takes ownership of the source + + /// Renders the biomes into the given pixmap. Returns true if some biome data was missing and can be retrieved later + bool Render(cPixmap & a_Pixmap); + + /// Returns the RGB color value for the specified biome + int GetBiomeColor(EMCSBiome a_Biome); + + void MoveViewBy(int a_OffsX, int a_OffsY); + +protected: + cBiomeCache m_Cache; + + int m_OriginX; + int m_OriginY; + int m_Zoom; +} ; + + + + diff --git a/Tools/BiomeVisualiser/BiomeSource.h b/Tools/BiomeVisualiser/BiomeSource.h index e09242d04..4a5153457 100644 --- a/Tools/BiomeVisualiser/BiomeSource.h +++ b/Tools/BiomeVisualiser/BiomeSource.h @@ -1,37 +1,37 @@ - -// BiomeSource.h - -// Declares the cBiomeSource abstract class used as an interface for getting biomes from any source - -#pragma once - - - - - -#include "ChunkDef.h" - - - - - -class cBiomeSource abstract -{ -public: - enum eAvailability - { - baNow, // Data returned now - baLater, // Data not returned, but will be available later, try again after a while - baNever, // Data not returned, will not be available at all - } ; - - /// Fills a_Biomes with the biomes for the chunk specified - virtual eAvailability GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) = 0; - - /// Used to inform the source about the view area that will be queried in the near future. - virtual void HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) = 0; -} ; - - - - + +// BiomeSource.h + +// Declares the cBiomeSource abstract class used as an interface for getting biomes from any source + +#pragma once + + + + + +#include "ChunkDef.h" + + + + + +class cBiomeSource abstract +{ +public: + enum eAvailability + { + baNow, // Data returned now + baLater, // Data not returned, but will be available later, try again after a while + baNever, // Data not returned, will not be available at all + } ; + + /// Fills a_Biomes with the biomes for the chunk specified + virtual eAvailability GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) = 0; + + /// Used to inform the source about the view area that will be queried in the near future. + virtual void HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) = 0; +} ; + + + + diff --git a/Tools/BiomeVisualiser/BiomeViewWnd.cpp b/Tools/BiomeVisualiser/BiomeViewWnd.cpp index 503b94f0c..3dd1bb4e0 100644 --- a/Tools/BiomeVisualiser/BiomeViewWnd.cpp +++ b/Tools/BiomeVisualiser/BiomeViewWnd.cpp @@ -1,190 +1,190 @@ - -// BiomeViewWnd.cpp - -// Implements the cBiomeViewWnd class representing the window that displays biomes - -#include "Globals.h" -#include "BiomeViewWnd.h" -#include "BiomeCache.h" -#include "GeneratorBiomeSource.h" -#include "../iniFile/iniFile.h" - - - - - -const int TIMER_RERENDER = 1200; - - - - - -cBiomeViewWnd::cBiomeViewWnd(void) : - m_Wnd(NULL), - m_Thunk(&cBiomeViewWnd::WndProc, this), - m_IsLButtonDown(false) -{ -} - - - - - -bool cBiomeViewWnd::Create(HWND a_ParentWnd, LPCTSTR a_Title) -{ - ASSERT(m_Wnd == NULL); - - // Create a regular STATIC window, then override its window procedure with our own. No need for obnoxious RegisterWindowClass() stuff. - m_Wnd = CreateWindow("STATIC", a_Title, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 400, 300, a_ParentWnd, NULL, GetModuleHandle(NULL), NULL); - if (m_Wnd == NULL) - { - LOGERROR("Cannot create main window: %d", GetLastError()); - return false; - } - SetWindowLongPtr(m_Wnd, GWLP_WNDPROC, m_Thunk); - - cIniFile IniFile; - cBiomeGen * BioGen = new cBioGenMultiStepMap(2); - BioGen->Initialize(IniFile); - m_Renderer.SetSource(new cGeneratorBiomeSource(BioGen)); - - return true; -} - - - - - - -LRESULT cBiomeViewWnd::WndProc(HWND a_Wnd, UINT a_Msg, WPARAM wParam, LPARAM lParam) -{ - switch (a_Msg) - { - case WM_CLOSE: return OnClose(); - case WM_COMMAND: return OnCommand(wParam, lParam); - case WM_LBUTTONDOWN: return OnLButtonDown(wParam, lParam); - case WM_LBUTTONUP: return OnLButtonUp (wParam, lParam); - case WM_MOUSEMOVE: return OnMouseMove (wParam, lParam); - case WM_PAINT: return OnPaint(); - case WM_TIMER: return OnTimer(wParam); - } - return ::DefWindowProc(a_Wnd, a_Msg, wParam, lParam); -} - - - - - -LRESULT cBiomeViewWnd::OnClose(void) -{ - PostQuitMessage(0); - return 0; -} - - - - - -LRESULT cBiomeViewWnd::OnCommand(WPARAM wParam, LPARAM lParam) -{ - // TODO: Handle menu commands, when we get menu - return 0; -} - - - - - -LRESULT cBiomeViewWnd::OnLButtonDown(WPARAM wParam, LPARAM lParam) -{ - m_IsLButtonDown = true; - GetCursorPos(&m_MouseDown); - return 0; -} - - - - - -LRESULT cBiomeViewWnd::OnMouseMove(WPARAM wParam, LPARAM lParam) -{ - if (!m_IsLButtonDown) - { - return 0; - } - POINT pnt; - GetCursorPos(&pnt); - m_Renderer.MoveViewBy(pnt.x - m_MouseDown.x, pnt.y - m_MouseDown.y); - if (m_Renderer.Render(m_Pixmap)) - { - SetTimer(m_Wnd, TIMER_RERENDER, 200, NULL); - } - m_MouseDown = pnt; - InvalidateRect(m_Wnd, NULL, FALSE); - return 0; -} - - - - - -LRESULT cBiomeViewWnd::OnLButtonUp(WPARAM wParam, LPARAM lParam) -{ - OnMouseMove(wParam, lParam); // Last movement - if the mouse move hasn't been reported due to speed - m_IsLButtonDown = false; - InvalidateRect(m_Wnd, NULL, FALSE); - return 0; -} - - - - - -LRESULT cBiomeViewWnd::OnPaint(void) -{ - PAINTSTRUCT ps; - HDC DC = BeginPaint(m_Wnd, &ps); - - RECT rc; - GetClientRect(m_Wnd, &rc); - int Wid = rc.right - rc.left; - int Hei = rc.bottom - rc.top; - if ((m_Pixmap.GetWidth() != Wid) || (m_Pixmap.GetHeight() != Hei)) - { - m_Pixmap.SetSize(Wid, Hei); - if (m_Renderer.Render(m_Pixmap)) - { - SetTimer(m_Wnd, TIMER_RERENDER, 200, NULL); - } - } - - m_Pixmap.DrawToDC(DC, 0, 0); - - EndPaint(m_Wnd, &ps); - return 0; -} - - - - - -LRESULT cBiomeViewWnd::OnTimer(WPARAM wParam) -{ - switch (wParam) - { - case TIMER_RERENDER: - { - if (!m_Renderer.Render(m_Pixmap)) - { - KillTimer(m_Wnd, TIMER_RERENDER); - } - InvalidateRect(m_Wnd, NULL, FALSE); - break; - } - } - return 0; -} - - - - + +// BiomeViewWnd.cpp + +// Implements the cBiomeViewWnd class representing the window that displays biomes + +#include "Globals.h" +#include "BiomeViewWnd.h" +#include "BiomeCache.h" +#include "GeneratorBiomeSource.h" +#include "../iniFile/iniFile.h" + + + + + +const int TIMER_RERENDER = 1200; + + + + + +cBiomeViewWnd::cBiomeViewWnd(void) : + m_Wnd(NULL), + m_Thunk(&cBiomeViewWnd::WndProc, this), + m_IsLButtonDown(false) +{ +} + + + + + +bool cBiomeViewWnd::Create(HWND a_ParentWnd, LPCTSTR a_Title) +{ + ASSERT(m_Wnd == NULL); + + // Create a regular STATIC window, then override its window procedure with our own. No need for obnoxious RegisterWindowClass() stuff. + m_Wnd = CreateWindow("STATIC", a_Title, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 400, 300, a_ParentWnd, NULL, GetModuleHandle(NULL), NULL); + if (m_Wnd == NULL) + { + LOGERROR("Cannot create main window: %d", GetLastError()); + return false; + } + SetWindowLongPtr(m_Wnd, GWLP_WNDPROC, m_Thunk); + + cIniFile IniFile; + cBiomeGen * BioGen = new cBioGenMultiStepMap(2); + BioGen->Initialize(IniFile); + m_Renderer.SetSource(new cGeneratorBiomeSource(BioGen)); + + return true; +} + + + + + + +LRESULT cBiomeViewWnd::WndProc(HWND a_Wnd, UINT a_Msg, WPARAM wParam, LPARAM lParam) +{ + switch (a_Msg) + { + case WM_CLOSE: return OnClose(); + case WM_COMMAND: return OnCommand(wParam, lParam); + case WM_LBUTTONDOWN: return OnLButtonDown(wParam, lParam); + case WM_LBUTTONUP: return OnLButtonUp (wParam, lParam); + case WM_MOUSEMOVE: return OnMouseMove (wParam, lParam); + case WM_PAINT: return OnPaint(); + case WM_TIMER: return OnTimer(wParam); + } + return ::DefWindowProc(a_Wnd, a_Msg, wParam, lParam); +} + + + + + +LRESULT cBiomeViewWnd::OnClose(void) +{ + PostQuitMessage(0); + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnCommand(WPARAM wParam, LPARAM lParam) +{ + // TODO: Handle menu commands, when we get menu + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnLButtonDown(WPARAM wParam, LPARAM lParam) +{ + m_IsLButtonDown = true; + GetCursorPos(&m_MouseDown); + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnMouseMove(WPARAM wParam, LPARAM lParam) +{ + if (!m_IsLButtonDown) + { + return 0; + } + POINT pnt; + GetCursorPos(&pnt); + m_Renderer.MoveViewBy(pnt.x - m_MouseDown.x, pnt.y - m_MouseDown.y); + if (m_Renderer.Render(m_Pixmap)) + { + SetTimer(m_Wnd, TIMER_RERENDER, 200, NULL); + } + m_MouseDown = pnt; + InvalidateRect(m_Wnd, NULL, FALSE); + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnLButtonUp(WPARAM wParam, LPARAM lParam) +{ + OnMouseMove(wParam, lParam); // Last movement - if the mouse move hasn't been reported due to speed + m_IsLButtonDown = false; + InvalidateRect(m_Wnd, NULL, FALSE); + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnPaint(void) +{ + PAINTSTRUCT ps; + HDC DC = BeginPaint(m_Wnd, &ps); + + RECT rc; + GetClientRect(m_Wnd, &rc); + int Wid = rc.right - rc.left; + int Hei = rc.bottom - rc.top; + if ((m_Pixmap.GetWidth() != Wid) || (m_Pixmap.GetHeight() != Hei)) + { + m_Pixmap.SetSize(Wid, Hei); + if (m_Renderer.Render(m_Pixmap)) + { + SetTimer(m_Wnd, TIMER_RERENDER, 200, NULL); + } + } + + m_Pixmap.DrawToDC(DC, 0, 0); + + EndPaint(m_Wnd, &ps); + return 0; +} + + + + + +LRESULT cBiomeViewWnd::OnTimer(WPARAM wParam) +{ + switch (wParam) + { + case TIMER_RERENDER: + { + if (!m_Renderer.Render(m_Pixmap)) + { + KillTimer(m_Wnd, TIMER_RERENDER); + } + InvalidateRect(m_Wnd, NULL, FALSE); + break; + } + } + return 0; +} + + + + diff --git a/Tools/BiomeVisualiser/BiomeViewWnd.h b/Tools/BiomeVisualiser/BiomeViewWnd.h index d8ad8d182..88e808ab3 100644 --- a/Tools/BiomeVisualiser/BiomeViewWnd.h +++ b/Tools/BiomeVisualiser/BiomeViewWnd.h @@ -1,46 +1,46 @@ - -// BiomeViewWnd.h - -// Declares the cBiomeViewWnd class representing the window that displays biomes - -#include "WndProcThunk.h" -#include "BiomeRenderer.h" -#include "BiomeCache.h" -#include "Pixmap.h" - - - - - -class cBiomeViewWnd -{ -public: - cBiomeViewWnd(void); - - bool Create(HWND a_ParentWnd, LPCTSTR a_Title); - -protected: - HWND m_Wnd; - CWndProcThunk m_Thunk; - - cBiomeRenderer m_Renderer; - cPixmap m_Pixmap; - - bool m_IsLButtonDown; - POINT m_MouseDown; - - LRESULT WndProc(HWND a_Wnd, UINT a_Msg, WPARAM wParam, LPARAM lParam); - - // Message handlers: - LRESULT OnClose (void); - LRESULT OnCommand (WPARAM wParam, LPARAM lParam); - LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam); - LRESULT OnMouseMove (WPARAM wParam, LPARAM lParam); - LRESULT OnLButtonUp (WPARAM wParam, LPARAM lParam); - LRESULT OnPaint (void); - LRESULT OnTimer (WPARAM wParam); -} ; - - - - + +// BiomeViewWnd.h + +// Declares the cBiomeViewWnd class representing the window that displays biomes + +#include "WndProcThunk.h" +#include "BiomeRenderer.h" +#include "BiomeCache.h" +#include "Pixmap.h" + + + + + +class cBiomeViewWnd +{ +public: + cBiomeViewWnd(void); + + bool Create(HWND a_ParentWnd, LPCTSTR a_Title); + +protected: + HWND m_Wnd; + CWndProcThunk m_Thunk; + + cBiomeRenderer m_Renderer; + cPixmap m_Pixmap; + + bool m_IsLButtonDown; + POINT m_MouseDown; + + LRESULT WndProc(HWND a_Wnd, UINT a_Msg, WPARAM wParam, LPARAM lParam); + + // Message handlers: + LRESULT OnClose (void); + LRESULT OnCommand (WPARAM wParam, LPARAM lParam); + LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam); + LRESULT OnMouseMove (WPARAM wParam, LPARAM lParam); + LRESULT OnLButtonUp (WPARAM wParam, LPARAM lParam); + LRESULT OnPaint (void); + LRESULT OnTimer (WPARAM wParam); +} ; + + + + diff --git a/Tools/BiomeVisualiser/BiomeVisualiser.cpp b/Tools/BiomeVisualiser/BiomeVisualiser.cpp index dc1d490e8..e1d379f83 100644 --- a/Tools/BiomeVisualiser/BiomeVisualiser.cpp +++ b/Tools/BiomeVisualiser/BiomeVisualiser.cpp @@ -1,52 +1,52 @@ - -// BiomeVisualiser.cpp - -// Implements the cBiomeVisualiser class representing the entire app. Also implements the WinMain() entrypoint - -#include "Globals.h" -#include "time.h" -#include "BiomeVisualiser.h" - - - - - -int WINAPI WinMain(HINSTANCE a_Instance, HINSTANCE a_PrevInstance, LPSTR a_CmdLine, int a_ShowCmd) -{ - cBiomeVisualiser App; - return App.Run(); -} - - - - - -cBiomeVisualiser::cBiomeVisualiser(void) - // : m_Logger(Printf("BiomeVisualiser_%08x", time(NULL))) -{ -} - - - - - -int cBiomeVisualiser::Run(void) -{ - if (!m_MainWnd.Create(GetDesktopWindow(), TEXT("BiomeVisualiser"))) - { - LOGERROR("Cannot create main window: %d", GetLastError()); - return 1; - } - - MSG msg; - while (GetMessage(&msg, NULL, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } // while (GetMessage) - return msg.lParam; -} - - - - + +// BiomeVisualiser.cpp + +// Implements the cBiomeVisualiser class representing the entire app. Also implements the WinMain() entrypoint + +#include "Globals.h" +#include "time.h" +#include "BiomeVisualiser.h" + + + + + +int WINAPI WinMain(HINSTANCE a_Instance, HINSTANCE a_PrevInstance, LPSTR a_CmdLine, int a_ShowCmd) +{ + cBiomeVisualiser App; + return App.Run(); +} + + + + + +cBiomeVisualiser::cBiomeVisualiser(void) + // : m_Logger(Printf("BiomeVisualiser_%08x", time(NULL))) +{ +} + + + + + +int cBiomeVisualiser::Run(void) +{ + if (!m_MainWnd.Create(GetDesktopWindow(), TEXT("BiomeVisualiser"))) + { + LOGERROR("Cannot create main window: %d", GetLastError()); + return 1; + } + + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } // while (GetMessage) + return msg.lParam; +} + + + + diff --git a/Tools/BiomeVisualiser/BiomeVisualiser.h b/Tools/BiomeVisualiser/BiomeVisualiser.h index 4788bb7ea..3fa90646b 100644 --- a/Tools/BiomeVisualiser/BiomeVisualiser.h +++ b/Tools/BiomeVisualiser/BiomeVisualiser.h @@ -1,31 +1,31 @@ - -// BiomeVisualiser.h - -// Declares the cBiomeVisualiser class representing the entire application - - - - - -#include "BiomeViewWnd.h" - - - - - -class cBiomeVisualiser -{ -public: - cBiomeVisualiser(void); - - int Run(void); - -protected: - cBiomeViewWnd m_MainWnd; - - cMCLogger m_Logger; -} ; - - - - + +// BiomeVisualiser.h + +// Declares the cBiomeVisualiser class representing the entire application + + + + + +#include "BiomeViewWnd.h" + + + + + +class cBiomeVisualiser +{ +public: + cBiomeVisualiser(void); + + int Run(void); + +protected: + cBiomeViewWnd m_MainWnd; + + cMCLogger m_Logger; +} ; + + + + diff --git a/Tools/BiomeVisualiser/BiomeVisualiser.sln b/Tools/BiomeVisualiser/BiomeVisualiser.sln index b10d65f2f..bdfb586b1 100644 --- a/Tools/BiomeVisualiser/BiomeVisualiser.sln +++ b/Tools/BiomeVisualiser/BiomeVisualiser.sln @@ -1,23 +1,23 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual C++ Express 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BiomeVisualiser", "BiomeVisualiser.vcproj", "{6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release profiled|Win32 = Release profiled|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Debug|Win32.ActiveCfg = Debug|Win32 - {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Debug|Win32.Build.0 = Debug|Win32 - {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release profiled|Win32.ActiveCfg = Release profiled|Win32 - {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release profiled|Win32.Build.0 = Release profiled|Win32 - {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release|Win32.ActiveCfg = Release|Win32 - {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BiomeVisualiser", "BiomeVisualiser.vcproj", "{6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release profiled|Win32 = Release profiled|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Debug|Win32.ActiveCfg = Debug|Win32 + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Debug|Win32.Build.0 = Debug|Win32 + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release profiled|Win32.ActiveCfg = Release profiled|Win32 + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release profiled|Win32.Build.0 = Release profiled|Win32 + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release|Win32.ActiveCfg = Release|Win32 + {6DF3D88B-AD47-45B6-B831-1BDE74F86B5C}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Tools/BiomeVisualiser/BiomeVisualiser.vcproj b/Tools/BiomeVisualiser/BiomeVisualiser.vcproj index 0e1f1bfe0..522606d60 100644 --- a/Tools/BiomeVisualiser/BiomeVisualiser.vcproj +++ b/Tools/BiomeVisualiser/BiomeVisualiser.vcproj @@ -1,483 +1,483 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/BiomeVisualiser/GeneratorBiomeSource.h b/Tools/BiomeVisualiser/GeneratorBiomeSource.h index 34970491e..0b47e5f93 100644 --- a/Tools/BiomeVisualiser/GeneratorBiomeSource.h +++ b/Tools/BiomeVisualiser/GeneratorBiomeSource.h @@ -1,42 +1,42 @@ - -// GeneratorBiomeSource.h - -// Declares the cGeneratorBiomeSource that adapts a cBiomeGen into a cBiomeSource - -#include "../source/Generating/BioGen.h" -#include "BiomeSource.h" - - - - - -class cGeneratorBiomeSource : - public cBiomeSource -{ -public: - cGeneratorBiomeSource(cBiomeGen * a_Generator) : m_Generator(a_Generator) {} // Takes ownership of the generator ptr - - ~cGeneratorBiomeSource() - { - delete m_Generator; - } - - // cBiomeSource overrides: - virtual eAvailability GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) override - { - m_Generator->GenBiomes(a_ChunkX, a_ChunkZ, a_Biomes); - return baNow; - } - - virtual void HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) override - { - // Nothing needed - } - -protected: - cBiomeGen * m_Generator; -} ; - - - - + +// GeneratorBiomeSource.h + +// Declares the cGeneratorBiomeSource that adapts a cBiomeGen into a cBiomeSource + +#include "../source/Generating/BioGen.h" +#include "BiomeSource.h" + + + + + +class cGeneratorBiomeSource : + public cBiomeSource +{ +public: + cGeneratorBiomeSource(cBiomeGen * a_Generator) : m_Generator(a_Generator) {} // Takes ownership of the generator ptr + + ~cGeneratorBiomeSource() + { + delete m_Generator; + } + + // cBiomeSource overrides: + virtual eAvailability GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes) override + { + m_Generator->GenBiomes(a_ChunkX, a_ChunkZ, a_Biomes); + return baNow; + } + + virtual void HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ) override + { + // Nothing needed + } + +protected: + cBiomeGen * m_Generator; +} ; + + + + diff --git a/Tools/BiomeVisualiser/Pixmap.cpp b/Tools/BiomeVisualiser/Pixmap.cpp index 39a015b9c..1a80cf465 100644 --- a/Tools/BiomeVisualiser/Pixmap.cpp +++ b/Tools/BiomeVisualiser/Pixmap.cpp @@ -1,120 +1,120 @@ - -// Pixmap.cpp - -// Implements the cPixmap class that represents a RGB pixmap and allows simple operations on it - -#include "Globals.h" -#include "Pixmap.h" - - - - - -cPixmap::cPixmap(void) : - m_Width(0), - m_Height(0), - m_Stride(0), - m_Pixels(NULL) -{ -} - - - - - -cPixmap::cPixmap(int a_Width, int a_Height) : - m_Width(0), - m_Height(0), - m_Stride(0), - m_Pixels(NULL) -{ - SetSize(a_Width, a_Height); -} - - - - - -cPixmap::~cPixmap() -{ - delete m_Pixels; -} - - - - - -void cPixmap::SetSize(int a_Width, int a_Height) -{ - delete m_Pixels; - m_Pixels = new int[a_Width * a_Height]; - m_Width = a_Width; - m_Height = a_Height; - m_Stride = m_Width; // Currently we don't need a special stride value, but let's support it for the future :) -} - - - - - -void cPixmap::SetPixel(int a_X, int a_Y, int a_Color) -{ - ASSERT(a_X >= 0); - ASSERT(a_X < m_Width); - ASSERT(a_Y >= 0); - ASSERT(a_Y < m_Height); - - m_Pixels[a_X + a_Y * m_Stride] = a_Color; -} - - - - - -int cPixmap::GetPixel(int a_X, int a_Y) const -{ - ASSERT(a_X >= 0); - ASSERT(a_X < m_Width); - ASSERT(a_Y >= 0); - ASSERT(a_Y < m_Height); - - return m_Pixels[a_X + a_Y * m_Stride]; -} - - - - - -void cPixmap::Fill(int a_Color) -{ - int NumElements = m_Height * m_Stride; - for (int i = 0; i < NumElements; i++) - { - m_Pixels[i] = a_Color; - } -} - - - - - -void cPixmap::DrawToDC(HDC a_DC, int a_OriginX, int a_OriginY) -{ - BITMAPINFO bmi; - bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); - bmi.bmiHeader.biWidth = m_Width; - bmi.bmiHeader.biHeight = -m_Height; // Negative, we are top-down, unlike BMPs - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - bmi.bmiHeader.biSizeImage = m_Stride * m_Height * 4; - bmi.bmiHeader.biXPelsPerMeter = 1440; - bmi.bmiHeader.biYPelsPerMeter = 1440; - bmi.bmiHeader.biClrUsed = 0; - bmi.bmiHeader.biClrImportant = 0; - SetDIBitsToDevice(a_DC, a_OriginX, a_OriginY, m_Width, m_Height, 0, 0, 0, m_Height, m_Pixels, &bmi, DIB_RGB_COLORS); -} - - - - + +// Pixmap.cpp + +// Implements the cPixmap class that represents a RGB pixmap and allows simple operations on it + +#include "Globals.h" +#include "Pixmap.h" + + + + + +cPixmap::cPixmap(void) : + m_Width(0), + m_Height(0), + m_Stride(0), + m_Pixels(NULL) +{ +} + + + + + +cPixmap::cPixmap(int a_Width, int a_Height) : + m_Width(0), + m_Height(0), + m_Stride(0), + m_Pixels(NULL) +{ + SetSize(a_Width, a_Height); +} + + + + + +cPixmap::~cPixmap() +{ + delete m_Pixels; +} + + + + + +void cPixmap::SetSize(int a_Width, int a_Height) +{ + delete m_Pixels; + m_Pixels = new int[a_Width * a_Height]; + m_Width = a_Width; + m_Height = a_Height; + m_Stride = m_Width; // Currently we don't need a special stride value, but let's support it for the future :) +} + + + + + +void cPixmap::SetPixel(int a_X, int a_Y, int a_Color) +{ + ASSERT(a_X >= 0); + ASSERT(a_X < m_Width); + ASSERT(a_Y >= 0); + ASSERT(a_Y < m_Height); + + m_Pixels[a_X + a_Y * m_Stride] = a_Color; +} + + + + + +int cPixmap::GetPixel(int a_X, int a_Y) const +{ + ASSERT(a_X >= 0); + ASSERT(a_X < m_Width); + ASSERT(a_Y >= 0); + ASSERT(a_Y < m_Height); + + return m_Pixels[a_X + a_Y * m_Stride]; +} + + + + + +void cPixmap::Fill(int a_Color) +{ + int NumElements = m_Height * m_Stride; + for (int i = 0; i < NumElements; i++) + { + m_Pixels[i] = a_Color; + } +} + + + + + +void cPixmap::DrawToDC(HDC a_DC, int a_OriginX, int a_OriginY) +{ + BITMAPINFO bmi; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = m_Width; + bmi.bmiHeader.biHeight = -m_Height; // Negative, we are top-down, unlike BMPs + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + bmi.bmiHeader.biSizeImage = m_Stride * m_Height * 4; + bmi.bmiHeader.biXPelsPerMeter = 1440; + bmi.bmiHeader.biYPelsPerMeter = 1440; + bmi.bmiHeader.biClrUsed = 0; + bmi.bmiHeader.biClrImportant = 0; + SetDIBitsToDevice(a_DC, a_OriginX, a_OriginY, m_Width, m_Height, 0, 0, 0, m_Height, m_Pixels, &bmi, DIB_RGB_COLORS); +} + + + + diff --git a/Tools/BiomeVisualiser/Pixmap.h b/Tools/BiomeVisualiser/Pixmap.h index d0159a886..e50f6e946 100644 --- a/Tools/BiomeVisualiser/Pixmap.h +++ b/Tools/BiomeVisualiser/Pixmap.h @@ -1,39 +1,39 @@ - -// Pixmap.h - -// Declares a cPixmap class that represents a RGB pixmap and allows simple operations on it - -#pragma once - - - - - -class cPixmap -{ -public: - cPixmap(void); - cPixmap(int a_Width, int a_Height); - ~cPixmap(); - - void SetSize(int a_Width, int a_Height); - - int GetWidth (void) const { return m_Width; } - int GetHeight(void) const { return m_Height; } - - void SetPixel(int a_X, int a_Y, int a_Color); - int GetPixel(int a_X, int a_Y) const; - void Fill(int a_Color); - - void DrawToDC(HDC a_DC, int a_OriginX, int a_OriginY); - -protected: - int m_Width; - int m_Height; - int m_Stride; - int * m_Pixels; -} ; - - - - + +// Pixmap.h + +// Declares a cPixmap class that represents a RGB pixmap and allows simple operations on it + +#pragma once + + + + + +class cPixmap +{ +public: + cPixmap(void); + cPixmap(int a_Width, int a_Height); + ~cPixmap(); + + void SetSize(int a_Width, int a_Height); + + int GetWidth (void) const { return m_Width; } + int GetHeight(void) const { return m_Height; } + + void SetPixel(int a_X, int a_Y, int a_Color); + int GetPixel(int a_X, int a_Y) const; + void Fill(int a_Color); + + void DrawToDC(HDC a_DC, int a_OriginX, int a_OriginY); + +protected: + int m_Width; + int m_Height; + int m_Stride; + int * m_Pixels; +} ; + + + + diff --git a/Tools/BiomeVisualiser/Timer.h b/Tools/BiomeVisualiser/Timer.h index b18d37cb7..78c4b42c7 100644 --- a/Tools/BiomeVisualiser/Timer.h +++ b/Tools/BiomeVisualiser/Timer.h @@ -1,40 +1,40 @@ - -// Timer.h - -// Declares the cTimer class representing a RAII class that measures time from its creation till its destruction - - - - - -#pragma once - -#include "time.h" - - - - - -class cTimer -{ -public: - cTimer(const AString & a_Title) : - m_Title(a_Title), - m_StartTime(clock()) - { - } - - ~cTimer() - { - clock_t NumTicks = clock() - m_StartTime; - LOG("%s took %d ticks (%.02f sec)", m_Title.c_str(), NumTicks, (double)NumTicks / CLOCKS_PER_SEC); - } - -protected: - AString m_Title; - clock_t m_StartTime; -} ; - - - - + +// Timer.h + +// Declares the cTimer class representing a RAII class that measures time from its creation till its destruction + + + + + +#pragma once + +#include "time.h" + + + + + +class cTimer +{ +public: + cTimer(const AString & a_Title) : + m_Title(a_Title), + m_StartTime(clock()) + { + } + + ~cTimer() + { + clock_t NumTicks = clock() - m_StartTime; + LOG("%s took %d ticks (%.02f sec)", m_Title.c_str(), NumTicks, (double)NumTicks / CLOCKS_PER_SEC); + } + +protected: + AString m_Title; + clock_t m_StartTime; +} ; + + + + diff --git a/Tools/BiomeVisualiser/WndProcThunk.h b/Tools/BiomeVisualiser/WndProcThunk.h index 6f33b5c3f..da995eb5f 100644 --- a/Tools/BiomeVisualiser/WndProcThunk.h +++ b/Tools/BiomeVisualiser/WndProcThunk.h @@ -1,143 +1,143 @@ - -// WndProcThunk.h - -// Interfaces to the CWndProcThunk class responsible for WNDPROC class-thunking -// For details, see http://www.hackcraft.net/cpp/windowsThunk/thiscall/ -// Also available is a CDlgProcThunk class doing the same work for DIALOGPROC - -// MD: Made NX-compat by allocating the code structure using VirtualAlloc(..., PAGE_EXECUTE_READWRITE) - - - - - -// fwd: -template class CWndProcThunk; - - - - - -#ifndef WNDPROCTHUNK_H_INCLUDED -#define WNDPROCTHUNK_H_INCLUDED - - - - -template inline To union_cast(From fr) throw() -{ - union - { - From f; - To t; - } uc; - uc.f = fr; - return uc.t; -} - - - - - -#pragma warning(push) -#pragma warning(disable : 4355) - -#if defined(_M_IX86) - -#pragma pack(push,1) - -template class CWndProcThunk -{ - typedef ::LRESULT (W::* WndProc)(::HWND, ::UINT, ::WPARAM, ::LPARAM); - typedef CWndProcThunk ThisClass; - - struct SCode - { - BYTE m_mov; // mov ECX, m_this - W * m_this; // - BYTE m_jmp; // jmp m_relproc - ptrdiff_t m_relproc; // relative jmp - }; - - SCode * Code; - -public: - ThisClass(WndProc proc, W * obj) - { - Code = (SCode *)VirtualAlloc(NULL, sizeof(SCode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); - Code->m_mov = 0xB9, - Code->m_this = obj, - Code->m_jmp = 0xE9, - Code->m_relproc = union_cast(proc) - reinterpret_cast(Code) - sizeof(*Code); - ::FlushInstructionCache(::GetCurrentProcess(), Code, sizeof(*Code)); - } - - virtual ~CWndProcThunk() - { - VirtualFree(Code, sizeof(*Code), MEM_RELEASE); - Code = NULL; - } - - operator ::WNDPROC() const {return reinterpret_cast<::WNDPROC>(Code); } - operator ::LONG_PTR() const {return reinterpret_cast<::LONG_PTR>(Code); } -} ; - - - - - -template class CDlgProcThunk -{ - typedef ::BOOL (W::* DlgProc)(::HWND, ::UINT, ::WPARAM, ::LPARAM); - typedef CDlgProcThunk ThisClass; - - struct SCode - { - BYTE m_mov; // mov ECX, m_this - W * m_this; // - BYTE m_jmp; // jmp m_relproc - ptrdiff_t m_relproc; // relative jmp - }; - - SCode * Code; - -public: - CDlgProcThunk(DlgProc proc, W * obj) - { - Code = (SCode *)VirtualAlloc(NULL, sizeof(SCode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); - Code->m_mov = 0xB9, - Code->m_this = obj, - Code->m_jmp = 0xE9, - Code->m_relproc = union_cast(proc) - reinterpret_cast(Code) - sizeof(*Code); - ::FlushInstructionCache(::GetCurrentProcess(), Code, sizeof(*Code)); - } - - virtual ~CDlgProcThunk() - { - VirtualFree(Code, sizeof(*Code), MEM_RELEASE); - Code = NULL; - } - - operator ::DLGPROC() const {return reinterpret_cast<::DLGPROC>(Code); } - operator ::LONG_PTR() const {return reinterpret_cast<::LONG_PTR>(Code); } -} ; - - - - - - #pragma pack(pop) - -#else // _M_IX86 - #error Only X86 supported -#endif - - - - - -#endif // WNDPROCTHUNK_H_INCLUDED - - - - + +// WndProcThunk.h + +// Interfaces to the CWndProcThunk class responsible for WNDPROC class-thunking +// For details, see http://www.hackcraft.net/cpp/windowsThunk/thiscall/ +// Also available is a CDlgProcThunk class doing the same work for DIALOGPROC + +// MD: Made NX-compat by allocating the code structure using VirtualAlloc(..., PAGE_EXECUTE_READWRITE) + + + + + +// fwd: +template class CWndProcThunk; + + + + + +#ifndef WNDPROCTHUNK_H_INCLUDED +#define WNDPROCTHUNK_H_INCLUDED + + + + +template inline To union_cast(From fr) throw() +{ + union + { + From f; + To t; + } uc; + uc.f = fr; + return uc.t; +} + + + + + +#pragma warning(push) +#pragma warning(disable : 4355) + +#if defined(_M_IX86) + +#pragma pack(push,1) + +template class CWndProcThunk +{ + typedef ::LRESULT (W::* WndProc)(::HWND, ::UINT, ::WPARAM, ::LPARAM); + typedef CWndProcThunk ThisClass; + + struct SCode + { + BYTE m_mov; // mov ECX, m_this + W * m_this; // + BYTE m_jmp; // jmp m_relproc + ptrdiff_t m_relproc; // relative jmp + }; + + SCode * Code; + +public: + ThisClass(WndProc proc, W * obj) + { + Code = (SCode *)VirtualAlloc(NULL, sizeof(SCode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + Code->m_mov = 0xB9, + Code->m_this = obj, + Code->m_jmp = 0xE9, + Code->m_relproc = union_cast(proc) - reinterpret_cast(Code) - sizeof(*Code); + ::FlushInstructionCache(::GetCurrentProcess(), Code, sizeof(*Code)); + } + + virtual ~CWndProcThunk() + { + VirtualFree(Code, sizeof(*Code), MEM_RELEASE); + Code = NULL; + } + + operator ::WNDPROC() const {return reinterpret_cast<::WNDPROC>(Code); } + operator ::LONG_PTR() const {return reinterpret_cast<::LONG_PTR>(Code); } +} ; + + + + + +template class CDlgProcThunk +{ + typedef ::BOOL (W::* DlgProc)(::HWND, ::UINT, ::WPARAM, ::LPARAM); + typedef CDlgProcThunk ThisClass; + + struct SCode + { + BYTE m_mov; // mov ECX, m_this + W * m_this; // + BYTE m_jmp; // jmp m_relproc + ptrdiff_t m_relproc; // relative jmp + }; + + SCode * Code; + +public: + CDlgProcThunk(DlgProc proc, W * obj) + { + Code = (SCode *)VirtualAlloc(NULL, sizeof(SCode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); + Code->m_mov = 0xB9, + Code->m_this = obj, + Code->m_jmp = 0xE9, + Code->m_relproc = union_cast(proc) - reinterpret_cast(Code) - sizeof(*Code); + ::FlushInstructionCache(::GetCurrentProcess(), Code, sizeof(*Code)); + } + + virtual ~CDlgProcThunk() + { + VirtualFree(Code, sizeof(*Code), MEM_RELEASE); + Code = NULL; + } + + operator ::DLGPROC() const {return reinterpret_cast<::DLGPROC>(Code); } + operator ::LONG_PTR() const {return reinterpret_cast<::LONG_PTR>(Code); } +} ; + + + + + + #pragma pack(pop) + +#else // _M_IX86 + #error Only X86 supported +#endif + + + + + +#endif // WNDPROCTHUNK_H_INCLUDED + + + + diff --git a/Tools/BiomeVisualiser/profile_run.cmd b/Tools/BiomeVisualiser/profile_run.cmd index 753ff18fb..d4826d06a 100644 --- a/Tools/BiomeVisualiser/profile_run.cmd +++ b/Tools/BiomeVisualiser/profile_run.cmd @@ -1,70 +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\BiomeVisualiser.exe" -set args="" - -:: 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 +@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\BiomeVisualiser.exe" +set args="" + +:: 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 diff --git a/Tools/BlockZapper/BlockZapper.cpp b/Tools/BlockZapper/BlockZapper.cpp index fb2050d56..c2a0a4fac 100644 --- a/Tools/BlockZapper/BlockZapper.cpp +++ b/Tools/BlockZapper/BlockZapper.cpp @@ -1,97 +1,97 @@ - -// BlockZapper.cpp - -// Implements the main app entrypoint - -#include "Globals.h" - -#include - -#include "Regions.h" -#include "Zapper.h" - - - - - -#ifdef _MSC_VER - // Under MSVC, link to WinSock2 (needed by FastNBT's byteswapping) - #pragma comment(lib, "ws2_32.lib") -#endif - - - - - -void ShowHelp(const char * a_ProgramFullName) -{ - AString ProgramName(a_ProgramFullName); - size_t idx = ProgramName.rfind(cFile::PathSeparator); - if (idx != AString::npos) - { - ProgramName.erase(0, idx + 1); - } - printf("Tool written by _Xoft(o), code is public domain.\n"); - printf("Usage:\n"); - printf("%s [-w ]\n", ProgramName.c_str()); - printf("Zaps blocks and / or entities in specified regions.\n"); - printf("Regions are read from stdin, the format is:\n"); - printf(" x1 x2 y1 y2 z1 z2 [B|E|BE]\n"); - printf("B or no specifier zaps blocks only\n"); - printf("E zaps entities only\n"); - printf("BE zaps blocks and entities\n"); - printf("MCA files are searched in the ; if not specified, in the current folder.\n"); -} - - - - - -int main(int argc, char * argv[]) -{ - new cMCLogger; // Create a new logger, it will assign itself as the main logger instance - - AString MCAFolder = "."; - for (int i = 1; i < argc; i++) - { - if (strcmp(argv[i], "-w") == 0) - { - if (i < argc - 1) - { - MCAFolder = argv[i + 1]; - } - i++; - } - else if ( - (strcmp(argv[i], "help") == 0) || - (strcmp(argv[i], "-?") == 0) || - (strcmp(argv[i], "/?") == 0) || - (strcmp(argv[i], "-h") == 0) || - (strcmp(argv[i], "--help") == 0) - ) - { - ShowHelp(argv[0]); - return 0; - } - } - - cRegions Regions; - - /* - // DEBUG: Read input from a file instead of stdin: - std::fstream fs("test_in.txt"); - Regions.Read(fs); - //*/ - - Regions.Read(std::cin); - - cZapper Zapper(MCAFolder); - Zapper.ZapRegions(Regions.GetAll()); - - LOGINFO("Done"); - return 0; -} ; - - - - + +// BlockZapper.cpp + +// Implements the main app entrypoint + +#include "Globals.h" + +#include + +#include "Regions.h" +#include "Zapper.h" + + + + + +#ifdef _MSC_VER + // Under MSVC, link to WinSock2 (needed by FastNBT's byteswapping) + #pragma comment(lib, "ws2_32.lib") +#endif + + + + + +void ShowHelp(const char * a_ProgramFullName) +{ + AString ProgramName(a_ProgramFullName); + size_t idx = ProgramName.rfind(cFile::PathSeparator); + if (idx != AString::npos) + { + ProgramName.erase(0, idx + 1); + } + printf("Tool written by _Xoft(o), code is public domain.\n"); + printf("Usage:\n"); + printf("%s [-w ]\n", ProgramName.c_str()); + printf("Zaps blocks and / or entities in specified regions.\n"); + printf("Regions are read from stdin, the format is:\n"); + printf(" x1 x2 y1 y2 z1 z2 [B|E|BE]\n"); + printf("B or no specifier zaps blocks only\n"); + printf("E zaps entities only\n"); + printf("BE zaps blocks and entities\n"); + printf("MCA files are searched in the ; if not specified, in the current folder.\n"); +} + + + + + +int main(int argc, char * argv[]) +{ + new cMCLogger; // Create a new logger, it will assign itself as the main logger instance + + AString MCAFolder = "."; + for (int i = 1; i < argc; i++) + { + if (strcmp(argv[i], "-w") == 0) + { + if (i < argc - 1) + { + MCAFolder = argv[i + 1]; + } + i++; + } + else if ( + (strcmp(argv[i], "help") == 0) || + (strcmp(argv[i], "-?") == 0) || + (strcmp(argv[i], "/?") == 0) || + (strcmp(argv[i], "-h") == 0) || + (strcmp(argv[i], "--help") == 0) + ) + { + ShowHelp(argv[0]); + return 0; + } + } + + cRegions Regions; + + /* + // DEBUG: Read input from a file instead of stdin: + std::fstream fs("test_in.txt"); + Regions.Read(fs); + //*/ + + Regions.Read(std::cin); + + cZapper Zapper(MCAFolder); + Zapper.ZapRegions(Regions.GetAll()); + + LOGINFO("Done"); + return 0; +} ; + + + + diff --git a/Tools/BlockZapper/BlockZapper.sln b/Tools/BlockZapper/BlockZapper.sln index fd8029ed0..9a71b9311 100644 --- a/Tools/BlockZapper/BlockZapper.sln +++ b/Tools/BlockZapper/BlockZapper.sln @@ -1,34 +1,34 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual C++ Express 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlockZapper", "BlockZapper.vcproj", "{CE317695-CCCC-4B11-B07B-21729A110FC2}" - ProjectSection(ProjectDependencies) = postProject - {EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA} = {EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\..\..\mc-server.clean\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 - {CE317695-CCCC-4B11-B07B-21729A110FC2}.Debug|Win32.ActiveCfg = Debug|Win32 - {CE317695-CCCC-4B11-B07B-21729A110FC2}.Debug|Win32.Build.0 = Debug|Win32 - {CE317695-CCCC-4B11-B07B-21729A110FC2}.Release profiled|Win32.ActiveCfg = Release|Win32 - {CE317695-CCCC-4B11-B07B-21729A110FC2}.Release profiled|Win32.Build.0 = Release|Win32 - {CE317695-CCCC-4B11-B07B-21729A110FC2}.Release|Win32.ActiveCfg = Release|Win32 - {CE317695-CCCC-4B11-B07B-21729A110FC2}.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 + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "BlockZapper", "BlockZapper.vcproj", "{CE317695-CCCC-4B11-B07B-21729A110FC2}" + ProjectSection(ProjectDependencies) = postProject + {EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA} = {EA9D50FD-937A-4EF5-8C37-5F4175AF4FEA} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\..\..\mc-server.clean\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 + {CE317695-CCCC-4B11-B07B-21729A110FC2}.Debug|Win32.ActiveCfg = Debug|Win32 + {CE317695-CCCC-4B11-B07B-21729A110FC2}.Debug|Win32.Build.0 = Debug|Win32 + {CE317695-CCCC-4B11-B07B-21729A110FC2}.Release profiled|Win32.ActiveCfg = Release|Win32 + {CE317695-CCCC-4B11-B07B-21729A110FC2}.Release profiled|Win32.Build.0 = Release|Win32 + {CE317695-CCCC-4B11-B07B-21729A110FC2}.Release|Win32.ActiveCfg = Release|Win32 + {CE317695-CCCC-4B11-B07B-21729A110FC2}.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 diff --git a/Tools/BlockZapper/BlockZapper.txt b/Tools/BlockZapper/BlockZapper.txt index af6b94054..2b52c477e 100644 --- a/Tools/BlockZapper/BlockZapper.txt +++ b/Tools/BlockZapper/BlockZapper.txt @@ -1,19 +1,19 @@ - -// BlockZapper.txt - -/* -This project implements a simple tool that can "zap" blocks out of an Anvil-stored MineCraft world. -It is usually used by server admins when their servers fail with a bug and store an invalid block in the world. -This tool takes a coord triplet and a radius triplet and replaces all blocks within the (new york-metric) radius of the coords with air. -The triplets pair is given on stdin, and multiple such specifiers are allowed, each on a separate file. -If the specifier line ends with an additional " E", entities within that radius are zapped instead of blocks -If the specifier line ends with an additional " BE", both blocks and entities are zapped. - -The tool is aware of extended blocktypes (256 .. 4096). - -The source code for this tool is public domain, but note that it depends on a few shared sources in MCServer that may be under other licenses. -*/ - - - - + +// BlockZapper.txt + +/* +This project implements a simple tool that can "zap" blocks out of an Anvil-stored MineCraft world. +It is usually used by server admins when their servers fail with a bug and store an invalid block in the world. +This tool takes a coord triplet and a radius triplet and replaces all blocks within the (new york-metric) radius of the coords with air. +The triplets pair is given on stdin, and multiple such specifiers are allowed, each on a separate file. +If the specifier line ends with an additional " E", entities within that radius are zapped instead of blocks +If the specifier line ends with an additional " BE", both blocks and entities are zapped. + +The tool is aware of extended blocktypes (256 .. 4096). + +The source code for this tool is public domain, but note that it depends on a few shared sources in MCServer that may be under other licenses. +*/ + + + + diff --git a/Tools/BlockZapper/BlockZapper.vcproj b/Tools/BlockZapper/BlockZapper.vcproj index 195cdafbe..2a98197eb 100644 --- a/Tools/BlockZapper/BlockZapper.vcproj +++ b/Tools/BlockZapper/BlockZapper.vcproj @@ -1,313 +1,313 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/BlockZapper/Globals.cpp b/Tools/BlockZapper/Globals.cpp index 31440fd44..d73265a60 100644 --- a/Tools/BlockZapper/Globals.cpp +++ b/Tools/BlockZapper/Globals.cpp @@ -1,10 +1,10 @@ - -// Globals.cpp - -// Used for precompiled header generation in MSVC - -#include "Globals.h" - - - - + +// Globals.cpp + +// Used for precompiled header generation in MSVC + +#include "Globals.h" + + + + diff --git a/Tools/BlockZapper/Globals.h b/Tools/BlockZapper/Globals.h index e90fa96bd..e27315ec5 100644 --- a/Tools/BlockZapper/Globals.h +++ b/Tools/BlockZapper/Globals.h @@ -1,14 +1,14 @@ - -// Globals.h - -// This file is used for precompiled header generation in MSVC - - - - - -#include "../../source/Globals.h" - - - - + +// Globals.h + +// This file is used for precompiled header generation in MSVC + + + + + +#include "../../source/Globals.h" + + + + diff --git a/Tools/BlockZapper/Regions.cpp b/Tools/BlockZapper/Regions.cpp index b4c8bddcd..515699532 100644 --- a/Tools/BlockZapper/Regions.cpp +++ b/Tools/BlockZapper/Regions.cpp @@ -1,167 +1,167 @@ - -// Regions.cpp - -// Implements the cRegions class representing the list of regions to zap - -#include "Globals.h" - -#include "Regions.h" - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cRegion: - -cRegion::cRegion(void) : - m_MinX(0), - m_MaxX(0), - m_MinY(0), - m_MaxY(0), - m_MinZ(0), - m_MaxZ(0), - m_ShouldZapBlocks(false), - m_ShouldZapEntities(false) -{ -} - - - - - -cRegion::cRegion(int a_MinX, int a_MaxX, int a_MinY, int a_MaxY, int a_MinZ, int a_MaxZ, bool a_ShouldZapBlocks, bool a_ShouldZapEntities) : - m_MinX(a_MinX), - m_MaxX(a_MaxX), - m_MinY(std::max(0, std::min(255, a_MinY))), - m_MaxY(std::max(0, std::min(255, a_MaxY))), - m_MinZ(a_MinZ), - m_MaxZ(a_MaxZ), - m_ShouldZapBlocks(a_ShouldZapBlocks), - m_ShouldZapEntities(a_ShouldZapEntities) -{ -} - - - - - -bool cRegion::TouchesChunk(int a_ChunkX, int a_ChunkZ) const -{ - int ChunkBeginX = a_ChunkX * 16; - int ChunkEndX = a_ChunkX * 16 + 15; - int ChunkBeginZ = a_ChunkZ * 16; - int ChunkEndZ = a_ChunkZ * 16 + 15; - if ( - (m_MinX > ChunkEndX) || (m_MaxX < ChunkBeginX) || - (m_MinZ > ChunkEndZ) || (m_MaxZ < ChunkBeginZ) - ) - { - return false; - } - return true; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cRegions: - -void cRegions::Read(std::istream & a_Stream) -{ - while (!a_Stream.eof()) - { - AString Line; - std::getline(a_Stream, Line); - - // Process the line - AStringVector Split = StringSplit(Line, " \t"); - AStringVector NonEmpty; - for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr) - { - if (!itr->empty()) - { - NonEmpty.push_back(*itr); - } - } // for itr - Split[] - switch (NonEmpty.size()) - { - case 6: - case 7: - { - AddRegion(NonEmpty); - break; - } - default: - { - fprintf(stderr, "Cannot parse line \"%s\", ignoring", Line.c_str()); - break; - } - } - } -} - - - - - -void cRegions::AddRegion(const AStringVector & a_Split) -{ - ASSERT((a_Split.size() == 6) || (a_Split.size() == 7)); - - int Coords[6]; - for (int i = 0; i < 6; i++) - { - Coords[i] = atoi(a_Split[i].c_str()); - if ((Coords[i] == 0) && (a_Split[i] != "0")) - { - fprintf(stderr, "Bad coord: \"%s\". Ignoring line.", a_Split[i].c_str()); - return; - } - } // for i - a_Split[] - - bool ShouldZapBlocks = true; - bool ShouldZapEntities = false; - - if (a_Split.size() == 7) - { - AString Upper = a_Split[6]; - StrToUpper(Upper); - if (Upper == "E") - { - ShouldZapEntities = true; - ShouldZapBlocks = false; - } - else if (Upper == "BE") - { - ShouldZapEntities = true; - } - else if (Upper == "B") - { - // Nothing needed - } - else - { - fprintf(stderr, "Bad zap specifier: \"%s\". Ignoring line.", a_Split[6].c_str()); - return; - } - } - - // Swap coords, if needed: - for (int i = 0; i < 3; i++) - { - if (Coords[2 * i] > Coords[2 * i + 1]) - { - std::swap(Coords[2 * i], Coords[2 * i + 1]); - } - } - - // Store the region - m_Regions.push_back(cRegion(Coords[0], Coords[1], Coords[2], Coords[3], Coords[4], Coords[5], ShouldZapBlocks, ShouldZapEntities)); -} - - - - + +// Regions.cpp + +// Implements the cRegions class representing the list of regions to zap + +#include "Globals.h" + +#include "Regions.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRegion: + +cRegion::cRegion(void) : + m_MinX(0), + m_MaxX(0), + m_MinY(0), + m_MaxY(0), + m_MinZ(0), + m_MaxZ(0), + m_ShouldZapBlocks(false), + m_ShouldZapEntities(false) +{ +} + + + + + +cRegion::cRegion(int a_MinX, int a_MaxX, int a_MinY, int a_MaxY, int a_MinZ, int a_MaxZ, bool a_ShouldZapBlocks, bool a_ShouldZapEntities) : + m_MinX(a_MinX), + m_MaxX(a_MaxX), + m_MinY(std::max(0, std::min(255, a_MinY))), + m_MaxY(std::max(0, std::min(255, a_MaxY))), + m_MinZ(a_MinZ), + m_MaxZ(a_MaxZ), + m_ShouldZapBlocks(a_ShouldZapBlocks), + m_ShouldZapEntities(a_ShouldZapEntities) +{ +} + + + + + +bool cRegion::TouchesChunk(int a_ChunkX, int a_ChunkZ) const +{ + int ChunkBeginX = a_ChunkX * 16; + int ChunkEndX = a_ChunkX * 16 + 15; + int ChunkBeginZ = a_ChunkZ * 16; + int ChunkEndZ = a_ChunkZ * 16 + 15; + if ( + (m_MinX > ChunkEndX) || (m_MaxX < ChunkBeginX) || + (m_MinZ > ChunkEndZ) || (m_MaxZ < ChunkBeginZ) + ) + { + return false; + } + return true; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRegions: + +void cRegions::Read(std::istream & a_Stream) +{ + while (!a_Stream.eof()) + { + AString Line; + std::getline(a_Stream, Line); + + // Process the line + AStringVector Split = StringSplit(Line, " \t"); + AStringVector NonEmpty; + for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr) + { + if (!itr->empty()) + { + NonEmpty.push_back(*itr); + } + } // for itr - Split[] + switch (NonEmpty.size()) + { + case 6: + case 7: + { + AddRegion(NonEmpty); + break; + } + default: + { + fprintf(stderr, "Cannot parse line \"%s\", ignoring", Line.c_str()); + break; + } + } + } +} + + + + + +void cRegions::AddRegion(const AStringVector & a_Split) +{ + ASSERT((a_Split.size() == 6) || (a_Split.size() == 7)); + + int Coords[6]; + for (int i = 0; i < 6; i++) + { + Coords[i] = atoi(a_Split[i].c_str()); + if ((Coords[i] == 0) && (a_Split[i] != "0")) + { + fprintf(stderr, "Bad coord: \"%s\". Ignoring line.", a_Split[i].c_str()); + return; + } + } // for i - a_Split[] + + bool ShouldZapBlocks = true; + bool ShouldZapEntities = false; + + if (a_Split.size() == 7) + { + AString Upper = a_Split[6]; + StrToUpper(Upper); + if (Upper == "E") + { + ShouldZapEntities = true; + ShouldZapBlocks = false; + } + else if (Upper == "BE") + { + ShouldZapEntities = true; + } + else if (Upper == "B") + { + // Nothing needed + } + else + { + fprintf(stderr, "Bad zap specifier: \"%s\". Ignoring line.", a_Split[6].c_str()); + return; + } + } + + // Swap coords, if needed: + for (int i = 0; i < 3; i++) + { + if (Coords[2 * i] > Coords[2 * i + 1]) + { + std::swap(Coords[2 * i], Coords[2 * i + 1]); + } + } + + // Store the region + m_Regions.push_back(cRegion(Coords[0], Coords[1], Coords[2], Coords[3], Coords[4], Coords[5], ShouldZapBlocks, ShouldZapEntities)); +} + + + + diff --git a/Tools/BlockZapper/Regions.h b/Tools/BlockZapper/Regions.h index d959200d2..997b9a047 100644 --- a/Tools/BlockZapper/Regions.h +++ b/Tools/BlockZapper/Regions.h @@ -1,58 +1,58 @@ - -// Regions.h - -// Declares the cRegions class representing individual regions to zap - - - - - -#pragma once - -#include - - - - - -struct cRegion -{ - int m_MinX, m_MaxX; - int m_MinY, m_MaxY; - int m_MinZ, m_MaxZ; - - bool m_ShouldZapBlocks; - bool m_ShouldZapEntities; - - cRegion(void); - cRegion(int a_MinX, int a_MaxX, int a_MinY, int a_MaxY, int a_MinZ, int a_MaxZ, bool a_ShouldZapBlocks, bool a_ShouldZapEntities); - - bool TouchesChunk(int a_ChunkX, int a_ChunkZ) const; -} ; - -typedef std::vector cRegionVector; - - - - - -class cRegions -{ -public: - /// Reads the list of regions from the specified stream - void Read(std::istream & a_Stream); - - /// Returns all regions in this container - const cRegionVector & GetAll(void) const { return m_Regions; } - -protected: - cRegionVector m_Regions; - - /// Adds a new region based on the contents of the split line. The split must already be the correct size - void AddRegion(const AStringVector & a_Split); - -} ; - - - - + +// Regions.h + +// Declares the cRegions class representing individual regions to zap + + + + + +#pragma once + +#include + + + + + +struct cRegion +{ + int m_MinX, m_MaxX; + int m_MinY, m_MaxY; + int m_MinZ, m_MaxZ; + + bool m_ShouldZapBlocks; + bool m_ShouldZapEntities; + + cRegion(void); + cRegion(int a_MinX, int a_MaxX, int a_MinY, int a_MaxY, int a_MinZ, int a_MaxZ, bool a_ShouldZapBlocks, bool a_ShouldZapEntities); + + bool TouchesChunk(int a_ChunkX, int a_ChunkZ) const; +} ; + +typedef std::vector cRegionVector; + + + + + +class cRegions +{ +public: + /// Reads the list of regions from the specified stream + void Read(std::istream & a_Stream); + + /// Returns all regions in this container + const cRegionVector & GetAll(void) const { return m_Regions; } + +protected: + cRegionVector m_Regions; + + /// Adds a new region based on the contents of the split line. The split must already be the correct size + void AddRegion(const AStringVector & a_Split); + +} ; + + + + diff --git a/Tools/BlockZapper/Zapper.cpp b/Tools/BlockZapper/Zapper.cpp index d5bc576ba..702f968ab 100644 --- a/Tools/BlockZapper/Zapper.cpp +++ b/Tools/BlockZapper/Zapper.cpp @@ -1,440 +1,440 @@ - -// Zapper.cpp - -// Implements the cZapper class representing the processor that actually zaps blocks and entities - -#include "Globals.h" - -#include "WorldStorage/FastNBT.h" -#include "StringCompression.h" -#include "zlib.h" - -#include "Zapper.h" - - - - - -/// The maximum size of an inflated chunk; raw chunk data is 192 KiB, allow 64 KiB more of entities -#define CHUNK_INFLATE_MAX 256 KiB - - - - - -cZapper::cZapper(const AString & a_MCAFolder) : - m_MCAFolder(a_MCAFolder) -{ -} - - - - - -void cZapper::ZapRegions(const cRegionVector & a_Regions) -{ - for (cRegionVector::const_iterator itr = a_Regions.begin(), end = a_Regions.end(); itr != end; ++itr) - { - int MinAnvX, MinAnvZ; - int MaxAnvX, MaxAnvZ; - BlockToMCA(itr->m_MinX, itr->m_MinZ, MinAnvX, MinAnvZ); - BlockToMCA(itr->m_MaxX, itr->m_MaxZ, MaxAnvX, MaxAnvZ); - for (int x = MinAnvX; x <= MaxAnvX; x++) - { - for (int z = MinAnvZ; z <= MaxAnvZ; z++) - { - ZapRegionInMCAFile(*itr, x, z); - } - } - } // for itr - a_Regions -} - - - - - -void cZapper::BlockToMCA(int a_BlockX, int a_BlockZ, int & a_MCAX, int & a_MCAZ) -{ - // These need to be arithmetic shifts, consult your compiler documentation to see if it's so - // MSVC and GCC both use arithmetic shifts - a_MCAX = a_BlockX >> 10; - a_MCAZ = a_BlockZ >> 10; -} - - - - - -void cZapper::BlockToChunk(int a_BlockX, int a_BlockZ, int & a_ChunkX, int & a_ChunkZ) -{ - // These need to be arithmetic shifts, consult your compiler documentation to see if it's so - // MSVC and GCC both use arithmetic shifts - a_ChunkX = a_BlockX >> 4; - a_ChunkZ = a_BlockZ >> 4; -} - - - - - -void cZapper::ZapRegionInMCAFile(const cRegion & a_Region, int a_MCAX, int a_MCAZ) -{ - cFile fIn; - AString FileNameIn = Printf("%s/r.%d.%d.mca", m_MCAFolder.c_str(), a_MCAX, a_MCAZ); - if (!fIn.Open(FileNameIn, cFile::fmRead)) - { - return; - } - cFile fOut; - AString FileNameOut = Printf("%s/r.%d.%d.zap", m_MCAFolder.c_str(), a_MCAX, a_MCAZ); - if (!fOut.Open(FileNameOut, cFile::fmWrite)) - { - fprintf(stderr, "Cannot open temporary file \"%s\" for writing, skipping file \"%s\".", FileNameOut.c_str(), FileNameIn.c_str()); - return; - } - - AString DataOut; - DataOut.reserve(fIn.GetSize()); - - int HeaderIn[2048]; - if (fIn.Read(HeaderIn, sizeof(HeaderIn)) != sizeof(HeaderIn)) - { - fprintf(stderr, "Cannot read header from file \"%s\", skipping file.", FileNameIn.c_str()); - } - int HeaderOut[2048]; - for (int i = 0; i < 1024; i++) - { - if (HeaderIn[i] == 0) - { - // Chunk not present - HeaderOut[i] = 0; - continue; - } - AString ChunkData; - int ChunkX = a_MCAX * ChunksPerMCAX + (i % ChunksPerMCAX); - int ChunkZ = a_MCAZ * ChunksPerMCAZ + (i / ChunksPerMCAX); - - LoadChunkData(fIn, HeaderIn[i], ChunkData, ChunkX, ChunkZ); - - if (a_Region.TouchesChunk(ChunkX, ChunkZ)) - { - ZapRegionInRawChunkData(a_Region, ChunkData, ChunkX, ChunkZ); - } - unsigned char ChunkHeader[5]; - size_t DataSize = ChunkData.size() + 1; - ChunkHeader[0] = (DataSize >> 24) & 0xff; - ChunkHeader[1] = (DataSize >> 16) & 0xff; - ChunkHeader[2] = (DataSize >> 8) & 0xff; - ChunkHeader[3] = DataSize & 0xff; - ChunkHeader[4] = 2; // zlib compression - size_t Alignment = 4096 - (ChunkData.size() + 5) % 4096; // 5 bytes of the header are appended outside of ChunkData - if (Alignment > 0) - { - ChunkData.append(Alignment, (char)0); - } - HeaderOut[i] = htonl(((DataOut.size() / 4096 + 2) << 8) | ((ChunkData.size() + 5) / 4096)); - DataOut.append((const char *)ChunkHeader, sizeof(ChunkHeader)); - DataOut.append(ChunkData); - } // for i - chunks in fIn - for (int i = 1024; i < 2048; i++) - { - HeaderOut[i] = HeaderIn[i]; - } - fIn.Close(); - fOut.Write(HeaderOut, sizeof(HeaderOut)); - fOut.Write(DataOut.data(), DataOut.size()); - fOut.Close(); - cFile::Delete(FileNameIn); - cFile::Rename(FileNameOut, FileNameIn); -} - - - - - -void cZapper::LoadChunkData(cFile & a_InFile, int a_ChunkHeaderValue, AString & a_ChunkData, int a_ChunkX, int a_ChunkZ) -{ - a_ChunkHeaderValue = ntohl(a_ChunkHeaderValue); // Convert from big-endian to system-endian - int ChunkOffset = (a_ChunkHeaderValue >> 8) * 4096; - int ChunkSize = (a_ChunkHeaderValue & 0xff) * 4096; - a_InFile.Seek(ChunkOffset); - unsigned char ChunkHeader[5]; - a_InFile.Read(ChunkHeader, sizeof(ChunkHeader)); - if (ChunkHeader[4] != 2) - { - fprintf(stderr, "Chunk [%d, %d] is compressed in an unknown scheme (%d), skipping", a_ChunkX, a_ChunkZ, ChunkHeader[5]); - return; - } - int ActualSize = (ChunkHeader[0] << 24) | (ChunkHeader[1] << 16) | (ChunkHeader[2] << 8) | ChunkHeader[3]; - ActualSize -= 1; // Compression took 1 byte - a_ChunkData.resize(ActualSize); - int BytesRead = a_InFile.Read((void *)(a_ChunkData.data()), ActualSize); - if (BytesRead != ActualSize) - { - fprintf(stderr, "Chunk is truncated in file (%d bytes out of %d), skipping.", BytesRead, ActualSize); - a_ChunkData.clear(); - return; - } -} - - - - - -void cZapper::ZapRegionInRawChunkData(const cRegion & a_Region, AString & a_ChunkData, int a_ChunkX, int a_ChunkZ) -{ - // Decompress the data: - char Uncompressed[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 *)Uncompressed; - strm.avail_out = sizeof(Uncompressed); - strm.next_in = (Bytef *)a_ChunkData.data(); - strm.avail_in = a_ChunkData.size(); - int res = inflate(&strm, Z_FINISH); - inflateEnd(&strm); - if (res != Z_STREAM_END) - { - fprintf(stderr, "Chunk [%d, %d] failed to decompress: error %d. Skipping chunk.", a_ChunkX, a_ChunkZ, res); - return; - } - - /* - // DEBUG: Output src to a file: - cFile f1; - if (f1.Open(Printf("chunk_%d_%d_in.nbt", a_ChunkX, a_ChunkZ), cFile::fmWrite)) - { - f1.Write(Uncompressed, strm.total_out); - } - //*/ - - cParsedNBT NBT(Uncompressed, strm.total_out); - if (!NBT.IsValid()) - { - fprintf(stderr, "Chunk [%d, %d] failed to parse. Skipping chunk.", a_ChunkX, a_ChunkZ); - return; - } - ZapRegionInNBTChunk(a_Region, NBT, a_ChunkX, a_ChunkZ); - - cFastNBTWriter Writer; - for (int ch = NBT.GetFirstChild(0); ch >= 0; ch = NBT.GetNextSibling(ch)) - { - SerializeNBTTag(NBT, ch, Writer); - } - Writer.Finish(); - - /* - // DEBUG: Output dst to a file: - cFile f2; - if (f2.Open(Printf("chunk_%d_%d_out.nbt", a_ChunkX, a_ChunkZ), cFile::fmWrite)) - { - f2.Write(Writer.GetResult().data(), Writer.GetResult().size()); - } - //*/ - - // Compress the serialized data into "Uncompressed" (reuse buffer) - CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_ChunkData); -} - - - - - -void cZapper::ZapRegionInNBTChunk(const cRegion & a_Region, cParsedNBT & a_NBT, int a_ChunkX, int a_ChunkZ) -{ - int LevelTag = a_NBT.FindChildByName(a_NBT.GetRoot(), "Level"); - if (LevelTag < 0) - { - fprintf(stderr, "Cannot find Level tag in chunk [%d, %d]'s NBT. Skipping chunk.", a_ChunkX, a_ChunkZ); - return; - } - - // Create a copy of the region and limit it to the current chunk: - int BlockX = a_ChunkX * 16; - int BlockZ = a_ChunkZ * 16; - cRegion Local; - Local.m_MinX = std::max(0, a_Region.m_MinX - BlockX); - Local.m_MaxX = std::min(15, a_Region.m_MaxX - BlockX); - Local.m_MinY = a_Region.m_MinY; - Local.m_MaxY = a_Region.m_MaxY; - Local.m_MinZ = std::max(0, a_Region.m_MinZ - BlockZ); - Local.m_MaxZ = std::min(15, a_Region.m_MaxZ - BlockZ); - - if (a_Region.m_ShouldZapBlocks) - { - int SectionsTag = a_NBT.FindChildByName(LevelTag, "Sections"); - if (SectionsTag < 0) - { - fprintf(stderr, "Cannot find the Sections tag in the Level tag in chunk [%d, %d]'s NBT. Skipping block-zapping in chunk.", a_ChunkX, a_ChunkZ); - return; - } - ZapRegionBlocksInNBT(Local, a_NBT, SectionsTag); - } - - if (a_Region.m_ShouldZapEntities) - { - int EntitiesTag = a_NBT.FindChildByName(LevelTag, "Entities"); - if (EntitiesTag < 0) - { - fprintf(stderr, "Cannot find the Entities tag in the Level tag in chunk [%d, %d]'s NBT. Skipping entity-zapping in chunk.", a_ChunkX, a_ChunkZ); - return; - } - ZapRegionEntitiesInNBT(Local, a_NBT, EntitiesTag); - } -} - - - - - -void cZapper::ZapRegionBlocksInNBT(const cRegion & a_Region, cParsedNBT & a_NBT, int a_SectionsTag) -{ - for (int Child = a_NBT.GetFirstChild(a_SectionsTag); Child >= 0; Child = a_NBT.GetNextSibling(Child)) - { - int y = 0; - int SectionY = a_NBT.FindChildByName(Child, "Y"); - if ((SectionY < 0) || (a_NBT.GetType(SectionY) != TAG_Byte)) - { - continue; - } - y = a_NBT.GetByte(SectionY); - if ((y * 16 > a_Region.m_MaxY) || (y * 16 + 16 < a_Region.m_MinY)) - { - continue; - } - int BlockDataTag = a_NBT.FindChildByName(Child, "Blocks"); - int BlockMetaTag = a_NBT.FindChildByName(Child, "Data"); - int BlockAddTag = a_NBT.FindChildByName(Child, "Add"); - if (BlockDataTag > 0) - { - ZapRegionInNBTSectionBytes(a_Region, y, (unsigned char *)(a_NBT.GetData(BlockDataTag))); - } - if (BlockMetaTag > 0) - { - ZapRegionInNBTSectionNibbles(a_Region, y, (unsigned char *)(a_NBT.GetData(BlockMetaTag))); - } - if (BlockAddTag > 0) - { - ZapRegionInNBTSectionNibbles(a_Region, y, (unsigned char *)(a_NBT.GetData(BlockAddTag))); - } - } // for Child - Level/Sections/[] -} - - - - - -void cZapper::ZapRegionInNBTSectionBytes(const cRegion & a_Region, int a_SectionY, unsigned char * a_BlockBytes) -{ - int MinY = std::max(0, a_Region.m_MinY - a_SectionY * 16); - int MaxY = std::min(15, a_Region.m_MaxY - a_SectionY * 16); - ASSERT(MinY >= 0); - ASSERT(MaxY >= 0); - for (int y = MinY; y <= MaxY; y++) - { - for (int z = a_Region.m_MinZ; z <= a_Region.m_MaxZ; z++) - { - for (int x = a_Region.m_MinX; x <= a_Region.m_MaxX; x++) - { - a_BlockBytes[x + z * 16 + y * 16 * 16] = 0; - } - } - } -} - - - - - -void cZapper::ZapRegionInNBTSectionNibbles(const cRegion & a_Region, int a_SectionY, unsigned char * a_BlockNibbles) -{ - int MinY = std::max(0, a_Region.m_MinY - a_SectionY * 16); - int MaxY = std::min(15, a_Region.m_MaxY - a_SectionY * 16); - ASSERT(MinY >= 0); - ASSERT(MaxY >= 0); - for (int y = MinY; y <= MaxY; y++) - { - for (int z = a_Region.m_MinZ; z < a_Region.m_MaxZ; z++) - { - for (int x = a_Region.m_MinX; x < a_Region.m_MaxX; x++) - { - cChunkDef::SetNibble(a_BlockNibbles, x, y, z, 0); - } - } - } -} - - - - - -void cZapper::ZapRegionEntitiesInNBT(const cRegion & a_Region, cParsedNBT & a_NBT, int a_EntitiesTag) -{ - // TODO -} - - - - - -void cZapper::SerializeNBTTag(const cParsedNBT & a_NBT, int a_Tag, cFastNBTWriter & a_Writer) -{ - switch (a_NBT.GetType(a_Tag)) - { - case TAG_Byte: a_Writer.AddByte (a_NBT.GetName(a_Tag), a_NBT.GetByte (a_Tag)); break; - case TAG_Short: a_Writer.AddShort (a_NBT.GetName(a_Tag), a_NBT.GetShort (a_Tag)); break; - case TAG_Int: a_Writer.AddInt (a_NBT.GetName(a_Tag), a_NBT.GetInt (a_Tag)); break; - case TAG_Long: a_Writer.AddLong (a_NBT.GetName(a_Tag), a_NBT.GetLong (a_Tag)); break; - case TAG_Float: a_Writer.AddFloat (a_NBT.GetName(a_Tag), a_NBT.GetFloat (a_Tag)); break; - case TAG_Double: a_Writer.AddDouble (a_NBT.GetName(a_Tag), a_NBT.GetDouble(a_Tag)); break; - case TAG_ByteArray: a_Writer.AddByteArray(a_NBT.GetName(a_Tag), a_NBT.GetData (a_Tag), a_NBT.GetDataLength(a_Tag)); break; - case TAG_String: a_Writer.AddString (a_NBT.GetName(a_Tag), a_NBT.GetString(a_Tag)); break; - case TAG_IntArray: - { - std::vector Data; - int NumInts = a_NBT.GetDataLength(a_Tag) / 4; - Data.reserve(NumInts); - int * OrigData = (int *)(a_NBT.GetData(a_Tag)); - for (int i = 0; i < NumInts; i++) - { - Data.push_back(ntohl(OrigData[i])); - } - a_Writer.AddIntArray (a_NBT.GetName(a_Tag), &Data.front(), Data.size()); break; - } - - case TAG_List: - { - a_Writer.BeginList(a_NBT.GetName(a_Tag), a_NBT.GetChildrenType(a_Tag)); - for (int ch = a_NBT.GetFirstChild(a_Tag); ch >= 0; ch = a_NBT.GetNextSibling(ch)) - { - SerializeNBTTag(a_NBT, ch, a_Writer); - } // for ch - children[] - a_Writer.EndList(); - break; - } - - case TAG_Compound: - { - a_Writer.BeginCompound(a_NBT.GetName(a_Tag)); - for (int ch = a_NBT.GetFirstChild(a_Tag); ch >= 0; ch = a_NBT.GetNextSibling(ch)) - { - SerializeNBTTag(a_NBT, ch, a_Writer); - } // for ch - children[] - a_Writer.EndCompound(); - break; - } - - default: - { - ASSERT(!"Unknown NBT tag"); - break; - } - } -} - - - - + +// Zapper.cpp + +// Implements the cZapper class representing the processor that actually zaps blocks and entities + +#include "Globals.h" + +#include "WorldStorage/FastNBT.h" +#include "StringCompression.h" +#include "zlib.h" + +#include "Zapper.h" + + + + + +/// The maximum size of an inflated chunk; raw chunk data is 192 KiB, allow 64 KiB more of entities +#define CHUNK_INFLATE_MAX 256 KiB + + + + + +cZapper::cZapper(const AString & a_MCAFolder) : + m_MCAFolder(a_MCAFolder) +{ +} + + + + + +void cZapper::ZapRegions(const cRegionVector & a_Regions) +{ + for (cRegionVector::const_iterator itr = a_Regions.begin(), end = a_Regions.end(); itr != end; ++itr) + { + int MinAnvX, MinAnvZ; + int MaxAnvX, MaxAnvZ; + BlockToMCA(itr->m_MinX, itr->m_MinZ, MinAnvX, MinAnvZ); + BlockToMCA(itr->m_MaxX, itr->m_MaxZ, MaxAnvX, MaxAnvZ); + for (int x = MinAnvX; x <= MaxAnvX; x++) + { + for (int z = MinAnvZ; z <= MaxAnvZ; z++) + { + ZapRegionInMCAFile(*itr, x, z); + } + } + } // for itr - a_Regions +} + + + + + +void cZapper::BlockToMCA(int a_BlockX, int a_BlockZ, int & a_MCAX, int & a_MCAZ) +{ + // These need to be arithmetic shifts, consult your compiler documentation to see if it's so + // MSVC and GCC both use arithmetic shifts + a_MCAX = a_BlockX >> 10; + a_MCAZ = a_BlockZ >> 10; +} + + + + + +void cZapper::BlockToChunk(int a_BlockX, int a_BlockZ, int & a_ChunkX, int & a_ChunkZ) +{ + // These need to be arithmetic shifts, consult your compiler documentation to see if it's so + // MSVC and GCC both use arithmetic shifts + a_ChunkX = a_BlockX >> 4; + a_ChunkZ = a_BlockZ >> 4; +} + + + + + +void cZapper::ZapRegionInMCAFile(const cRegion & a_Region, int a_MCAX, int a_MCAZ) +{ + cFile fIn; + AString FileNameIn = Printf("%s/r.%d.%d.mca", m_MCAFolder.c_str(), a_MCAX, a_MCAZ); + if (!fIn.Open(FileNameIn, cFile::fmRead)) + { + return; + } + cFile fOut; + AString FileNameOut = Printf("%s/r.%d.%d.zap", m_MCAFolder.c_str(), a_MCAX, a_MCAZ); + if (!fOut.Open(FileNameOut, cFile::fmWrite)) + { + fprintf(stderr, "Cannot open temporary file \"%s\" for writing, skipping file \"%s\".", FileNameOut.c_str(), FileNameIn.c_str()); + return; + } + + AString DataOut; + DataOut.reserve(fIn.GetSize()); + + int HeaderIn[2048]; + if (fIn.Read(HeaderIn, sizeof(HeaderIn)) != sizeof(HeaderIn)) + { + fprintf(stderr, "Cannot read header from file \"%s\", skipping file.", FileNameIn.c_str()); + } + int HeaderOut[2048]; + for (int i = 0; i < 1024; i++) + { + if (HeaderIn[i] == 0) + { + // Chunk not present + HeaderOut[i] = 0; + continue; + } + AString ChunkData; + int ChunkX = a_MCAX * ChunksPerMCAX + (i % ChunksPerMCAX); + int ChunkZ = a_MCAZ * ChunksPerMCAZ + (i / ChunksPerMCAX); + + LoadChunkData(fIn, HeaderIn[i], ChunkData, ChunkX, ChunkZ); + + if (a_Region.TouchesChunk(ChunkX, ChunkZ)) + { + ZapRegionInRawChunkData(a_Region, ChunkData, ChunkX, ChunkZ); + } + unsigned char ChunkHeader[5]; + size_t DataSize = ChunkData.size() + 1; + ChunkHeader[0] = (DataSize >> 24) & 0xff; + ChunkHeader[1] = (DataSize >> 16) & 0xff; + ChunkHeader[2] = (DataSize >> 8) & 0xff; + ChunkHeader[3] = DataSize & 0xff; + ChunkHeader[4] = 2; // zlib compression + size_t Alignment = 4096 - (ChunkData.size() + 5) % 4096; // 5 bytes of the header are appended outside of ChunkData + if (Alignment > 0) + { + ChunkData.append(Alignment, (char)0); + } + HeaderOut[i] = htonl(((DataOut.size() / 4096 + 2) << 8) | ((ChunkData.size() + 5) / 4096)); + DataOut.append((const char *)ChunkHeader, sizeof(ChunkHeader)); + DataOut.append(ChunkData); + } // for i - chunks in fIn + for (int i = 1024; i < 2048; i++) + { + HeaderOut[i] = HeaderIn[i]; + } + fIn.Close(); + fOut.Write(HeaderOut, sizeof(HeaderOut)); + fOut.Write(DataOut.data(), DataOut.size()); + fOut.Close(); + cFile::Delete(FileNameIn); + cFile::Rename(FileNameOut, FileNameIn); +} + + + + + +void cZapper::LoadChunkData(cFile & a_InFile, int a_ChunkHeaderValue, AString & a_ChunkData, int a_ChunkX, int a_ChunkZ) +{ + a_ChunkHeaderValue = ntohl(a_ChunkHeaderValue); // Convert from big-endian to system-endian + int ChunkOffset = (a_ChunkHeaderValue >> 8) * 4096; + int ChunkSize = (a_ChunkHeaderValue & 0xff) * 4096; + a_InFile.Seek(ChunkOffset); + unsigned char ChunkHeader[5]; + a_InFile.Read(ChunkHeader, sizeof(ChunkHeader)); + if (ChunkHeader[4] != 2) + { + fprintf(stderr, "Chunk [%d, %d] is compressed in an unknown scheme (%d), skipping", a_ChunkX, a_ChunkZ, ChunkHeader[5]); + return; + } + int ActualSize = (ChunkHeader[0] << 24) | (ChunkHeader[1] << 16) | (ChunkHeader[2] << 8) | ChunkHeader[3]; + ActualSize -= 1; // Compression took 1 byte + a_ChunkData.resize(ActualSize); + int BytesRead = a_InFile.Read((void *)(a_ChunkData.data()), ActualSize); + if (BytesRead != ActualSize) + { + fprintf(stderr, "Chunk is truncated in file (%d bytes out of %d), skipping.", BytesRead, ActualSize); + a_ChunkData.clear(); + return; + } +} + + + + + +void cZapper::ZapRegionInRawChunkData(const cRegion & a_Region, AString & a_ChunkData, int a_ChunkX, int a_ChunkZ) +{ + // Decompress the data: + char Uncompressed[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 *)Uncompressed; + strm.avail_out = sizeof(Uncompressed); + strm.next_in = (Bytef *)a_ChunkData.data(); + strm.avail_in = a_ChunkData.size(); + int res = inflate(&strm, Z_FINISH); + inflateEnd(&strm); + if (res != Z_STREAM_END) + { + fprintf(stderr, "Chunk [%d, %d] failed to decompress: error %d. Skipping chunk.", a_ChunkX, a_ChunkZ, res); + return; + } + + /* + // DEBUG: Output src to a file: + cFile f1; + if (f1.Open(Printf("chunk_%d_%d_in.nbt", a_ChunkX, a_ChunkZ), cFile::fmWrite)) + { + f1.Write(Uncompressed, strm.total_out); + } + //*/ + + cParsedNBT NBT(Uncompressed, strm.total_out); + if (!NBT.IsValid()) + { + fprintf(stderr, "Chunk [%d, %d] failed to parse. Skipping chunk.", a_ChunkX, a_ChunkZ); + return; + } + ZapRegionInNBTChunk(a_Region, NBT, a_ChunkX, a_ChunkZ); + + cFastNBTWriter Writer; + for (int ch = NBT.GetFirstChild(0); ch >= 0; ch = NBT.GetNextSibling(ch)) + { + SerializeNBTTag(NBT, ch, Writer); + } + Writer.Finish(); + + /* + // DEBUG: Output dst to a file: + cFile f2; + if (f2.Open(Printf("chunk_%d_%d_out.nbt", a_ChunkX, a_ChunkZ), cFile::fmWrite)) + { + f2.Write(Writer.GetResult().data(), Writer.GetResult().size()); + } + //*/ + + // Compress the serialized data into "Uncompressed" (reuse buffer) + CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_ChunkData); +} + + + + + +void cZapper::ZapRegionInNBTChunk(const cRegion & a_Region, cParsedNBT & a_NBT, int a_ChunkX, int a_ChunkZ) +{ + int LevelTag = a_NBT.FindChildByName(a_NBT.GetRoot(), "Level"); + if (LevelTag < 0) + { + fprintf(stderr, "Cannot find Level tag in chunk [%d, %d]'s NBT. Skipping chunk.", a_ChunkX, a_ChunkZ); + return; + } + + // Create a copy of the region and limit it to the current chunk: + int BlockX = a_ChunkX * 16; + int BlockZ = a_ChunkZ * 16; + cRegion Local; + Local.m_MinX = std::max(0, a_Region.m_MinX - BlockX); + Local.m_MaxX = std::min(15, a_Region.m_MaxX - BlockX); + Local.m_MinY = a_Region.m_MinY; + Local.m_MaxY = a_Region.m_MaxY; + Local.m_MinZ = std::max(0, a_Region.m_MinZ - BlockZ); + Local.m_MaxZ = std::min(15, a_Region.m_MaxZ - BlockZ); + + if (a_Region.m_ShouldZapBlocks) + { + int SectionsTag = a_NBT.FindChildByName(LevelTag, "Sections"); + if (SectionsTag < 0) + { + fprintf(stderr, "Cannot find the Sections tag in the Level tag in chunk [%d, %d]'s NBT. Skipping block-zapping in chunk.", a_ChunkX, a_ChunkZ); + return; + } + ZapRegionBlocksInNBT(Local, a_NBT, SectionsTag); + } + + if (a_Region.m_ShouldZapEntities) + { + int EntitiesTag = a_NBT.FindChildByName(LevelTag, "Entities"); + if (EntitiesTag < 0) + { + fprintf(stderr, "Cannot find the Entities tag in the Level tag in chunk [%d, %d]'s NBT. Skipping entity-zapping in chunk.", a_ChunkX, a_ChunkZ); + return; + } + ZapRegionEntitiesInNBT(Local, a_NBT, EntitiesTag); + } +} + + + + + +void cZapper::ZapRegionBlocksInNBT(const cRegion & a_Region, cParsedNBT & a_NBT, int a_SectionsTag) +{ + for (int Child = a_NBT.GetFirstChild(a_SectionsTag); Child >= 0; Child = a_NBT.GetNextSibling(Child)) + { + int y = 0; + int SectionY = a_NBT.FindChildByName(Child, "Y"); + if ((SectionY < 0) || (a_NBT.GetType(SectionY) != TAG_Byte)) + { + continue; + } + y = a_NBT.GetByte(SectionY); + if ((y * 16 > a_Region.m_MaxY) || (y * 16 + 16 < a_Region.m_MinY)) + { + continue; + } + int BlockDataTag = a_NBT.FindChildByName(Child, "Blocks"); + int BlockMetaTag = a_NBT.FindChildByName(Child, "Data"); + int BlockAddTag = a_NBT.FindChildByName(Child, "Add"); + if (BlockDataTag > 0) + { + ZapRegionInNBTSectionBytes(a_Region, y, (unsigned char *)(a_NBT.GetData(BlockDataTag))); + } + if (BlockMetaTag > 0) + { + ZapRegionInNBTSectionNibbles(a_Region, y, (unsigned char *)(a_NBT.GetData(BlockMetaTag))); + } + if (BlockAddTag > 0) + { + ZapRegionInNBTSectionNibbles(a_Region, y, (unsigned char *)(a_NBT.GetData(BlockAddTag))); + } + } // for Child - Level/Sections/[] +} + + + + + +void cZapper::ZapRegionInNBTSectionBytes(const cRegion & a_Region, int a_SectionY, unsigned char * a_BlockBytes) +{ + int MinY = std::max(0, a_Region.m_MinY - a_SectionY * 16); + int MaxY = std::min(15, a_Region.m_MaxY - a_SectionY * 16); + ASSERT(MinY >= 0); + ASSERT(MaxY >= 0); + for (int y = MinY; y <= MaxY; y++) + { + for (int z = a_Region.m_MinZ; z <= a_Region.m_MaxZ; z++) + { + for (int x = a_Region.m_MinX; x <= a_Region.m_MaxX; x++) + { + a_BlockBytes[x + z * 16 + y * 16 * 16] = 0; + } + } + } +} + + + + + +void cZapper::ZapRegionInNBTSectionNibbles(const cRegion & a_Region, int a_SectionY, unsigned char * a_BlockNibbles) +{ + int MinY = std::max(0, a_Region.m_MinY - a_SectionY * 16); + int MaxY = std::min(15, a_Region.m_MaxY - a_SectionY * 16); + ASSERT(MinY >= 0); + ASSERT(MaxY >= 0); + for (int y = MinY; y <= MaxY; y++) + { + for (int z = a_Region.m_MinZ; z < a_Region.m_MaxZ; z++) + { + for (int x = a_Region.m_MinX; x < a_Region.m_MaxX; x++) + { + cChunkDef::SetNibble(a_BlockNibbles, x, y, z, 0); + } + } + } +} + + + + + +void cZapper::ZapRegionEntitiesInNBT(const cRegion & a_Region, cParsedNBT & a_NBT, int a_EntitiesTag) +{ + // TODO +} + + + + + +void cZapper::SerializeNBTTag(const cParsedNBT & a_NBT, int a_Tag, cFastNBTWriter & a_Writer) +{ + switch (a_NBT.GetType(a_Tag)) + { + case TAG_Byte: a_Writer.AddByte (a_NBT.GetName(a_Tag), a_NBT.GetByte (a_Tag)); break; + case TAG_Short: a_Writer.AddShort (a_NBT.GetName(a_Tag), a_NBT.GetShort (a_Tag)); break; + case TAG_Int: a_Writer.AddInt (a_NBT.GetName(a_Tag), a_NBT.GetInt (a_Tag)); break; + case TAG_Long: a_Writer.AddLong (a_NBT.GetName(a_Tag), a_NBT.GetLong (a_Tag)); break; + case TAG_Float: a_Writer.AddFloat (a_NBT.GetName(a_Tag), a_NBT.GetFloat (a_Tag)); break; + case TAG_Double: a_Writer.AddDouble (a_NBT.GetName(a_Tag), a_NBT.GetDouble(a_Tag)); break; + case TAG_ByteArray: a_Writer.AddByteArray(a_NBT.GetName(a_Tag), a_NBT.GetData (a_Tag), a_NBT.GetDataLength(a_Tag)); break; + case TAG_String: a_Writer.AddString (a_NBT.GetName(a_Tag), a_NBT.GetString(a_Tag)); break; + case TAG_IntArray: + { + std::vector Data; + int NumInts = a_NBT.GetDataLength(a_Tag) / 4; + Data.reserve(NumInts); + int * OrigData = (int *)(a_NBT.GetData(a_Tag)); + for (int i = 0; i < NumInts; i++) + { + Data.push_back(ntohl(OrigData[i])); + } + a_Writer.AddIntArray (a_NBT.GetName(a_Tag), &Data.front(), Data.size()); break; + } + + case TAG_List: + { + a_Writer.BeginList(a_NBT.GetName(a_Tag), a_NBT.GetChildrenType(a_Tag)); + for (int ch = a_NBT.GetFirstChild(a_Tag); ch >= 0; ch = a_NBT.GetNextSibling(ch)) + { + SerializeNBTTag(a_NBT, ch, a_Writer); + } // for ch - children[] + a_Writer.EndList(); + break; + } + + case TAG_Compound: + { + a_Writer.BeginCompound(a_NBT.GetName(a_Tag)); + for (int ch = a_NBT.GetFirstChild(a_Tag); ch >= 0; ch = a_NBT.GetNextSibling(ch)) + { + SerializeNBTTag(a_NBT, ch, a_Writer); + } // for ch - children[] + a_Writer.EndCompound(); + break; + } + + default: + { + ASSERT(!"Unknown NBT tag"); + break; + } + } +} + + + + diff --git a/Tools/BlockZapper/Zapper.h b/Tools/BlockZapper/Zapper.h index 585c3a5ca..6bcd6aa2a 100644 --- a/Tools/BlockZapper/Zapper.h +++ b/Tools/BlockZapper/Zapper.h @@ -1,80 +1,80 @@ - -// Zapper.h - -// Declares the cZapper class representing the processor that actually zaps blocks and entities - - - - - -#pragma once - -#include "Regions.h" - - - - - -// fwd: ParsedNBT.h -class cParsedNBT; -class cFastNBTWriter; - - - - - -class cZapper -{ -public: - cZapper(const AString & a_MCAFolder); - - /// Zaps all the specified regions - void ZapRegions(const cRegionVector & a_Regions); - -protected: - static const int BlocksPerChunkX = 16; - static const int BlocksPerChunkZ = 16; - static const int ChunksPerMCAX = 32; - static const int ChunksPerMCAZ = 32; - - AString m_MCAFolder; - - /// Converts from block coords to MCA coords - void BlockToMCA(int a_BlockX, int a_BlockZ, int & a_MCAX, int & a_MCAZ); - - /// Converts from block coords to chunk coords - void BlockToChunk(int a_BlockX, int a_BlockZ, int & a_ChunkX, int & a_ChunkZ); - - /// Zaps the specified region in the MCA file with the specified MCA coords - void ZapRegionInMCAFile(const cRegion & a_Region, int a_MCAX, int a_MCAZ); - - /** Loads raw compressed chunk data from the specified file - * chunk is specified by ChunkHeaderValue, which is the int describing the chunk in file header. - */ - void LoadChunkData(cFile & a_InFile, int a_ChunkHeaderValue, AString & a_ChunkData, int a_ChunkX, int a_ChunkZ); - - /// Zaps the specified region in the raw (compressed) chunk data. - void ZapRegionInRawChunkData(const cRegion & a_Region, AString & a_ChunkData, int a_ChunkX, int a_ChunkZ); - - /// Zaps the specified region in the specified NBT structure - void ZapRegionInNBTChunk(const cRegion & a_Region, cParsedNBT & a_NBT, int a_ChunkX, int a_ChunkZ); - - /// Zaps the blocks in the specified region from the specified NBT - void ZapRegionBlocksInNBT(const cRegion & a_Region, cParsedNBT & a_NBT, int a_SectionsTag); - - /// Zaps the blocks in the specified bytes (types) from one vertical section (16^3 blocks) of a chunk. - void ZapRegionInNBTSectionBytes(const cRegion & a_Region, int a_SectionY, unsigned char * a_BlockBytes); - - /// Zaps the blocks in the specified nibbles (meta, add) from one vertical section (16^3 blocks) of a chunk. - void ZapRegionInNBTSectionNibbles(const cRegion & a_Region, int a_SectionY, unsigned char * a_BlockNibbles); - - /// Zaps entities in the specified region from the specified NBT - void ZapRegionEntitiesInNBT(const cRegion & a_Region, cParsedNBT & a_NBT, int a_EntitiesTag); - - /// Serializes the NBT subtree into a writer - void SerializeNBTTag(const cParsedNBT & a_NBT, int a_Tag, cFastNBTWriter & a_Writer); -} ; - - - - + +// Zapper.h + +// Declares the cZapper class representing the processor that actually zaps blocks and entities + + + + + +#pragma once + +#include "Regions.h" + + + + + +// fwd: ParsedNBT.h +class cParsedNBT; +class cFastNBTWriter; + + + + + +class cZapper +{ +public: + cZapper(const AString & a_MCAFolder); + + /// Zaps all the specified regions + void ZapRegions(const cRegionVector & a_Regions); + +protected: + static const int BlocksPerChunkX = 16; + static const int BlocksPerChunkZ = 16; + static const int ChunksPerMCAX = 32; + static const int ChunksPerMCAZ = 32; + + AString m_MCAFolder; + + /// Converts from block coords to MCA coords + void BlockToMCA(int a_BlockX, int a_BlockZ, int & a_MCAX, int & a_MCAZ); + + /// Converts from block coords to chunk coords + void BlockToChunk(int a_BlockX, int a_BlockZ, int & a_ChunkX, int & a_ChunkZ); + + /// Zaps the specified region in the MCA file with the specified MCA coords + void ZapRegionInMCAFile(const cRegion & a_Region, int a_MCAX, int a_MCAZ); + + /** Loads raw compressed chunk data from the specified file + * chunk is specified by ChunkHeaderValue, which is the int describing the chunk in file header. + */ + void LoadChunkData(cFile & a_InFile, int a_ChunkHeaderValue, AString & a_ChunkData, int a_ChunkX, int a_ChunkZ); + + /// Zaps the specified region in the raw (compressed) chunk data. + void ZapRegionInRawChunkData(const cRegion & a_Region, AString & a_ChunkData, int a_ChunkX, int a_ChunkZ); + + /// Zaps the specified region in the specified NBT structure + void ZapRegionInNBTChunk(const cRegion & a_Region, cParsedNBT & a_NBT, int a_ChunkX, int a_ChunkZ); + + /// Zaps the blocks in the specified region from the specified NBT + void ZapRegionBlocksInNBT(const cRegion & a_Region, cParsedNBT & a_NBT, int a_SectionsTag); + + /// Zaps the blocks in the specified bytes (types) from one vertical section (16^3 blocks) of a chunk. + void ZapRegionInNBTSectionBytes(const cRegion & a_Region, int a_SectionY, unsigned char * a_BlockBytes); + + /// Zaps the blocks in the specified nibbles (meta, add) from one vertical section (16^3 blocks) of a chunk. + void ZapRegionInNBTSectionNibbles(const cRegion & a_Region, int a_SectionY, unsigned char * a_BlockNibbles); + + /// Zaps entities in the specified region from the specified NBT + void ZapRegionEntitiesInNBT(const cRegion & a_Region, cParsedNBT & a_NBT, int a_EntitiesTag); + + /// Serializes the NBT subtree into a writer + void SerializeNBTTag(const cParsedNBT & a_NBT, int a_Tag, cFastNBTWriter & a_Writer); +} ; + + + + diff --git a/Tools/MemDumpAnalysis/Globals.cpp b/Tools/MemDumpAnalysis/Globals.cpp index 6ec09efda..5b366f68e 100644 --- a/Tools/MemDumpAnalysis/Globals.cpp +++ b/Tools/MemDumpAnalysis/Globals.cpp @@ -1,10 +1,10 @@ - -// Globals.cpp - -// Used for precompiled header generation - -#include "Globals.h" - - - - + +// Globals.cpp + +// Used for precompiled header generation + +#include "Globals.h" + + + + diff --git a/Tools/MemDumpAnalysis/Globals.h b/Tools/MemDumpAnalysis/Globals.h index 5208a9fa5..92d27da2c 100644 --- a/Tools/MemDumpAnalysis/Globals.h +++ b/Tools/MemDumpAnalysis/Globals.h @@ -1,35 +1,35 @@ - -// Globals.h - -// Used for precompiled header generation - - - - - -#pragma once - -#include "../../source/Globals.h" - -/* -// System headers: -#include "targetver.h" -#include - -// STL headers: -#include -#include -#include -#include - -// Common: -#include "../source/StringUtils.h" -#include "../source/OSSupport/File.h" -*/ - -// Libraries: -#include "../../expat/expat.h" - - - - + +// Globals.h + +// Used for precompiled header generation + + + + + +#pragma once + +#include "../../source/Globals.h" + +/* +// System headers: +#include "targetver.h" +#include + +// STL headers: +#include +#include +#include +#include + +// Common: +#include "../source/StringUtils.h" +#include "../source/OSSupport/File.h" +*/ + +// Libraries: +#include "../../expat/expat.h" + + + + diff --git a/Tools/MemDumpAnalysis/MemDumpAnalysis.cpp b/Tools/MemDumpAnalysis/MemDumpAnalysis.cpp index 9e7759085..9faaef20f 100644 --- a/Tools/MemDumpAnalysis/MemDumpAnalysis.cpp +++ b/Tools/MemDumpAnalysis/MemDumpAnalysis.cpp @@ -1,321 +1,321 @@ - -// MemDumpAnalysis.cpp - -// Defines the entry point for the console application. - -#include "Globals.h" - -#ifdef _WIN32 - #pragma comment(lib, "ws2_32.lib") // Needed for StringUtils' RawBEToUtf8() et al. -#endif // _WIN32 - - - - - -typedef std::set AStringSet; - - - - - -class cFunction -{ -public: - int m_Size; ///< Sum of memory block sizes allocated by this function or its children - int m_Count; ///< Total number of memory blocks allocated by this function or its children - AStringSet m_ChildrenNames; - - cFunction(void) : - m_Size(0), - m_Count(0) - { - } -} ; - -typedef std::map FunctionMap; - - - - - -int g_CurrentID = 0; -int g_CurrentSize = 0; -FunctionMap g_FnMap; -AString g_PrevFunctionName; - - - - - -bool IsFnBlackListed(const char * a_FnName) -{ - static const char * BlackList[] = - { - "MyAllocHook", - "_heap_alloc_dbg_impl", - "_nh_malloc_dbg_impl", - "_nh_malloc_dbg", - "malloc", - "operator new", - "_malloc_dbg", - "realloc_help", - "_realloc_dbg", - "realloc", - "l_alloc", - "luaM_realloc_", - "", - } ; - - for (int i = 0; i < ARRAYCOUNT(BlackList); i++) - { - if (strcmp(BlackList[i], a_FnName) == 0) - { - return true; - } - } - return false; -} - - - - - -const char * FindAttr(const char ** a_Attrs, const char * a_AttrName) -{ - for (const char ** Attr = a_Attrs; *Attr != NULL; Attr += 2) - { - if (strcmp(*Attr, a_AttrName) == 0) - { - return *(Attr + 1); - } - } // for Attr - a_Attrs[] - return NULL; -} - - - - - -void OnStartElement(void * a_Data, const char * a_Element, const char ** a_Attrs) -{ - if (strcmp(a_Element, "LEAK") == 0) - { - const char * attrID = FindAttr(a_Attrs, "requestID"); - const char * attrSize = FindAttr(a_Attrs, "size"); - g_CurrentID = atoi((attrID == NULL) ? "-1" : attrID); - g_CurrentSize = atoi((attrSize == NULL) ? "-1" : attrSize); - g_PrevFunctionName.clear(); - return; - } - if (strcmp(a_Element, "STACKENTRY") == 0) - { - const char * fnName = FindAttr(a_Attrs, "decl"); - if (fnName == NULL) - { - g_CurrentID = -1; - g_CurrentSize = -1; - return; - } - if (g_CurrentSize < 0) - { - return; - } - if (IsFnBlackListed(fnName)) - { - return; - } - AString FunctionName = fnName; - cFunction & Function = g_FnMap[FunctionName]; - Function.m_Size += g_CurrentSize; - Function.m_Count += 1; - if (!g_PrevFunctionName.empty()) - { - Function.m_ChildrenNames.insert(g_PrevFunctionName); - } - std::swap(g_PrevFunctionName, FunctionName); // We only care about moving FunctionName into g_PrevFunctionName - return; - } -} - - - - - -void OnEndElement(void * a_Data, const char * a_Element) -{ - if (strcmp(a_Element, "LEAK") == 0) - { - g_CurrentID = -1; - g_CurrentSize = -1; - return; - } -} - - - - - -bool CompareFnInt(const std::pair & a_First, const std::pair & a_Second) -{ - return (a_First.second < a_Second.second); -} - - - - - -void WriteSizeStatistics(void) -{ - typedef std::vector > StringIntPairs; - StringIntPairs FnSizes; - - cFile f("memdump_totals.txt", cFile::fmWrite); - if (!f.IsOpen()) - { - LOGERROR("Cannot open memdump_totals.txt"); - return; - } - - for (FunctionMap::iterator itr = g_FnMap.begin(), end = g_FnMap.end(); itr != end; ++itr) - { - FnSizes.push_back(std::pair(itr->first, itr->second.m_Size)); - } // for itr - g_FnSizes[] - std::sort(FnSizes.begin(), FnSizes.end(), CompareFnInt); - - for (StringIntPairs::const_iterator itr = FnSizes.begin(), end = FnSizes.end(); itr != end; ++itr) - { - f.Printf("%d\t%s\n", itr->second, itr->first.c_str()); - } // for itr - FnSizes[] -} - - - - - -void WriteCountStatistics(void) -{ - typedef std::vector > StringIntPairs; - StringIntPairs FnCounts; - - cFile f("memdump_counts.txt", cFile::fmWrite); - if (!f.IsOpen()) - { - LOGERROR("Cannot open memdump_counts.txt"); - return; - } - - for (FunctionMap::iterator itr = g_FnMap.begin(), end = g_FnMap.end(); itr != end; ++itr) - { - FnCounts.push_back(std::pair(itr->first, itr->second.m_Count)); - } // for itr - g_FnSizes[] - std::sort(FnCounts.begin(), FnCounts.end(), CompareFnInt); - - for (StringIntPairs::const_iterator itr = FnCounts.begin(), end = FnCounts.end(); itr != end; ++itr) - { - f.Printf("%d\t%s\n", itr->second, itr->first.c_str()); - } // for itr - FnSizes[] -} - - - - - -AString HTMLEscape(const AString & a_Text) -{ - AString res; - res.reserve(a_Text.size()); - size_t len = a_Text.length(); - for (size_t i = 0; i < len; i++) - { - switch (a_Text[i]) - { - case '<': res.append("<
"); break; - case '>': res.append("
>"); break; - case '&': res.append("&"); break; - default: - { - res.push_back(a_Text[i]); - } - } - } // for i - a_Text[] - return res; -} - - - - - -void WriteDotGraph(void) -{ - cFile f("memdump.dot", cFile::fmWrite); - if (!f.IsOpen()) - { - LOGERROR("Cannot open memdump.dot"); - return; - } - - f.Printf("digraph {\n\tnode [shape=plaintext]\n\n"); - for (FunctionMap::const_iterator itrF = g_FnMap.begin(), endF = g_FnMap.end(); itrF != endF; ++itrF) - { - f.Printf("\t\"%s\" [label=<%s
%d bytes (%d KiB)
%d blocks>]\n", - itrF->first.c_str(), - HTMLEscape(itrF->first).c_str(), - itrF->second.m_Size, - (itrF->second.m_Size + 1023) / 1024, - itrF->second.m_Count - ); - const AStringSet & Children = itrF->second.m_ChildrenNames; - for (AStringSet::const_iterator itrN = Children.begin(), endN = Children.end(); itrN != endN; ++itrN) - { - f.Printf("\t\t\"%s\" -> \"%s\"\n", itrF->first.c_str(), itrN->c_str()); - } - f.Printf("\n"); - } // for itr - f.Printf("}\n"); -} - - - - - -int main(int argc, char * argv[]) -{ - // Open the dump file: - cFile f("memdump.xml", cFile::fmRead); - if (!f.IsOpen()) - { - printf("Cannot open memdump.xml\n"); - return 1; - } - - // Create the XML parser: - XML_Parser Parser = XML_ParserCreate(NULL); - XML_SetElementHandler(Parser, OnStartElement, OnEndElement); - - // Feed the file through XML parser: - char Buffer[512 KiB]; - while (true) - { - int NumBytes = f.Read(Buffer, sizeof(Buffer)); - if (NumBytes <= 0) - { - break; - } - XML_Parse(Parser, Buffer, NumBytes, false); - putc('.', stdout); - } - XML_Parse(Parser, "", 0, true); - f.Close(); - - // Output the statistics - WriteSizeStatistics(); - WriteCountStatistics(); - WriteDotGraph(); - - return 0; -} - - - - + +// MemDumpAnalysis.cpp + +// Defines the entry point for the console application. + +#include "Globals.h" + +#ifdef _WIN32 + #pragma comment(lib, "ws2_32.lib") // Needed for StringUtils' RawBEToUtf8() et al. +#endif // _WIN32 + + + + + +typedef std::set AStringSet; + + + + + +class cFunction +{ +public: + int m_Size; ///< Sum of memory block sizes allocated by this function or its children + int m_Count; ///< Total number of memory blocks allocated by this function or its children + AStringSet m_ChildrenNames; + + cFunction(void) : + m_Size(0), + m_Count(0) + { + } +} ; + +typedef std::map FunctionMap; + + + + + +int g_CurrentID = 0; +int g_CurrentSize = 0; +FunctionMap g_FnMap; +AString g_PrevFunctionName; + + + + + +bool IsFnBlackListed(const char * a_FnName) +{ + static const char * BlackList[] = + { + "MyAllocHook", + "_heap_alloc_dbg_impl", + "_nh_malloc_dbg_impl", + "_nh_malloc_dbg", + "malloc", + "operator new", + "_malloc_dbg", + "realloc_help", + "_realloc_dbg", + "realloc", + "l_alloc", + "luaM_realloc_", + "", + } ; + + for (int i = 0; i < ARRAYCOUNT(BlackList); i++) + { + if (strcmp(BlackList[i], a_FnName) == 0) + { + return true; + } + } + return false; +} + + + + + +const char * FindAttr(const char ** a_Attrs, const char * a_AttrName) +{ + for (const char ** Attr = a_Attrs; *Attr != NULL; Attr += 2) + { + if (strcmp(*Attr, a_AttrName) == 0) + { + return *(Attr + 1); + } + } // for Attr - a_Attrs[] + return NULL; +} + + + + + +void OnStartElement(void * a_Data, const char * a_Element, const char ** a_Attrs) +{ + if (strcmp(a_Element, "LEAK") == 0) + { + const char * attrID = FindAttr(a_Attrs, "requestID"); + const char * attrSize = FindAttr(a_Attrs, "size"); + g_CurrentID = atoi((attrID == NULL) ? "-1" : attrID); + g_CurrentSize = atoi((attrSize == NULL) ? "-1" : attrSize); + g_PrevFunctionName.clear(); + return; + } + if (strcmp(a_Element, "STACKENTRY") == 0) + { + const char * fnName = FindAttr(a_Attrs, "decl"); + if (fnName == NULL) + { + g_CurrentID = -1; + g_CurrentSize = -1; + return; + } + if (g_CurrentSize < 0) + { + return; + } + if (IsFnBlackListed(fnName)) + { + return; + } + AString FunctionName = fnName; + cFunction & Function = g_FnMap[FunctionName]; + Function.m_Size += g_CurrentSize; + Function.m_Count += 1; + if (!g_PrevFunctionName.empty()) + { + Function.m_ChildrenNames.insert(g_PrevFunctionName); + } + std::swap(g_PrevFunctionName, FunctionName); // We only care about moving FunctionName into g_PrevFunctionName + return; + } +} + + + + + +void OnEndElement(void * a_Data, const char * a_Element) +{ + if (strcmp(a_Element, "LEAK") == 0) + { + g_CurrentID = -1; + g_CurrentSize = -1; + return; + } +} + + + + + +bool CompareFnInt(const std::pair & a_First, const std::pair & a_Second) +{ + return (a_First.second < a_Second.second); +} + + + + + +void WriteSizeStatistics(void) +{ + typedef std::vector > StringIntPairs; + StringIntPairs FnSizes; + + cFile f("memdump_totals.txt", cFile::fmWrite); + if (!f.IsOpen()) + { + LOGERROR("Cannot open memdump_totals.txt"); + return; + } + + for (FunctionMap::iterator itr = g_FnMap.begin(), end = g_FnMap.end(); itr != end; ++itr) + { + FnSizes.push_back(std::pair(itr->first, itr->second.m_Size)); + } // for itr - g_FnSizes[] + std::sort(FnSizes.begin(), FnSizes.end(), CompareFnInt); + + for (StringIntPairs::const_iterator itr = FnSizes.begin(), end = FnSizes.end(); itr != end; ++itr) + { + f.Printf("%d\t%s\n", itr->second, itr->first.c_str()); + } // for itr - FnSizes[] +} + + + + + +void WriteCountStatistics(void) +{ + typedef std::vector > StringIntPairs; + StringIntPairs FnCounts; + + cFile f("memdump_counts.txt", cFile::fmWrite); + if (!f.IsOpen()) + { + LOGERROR("Cannot open memdump_counts.txt"); + return; + } + + for (FunctionMap::iterator itr = g_FnMap.begin(), end = g_FnMap.end(); itr != end; ++itr) + { + FnCounts.push_back(std::pair(itr->first, itr->second.m_Count)); + } // for itr - g_FnSizes[] + std::sort(FnCounts.begin(), FnCounts.end(), CompareFnInt); + + for (StringIntPairs::const_iterator itr = FnCounts.begin(), end = FnCounts.end(); itr != end; ++itr) + { + f.Printf("%d\t%s\n", itr->second, itr->first.c_str()); + } // for itr - FnSizes[] +} + + + + + +AString HTMLEscape(const AString & a_Text) +{ + AString res; + res.reserve(a_Text.size()); + size_t len = a_Text.length(); + for (size_t i = 0; i < len; i++) + { + switch (a_Text[i]) + { + case '<': res.append("<
"); break; + case '>': res.append("
>"); break; + case '&': res.append("&"); break; + default: + { + res.push_back(a_Text[i]); + } + } + } // for i - a_Text[] + return res; +} + + + + + +void WriteDotGraph(void) +{ + cFile f("memdump.dot", cFile::fmWrite); + if (!f.IsOpen()) + { + LOGERROR("Cannot open memdump.dot"); + return; + } + + f.Printf("digraph {\n\tnode [shape=plaintext]\n\n"); + for (FunctionMap::const_iterator itrF = g_FnMap.begin(), endF = g_FnMap.end(); itrF != endF; ++itrF) + { + f.Printf("\t\"%s\" [label=<%s
%d bytes (%d KiB)
%d blocks>]\n", + itrF->first.c_str(), + HTMLEscape(itrF->first).c_str(), + itrF->second.m_Size, + (itrF->second.m_Size + 1023) / 1024, + itrF->second.m_Count + ); + const AStringSet & Children = itrF->second.m_ChildrenNames; + for (AStringSet::const_iterator itrN = Children.begin(), endN = Children.end(); itrN != endN; ++itrN) + { + f.Printf("\t\t\"%s\" -> \"%s\"\n", itrF->first.c_str(), itrN->c_str()); + } + f.Printf("\n"); + } // for itr + f.Printf("}\n"); +} + + + + + +int main(int argc, char * argv[]) +{ + // Open the dump file: + cFile f("memdump.xml", cFile::fmRead); + if (!f.IsOpen()) + { + printf("Cannot open memdump.xml\n"); + return 1; + } + + // Create the XML parser: + XML_Parser Parser = XML_ParserCreate(NULL); + XML_SetElementHandler(Parser, OnStartElement, OnEndElement); + + // Feed the file through XML parser: + char Buffer[512 KiB]; + while (true) + { + int NumBytes = f.Read(Buffer, sizeof(Buffer)); + if (NumBytes <= 0) + { + break; + } + XML_Parse(Parser, Buffer, NumBytes, false); + putc('.', stdout); + } + XML_Parse(Parser, "", 0, true); + f.Close(); + + // Output the statistics + WriteSizeStatistics(); + WriteCountStatistics(); + WriteDotGraph(); + + return 0; +} + + + + diff --git a/Tools/MemDumpAnalysis/MemDumpAnalysis.sln b/Tools/MemDumpAnalysis/MemDumpAnalysis.sln index 45fbd933f..096a9009c 100644 --- a/Tools/MemDumpAnalysis/MemDumpAnalysis.sln +++ b/Tools/MemDumpAnalysis/MemDumpAnalysis.sln @@ -1,20 +1,20 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual C++ Express 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MemDumpAnalysis", "MemDumpAnalysis.vcproj", "{110861A3-EF64-494F-8FC2-7F7ACFD3FEAC}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {110861A3-EF64-494F-8FC2-7F7ACFD3FEAC}.Debug|Win32.ActiveCfg = Debug|Win32 - {110861A3-EF64-494F-8FC2-7F7ACFD3FEAC}.Debug|Win32.Build.0 = Debug|Win32 - {110861A3-EF64-494F-8FC2-7F7ACFD3FEAC}.Release|Win32.ActiveCfg = Release|Win32 - {110861A3-EF64-494F-8FC2-7F7ACFD3FEAC}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MemDumpAnalysis", "MemDumpAnalysis.vcproj", "{110861A3-EF64-494F-8FC2-7F7ACFD3FEAC}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {110861A3-EF64-494F-8FC2-7F7ACFD3FEAC}.Debug|Win32.ActiveCfg = Debug|Win32 + {110861A3-EF64-494F-8FC2-7F7ACFD3FEAC}.Debug|Win32.Build.0 = Debug|Win32 + {110861A3-EF64-494F-8FC2-7F7ACFD3FEAC}.Release|Win32.ActiveCfg = Release|Win32 + {110861A3-EF64-494F-8FC2-7F7ACFD3FEAC}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Tools/MemDumpAnalysis/MemDumpAnalysis.vcproj b/Tools/MemDumpAnalysis/MemDumpAnalysis.vcproj index ffb935868..345dfa962 100644 --- a/Tools/MemDumpAnalysis/MemDumpAnalysis.vcproj +++ b/Tools/MemDumpAnalysis/MemDumpAnalysis.vcproj @@ -1,439 +1,439 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/MemDumpAnalysis/MemDumpAnalysis.vcproj.user b/Tools/MemDumpAnalysis/MemDumpAnalysis.vcproj.user index 11ddaa32b..f4ced837a 100644 --- a/Tools/MemDumpAnalysis/MemDumpAnalysis.vcproj.user +++ b/Tools/MemDumpAnalysis/MemDumpAnalysis.vcproj.user @@ -1,31 +1,31 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/Tools/MemDumpAnalysis/targetver.h b/Tools/MemDumpAnalysis/targetver.h index 56ab74a5e..7f4aeaca1 100644 --- a/Tools/MemDumpAnalysis/targetver.h +++ b/Tools/MemDumpAnalysis/targetver.h @@ -1,25 +1,25 @@ - -// targetver.h - -// Used by MSWin builds to target the appropriate OS version - - - - - -#pragma once - -// The following macros define the minimum required platform. The minimum required platform -// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run -// your application. The macros work by enabling all features available on platform versions up to and -// including the version specified. - -// Modify the following defines if you have to target a platform prior to the ones specified below. -// Refer to MSDN for the latest info on corresponding values for different platforms. -#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. -#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. -#endif - - - - + +// targetver.h + +// Used by MSWin builds to target the appropriate OS version + + + + + +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. +#define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. +#endif + + + + diff --git a/Tools/ProtoProxy/Connection.cpp b/Tools/ProtoProxy/Connection.cpp index 32dfe321e..7c808f2c1 100644 --- a/Tools/ProtoProxy/Connection.cpp +++ b/Tools/ProtoProxy/Connection.cpp @@ -1,2551 +1,2551 @@ - -// Connection.cpp - -// Interfaces to the cConnection class representing a single pair of connected sockets - -#include "Globals.h" -#include "Connection.h" -#include "Server.h" -#include - - - - - -#ifdef _DEBUG - #define DebugSleep Sleep -#else - #define DebugSleep(X) -#endif // else _DEBUG - - - - - -#define HANDLE_CLIENT_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ClientBuffer.Proc(Var)) \ - { \ - return false; \ - } \ - } - -#define HANDLE_SERVER_PACKET_READ(Proc, Type, Var) \ - Type Var; \ - { \ - if (!m_ServerBuffer.Proc(Var)) \ - { \ - return false; \ - } \ - } - -#define CLIENTSEND(...) SendData(m_ClientSocket, __VA_ARGS__, "Client") -#define SERVERSEND(...) SendData(m_ServerSocket, __VA_ARGS__, "Server") -#define CLIENTENCRYPTSEND(...) SendEncryptedData(m_ClientSocket, m_ClientEncryptor, __VA_ARGS__, "Client") -#define SERVERENCRYPTSEND(...) SendEncryptedData(m_ServerSocket, m_ServerEncryptor, __VA_ARGS__, "Server") - -#define COPY_TO_SERVER() \ - { \ - AString ToServer; \ - m_ClientBuffer.ReadAgain(ToServer); \ - switch (m_ServerState) \ - { \ - case csUnencrypted: \ - { \ - SERVERSEND(ToServer.data(), ToServer.size()); \ - break; \ - } \ - case csEncryptedUnderstood: \ - case csEncryptedUnknown: \ - { \ - SERVERENCRYPTSEND(ToServer.data(), ToServer.size()); \ - break; \ - } \ - case csWaitingForEncryption: \ - { \ - Log("Waiting for server encryption, queued %u bytes", ToServer.size()); \ - m_ServerEncryptionBuffer.append(ToServer.data(), ToServer.size()); \ - break; \ - } \ - } \ - DebugSleep(50); \ - } - -#define COPY_TO_CLIENT() \ - { \ - AString ToClient; \ - m_ServerBuffer.ReadAgain(ToClient); \ - switch (m_ClientState) \ - { \ - case csUnencrypted: \ - { \ - CLIENTSEND(ToClient.data(), ToClient.size()); \ - break; \ - } \ - case csEncryptedUnderstood: \ - case csEncryptedUnknown: \ - { \ - CLIENTENCRYPTSEND(ToClient.data(), ToClient.size()); \ - break; \ - } \ - case csWaitingForEncryption: \ - { \ - Log("Waiting for client encryption, queued %u bytes", ToClient.size()); \ - m_ClientEncryptionBuffer.append(ToClient.data(), ToClient.size()); \ - break; \ - } \ - } \ - DebugSleep(50); \ - } - -#define HANDLE_CLIENT_READ(Proc) \ - { \ - if (!Proc()) \ - { \ - AString Leftover; \ - m_ClientBuffer.ReadAgain(Leftover); \ - DataLog(Leftover.data(), Leftover.size(), "Leftover data after client packet parsing, %d bytes:", Leftover.size()); \ - m_ClientBuffer.ResetRead(); \ - return true; \ - } \ - } - -#define HANDLE_SERVER_READ(Proc) \ - { \ - if (!Proc()) \ - { \ - m_ServerBuffer.ResetRead(); \ - return true; \ - } \ - } - - -#define MAX_ENC_LEN 1024 - - - - -typedef unsigned char Byte; - - - - - -enum -{ - PACKET_KEEPALIVE = 0x00, - PACKET_LOGIN = 0x01, - PACKET_HANDSHAKE = 0x02, - PACKET_CHAT_MESSAGE = 0x03, - PACKET_TIME_UPDATE = 0x04, - PACKET_ENTITY_EQUIPMENT = 0x05, - PACKET_COMPASS = 0x06, - PACKET_USE_ENTITY = 0x07, - PACKET_UPDATE_HEALTH = 0x08, - PACKET_PLAYER_ON_GROUND = 0x0a, - PACKET_PLAYER_POSITION = 0x0b, - PACKET_PLAYER_LOOK = 0x0c, - PACKET_PLAYER_POSITION_LOOK = 0x0d, - PACKET_BLOCK_DIG = 0x0e, - PACKET_BLOCK_PLACE = 0x0f, - PACKET_SLOT_SELECT = 0x10, - PACKET_PLAYER_ANIMATION = 0x12, - PACKET_ENTITY_ACTION = 0x13, - PACKET_SPAWN_NAMED_ENTITY = 0x14, - PACKET_SPAWN_PICKUP = 0x15, - PACKET_COLLECT_PICKUP = 0x16, - PACKET_SPAWN_OBJECT_VEHICLE = 0x17, - PACKET_SPAWN_MOB = 0x18, - PACKET_SPAWN_PAINTING = 0x19, - PACKET_SPAWN_EXPERIENCE_ORB = 0x1a, - PACKET_ENTITY_VELOCITY = 0x1c, - PACKET_DESTROY_ENTITIES = 0x1d, - PACKET_ENTITY = 0x1e, - PACKET_ENTITY_RELATIVE_MOVE = 0x1f, - PACKET_ENTITY_LOOK = 0x20, - PACKET_ENTITY_RELATIVE_MOVE_LOOK = 0x21, - PACKET_ENTITY_TELEPORT = 0x22, - PACKET_ENTITY_HEAD_LOOK = 0x23, - PACKET_ENTITY_STATUS = 0x26, - PACKET_ATTACH_ENTITY = 0x27, - PACKET_ENTITY_METADATA = 0x28, - PACKET_ENTITY_EFFECT = 0x29, - PACKET_ENTITY_EFFECT_REMOVE = 0x2a, - PACKET_SET_EXPERIENCE = 0x2b, - PACKET_ENTITY_PROPERTIES = 0x2c, - PACKET_MAP_CHUNK = 0x33, - PACKET_MULTI_BLOCK_CHANGE = 0x34, - PACKET_BLOCK_CHANGE = 0x35, - PACKET_BLOCK_ACTION = 0x36, - PACKET_MAP_CHUNK_BULK = 0x38, - PACKET_SOUND_EFFECT = 0x3d, - PACKET_NAMED_SOUND_EFFECT = 0x3e, - PACKET_CHANGE_GAME_STATE = 0x46, - PACKET_WINDOW_OPEN = 0x64, - PACKET_WINDOW_CLOSE = 0x65, - PACKET_WINDOW_CLICK = 0x66, - PACKET_SET_SLOT = 0x67, - PACKET_WINDOW_CONTENTS = 0x68, - PACKET_CREATIVE_INVENTORY_ACTION = 0x6b, - PACKET_UPDATE_SIGN = 0x82, - PACKET_UPDATE_TILE_ENTITY = 0x84, - PACKET_PLAYER_LIST_ITEM = 0xc9, - PACKET_PLAYER_ABILITIES = 0xca, - PACKET_INCREMENT_STATISTIC = 0xc8, - PACKET_LOCALE_AND_VIEW = 0xcc, - PACKET_CLIENT_STATUSES = 0xcd, - PACKET_PLUGIN_MESSAGE = 0xfa, - PACKET_ENCRYPTION_KEY_RESPONSE = 0xfc, - PACKET_ENCRYPTION_KEY_REQUEST = 0xfd, - PACKET_PING = 0xfe, - PACKET_KICK = 0xff, -} ; - - - - -enum -{ - OBJECT_BOAT = 1, - OBJECT_MINECART = 10, - OBJECT_MINECART_STORAGE = 11, - OBJECT_MINECART_POWERED = 12, - OBJECT_TNT = 50, - OBJECT_ENDERCRYSTAL = 51, - OBJECT_ARROW = 60, - OBJECT_SNOWBALL = 61, - OBJECT_EGG = 62, - OBJECT_FALLING_BLOCK = 70, - OBJECT_EYE_OF_ENDER = 72, - OBJECT_DRAGON_EGG = 74, - OBJECT_FISHING_FLOAT = 90, -} ; - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cConnection: - -cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) : - m_ItemIdx(0), - m_LogFile(NULL), - m_Server(a_Server), - m_ClientSocket(a_ClientSocket), - m_ServerSocket(-1), - m_BeginTick(clock()), - m_ClientState(csUnencrypted), - m_ServerState(csUnencrypted), - m_Nonce(0), - m_ClientBuffer(1024 KiB), - m_ServerBuffer(1024 KiB), - m_HasClientPinged(false) -{ - Printf(m_LogNameBase, "Log_%d", (int)time(NULL)); - AString fnam(m_LogNameBase); - fnam.append(".log"); - m_LogFile = fopen(fnam.c_str(), "w"); - Log("Log file created"); - printf("Connection is logged to file \"%s\"\n", fnam.c_str()); -} - - - - - -cConnection::~cConnection() -{ - fclose(m_LogFile); -} - - - - - -void cConnection::Run(void) -{ - if (!ConnectToServer()) - { - Log("Cannot connect to server; aborting"); - return; - } - - while (true) - { - fd_set ReadFDs; - FD_ZERO(&ReadFDs); - FD_SET(m_ServerSocket, &ReadFDs); - FD_SET(m_ClientSocket, &ReadFDs); - int res = select(2, &ReadFDs, NULL, NULL, NULL); - if (res <= 0) - { - printf("select() failed: %d; aborting client", WSAGetLastError()); - break; - } - if (FD_ISSET(m_ServerSocket, &ReadFDs)) - { - if (!RelayFromServer()) - { - break; - } - } - if (FD_ISSET(m_ClientSocket, &ReadFDs)) - { - if (!RelayFromClient()) - { - break; - } - } - } - Log("Relaying ended, closing sockets"); - closesocket(m_ServerSocket); - closesocket(m_ClientSocket); -} - - - - - -void cConnection::Log(const char * a_Format, ...) -{ - va_list args; - va_start(args, a_Format); - AString msg; - AppendVPrintf(msg, a_Format, args); - va_end(args); - AString FullMsg; - Printf(FullMsg, "[%5.3f] %s\n", GetRelativeTime(), msg.c_str()); - - // Log to file: - cCSLock Lock(m_CSLog); - fputs(FullMsg.c_str(), m_LogFile); - - // Log to screen: - // std::cout << FullMsg; -} - - - - - -void cConnection::DataLog(const void * a_Data, int a_Size, const char * a_Format, ...) -{ - va_list args; - va_start(args, a_Format); - AString msg; - AppendVPrintf(msg, a_Format, args); - va_end(args); - AString FullMsg; - AString Hex; - Printf(FullMsg, "[%5.3f] %s\n%s\n", GetRelativeTime(), msg.c_str(), CreateHexDump(Hex, a_Data, a_Size, 16).c_str()); - - // Log to file: - cCSLock Lock(m_CSLog); - fputs(FullMsg.c_str(), m_LogFile); - - /* - // Log to screen: - std::cout << FullMsg; - //*/ -} - - - - - -void cConnection::LogFlush(void) -{ - fflush(m_LogFile); -} - - - - - -bool cConnection::ConnectToServer(void) -{ - m_ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (m_ServerSocket == INVALID_SOCKET) - { - return false; - } - sockaddr_in localhost; - localhost.sin_family = AF_INET; - localhost.sin_port = htons(m_Server.GetConnectPort()); - localhost.sin_addr.s_addr = htonl(0x7f000001); // localhost - if (connect(m_ServerSocket, (sockaddr *)&localhost, sizeof(localhost)) != 0) - { - printf("connection to server failed: %d\n", WSAGetLastError()); - return false; - } - Log("Connected to SERVER"); - return true; -} - - - - - -bool cConnection::RelayFromServer(void) -{ - char Buffer[64 KiB]; - int res = recv(m_ServerSocket, Buffer, sizeof(Buffer), 0); - if (res <= 0) - { - Log("Server closed the socket: %d; %d; aborting connection", res, WSAGetLastError()); - return false; - } - - DataLog(Buffer, res, "Received %d bytes from the SERVER", res); - - switch (m_ServerState) - { - case csUnencrypted: - case csWaitingForEncryption: - { - return DecodeServersPackets(Buffer, res); - } - case csEncryptedUnderstood: - { - m_ServerDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); - DataLog(Buffer, res, "Decrypted %d bytes from the SERVER", res); - return DecodeServersPackets(Buffer, res); - } - case csEncryptedUnknown: - { - m_ServerDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); - DataLog(Buffer, res, "Decrypted %d bytes from the SERVER", res); - m_ClientEncryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); - return CLIENTSEND(Buffer, res); - } - } - - return true; -} - - - - - -bool cConnection::RelayFromClient(void) -{ - char Buffer[64 KiB]; - int res = recv(m_ClientSocket, Buffer, sizeof(Buffer), 0); - if (res <= 0) - { - Log("Client closed the socket: %d; %d; aborting connection", res, WSAGetLastError()); - return false; - } - - DataLog(Buffer, res, "Received %d bytes from the CLIENT", res); - - switch (m_ClientState) - { - case csUnencrypted: - case csWaitingForEncryption: - { - return DecodeClientsPackets(Buffer, res); - } - case csEncryptedUnderstood: - { - m_ClientDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); - DataLog(Buffer, res, "Decrypted %d bytes from the CLIENT", res); - return DecodeClientsPackets(Buffer, res); - } - case csEncryptedUnknown: - { - m_ClientDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); - DataLog(Buffer, res, "Decrypted %d bytes from the CLIENT", res); - m_ServerEncryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); - return SERVERSEND(Buffer, res); - } - } - - return true; -} - - - - - -double cConnection::GetRelativeTime(void) -{ - return (double)(clock() - m_BeginTick) / CLOCKS_PER_SEC; - -} - - - - - -bool cConnection::SendData(SOCKET a_Socket, const char * a_Data, int a_Size, const char * a_Peer) -{ - DataLog(a_Data, a_Size, "Sending data to %s", a_Peer); - - int res = send(a_Socket, a_Data, a_Size, 0); - if (res <= 0) - { - Log("%s closed the socket: %d, %d; aborting connection", a_Peer, res, WSAGetLastError()); - return false; - } - return true; -} - - - - - -bool cConnection::SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer) -{ - AString All; - a_Data.ReadAll(All); - a_Data.CommitRead(); - return SendData(a_Socket, All.data(), All.size(), a_Peer); -} - - - - - -bool cConnection::SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, const char * a_Data, int a_Size, const char * a_Peer) -{ - DataLog(a_Data, a_Size, "Encrypting %d bytes to %s", a_Size, a_Peer); - const byte * Data = (const byte *)a_Data; - while (a_Size > 0) - { - byte Buffer[64 KiB]; - int NumBytes = (a_Size > sizeof(Buffer)) ? sizeof(Buffer) : a_Size; - a_Encryptor.ProcessData(Buffer, Data, NumBytes); - bool res = SendData(a_Socket, (const char *)Buffer, NumBytes, a_Peer); - if (!res) - { - return false; - } - Data += NumBytes; - a_Size -= NumBytes; - } - return true; -} - - - - - -bool cConnection::SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer) -{ - AString All; - a_Data.ReadAll(All); - a_Data.CommitRead(); - return SendEncryptedData(a_Socket, a_Encryptor, All.data(), All.size(), a_Peer); -} - - - - - -bool cConnection::DecodeClientsPackets(const char * a_Data, int a_Size) -{ - if (!m_ClientBuffer.Write(a_Data, a_Size)) - { - Log("Too much queued data for the server, aborting connection"); - return false; - } - - while (m_ClientBuffer.CanReadBytes(1)) - { - Log("Decoding client's packets, there are now %d bytes in the queue", m_ClientBuffer.GetReadableSpace()); - unsigned char PacketType; - m_ClientBuffer.ReadByte(PacketType); - switch (PacketType) - { - case PACKET_BLOCK_DIG: HANDLE_CLIENT_READ(HandleClientBlockDig); break; - case PACKET_BLOCK_PLACE: HANDLE_CLIENT_READ(HandleClientBlockPlace); break; - case PACKET_CHAT_MESSAGE: HANDLE_CLIENT_READ(HandleClientChatMessage); break; - case PACKET_CLIENT_STATUSES: HANDLE_CLIENT_READ(HandleClientClientStatuses); break; - case PACKET_CREATIVE_INVENTORY_ACTION: HANDLE_CLIENT_READ(HandleClientCreativeInventoryAction); break; - case PACKET_ENCRYPTION_KEY_RESPONSE: HANDLE_CLIENT_READ(HandleClientEncryptionKeyResponse); break; - case PACKET_ENTITY_ACTION: HANDLE_CLIENT_READ(HandleClientEntityAction); break; - case PACKET_HANDSHAKE: HANDLE_CLIENT_READ(HandleClientHandshake); break; - case PACKET_KEEPALIVE: HANDLE_CLIENT_READ(HandleClientKeepAlive); break; - case PACKET_LOCALE_AND_VIEW: HANDLE_CLIENT_READ(HandleClientLocaleAndView); break; - case PACKET_PING: HANDLE_CLIENT_READ(HandleClientPing); break; - case PACKET_PLAYER_ABILITIES: HANDLE_CLIENT_READ(HandleClientPlayerAbilities); break; - case PACKET_PLAYER_ANIMATION: HANDLE_CLIENT_READ(HandleClientAnimation); break; - case PACKET_PLAYER_LOOK: HANDLE_CLIENT_READ(HandleClientPlayerLook); break; - case PACKET_PLAYER_ON_GROUND: HANDLE_CLIENT_READ(HandleClientPlayerOnGround); break; - case PACKET_PLAYER_POSITION: HANDLE_CLIENT_READ(HandleClientPlayerPosition); break; - case PACKET_PLAYER_POSITION_LOOK: HANDLE_CLIENT_READ(HandleClientPlayerPositionLook); break; - case PACKET_PLUGIN_MESSAGE: HANDLE_CLIENT_READ(HandleClientPluginMessage); break; - case PACKET_SLOT_SELECT: HANDLE_CLIENT_READ(HandleClientSlotSelect); break; - case PACKET_UPDATE_SIGN: HANDLE_CLIENT_READ(HandleClientUpdateSign); break; - case PACKET_USE_ENTITY: HANDLE_CLIENT_READ(HandleClientUseEntity); break; - case PACKET_WINDOW_CLICK: HANDLE_CLIENT_READ(HandleClientWindowClick); break; - case PACKET_WINDOW_CLOSE: HANDLE_CLIENT_READ(HandleClientWindowClose); break; - default: - { - if (m_ClientState == csEncryptedUnderstood) - { - Log("****************** Unknown packet 0x%02x from the client while encrypted; continuing to relay blind only", PacketType); - AString Data; - m_ClientBuffer.ResetRead(); - m_ClientBuffer.ReadAll(Data); - DataLog(Data.data(), Data.size(), "Current data in the client packet queue: %d bytes", Data.size()); - m_ClientState = csEncryptedUnknown; - m_ClientBuffer.ResetRead(); - if (m_ServerState == csUnencrypted) - { - SERVERSEND(m_ClientBuffer); - } - else - { - SERVERENCRYPTSEND(m_ClientBuffer); - } - return true; - } - else - { - Log("Unknown packet 0x%02x from the client while unencrypted; aborting connection", PacketType); - return false; - } - } - } // switch (PacketType) - m_ClientBuffer.CommitRead(); - } // while (CanReadBytes(1)) - return true; -} - - - - - -bool cConnection::DecodeServersPackets(const char * a_Data, int a_Size) -{ - if (!m_ServerBuffer.Write(a_Data, a_Size)) - { - Log("Too much queued data for the client, aborting connection"); - return false; - } - - if ( - (m_ServerState == csEncryptedUnderstood) && - (m_ClientState == csUnencrypted) - ) - { - // Client hasn't finished encryption handshake yet, don't send them any data yet - } - - while (m_ServerBuffer.CanReadBytes(1)) - { - unsigned char PacketType; - m_ServerBuffer.ReadByte(PacketType); - Log("Decoding server's packets, there are now %d bytes in the queue; next packet is 0x%x", m_ServerBuffer.GetReadableSpace(), PacketType); - LogFlush(); - switch (PacketType) - { - case PACKET_ATTACH_ENTITY: HANDLE_SERVER_READ(HandleServerAttachEntity); break; - case PACKET_BLOCK_ACTION: HANDLE_SERVER_READ(HandleServerBlockAction); break; - case PACKET_BLOCK_CHANGE: HANDLE_SERVER_READ(HandleServerBlockChange); break; - case PACKET_CHANGE_GAME_STATE: HANDLE_SERVER_READ(HandleServerChangeGameState); break; - case PACKET_CHAT_MESSAGE: HANDLE_SERVER_READ(HandleServerChatMessage); break; - case PACKET_COLLECT_PICKUP: HANDLE_SERVER_READ(HandleServerCollectPickup); break; - case PACKET_COMPASS: HANDLE_SERVER_READ(HandleServerCompass); break; - case PACKET_DESTROY_ENTITIES: HANDLE_SERVER_READ(HandleServerDestroyEntities); break; - case PACKET_ENCRYPTION_KEY_REQUEST: HANDLE_SERVER_READ(HandleServerEncryptionKeyRequest); break; - case PACKET_ENCRYPTION_KEY_RESPONSE: HANDLE_SERVER_READ(HandleServerEncryptionKeyResponse); break; - case PACKET_ENTITY: HANDLE_SERVER_READ(HandleServerEntity); break; - case PACKET_ENTITY_EQUIPMENT: HANDLE_SERVER_READ(HandleServerEntityEquipment); break; - case PACKET_ENTITY_HEAD_LOOK: HANDLE_SERVER_READ(HandleServerEntityHeadLook); break; - case PACKET_ENTITY_LOOK: HANDLE_SERVER_READ(HandleServerEntityLook); break; - case PACKET_ENTITY_METADATA: HANDLE_SERVER_READ(HandleServerEntityMetadata); break; - case PACKET_ENTITY_PROPERTIES: HANDLE_SERVER_READ(HandleServerEntityProperties); break; - case PACKET_ENTITY_RELATIVE_MOVE: HANDLE_SERVER_READ(HandleServerEntityRelativeMove); break; - case PACKET_ENTITY_RELATIVE_MOVE_LOOK: HANDLE_SERVER_READ(HandleServerEntityRelativeMoveLook); break; - case PACKET_ENTITY_STATUS: HANDLE_SERVER_READ(HandleServerEntityStatus); break; - case PACKET_ENTITY_TELEPORT: HANDLE_SERVER_READ(HandleServerEntityTeleport); break; - case PACKET_ENTITY_VELOCITY: HANDLE_SERVER_READ(HandleServerEntityVelocity); break; - case PACKET_INCREMENT_STATISTIC: HANDLE_SERVER_READ(HandleServerIncrementStatistic); break; - case PACKET_KEEPALIVE: HANDLE_SERVER_READ(HandleServerKeepAlive); break; - case PACKET_KICK: HANDLE_SERVER_READ(HandleServerKick); break; - case PACKET_LOGIN: HANDLE_SERVER_READ(HandleServerLogin); break; - case PACKET_MAP_CHUNK: HANDLE_SERVER_READ(HandleServerMapChunk); break; - case PACKET_MAP_CHUNK_BULK: HANDLE_SERVER_READ(HandleServerMapChunkBulk); break; - case PACKET_MULTI_BLOCK_CHANGE: HANDLE_SERVER_READ(HandleServerMultiBlockChange); break; - case PACKET_NAMED_SOUND_EFFECT: HANDLE_SERVER_READ(HandleServerNamedSoundEffect); break; - case PACKET_PLAYER_ABILITIES: HANDLE_SERVER_READ(HandleServerPlayerAbilities); break; - case PACKET_PLAYER_ANIMATION: HANDLE_SERVER_READ(HandleServerPlayerAnimation); break; - case PACKET_PLAYER_LIST_ITEM: HANDLE_SERVER_READ(HandleServerPlayerListItem); break; - case PACKET_PLAYER_POSITION_LOOK: HANDLE_SERVER_READ(HandleServerPlayerPositionLook); break; - case PACKET_PLUGIN_MESSAGE: HANDLE_SERVER_READ(HandleServerPluginMessage); break; - case PACKET_SET_EXPERIENCE: HANDLE_SERVER_READ(HandleServerSetExperience); break; - case PACKET_SET_SLOT: HANDLE_SERVER_READ(HandleServerSetSlot); break; - case PACKET_SLOT_SELECT: HANDLE_SERVER_READ(HandleServerSlotSelect); break; - case PACKET_SOUND_EFFECT: HANDLE_SERVER_READ(HandleServerSoundEffect); break; - case PACKET_SPAWN_MOB: HANDLE_SERVER_READ(HandleServerSpawnMob); break; - case PACKET_SPAWN_NAMED_ENTITY: HANDLE_SERVER_READ(HandleServerSpawnNamedEntity); break; - case PACKET_SPAWN_OBJECT_VEHICLE: HANDLE_SERVER_READ(HandleServerSpawnObjectVehicle); break; - case PACKET_SPAWN_PAINTING: HANDLE_SERVER_READ(HandleServerSpawnPainting); break; - case PACKET_SPAWN_PICKUP: HANDLE_SERVER_READ(HandleServerSpawnPickup); break; - case PACKET_TIME_UPDATE: HANDLE_SERVER_READ(HandleServerTimeUpdate); break; - case PACKET_UPDATE_HEALTH: HANDLE_SERVER_READ(HandleServerUpdateHealth); break; - case PACKET_UPDATE_SIGN: HANDLE_SERVER_READ(HandleServerUpdateSign); break; - case PACKET_UPDATE_TILE_ENTITY: HANDLE_SERVER_READ(HandleServerUpdateTileEntity); break; - case PACKET_WINDOW_CLOSE: HANDLE_SERVER_READ(HandleServerWindowClose); break; - case PACKET_WINDOW_CONTENTS: HANDLE_SERVER_READ(HandleServerWindowContents); break; - case PACKET_WINDOW_OPEN: HANDLE_SERVER_READ(HandleServerWindowOpen); break; - default: - { - if (m_ServerState == csEncryptedUnderstood) - { - Log("********************** Unknown packet 0x%02x from the server while encrypted; continuing to relay blind only", PacketType); - AString Data; - m_ServerBuffer.ResetRead(); - m_ServerBuffer.ReadAll(Data); - DataLog(Data.data(), Data.size(), "Current data in the server packet queue: %d bytes", Data.size()); - m_ServerState = csEncryptedUnknown; - m_ServerBuffer.ResetRead(); - if (m_ClientState == csUnencrypted) - { - CLIENTSEND(m_ServerBuffer); - } - else - { - CLIENTENCRYPTSEND(m_ServerBuffer); - } - return true; - } - else - { - Log("Unknown packet 0x%02x from the server while unencrypted; aborting connection", PacketType); - return false; - } - } - } // switch (PacketType) - m_ServerBuffer.CommitRead(); - } // while (CanReadBytes(1)) - return true; -} - - - - - -bool cConnection::HandleClientAnimation(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, Animation); - Log("Received a PACKET_ANIMATION from the client:"); - Log(" EntityID: %d", EntityID); - Log(" Animation: %d", Animation); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientBlockDig(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, Status); - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockX); - HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, BlockY); - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockZ); - HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, BlockFace); - Log("Received a PACKET_BLOCK_DIG from the client:"); - Log(" Status = %d", Status); - Log(" Pos = <%d, %d, %d>", BlockX, BlockY, BlockZ); - Log(" BlockFace = %d", BlockFace); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientBlockPlace(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockX); - HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, BlockY); - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockZ); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, Face); - AString Desc; - if (!ParseSlot(m_ClientBuffer, Desc)) - { - return false; - } - HANDLE_CLIENT_PACKET_READ(ReadChar, char, CursorX); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, CursorY); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, CursorZ); - Log("Received a PACKET_BLOCK_PLACE from the client:"); - Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); - Log(" Face = %d", Face); - Log(" Item = %s", Desc.c_str()); - Log(" Cursor = <%d, %d, %d>", CursorX, CursorY, CursorZ); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientChatMessage(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Message); - Log("Received a PACKET_CHAT_MESSAGE from the client:"); - Log(" Message = \"%s\"", Message.c_str()); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientClientStatuses(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadChar, char, Statuses); - Log("Received a PACKET_CLIENT_STATUSES from the CLIENT:"); - Log(" Statuses = %d", Statuses); - - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientCreativeInventoryAction(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, SlotNum); - AString Item; - if (!ParseSlot(m_ClientBuffer, Item)) - { - return false; - } - Log("Received a PACKET_CREATIVE_INVENTORY_ACTION from the client:"); - Log(" SlotNum = %d", SlotNum); - Log(" Item = %s", Item.c_str()); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientEncryptionKeyResponse(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, EncKeyLength); - AString EncKey; - if (!m_ClientBuffer.ReadString(EncKey, EncKeyLength)) - { - return true; - } - HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, EncNonceLength); - AString EncNonce; - if (!m_ClientBuffer.ReadString(EncNonce, EncNonceLength)) - { - return true; - } - if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN)) - { - Log("Client: Too long encryption params"); - return true; - } - StartClientEncryption(EncKey, EncNonce); - return true; -} - - - - - -bool cConnection::HandleClientEntityAction(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, PlayerID); - HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, ActionType); - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, UnknownHorseVal); - Log("Received a PACKET_ENTITY_ACTION from the client:"); - Log(" PlayerID = %d", PlayerID); - Log(" ActionType = %d", ActionType); - Log(" UnknownHorseVal = %d (0x%08x)", UnknownHorseVal, UnknownHorseVal); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientHandshake(void) -{ - // Read the packet from the client: - HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, ProtocolVersion); - HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Username); - HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, ServerHost); - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, ServerPort); - m_ClientBuffer.CommitRead(); - - Log("Received a PACKET_HANDSHAKE from the client:"); - Log(" ProtocolVersion = %d", ProtocolVersion); - Log(" Username = \"%s\"", Username.c_str()); - Log(" ServerHost = \"%s\"", ServerHost.c_str()); - Log(" ServerPort = %d", ServerPort); - - // Send the same packet to the server, but with our port: - cByteBuffer ToServer(512); - ToServer.WriteByte (PACKET_HANDSHAKE); - ToServer.WriteByte (ProtocolVersion); - ToServer.WriteBEUTF16String16(Username); - ToServer.WriteBEUTF16String16(ServerHost); - ToServer.WriteBEInt (m_Server.GetConnectPort()); - SERVERSEND(ToServer); - - return true; -} - - - - - -bool cConnection::HandleClientKeepAlive(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, ID); - Log("Received a PACKET_KEEPALIVE from the client"); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientLocaleAndView(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Locale); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, ViewDistance); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, ChatFlags); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, Difficulty); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, ShowCape); - Log("Received a PACKET_LOCALE_AND_VIEW from the client"); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientPing(void) -{ - m_HasClientPinged = true; - Log("Received a PACKET_PING from the client"); - m_ClientBuffer.ResetRead(); - SERVERSEND(m_ClientBuffer); - return true; -} - - - - - -bool cConnection::HandleClientPlayerAbilities(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadChar, char, Flags); - HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, FlyingSpeed); - HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, WalkingSpeed); - Log("Receives a PACKET_PLAYER_ABILITIES from the client:"); - Log(" Flags = %d (0x%02x)", Flags, Flags); - Log(" FlyingSpeed = %f", FlyingSpeed); - Log(" WalkingSpeed = %f", WalkingSpeed); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientPlayerLook(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Yaw); - HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Pitch); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, OnGround); - Log("Received a PACKET_PLAYER_LOOK from the client"); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientPlayerOnGround(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadChar, char, OnGround); - Log("Received a PACKET_PLAYER_ON_GROUND from the client"); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientPlayerPosition(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosX); - HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance); - HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosY); - HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosZ); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsOnGround); - Log("Received a PACKET_PLAYER_POSITION from the client"); - - // TODO: list packet contents - - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientPlayerPositionLook(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosX); - HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance); - HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosY); - HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosZ); - HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Yaw); - HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Pitch); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsOnGround); - Log("Received a PACKET_PLAYER_POSITION_LOOK from the client"); - Log(" Pos = {%.03f, %.03f, %.03f}", PosX, PosY, PosZ); - Log(" Stance = %.03f", Stance); - Log(" Y, P = %.03f, %.03f", Yaw, Pitch); - Log(" IsOnGround = %s", IsOnGround ? "true" : "false"); - - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientPluginMessage(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, ChannelName); - HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, Length); - AString Data; - if (!m_ClientBuffer.ReadString(Data, Length)) - { - return false; - } - Log("Received a PACKET_PLUGIN_MESSAGE from the client"); - Log(" ChannelName = \"%s\"", ChannelName.c_str()); - DataLog(Data.data(), Length, " Data: %d bytes", Length); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientSlotSelect(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, SlotNum); - Log("Received a PACKET_SLOT_SELECT from the client"); - Log(" SlotNum = %d", SlotNum); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientUpdateSign(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockX); - HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, BlockY); - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockZ); - HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line1); - HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line2); - HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line3); - HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line4); - Log("Received a PACKET_UPDATE_SIGN from the client:"); - Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); - Log(" Lines = \"%s\", \"%s\", \"%s\", \"%s\"", Line1.c_str(), Line2.c_str(), Line3.c_str(), Line4.c_str()); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientUseEntity(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, PlayerID); - HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, MouseButton); - Log("Received a PACKET_USE_ENTITY from the client:"); - Log(" PlayerID = %d", PlayerID); - Log(" EntityID = %d", EntityID); - Log(" MouseButton = %d", MouseButton); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientWindowClick(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadChar, char, WindowID); - HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, SlotNum); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsRightClick); - HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, TransactionID); - HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsShiftClick); - AString Item; - if (!ParseSlot(m_ClientBuffer, Item)) - { - return false; - } - Log("Received a PACKET_WINDOW_CLICK from the client"); - Log(" WindowID = %d", WindowID); - Log(" SlotNum = %d", SlotNum); - Log(" IsRclk = %d, IsShift = %d", IsRightClick, IsShiftClick); - Log(" TransactionID = 0x%x", TransactionID); - Log(" ClickedItem = %s", Item.c_str()); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleClientWindowClose(void) -{ - HANDLE_CLIENT_PACKET_READ(ReadChar, char, WindowID); - Log("Received a PACKET_WINDOW_CLOSE from the client:"); - Log(" WindowID = %d", WindowID); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleServerAttachEntity(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, VehicleID); - HANDLE_SERVER_PACKET_READ(ReadBool, bool, Leash); - Log("Received a PACKET_ATTACH_ENTITY from the server:"); - Log(" EntityID = %d (0x%x)", EntityID, EntityID); - Log(" VehicleID = %d (0x%x)", VehicleID, VehicleID); - Log(" Leash = %s", Leash ? "true" : "false"); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerBlockAction(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Byte1); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Byte2); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockID); - Log("Received a PACKET_BLOCK_ACTION from the server:"); - Log(" Pos = {%d, %d, %d}", BlockX, BlockY, BlockZ); - Log(" Bytes = (%d, %d) == (0x%x, 0x%x)", Byte1, Byte2, Byte1, Byte2); - Log(" BlockID = %d", BlockID); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerBlockChange(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, BlockY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockType); - HANDLE_SERVER_PACKET_READ(ReadChar, char, BlockMeta); - Log("Received a PACKET_BLOCK_CHANGE from the server"); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerChangeGameState(void) -{ - HANDLE_SERVER_PACKET_READ(ReadChar, char, Reason); - HANDLE_SERVER_PACKET_READ(ReadChar, char, Data); - Log("Received a PACKET_CHANGE_GAME_STATE from the server:"); - Log(" Reason = %d", Reason); - Log(" Data = %d", Data); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerChatMessage(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Message); - Log("Received a PACKET_CHAT_MESSAGE from the server:"); - Log(" Message = \"%s\"", Message.c_str()); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerCollectPickup(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CollectedID); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CollectorID); - Log("Received a PACKET_COLLECT_PICKUP from the server:"); - Log(" CollectedID = %d", CollectedID); - Log(" CollectorID = %d", CollectorID); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerCompass(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, SpawnX); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, SpawnY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, SpawnZ); - Log("Received PACKET_COMPASS from the server:"); - Log(" Spawn = {%d, %d, %d}", SpawnX, SpawnY, SpawnZ); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerDestroyEntities(void) -{ - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, NumEntities); - if (!m_ServerBuffer.SkipRead((int)NumEntities * 4)) - { - return false; - } - Log("Received PACKET_DESTROY_ENTITIES from the server:"); - Log(" NumEntities = %d", NumEntities); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerEncryptionKeyRequest(void) -{ - // Read the packet from the server: - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ServerID); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PublicKeyLength); - AString PublicKey; - if (!m_ServerBuffer.ReadString(PublicKey, PublicKeyLength)) - { - return false; - } - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NonceLength); - AString Nonce; - if (!m_ServerBuffer.ReadString(Nonce, NonceLength)) - { - return false; - } - Log("Got PACKET_ENCRYPTION_KEY_REQUEST from the SERVER:"); - Log(" ServerID = %s", ServerID.c_str()); - - // Reply to the server: - SendEncryptionKeyResponse(PublicKey, Nonce); - - // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD to the client, using our own key: - Log("Sending PACKET_ENCRYPTION_KEY_REQUEST to the CLIENT"); - AString key; - StringSink sink(key); // GCC won't allow inline instantiation in the following line, damned temporary refs - m_Server.GetPublicKey().Save(sink); - cByteBuffer ToClient(512); - ToClient.WriteByte (PACKET_ENCRYPTION_KEY_REQUEST); - ToClient.WriteBEUTF16String16(ServerID); - ToClient.WriteBEShort ((short)key.size()); - ToClient.WriteBuf (key.data(), key.size()); - ToClient.WriteBEShort (4); - ToClient.WriteBEInt (m_Nonce); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) - CLIENTSEND(ToClient); - return true; -} - - - - - -bool cConnection::HandleServerEntity(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - Log("Received a PACKET_ENTITY from the server:"); - Log(" EntityID = %d", EntityID); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerEntityEquipment(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SlotNum); - AString Item; - if (!ParseSlot(m_ServerBuffer, Item)) - { - return false; - } - Log("Received a PACKET_ENTITY_EQUIPMENT from the server:"); - Log(" EntityID = %d", EntityID); - Log(" SlotNum = %d", SlotNum); - Log(" Item = %s", Item.c_str()); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerEntityHeadLook(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, HeadYaw); - Log("Received a PACKET_ENTITY_HEAD_LOOK from the server:"); - Log(" EntityID = %d", EntityID); - Log(" HeadYaw = %d", HeadYaw); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerEntityLook(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); - Log("Received a PACKET_ENTITY_LOOK from the server:"); - Log(" EntityID = %d", EntityID); - Log(" Yaw = %d", Yaw); - Log(" Pitch = %d", Pitch); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerEntityMetadata(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - AString Metadata; - if (!ParseMetadata(m_ServerBuffer, Metadata)) - { - return false; - } - AString HexDump; - CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32); - Log("Received a PACKET_ENTITY_METADATA from the server:"); - Log(" EntityID = %d", EntityID); - Log(" Metadata, length = %d (0x%x):\n%s", Metadata.length(), Metadata.length(), HexDump.c_str()); - LogMetadata(Metadata, 4); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerEntityProperties(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Count); - Log("Received a PACKET_ENTITY_PROPERTIES from the server:"); - Log(" EntityID = %d", EntityID); - Log(" Count = %d", Count); - - for (int i = 0; i < Count; i++) - { - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Key); - HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, Value); - Log(" \"%s\" = %f", Key.c_str(), Value); - } // for i - - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, ListLength); - Log(" ListLength = %d", ListLength); - for (int i = 0; i < ListLength; i++) - { - HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, UUIDHi); - HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, UUIDLo); - HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, DblVal); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, ByteVal); - Log(" [%d] = {0x%08llx%08llx, %f, %i}", i, UUIDHi, UUIDLo, DblVal, ByteVal); - } // for i - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerEntityRelativeMove(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dx); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dy); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dz); - Log("Received a PACKET_ENTITY_RELATIVE_MOVE from the server:"); - Log(" EntityID = %d", EntityID); - Log(" RelMove = <%d, %d, %d>", dx, dy, dz); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerEntityRelativeMoveLook(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dx); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dy); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dz); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); - Log("Received a PACKET_ENTITY_RELATIVE_MOVE_LOOK from the server:"); - Log(" EntityID = %d", EntityID); - Log(" RelMove = <%d, %d, %d>", dx, dy, dz); - Log(" Yaw = %d", Yaw); - Log(" Pitch = %d", Pitch); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerEntityStatus(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Status); - Log("Received a PACKET_ENTITY_STATUS from the server:"); - Log(" EntityID = %d", EntityID); - Log(" Status = %d", Status); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerEntityTeleport(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); - Log("Received a PACKET_ENTITY_TELEPORT from the server:"); - Log(" EntityID = %d", EntityID); - Log(" Pos = {%d, %d, %d}", BlockX, BlockY, BlockZ); - Log(" Yaw = %d", Yaw); - Log(" Pitch = %d", Pitch); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerEntityVelocity(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityX); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityY); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityZ); - Log("Received a PACKET_ENTITY_VELOCITY from the server:"); - Log(" EntityID = %d", EntityID); - Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerIncrementStatistic(void) -{ - // 0xc8 - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, StatisticID); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Amount); - Log("Received a PACKET_INCREMENT_STATISTIC from the server:"); - Log(" StatisticID = %d (0x%x)", StatisticID, StatisticID); - Log(" Amount = %d", Amount); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerKeepAlive(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PingID); - Log("Received a PACKET_KEEP_ALIVE from the server:"); - Log(" ID = %d", PingID); - COPY_TO_CLIENT() - return true; -} - - - - - -bool cConnection::HandleServerEncryptionKeyResponse(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Lengths); - if (Lengths != 0) - { - Log("Lengths are not zero!"); - return true; - } - Log("Server communication is now encrypted"); - m_ServerState = csEncryptedUnderstood; - DataLog(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size(), "Sending the queued data to server (%u bytes):", m_ServerEncryptionBuffer.size()); - SERVERENCRYPTSEND(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size()); - m_ServerEncryptionBuffer.clear(); - return true; -} - - - - - -bool cConnection::HandleServerKick(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Reason); - Log("Received PACKET_KICK from the SERVER:"); - if (m_HasClientPinged) - { - Log(" This was a std reply to client's PING"); - AStringVector Split; - - // Split by NULL chars (StringSplit() won't work here): - size_t Last = 0; - for (size_t i = 0; i < Reason.size(); i++) - { - if (Reason[i] == 0) - { - Split.push_back(Reason.substr(Last, i - Last)); - Last = i + 1; - } - } - - if (Split.size() == 5) - { - Log(" Protocol version: \"%s\"", Split[0].c_str()); - Log(" Server version: \"%s\"", Split[1].c_str()); - Log(" MOTD: \"%s\"", Split[2].c_str()); - Log(" Cur players: \"%s\"", Split[3].c_str()); - Log(" Max players: \"%s\"", Split[4].c_str()); - } - else - { - DataLog(Reason.data(), Reason.size(), " Unknown reply format, dumping hex:"); - } - } - else - { - Log(" Reason = \"%s\"", Reason.c_str()); - } - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerLogin(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, LevelType); - HANDLE_SERVER_PACKET_READ(ReadChar, char, GameMode); - HANDLE_SERVER_PACKET_READ(ReadChar, char, Dimension); - HANDLE_SERVER_PACKET_READ(ReadChar, char, Difficulty); - HANDLE_SERVER_PACKET_READ(ReadChar, char, Unused); - HANDLE_SERVER_PACKET_READ(ReadChar, char, MaxPlayers); - Log("Received a PACKET_LOGIN from the server:"); - Log(" EntityID = %d", EntityID); - Log(" LevelType = \"%s\"", LevelType.c_str()); - Log(" GameMode = %d", GameMode); - Log(" Dimension = %d", Dimension); - Log(" Difficulty = %d", Difficulty); - Log(" Unused = %d", Unused); - Log(" MaxPlayers = %d", MaxPlayers); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerMapChunk(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ); - HANDLE_SERVER_PACKET_READ(ReadChar, char, IsContiguous); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PrimaryBitmap); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, AdditionalBitmap); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CompressedSize); - AString CompressedData; - if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize)) - { - return false; - } - Log("Received a PACKET_MAP_CHUNK from the server:"); - Log(" ChunkPos = [%d, %d]", ChunkX, ChunkZ); - Log(" Compressed size = %d (0x%x)", CompressedSize, CompressedSize); - - // TODO: Save the compressed data into a file for later analysis - - COPY_TO_CLIENT() - return true; -} - - - - - -bool cConnection::HandleServerMapChunkBulk(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, ChunkCount); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CompressedSize); - HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsSkyLightSent); - AString CompressedData; - if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize)) - { - return false; - } - AString Meta; - if (!m_ServerBuffer.ReadString(Meta, ChunkCount * 12)) - { - return false; - } - Log("Received a PACKET_MAP_CHUNK_BULK from the server:"); - Log(" ChunkCount = %d", ChunkCount); - Log(" Compressed size = %d (0x%x)", CompressedSize, CompressedSize); - Log(" IsSkyLightSent = %s", IsSkyLightSent ? "true" : "false"); - - // TODO: Save the compressed data into a file for later analysis - - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerMultiBlockChange(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NumBlocks); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, DataSize); - AString BlockChangeData; - if (!m_ServerBuffer.ReadString(BlockChangeData, DataSize)) - { - return false; - } - Log("Received a PACKET_MULTI_BLOCK_CHANGE packet from the server:"); - Log(" Chunk = [%d, %d]", ChunkX, ChunkZ); - Log(" NumBlocks = %d", NumBlocks); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerNamedSoundEffect(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, SoundName); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Volume); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); - Log("Received a PACKET_NAMED_SOUND_EFFECT from the server:"); - Log(" SoundName = \"%s\"", SoundName.c_str()); - Log(" Pos = (%d, %d, %d) ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 8, PosY / 8, PosZ / 8); - Log(" Volume = %f", Volume); - Log(" Pitch = %d", Pitch); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerPlayerAbilities(void) -{ - HANDLE_SERVER_PACKET_READ(ReadChar, char, Flags); - HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, FlyingSpeed); - HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, WalkingSpeed); - Log("Received a PACKET_PLAYER_ABILITIES from the server:"); - Log(" Flags = %d (0x%02x)", Flags, Flags); - Log(" FlyingSpeed = %f", FlyingSpeed); - Log(" WalkingSpeed = %f", WalkingSpeed); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerPlayerAnimation(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PlayerID); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, AnimationID); - Log("Received a PACKET_PLAYER_ANIMATION from the server:"); - Log(" PlayerID: %d (0x%x)", PlayerID, PlayerID); - Log(" Animation: %d", AnimationID); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerPlayerListItem(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, PlayerName); - HANDLE_SERVER_PACKET_READ(ReadChar, char, IsOnline); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Ping); - Log("Received a PACKET_PLAYERLIST_ITEM from the server:"); - Log(" PlayerName = \"%s\"", PlayerName.c_str()); - Log(" Ping = %d", Ping); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerPlayerPositionLook(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosX); - HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, Stance); - HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosY); - HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosZ); - HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Yaw); - HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Pitch); - HANDLE_SERVER_PACKET_READ(ReadChar, char, IsOnGround); - Log("Received a PACKET_PLAYER_POSITION_LOOK from the server"); - - // TODO: list packet contents - - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerPluginMessage(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ChannelName); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Length); - AString Data; - if (!m_ServerBuffer.ReadString(Data, Length)) - { - return false; - } - Log("Received a PACKET_PLUGIN_MESSAGE from the server"); - Log(" ChannelName = \"%s\"", ChannelName.c_str()); - DataLog(Data.data(), Length, " Data: %d bytes", Length); - COPY_TO_SERVER(); - return true; -} - - - - - -bool cConnection::HandleServerSetExperience(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, ExperienceBar); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Level); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, TotalExperience); - Log("Received a PACKET_SET_EXPERIENCE from the server:"); - Log(" ExperienceBar = %.05f", ExperienceBar); - Log(" Level = %d", Level); - Log(" TotalExperience = %d", TotalExperience); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerSetSlot(void) -{ - HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SlotNum); - AString Item; - if (!ParseSlot(m_ServerBuffer, Item)) - { - return false; - } - Log("Received a PACKET_SET_SLOT from the server:"); - Log(" WindowID = %d", WindowID); - Log(" SlotNum = %d", SlotNum); - Log(" Item = %s", Item.c_str()); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerSlotSelect(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SlotNum); - Log("Received a PACKET_SLOT_SELECT from the server:"); - Log(" SlotNum = %d", SlotNum); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerSoundEffect(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EffectID); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, PosY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Data); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, NoVolumeDecrease); - Log("Received a PACKET_SOUND_EFFECT from the server:"); - Log(" EffectID = %d", EffectID); - Log(" Pos = {%d, %d, %d}", PosX, PosY, PosZ); - Log(" Data = %d", Data); - Log(" NoVolumeDecrease = %d", NoVolumeDecrease); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerSpawnMob(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadChar, char, MobType); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, HeadYaw); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityX); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityY); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityZ); - AString Metadata; - if (!ParseMetadata(m_ServerBuffer, Metadata)) - { - return false; - } - AString HexDump; - CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32); - Log("Received a PACKET_SPAWN_MOB from the server:"); - Log(" EntityID = %d", EntityID); - Log(" MobType = %d", MobType); - Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); - Log(" Angles = [%d, %d, %d]", Yaw, Pitch, HeadYaw); - Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ); - Log(" Metadata, length = %d (0x%x):\n%s", Metadata.length(), Metadata.length(), HexDump.c_str()); - LogMetadata(Metadata, 4); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerSpawnNamedEntity(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, EntityName); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, CurrentItem); - AString Metadata; - if (!ParseMetadata(m_ServerBuffer, Metadata)) - { - return false; - } - AString HexDump; - CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32); - Log("Received a PACKET_SPAWN_NAMED_ENTITY from the server:"); - Log(" EntityID = %d (0x%x)", EntityID, EntityID); - Log(" Name = %s", EntityName.c_str()); - Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); - Log(" Rotation = ", Yaw, Pitch); - Log(" CurrentItem = %d", CurrentItem); - Log(" Metadata, length = %d (0x%x):\n%s", Metadata.length(), Metadata.length(), HexDump.c_str()); - LogMetadata(Metadata, 4); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerSpawnObjectVehicle(void) -{ - #ifdef _DEBUG - // DEBUG: - // This packet is still troublesome when DataIndicator != 0 - AString Buffer; - m_ServerBuffer.ResetRead(); - m_ServerBuffer.ReadAll(Buffer); - m_ServerBuffer.ResetRead(); - m_ServerBuffer.SkipRead(1); - if (Buffer.size() > 128) - { - // Only log up to 128 bytes - Buffer.erase(128, AString::npos); - } - DataLog(Buffer.data(), Buffer.size(), "Buffer while parsing the PACKET_SPAWN_OBJECT_VEHICLE packet (%d bytes):", Buffer.size()); - #endif // _DEBUG - - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadChar, char, ObjType); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, DataIndicator); - AString ExtraData; - short VelocityX, VelocityY, VelocityZ; - if (DataIndicator != 0) - { - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SpeedX); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SpeedY); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SpeedZ); - VelocityX = SpeedX; VelocityY = SpeedY; VelocityZ = SpeedZ; // Speed vars are local to this scope, but we need them available later - /* - // This doesn't seem to work - for a falling block I'm getting no extra data at all - int ExtraLen = 0; - switch (ObjType) - { - case OBJECT_FALLING_BLOCK: ExtraLen = 4; break; // int: BlockType | (BlockMeta << 12) - case OBJECT_ARROW: - case OBJECT_SNOWBALL: - case OBJECT_EGG: - case OBJECT_EYE_OF_ENDER: - case OBJECT_DRAGON_EGG: - case OBJECT_FISHING_FLOAT: - { - ExtraLen = 4; break; // int: EntityID of the thrower - } - // TODO: Splash potions - } - if ((ExtraLen > 0) && !m_ServerBuffer.ReadString(ExtraData, ExtraLen)) - { - return false; - } - */ - } - Log("Received a PACKET_SPAWN_OBJECT_VEHICLE from the server:"); - Log(" EntityID = %d (0x%x)", EntityID, EntityID); - Log(" ObjType = %d (0x%x)", ObjType, ObjType); - Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); - Log(" Rotation = ", Yaw, Pitch); - Log(" DataIndicator = %d (0x%x)", DataIndicator, DataIndicator); - if (DataIndicator != 0) - { - Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ); - DataLog(ExtraData.data(), ExtraData.size(), " ExtraData size = %d:", ExtraData.size()); - } - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerSpawnPainting(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ImageName); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Direction); - Log("Received a PACKET_SPAWN_PAINTING from the server:"); - Log(" EntityID = %d", EntityID); - Log(" ImageName = \"%s\"", ImageName.c_str()); - Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); - Log(" Direction = %d", Direction); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerSpawnPickup(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); - AString ItemDesc; - if (!ParseSlot(m_ServerBuffer, ItemDesc)) - { - return false; - } - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Rotation); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Roll); - Log("Received a PACKET_SPAWN_PICKUP from the server:"); - Log(" EntityID = %d", EntityID); - Log(" Item = %s", ItemDesc.c_str()); - Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); - Log(" Angles = [%d, %d, %d]", Rotation, Pitch, Roll); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerTimeUpdate(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, WorldAge); - HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, TimeOfDay); - Log("Received a PACKET_TIME_UPDATE from the server"); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerUpdateHealth(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Health); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Food); - HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Saturation); - Log("Received a PACKET_UPDATE_HEALTH from the server"); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerUpdateSign(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line1); - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line2); - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line3); - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line4); - Log("Received a PACKET_UPDATE_SIGN from the server:"); - Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); - Log(" Lines = \"%s\", \"%s\", \"%s\", \"%s\"", Line1.c_str(), Line2.c_str(), Line3.c_str(), Line4.c_str()); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerUpdateTileEntity(void) -{ - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY); - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Action); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, DataLength); - AString Data; - if ((DataLength > 0) && !m_ServerBuffer.ReadString(Data, DataLength)) - { - return false; - } - Log("Received a PACKET_UPDATE_TILE_ENTITY from the server:"); - Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); - Log(" Action = %d", Action); - DataLog(Data.data(), Data.size(), " Data (%d bytes)", Data.size()); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerWindowClose(void) -{ - HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); - Log("Received a PACKET_WINDOW_CLOSE from the server:"); - Log(" WindowID = %d", WindowID); - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerWindowContents(void) -{ - HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); - HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NumSlots); - Log("Received a PACKET_WINDOW_CONTENTS from the server:"); - Log(" WindowID = %d", WindowID); - Log(" NumSlots = %d", NumSlots); - AStringVector Items; - for (short i = 0; i < NumSlots; i++) - { - AString Item; - if (!ParseSlot(m_ServerBuffer, Item)) - { - return false; - } - Log(" %d: %s", i, Item.c_str()); - } - - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::HandleServerWindowOpen(void) -{ - HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); - HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowType); - HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Title); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, NumSlots); - HANDLE_SERVER_PACKET_READ(ReadByte, Byte, UseProvidedTitle); - int HorseInt = 0; - if (WindowType == 11) // Horse / Donkey / Mule - { - HANDLE_SERVER_PACKET_READ(ReadBEInt, int, intHorseInt); - HorseInt = intHorseInt; - } - Log("Received a PACKET_WINDOW_OPEN from the server:"); - Log(" WindowID = %d", WindowID); - Log(" WindowType = %d", WindowType); - Log(" Title = \"%s\", Use = %d", Title.c_str(), UseProvidedTitle); - Log(" NumSlots = %d", NumSlots); - if (WindowType == 11) - { - Log(" HorseInt = %d (0x%08x)", HorseInt, HorseInt); - } - COPY_TO_CLIENT(); - return true; -} - - - - - -bool cConnection::ParseSlot(cByteBuffer & a_Buffer, AString & a_ItemDesc) -{ - short ItemType; - if (!a_Buffer.ReadBEShort(ItemType)) - { - return false; - } - if (ItemType <= 0) - { - a_ItemDesc = ""; - return true; - } - if (!a_Buffer.CanReadBytes(5)) - { - return false; - } - char ItemCount; - short ItemDamage; - short MetadataLength; - a_Buffer.ReadChar(ItemCount); - a_Buffer.ReadBEShort(ItemDamage); - a_Buffer.ReadBEShort(MetadataLength); - Printf(a_ItemDesc, "%d:%d * %d", ItemType, ItemDamage, ItemCount); - if (MetadataLength <= 0) - { - return true; - } - AString Metadata; - Metadata.resize(MetadataLength); - if (!a_Buffer.ReadBuf((void *)Metadata.data(), MetadataLength)) - { - return false; - } - AString MetaHex; - CreateHexDump(MetaHex, Metadata.data(), Metadata.size(), 16); - AppendPrintf(a_ItemDesc, "; %d bytes of meta:\n%s", MetadataLength, MetaHex.c_str()); - - // Save metadata to a file: - AString fnam; - Printf(fnam, "%s_item_%08x.nbt", m_LogNameBase.c_str(), m_ItemIdx++); - FILE * f = fopen(fnam.c_str(), "wb"); - if (f != NULL) - { - fwrite(Metadata.data(), 1, Metadata.size(), f); - fclose(f); - AppendPrintf(a_ItemDesc, "\n (saved to file \"%s\")", fnam.c_str()); - } - - return true; -} - - - - - -bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata) -{ - char x; - if (!a_Buffer.ReadChar(x)) - { - return false; - } - a_Metadata.push_back(x); - while (x != 0x7f) - { - int Index = ((unsigned)((unsigned char)x)) & 0x1f; // Lower 5 bits = index - int Type = ((unsigned)((unsigned char)x)) >> 5; // Upper 3 bits = type - int Length = 0; - switch (Type) - { - case 0: Length = 1; break; // byte - case 1: Length = 2; break; // short - case 2: Length = 4; break; // int - case 3: Length = 4; break; // float - case 4: // string16 - { - short Len = 0; - if (!a_Buffer.ReadBEShort(Len)) - { - return false; - } - short NetLen = htons(Len); - a_Metadata.append((char *)&NetLen, 2); - Length = Len; - break; - } - case 5: - { - int Before = a_Buffer.GetReadableSpace(); - AString ItemDesc; - if (!ParseSlot(a_Buffer, ItemDesc)) - { - return false; - } - int After = a_Buffer.GetReadableSpace(); - a_Buffer.ResetRead(); - a_Buffer.SkipRead(a_Buffer.GetReadableSpace() - Before); - Length = Before - After; - break; - } - case 6: Length = 12; break; // 3 * int - default: - { - ASSERT(!"Unknown metadata type"); - break; - } - } // switch (Type) - AString data; - if (!a_Buffer.ReadString(data, Length)) - { - return false; - } - a_Metadata.append(data); - if (!a_Buffer.ReadChar(x)) - { - return false; - } - a_Metadata.push_back(x); - } // while (x != 0x7f) - return true; -} - - - - - -void cConnection::LogMetadata(const AString & a_Metadata, size_t a_IndentCount) -{ - AString Indent(a_IndentCount, ' '); - int pos = 0; - while (a_Metadata[pos] != 0x7f) - { - int Index = ((unsigned)((unsigned char)a_Metadata[pos])) & 0x1f; // Lower 5 bits = index - int Type = ((unsigned)((unsigned char)a_Metadata[pos])) >> 5; // Upper 3 bits = type - int Length = 0; - switch (Type) - { - case 0: - { - Log("%sbyte[%d] = %d", Indent.c_str(), Index, a_Metadata[pos + 1]); - pos += 1; - break; - } - case 1: - { - Log("%sshort[%d] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 8) | a_Metadata[pos + 2]); - pos += 2; - break; - } - case 2: - { - Log("%sint[%d] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]); - pos += 4; - break; - } - case 3: - { - Log("%sfloat[%d] = 0x%x", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]); - pos += 4; - break; - } - case 4: // string16 - { - short Length = (a_Metadata[pos + 1] << 8) | a_Metadata[pos + 2]; - Log("%sstring[%d] = \"%*s\"", Indent.c_str(), Index, Length, a_Metadata.c_str() + pos + 3); - pos += Length + 2; - break; - } - case 5: - { - int BytesLeft = a_Metadata.size() - pos - 1; - cByteBuffer bb(BytesLeft); - bb.Write(a_Metadata.data() + pos + 1, BytesLeft); - AString ItemDesc; - if (!ParseSlot(bb, ItemDesc)) - { - ASSERT(!"Cannot parse item description from metadata"); - return; - } - int After = bb.GetReadableSpace(); - int BytesConsumed = BytesLeft - bb.GetReadableSpace(); - - Log("%sslot[%d] = %s (%d bytes)", Indent.c_str(), Index, ItemDesc.c_str(), BytesConsumed); - pos += BytesConsumed; - break; - } - case 6: - { - Log("%spos[%d] = <%d, %d, %d>", Indent.c_str(), Index, - (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4], - (a_Metadata[pos + 5] << 24) | (a_Metadata[pos + 6] << 16) | (a_Metadata[pos + 7] << 8) | a_Metadata[pos + 8], - (a_Metadata[pos + 9] << 24) | (a_Metadata[pos + 10] << 16) | (a_Metadata[pos + 11] << 8) | a_Metadata[pos + 12] - ); - pos += 12; - break; - } - default: - { - ASSERT(!"Unknown metadata type"); - break; - } - } // switch (Type) - pos += 1; - } // while (x != 0x7f) -} - - - - - -void cConnection::SendEncryptionKeyResponse(const AString & a_ServerPublicKey, const AString & a_Nonce) -{ - // Generate the shared secret and encrypt using the server's public key - byte SharedSecret[16]; - byte EncryptedSecret[128]; - memset(SharedSecret, 0, sizeof(SharedSecret)); // Use all zeroes for the initial secret - RSA::PublicKey pk; - CryptoPP::StringSource src(a_ServerPublicKey, true); - ByteQueue bq; - src.TransferTo(bq); - bq.MessageEnd(); - pk.Load(bq); - RSAES::Encryptor rsaEncryptor(pk); - RandomPool rng; - time_t CurTime = time(NULL); - rng.Put((const byte *)&CurTime, sizeof(CurTime)); - int EncryptedLength = rsaEncryptor.FixedCiphertextLength(); - ASSERT(EncryptedLength <= sizeof(EncryptedSecret)); - rsaEncryptor.Encrypt(rng, SharedSecret, sizeof(SharedSecret), EncryptedSecret); - m_ServerEncryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); - m_ServerDecryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); - - // Encrypt the nonce: - byte EncryptedNonce[128]; - rsaEncryptor.Encrypt(rng, (const byte *)(a_Nonce.data()), a_Nonce.size(), EncryptedNonce); - - // Send the packet to the server: - Log("Sending PACKET_ENCRYPTION_KEY_RESPONSE to the SERVER"); - cByteBuffer ToServer(1024); - ToServer.WriteByte(PACKET_ENCRYPTION_KEY_RESPONSE); - ToServer.WriteBEShort(EncryptedLength); - ToServer.WriteBuf(EncryptedSecret, EncryptedLength); - ToServer.WriteBEShort(EncryptedLength); - ToServer.WriteBuf(EncryptedNonce, EncryptedLength); - SERVERSEND(ToServer); - m_ServerState = csWaitingForEncryption; -} - - - - - -void cConnection::StartClientEncryption(const AString & a_EncKey, const AString & a_EncNonce) -{ - // Decrypt EncNonce using privkey - RSAES::Decryptor rsaDecryptor(m_Server.GetPrivateKey()); - time_t CurTime = time(NULL); - RandomPool rng; - rng.Put((const byte *)&CurTime, sizeof(CurTime)); - byte DecryptedNonce[MAX_ENC_LEN]; - DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce); - if (!res.isValidCoding || (res.messageLength != 4)) - { - Log("Client: Bad nonce length"); - return; - } - if (ntohl(*((int *)DecryptedNonce)) != m_Nonce) - { - Log("Bad nonce value"); - return; - } - - // Decrypt the symmetric encryption key using privkey: - byte SharedSecret[MAX_ENC_LEN]; - res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), SharedSecret); - if (!res.isValidCoding || (res.messageLength != 16)) - { - Log("Bad key length"); - return; - } - - // Send encryption key response: - cByteBuffer ToClient(6); - ToClient.WriteByte((char)0xfc); - ToClient.WriteBEShort(0); - ToClient.WriteBEShort(0); - CLIENTSEND(ToClient); - - // Start the encryption: - m_ClientEncryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); - m_ClientDecryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); - Log("Client connection is now encrypted"); - m_ClientState = csEncryptedUnderstood; - - // Send the queued data: - DataLog(m_ClientEncryptionBuffer.data(), m_ClientEncryptionBuffer.size(), "Sending the queued data to client (%u bytes):", m_ClientEncryptionBuffer.size()); - CLIENTENCRYPTSEND(m_ClientEncryptionBuffer.data(), m_ClientEncryptionBuffer.size()); - m_ClientEncryptionBuffer.clear(); - - // Handle all postponed server data - DecodeServersPackets(NULL, 0); -} - - - - + +// Connection.cpp + +// Interfaces to the cConnection class representing a single pair of connected sockets + +#include "Globals.h" +#include "Connection.h" +#include "Server.h" +#include + + + + + +#ifdef _DEBUG + #define DebugSleep Sleep +#else + #define DebugSleep(X) +#endif // else _DEBUG + + + + + +#define HANDLE_CLIENT_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ClientBuffer.Proc(Var)) \ + { \ + return false; \ + } \ + } + +#define HANDLE_SERVER_PACKET_READ(Proc, Type, Var) \ + Type Var; \ + { \ + if (!m_ServerBuffer.Proc(Var)) \ + { \ + return false; \ + } \ + } + +#define CLIENTSEND(...) SendData(m_ClientSocket, __VA_ARGS__, "Client") +#define SERVERSEND(...) SendData(m_ServerSocket, __VA_ARGS__, "Server") +#define CLIENTENCRYPTSEND(...) SendEncryptedData(m_ClientSocket, m_ClientEncryptor, __VA_ARGS__, "Client") +#define SERVERENCRYPTSEND(...) SendEncryptedData(m_ServerSocket, m_ServerEncryptor, __VA_ARGS__, "Server") + +#define COPY_TO_SERVER() \ + { \ + AString ToServer; \ + m_ClientBuffer.ReadAgain(ToServer); \ + switch (m_ServerState) \ + { \ + case csUnencrypted: \ + { \ + SERVERSEND(ToServer.data(), ToServer.size()); \ + break; \ + } \ + case csEncryptedUnderstood: \ + case csEncryptedUnknown: \ + { \ + SERVERENCRYPTSEND(ToServer.data(), ToServer.size()); \ + break; \ + } \ + case csWaitingForEncryption: \ + { \ + Log("Waiting for server encryption, queued %u bytes", ToServer.size()); \ + m_ServerEncryptionBuffer.append(ToServer.data(), ToServer.size()); \ + break; \ + } \ + } \ + DebugSleep(50); \ + } + +#define COPY_TO_CLIENT() \ + { \ + AString ToClient; \ + m_ServerBuffer.ReadAgain(ToClient); \ + switch (m_ClientState) \ + { \ + case csUnencrypted: \ + { \ + CLIENTSEND(ToClient.data(), ToClient.size()); \ + break; \ + } \ + case csEncryptedUnderstood: \ + case csEncryptedUnknown: \ + { \ + CLIENTENCRYPTSEND(ToClient.data(), ToClient.size()); \ + break; \ + } \ + case csWaitingForEncryption: \ + { \ + Log("Waiting for client encryption, queued %u bytes", ToClient.size()); \ + m_ClientEncryptionBuffer.append(ToClient.data(), ToClient.size()); \ + break; \ + } \ + } \ + DebugSleep(50); \ + } + +#define HANDLE_CLIENT_READ(Proc) \ + { \ + if (!Proc()) \ + { \ + AString Leftover; \ + m_ClientBuffer.ReadAgain(Leftover); \ + DataLog(Leftover.data(), Leftover.size(), "Leftover data after client packet parsing, %d bytes:", Leftover.size()); \ + m_ClientBuffer.ResetRead(); \ + return true; \ + } \ + } + +#define HANDLE_SERVER_READ(Proc) \ + { \ + if (!Proc()) \ + { \ + m_ServerBuffer.ResetRead(); \ + return true; \ + } \ + } + + +#define MAX_ENC_LEN 1024 + + + + +typedef unsigned char Byte; + + + + + +enum +{ + PACKET_KEEPALIVE = 0x00, + PACKET_LOGIN = 0x01, + PACKET_HANDSHAKE = 0x02, + PACKET_CHAT_MESSAGE = 0x03, + PACKET_TIME_UPDATE = 0x04, + PACKET_ENTITY_EQUIPMENT = 0x05, + PACKET_COMPASS = 0x06, + PACKET_USE_ENTITY = 0x07, + PACKET_UPDATE_HEALTH = 0x08, + PACKET_PLAYER_ON_GROUND = 0x0a, + PACKET_PLAYER_POSITION = 0x0b, + PACKET_PLAYER_LOOK = 0x0c, + PACKET_PLAYER_POSITION_LOOK = 0x0d, + PACKET_BLOCK_DIG = 0x0e, + PACKET_BLOCK_PLACE = 0x0f, + PACKET_SLOT_SELECT = 0x10, + PACKET_PLAYER_ANIMATION = 0x12, + PACKET_ENTITY_ACTION = 0x13, + PACKET_SPAWN_NAMED_ENTITY = 0x14, + PACKET_SPAWN_PICKUP = 0x15, + PACKET_COLLECT_PICKUP = 0x16, + PACKET_SPAWN_OBJECT_VEHICLE = 0x17, + PACKET_SPAWN_MOB = 0x18, + PACKET_SPAWN_PAINTING = 0x19, + PACKET_SPAWN_EXPERIENCE_ORB = 0x1a, + PACKET_ENTITY_VELOCITY = 0x1c, + PACKET_DESTROY_ENTITIES = 0x1d, + PACKET_ENTITY = 0x1e, + PACKET_ENTITY_RELATIVE_MOVE = 0x1f, + PACKET_ENTITY_LOOK = 0x20, + PACKET_ENTITY_RELATIVE_MOVE_LOOK = 0x21, + PACKET_ENTITY_TELEPORT = 0x22, + PACKET_ENTITY_HEAD_LOOK = 0x23, + PACKET_ENTITY_STATUS = 0x26, + PACKET_ATTACH_ENTITY = 0x27, + PACKET_ENTITY_METADATA = 0x28, + PACKET_ENTITY_EFFECT = 0x29, + PACKET_ENTITY_EFFECT_REMOVE = 0x2a, + PACKET_SET_EXPERIENCE = 0x2b, + PACKET_ENTITY_PROPERTIES = 0x2c, + PACKET_MAP_CHUNK = 0x33, + PACKET_MULTI_BLOCK_CHANGE = 0x34, + PACKET_BLOCK_CHANGE = 0x35, + PACKET_BLOCK_ACTION = 0x36, + PACKET_MAP_CHUNK_BULK = 0x38, + PACKET_SOUND_EFFECT = 0x3d, + PACKET_NAMED_SOUND_EFFECT = 0x3e, + PACKET_CHANGE_GAME_STATE = 0x46, + PACKET_WINDOW_OPEN = 0x64, + PACKET_WINDOW_CLOSE = 0x65, + PACKET_WINDOW_CLICK = 0x66, + PACKET_SET_SLOT = 0x67, + PACKET_WINDOW_CONTENTS = 0x68, + PACKET_CREATIVE_INVENTORY_ACTION = 0x6b, + PACKET_UPDATE_SIGN = 0x82, + PACKET_UPDATE_TILE_ENTITY = 0x84, + PACKET_PLAYER_LIST_ITEM = 0xc9, + PACKET_PLAYER_ABILITIES = 0xca, + PACKET_INCREMENT_STATISTIC = 0xc8, + PACKET_LOCALE_AND_VIEW = 0xcc, + PACKET_CLIENT_STATUSES = 0xcd, + PACKET_PLUGIN_MESSAGE = 0xfa, + PACKET_ENCRYPTION_KEY_RESPONSE = 0xfc, + PACKET_ENCRYPTION_KEY_REQUEST = 0xfd, + PACKET_PING = 0xfe, + PACKET_KICK = 0xff, +} ; + + + + +enum +{ + OBJECT_BOAT = 1, + OBJECT_MINECART = 10, + OBJECT_MINECART_STORAGE = 11, + OBJECT_MINECART_POWERED = 12, + OBJECT_TNT = 50, + OBJECT_ENDERCRYSTAL = 51, + OBJECT_ARROW = 60, + OBJECT_SNOWBALL = 61, + OBJECT_EGG = 62, + OBJECT_FALLING_BLOCK = 70, + OBJECT_EYE_OF_ENDER = 72, + OBJECT_DRAGON_EGG = 74, + OBJECT_FISHING_FLOAT = 90, +} ; + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cConnection: + +cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) : + m_ItemIdx(0), + m_LogFile(NULL), + m_Server(a_Server), + m_ClientSocket(a_ClientSocket), + m_ServerSocket(-1), + m_BeginTick(clock()), + m_ClientState(csUnencrypted), + m_ServerState(csUnencrypted), + m_Nonce(0), + m_ClientBuffer(1024 KiB), + m_ServerBuffer(1024 KiB), + m_HasClientPinged(false) +{ + Printf(m_LogNameBase, "Log_%d", (int)time(NULL)); + AString fnam(m_LogNameBase); + fnam.append(".log"); + m_LogFile = fopen(fnam.c_str(), "w"); + Log("Log file created"); + printf("Connection is logged to file \"%s\"\n", fnam.c_str()); +} + + + + + +cConnection::~cConnection() +{ + fclose(m_LogFile); +} + + + + + +void cConnection::Run(void) +{ + if (!ConnectToServer()) + { + Log("Cannot connect to server; aborting"); + return; + } + + while (true) + { + fd_set ReadFDs; + FD_ZERO(&ReadFDs); + FD_SET(m_ServerSocket, &ReadFDs); + FD_SET(m_ClientSocket, &ReadFDs); + int res = select(2, &ReadFDs, NULL, NULL, NULL); + if (res <= 0) + { + printf("select() failed: %d; aborting client", WSAGetLastError()); + break; + } + if (FD_ISSET(m_ServerSocket, &ReadFDs)) + { + if (!RelayFromServer()) + { + break; + } + } + if (FD_ISSET(m_ClientSocket, &ReadFDs)) + { + if (!RelayFromClient()) + { + break; + } + } + } + Log("Relaying ended, closing sockets"); + closesocket(m_ServerSocket); + closesocket(m_ClientSocket); +} + + + + + +void cConnection::Log(const char * a_Format, ...) +{ + va_list args; + va_start(args, a_Format); + AString msg; + AppendVPrintf(msg, a_Format, args); + va_end(args); + AString FullMsg; + Printf(FullMsg, "[%5.3f] %s\n", GetRelativeTime(), msg.c_str()); + + // Log to file: + cCSLock Lock(m_CSLog); + fputs(FullMsg.c_str(), m_LogFile); + + // Log to screen: + // std::cout << FullMsg; +} + + + + + +void cConnection::DataLog(const void * a_Data, int a_Size, const char * a_Format, ...) +{ + va_list args; + va_start(args, a_Format); + AString msg; + AppendVPrintf(msg, a_Format, args); + va_end(args); + AString FullMsg; + AString Hex; + Printf(FullMsg, "[%5.3f] %s\n%s\n", GetRelativeTime(), msg.c_str(), CreateHexDump(Hex, a_Data, a_Size, 16).c_str()); + + // Log to file: + cCSLock Lock(m_CSLog); + fputs(FullMsg.c_str(), m_LogFile); + + /* + // Log to screen: + std::cout << FullMsg; + //*/ +} + + + + + +void cConnection::LogFlush(void) +{ + fflush(m_LogFile); +} + + + + + +bool cConnection::ConnectToServer(void) +{ + m_ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_ServerSocket == INVALID_SOCKET) + { + return false; + } + sockaddr_in localhost; + localhost.sin_family = AF_INET; + localhost.sin_port = htons(m_Server.GetConnectPort()); + localhost.sin_addr.s_addr = htonl(0x7f000001); // localhost + if (connect(m_ServerSocket, (sockaddr *)&localhost, sizeof(localhost)) != 0) + { + printf("connection to server failed: %d\n", WSAGetLastError()); + return false; + } + Log("Connected to SERVER"); + return true; +} + + + + + +bool cConnection::RelayFromServer(void) +{ + char Buffer[64 KiB]; + int res = recv(m_ServerSocket, Buffer, sizeof(Buffer), 0); + if (res <= 0) + { + Log("Server closed the socket: %d; %d; aborting connection", res, WSAGetLastError()); + return false; + } + + DataLog(Buffer, res, "Received %d bytes from the SERVER", res); + + switch (m_ServerState) + { + case csUnencrypted: + case csWaitingForEncryption: + { + return DecodeServersPackets(Buffer, res); + } + case csEncryptedUnderstood: + { + m_ServerDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + DataLog(Buffer, res, "Decrypted %d bytes from the SERVER", res); + return DecodeServersPackets(Buffer, res); + } + case csEncryptedUnknown: + { + m_ServerDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + DataLog(Buffer, res, "Decrypted %d bytes from the SERVER", res); + m_ClientEncryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + return CLIENTSEND(Buffer, res); + } + } + + return true; +} + + + + + +bool cConnection::RelayFromClient(void) +{ + char Buffer[64 KiB]; + int res = recv(m_ClientSocket, Buffer, sizeof(Buffer), 0); + if (res <= 0) + { + Log("Client closed the socket: %d; %d; aborting connection", res, WSAGetLastError()); + return false; + } + + DataLog(Buffer, res, "Received %d bytes from the CLIENT", res); + + switch (m_ClientState) + { + case csUnencrypted: + case csWaitingForEncryption: + { + return DecodeClientsPackets(Buffer, res); + } + case csEncryptedUnderstood: + { + m_ClientDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + DataLog(Buffer, res, "Decrypted %d bytes from the CLIENT", res); + return DecodeClientsPackets(Buffer, res); + } + case csEncryptedUnknown: + { + m_ClientDecryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + DataLog(Buffer, res, "Decrypted %d bytes from the CLIENT", res); + m_ServerEncryptor.ProcessData((byte *)Buffer, (byte *)Buffer, res); + return SERVERSEND(Buffer, res); + } + } + + return true; +} + + + + + +double cConnection::GetRelativeTime(void) +{ + return (double)(clock() - m_BeginTick) / CLOCKS_PER_SEC; + +} + + + + + +bool cConnection::SendData(SOCKET a_Socket, const char * a_Data, int a_Size, const char * a_Peer) +{ + DataLog(a_Data, a_Size, "Sending data to %s", a_Peer); + + int res = send(a_Socket, a_Data, a_Size, 0); + if (res <= 0) + { + Log("%s closed the socket: %d, %d; aborting connection", a_Peer, res, WSAGetLastError()); + return false; + } + return true; +} + + + + + +bool cConnection::SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer) +{ + AString All; + a_Data.ReadAll(All); + a_Data.CommitRead(); + return SendData(a_Socket, All.data(), All.size(), a_Peer); +} + + + + + +bool cConnection::SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, const char * a_Data, int a_Size, const char * a_Peer) +{ + DataLog(a_Data, a_Size, "Encrypting %d bytes to %s", a_Size, a_Peer); + const byte * Data = (const byte *)a_Data; + while (a_Size > 0) + { + byte Buffer[64 KiB]; + int NumBytes = (a_Size > sizeof(Buffer)) ? sizeof(Buffer) : a_Size; + a_Encryptor.ProcessData(Buffer, Data, NumBytes); + bool res = SendData(a_Socket, (const char *)Buffer, NumBytes, a_Peer); + if (!res) + { + return false; + } + Data += NumBytes; + a_Size -= NumBytes; + } + return true; +} + + + + + +bool cConnection::SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer) +{ + AString All; + a_Data.ReadAll(All); + a_Data.CommitRead(); + return SendEncryptedData(a_Socket, a_Encryptor, All.data(), All.size(), a_Peer); +} + + + + + +bool cConnection::DecodeClientsPackets(const char * a_Data, int a_Size) +{ + if (!m_ClientBuffer.Write(a_Data, a_Size)) + { + Log("Too much queued data for the server, aborting connection"); + return false; + } + + while (m_ClientBuffer.CanReadBytes(1)) + { + Log("Decoding client's packets, there are now %d bytes in the queue", m_ClientBuffer.GetReadableSpace()); + unsigned char PacketType; + m_ClientBuffer.ReadByte(PacketType); + switch (PacketType) + { + case PACKET_BLOCK_DIG: HANDLE_CLIENT_READ(HandleClientBlockDig); break; + case PACKET_BLOCK_PLACE: HANDLE_CLIENT_READ(HandleClientBlockPlace); break; + case PACKET_CHAT_MESSAGE: HANDLE_CLIENT_READ(HandleClientChatMessage); break; + case PACKET_CLIENT_STATUSES: HANDLE_CLIENT_READ(HandleClientClientStatuses); break; + case PACKET_CREATIVE_INVENTORY_ACTION: HANDLE_CLIENT_READ(HandleClientCreativeInventoryAction); break; + case PACKET_ENCRYPTION_KEY_RESPONSE: HANDLE_CLIENT_READ(HandleClientEncryptionKeyResponse); break; + case PACKET_ENTITY_ACTION: HANDLE_CLIENT_READ(HandleClientEntityAction); break; + case PACKET_HANDSHAKE: HANDLE_CLIENT_READ(HandleClientHandshake); break; + case PACKET_KEEPALIVE: HANDLE_CLIENT_READ(HandleClientKeepAlive); break; + case PACKET_LOCALE_AND_VIEW: HANDLE_CLIENT_READ(HandleClientLocaleAndView); break; + case PACKET_PING: HANDLE_CLIENT_READ(HandleClientPing); break; + case PACKET_PLAYER_ABILITIES: HANDLE_CLIENT_READ(HandleClientPlayerAbilities); break; + case PACKET_PLAYER_ANIMATION: HANDLE_CLIENT_READ(HandleClientAnimation); break; + case PACKET_PLAYER_LOOK: HANDLE_CLIENT_READ(HandleClientPlayerLook); break; + case PACKET_PLAYER_ON_GROUND: HANDLE_CLIENT_READ(HandleClientPlayerOnGround); break; + case PACKET_PLAYER_POSITION: HANDLE_CLIENT_READ(HandleClientPlayerPosition); break; + case PACKET_PLAYER_POSITION_LOOK: HANDLE_CLIENT_READ(HandleClientPlayerPositionLook); break; + case PACKET_PLUGIN_MESSAGE: HANDLE_CLIENT_READ(HandleClientPluginMessage); break; + case PACKET_SLOT_SELECT: HANDLE_CLIENT_READ(HandleClientSlotSelect); break; + case PACKET_UPDATE_SIGN: HANDLE_CLIENT_READ(HandleClientUpdateSign); break; + case PACKET_USE_ENTITY: HANDLE_CLIENT_READ(HandleClientUseEntity); break; + case PACKET_WINDOW_CLICK: HANDLE_CLIENT_READ(HandleClientWindowClick); break; + case PACKET_WINDOW_CLOSE: HANDLE_CLIENT_READ(HandleClientWindowClose); break; + default: + { + if (m_ClientState == csEncryptedUnderstood) + { + Log("****************** Unknown packet 0x%02x from the client while encrypted; continuing to relay blind only", PacketType); + AString Data; + m_ClientBuffer.ResetRead(); + m_ClientBuffer.ReadAll(Data); + DataLog(Data.data(), Data.size(), "Current data in the client packet queue: %d bytes", Data.size()); + m_ClientState = csEncryptedUnknown; + m_ClientBuffer.ResetRead(); + if (m_ServerState == csUnencrypted) + { + SERVERSEND(m_ClientBuffer); + } + else + { + SERVERENCRYPTSEND(m_ClientBuffer); + } + return true; + } + else + { + Log("Unknown packet 0x%02x from the client while unencrypted; aborting connection", PacketType); + return false; + } + } + } // switch (PacketType) + m_ClientBuffer.CommitRead(); + } // while (CanReadBytes(1)) + return true; +} + + + + + +bool cConnection::DecodeServersPackets(const char * a_Data, int a_Size) +{ + if (!m_ServerBuffer.Write(a_Data, a_Size)) + { + Log("Too much queued data for the client, aborting connection"); + return false; + } + + if ( + (m_ServerState == csEncryptedUnderstood) && + (m_ClientState == csUnencrypted) + ) + { + // Client hasn't finished encryption handshake yet, don't send them any data yet + } + + while (m_ServerBuffer.CanReadBytes(1)) + { + unsigned char PacketType; + m_ServerBuffer.ReadByte(PacketType); + Log("Decoding server's packets, there are now %d bytes in the queue; next packet is 0x%x", m_ServerBuffer.GetReadableSpace(), PacketType); + LogFlush(); + switch (PacketType) + { + case PACKET_ATTACH_ENTITY: HANDLE_SERVER_READ(HandleServerAttachEntity); break; + case PACKET_BLOCK_ACTION: HANDLE_SERVER_READ(HandleServerBlockAction); break; + case PACKET_BLOCK_CHANGE: HANDLE_SERVER_READ(HandleServerBlockChange); break; + case PACKET_CHANGE_GAME_STATE: HANDLE_SERVER_READ(HandleServerChangeGameState); break; + case PACKET_CHAT_MESSAGE: HANDLE_SERVER_READ(HandleServerChatMessage); break; + case PACKET_COLLECT_PICKUP: HANDLE_SERVER_READ(HandleServerCollectPickup); break; + case PACKET_COMPASS: HANDLE_SERVER_READ(HandleServerCompass); break; + case PACKET_DESTROY_ENTITIES: HANDLE_SERVER_READ(HandleServerDestroyEntities); break; + case PACKET_ENCRYPTION_KEY_REQUEST: HANDLE_SERVER_READ(HandleServerEncryptionKeyRequest); break; + case PACKET_ENCRYPTION_KEY_RESPONSE: HANDLE_SERVER_READ(HandleServerEncryptionKeyResponse); break; + case PACKET_ENTITY: HANDLE_SERVER_READ(HandleServerEntity); break; + case PACKET_ENTITY_EQUIPMENT: HANDLE_SERVER_READ(HandleServerEntityEquipment); break; + case PACKET_ENTITY_HEAD_LOOK: HANDLE_SERVER_READ(HandleServerEntityHeadLook); break; + case PACKET_ENTITY_LOOK: HANDLE_SERVER_READ(HandleServerEntityLook); break; + case PACKET_ENTITY_METADATA: HANDLE_SERVER_READ(HandleServerEntityMetadata); break; + case PACKET_ENTITY_PROPERTIES: HANDLE_SERVER_READ(HandleServerEntityProperties); break; + case PACKET_ENTITY_RELATIVE_MOVE: HANDLE_SERVER_READ(HandleServerEntityRelativeMove); break; + case PACKET_ENTITY_RELATIVE_MOVE_LOOK: HANDLE_SERVER_READ(HandleServerEntityRelativeMoveLook); break; + case PACKET_ENTITY_STATUS: HANDLE_SERVER_READ(HandleServerEntityStatus); break; + case PACKET_ENTITY_TELEPORT: HANDLE_SERVER_READ(HandleServerEntityTeleport); break; + case PACKET_ENTITY_VELOCITY: HANDLE_SERVER_READ(HandleServerEntityVelocity); break; + case PACKET_INCREMENT_STATISTIC: HANDLE_SERVER_READ(HandleServerIncrementStatistic); break; + case PACKET_KEEPALIVE: HANDLE_SERVER_READ(HandleServerKeepAlive); break; + case PACKET_KICK: HANDLE_SERVER_READ(HandleServerKick); break; + case PACKET_LOGIN: HANDLE_SERVER_READ(HandleServerLogin); break; + case PACKET_MAP_CHUNK: HANDLE_SERVER_READ(HandleServerMapChunk); break; + case PACKET_MAP_CHUNK_BULK: HANDLE_SERVER_READ(HandleServerMapChunkBulk); break; + case PACKET_MULTI_BLOCK_CHANGE: HANDLE_SERVER_READ(HandleServerMultiBlockChange); break; + case PACKET_NAMED_SOUND_EFFECT: HANDLE_SERVER_READ(HandleServerNamedSoundEffect); break; + case PACKET_PLAYER_ABILITIES: HANDLE_SERVER_READ(HandleServerPlayerAbilities); break; + case PACKET_PLAYER_ANIMATION: HANDLE_SERVER_READ(HandleServerPlayerAnimation); break; + case PACKET_PLAYER_LIST_ITEM: HANDLE_SERVER_READ(HandleServerPlayerListItem); break; + case PACKET_PLAYER_POSITION_LOOK: HANDLE_SERVER_READ(HandleServerPlayerPositionLook); break; + case PACKET_PLUGIN_MESSAGE: HANDLE_SERVER_READ(HandleServerPluginMessage); break; + case PACKET_SET_EXPERIENCE: HANDLE_SERVER_READ(HandleServerSetExperience); break; + case PACKET_SET_SLOT: HANDLE_SERVER_READ(HandleServerSetSlot); break; + case PACKET_SLOT_SELECT: HANDLE_SERVER_READ(HandleServerSlotSelect); break; + case PACKET_SOUND_EFFECT: HANDLE_SERVER_READ(HandleServerSoundEffect); break; + case PACKET_SPAWN_MOB: HANDLE_SERVER_READ(HandleServerSpawnMob); break; + case PACKET_SPAWN_NAMED_ENTITY: HANDLE_SERVER_READ(HandleServerSpawnNamedEntity); break; + case PACKET_SPAWN_OBJECT_VEHICLE: HANDLE_SERVER_READ(HandleServerSpawnObjectVehicle); break; + case PACKET_SPAWN_PAINTING: HANDLE_SERVER_READ(HandleServerSpawnPainting); break; + case PACKET_SPAWN_PICKUP: HANDLE_SERVER_READ(HandleServerSpawnPickup); break; + case PACKET_TIME_UPDATE: HANDLE_SERVER_READ(HandleServerTimeUpdate); break; + case PACKET_UPDATE_HEALTH: HANDLE_SERVER_READ(HandleServerUpdateHealth); break; + case PACKET_UPDATE_SIGN: HANDLE_SERVER_READ(HandleServerUpdateSign); break; + case PACKET_UPDATE_TILE_ENTITY: HANDLE_SERVER_READ(HandleServerUpdateTileEntity); break; + case PACKET_WINDOW_CLOSE: HANDLE_SERVER_READ(HandleServerWindowClose); break; + case PACKET_WINDOW_CONTENTS: HANDLE_SERVER_READ(HandleServerWindowContents); break; + case PACKET_WINDOW_OPEN: HANDLE_SERVER_READ(HandleServerWindowOpen); break; + default: + { + if (m_ServerState == csEncryptedUnderstood) + { + Log("********************** Unknown packet 0x%02x from the server while encrypted; continuing to relay blind only", PacketType); + AString Data; + m_ServerBuffer.ResetRead(); + m_ServerBuffer.ReadAll(Data); + DataLog(Data.data(), Data.size(), "Current data in the server packet queue: %d bytes", Data.size()); + m_ServerState = csEncryptedUnknown; + m_ServerBuffer.ResetRead(); + if (m_ClientState == csUnencrypted) + { + CLIENTSEND(m_ServerBuffer); + } + else + { + CLIENTENCRYPTSEND(m_ServerBuffer); + } + return true; + } + else + { + Log("Unknown packet 0x%02x from the server while unencrypted; aborting connection", PacketType); + return false; + } + } + } // switch (PacketType) + m_ServerBuffer.CommitRead(); + } // while (CanReadBytes(1)) + return true; +} + + + + + +bool cConnection::HandleClientAnimation(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, Animation); + Log("Received a PACKET_ANIMATION from the client:"); + Log(" EntityID: %d", EntityID); + Log(" Animation: %d", Animation); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientBlockDig(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, Status); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, BlockY); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, BlockFace); + Log("Received a PACKET_BLOCK_DIG from the client:"); + Log(" Status = %d", Status); + Log(" Pos = <%d, %d, %d>", BlockX, BlockY, BlockZ); + Log(" BlockFace = %d", BlockFace); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientBlockPlace(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, BlockY); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, Face); + AString Desc; + if (!ParseSlot(m_ClientBuffer, Desc)) + { + return false; + } + HANDLE_CLIENT_PACKET_READ(ReadChar, char, CursorX); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, CursorY); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, CursorZ); + Log("Received a PACKET_BLOCK_PLACE from the client:"); + Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Face = %d", Face); + Log(" Item = %s", Desc.c_str()); + Log(" Cursor = <%d, %d, %d>", CursorX, CursorY, CursorZ); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientChatMessage(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Message); + Log("Received a PACKET_CHAT_MESSAGE from the client:"); + Log(" Message = \"%s\"", Message.c_str()); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientClientStatuses(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadChar, char, Statuses); + Log("Received a PACKET_CLIENT_STATUSES from the CLIENT:"); + Log(" Statuses = %d", Statuses); + + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientCreativeInventoryAction(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, SlotNum); + AString Item; + if (!ParseSlot(m_ClientBuffer, Item)) + { + return false; + } + Log("Received a PACKET_CREATIVE_INVENTORY_ACTION from the client:"); + Log(" SlotNum = %d", SlotNum); + Log(" Item = %s", Item.c_str()); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientEncryptionKeyResponse(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, EncKeyLength); + AString EncKey; + if (!m_ClientBuffer.ReadString(EncKey, EncKeyLength)) + { + return true; + } + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, EncNonceLength); + AString EncNonce; + if (!m_ClientBuffer.ReadString(EncNonce, EncNonceLength)) + { + return true; + } + if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN)) + { + Log("Client: Too long encryption params"); + return true; + } + StartClientEncryption(EncKey, EncNonce); + return true; +} + + + + + +bool cConnection::HandleClientEntityAction(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, PlayerID); + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, ActionType); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, UnknownHorseVal); + Log("Received a PACKET_ENTITY_ACTION from the client:"); + Log(" PlayerID = %d", PlayerID); + Log(" ActionType = %d", ActionType); + Log(" UnknownHorseVal = %d (0x%08x)", UnknownHorseVal, UnknownHorseVal); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientHandshake(void) +{ + // Read the packet from the client: + HANDLE_CLIENT_PACKET_READ(ReadByte, Byte, ProtocolVersion); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Username); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, ServerHost); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, ServerPort); + m_ClientBuffer.CommitRead(); + + Log("Received a PACKET_HANDSHAKE from the client:"); + Log(" ProtocolVersion = %d", ProtocolVersion); + Log(" Username = \"%s\"", Username.c_str()); + Log(" ServerHost = \"%s\"", ServerHost.c_str()); + Log(" ServerPort = %d", ServerPort); + + // Send the same packet to the server, but with our port: + cByteBuffer ToServer(512); + ToServer.WriteByte (PACKET_HANDSHAKE); + ToServer.WriteByte (ProtocolVersion); + ToServer.WriteBEUTF16String16(Username); + ToServer.WriteBEUTF16String16(ServerHost); + ToServer.WriteBEInt (m_Server.GetConnectPort()); + SERVERSEND(ToServer); + + return true; +} + + + + + +bool cConnection::HandleClientKeepAlive(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, ID); + Log("Received a PACKET_KEEPALIVE from the client"); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientLocaleAndView(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Locale); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, ViewDistance); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, ChatFlags); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, ShowCape); + Log("Received a PACKET_LOCALE_AND_VIEW from the client"); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientPing(void) +{ + m_HasClientPinged = true; + Log("Received a PACKET_PING from the client"); + m_ClientBuffer.ResetRead(); + SERVERSEND(m_ClientBuffer); + return true; +} + + + + + +bool cConnection::HandleClientPlayerAbilities(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadChar, char, Flags); + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, FlyingSpeed); + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, WalkingSpeed); + Log("Receives a PACKET_PLAYER_ABILITIES from the client:"); + Log(" Flags = %d (0x%02x)", Flags, Flags); + Log(" FlyingSpeed = %f", FlyingSpeed); + Log(" WalkingSpeed = %f", WalkingSpeed); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientPlayerLook(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Yaw); + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, OnGround); + Log("Received a PACKET_PLAYER_LOOK from the client"); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientPlayerOnGround(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadChar, char, OnGround); + Log("Received a PACKET_PLAYER_ON_GROUND from the client"); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientPlayerPosition(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsOnGround); + Log("Received a PACKET_PLAYER_POSITION from the client"); + + // TODO: list packet contents + + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientPlayerPositionLook(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_CLIENT_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Yaw); + HANDLE_CLIENT_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsOnGround); + Log("Received a PACKET_PLAYER_POSITION_LOOK from the client"); + Log(" Pos = {%.03f, %.03f, %.03f}", PosX, PosY, PosZ); + Log(" Stance = %.03f", Stance); + Log(" Y, P = %.03f, %.03f", Yaw, Pitch); + Log(" IsOnGround = %s", IsOnGround ? "true" : "false"); + + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientPluginMessage(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, ChannelName); + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, Length); + AString Data; + if (!m_ClientBuffer.ReadString(Data, Length)) + { + return false; + } + Log("Received a PACKET_PLUGIN_MESSAGE from the client"); + Log(" ChannelName = \"%s\"", ChannelName.c_str()); + DataLog(Data.data(), Length, " Data: %d bytes", Length); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientSlotSelect(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, SlotNum); + Log("Received a PACKET_SLOT_SELECT from the client"); + Log(" SlotNum = %d", SlotNum); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientUpdateSign(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line1); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line2); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line3); + HANDLE_CLIENT_PACKET_READ(ReadBEUTF16String16, AString, Line4); + Log("Received a PACKET_UPDATE_SIGN from the client:"); + Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Lines = \"%s\", \"%s\", \"%s\", \"%s\"", Line1.c_str(), Line2.c_str(), Line3.c_str(), Line4.c_str()); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientUseEntity(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, PlayerID); + HANDLE_CLIENT_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, MouseButton); + Log("Received a PACKET_USE_ENTITY from the client:"); + Log(" PlayerID = %d", PlayerID); + Log(" EntityID = %d", EntityID); + Log(" MouseButton = %d", MouseButton); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientWindowClick(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadChar, char, WindowID); + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, SlotNum); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsRightClick); + HANDLE_CLIENT_PACKET_READ(ReadBEShort, short, TransactionID); + HANDLE_CLIENT_PACKET_READ(ReadChar, char, IsShiftClick); + AString Item; + if (!ParseSlot(m_ClientBuffer, Item)) + { + return false; + } + Log("Received a PACKET_WINDOW_CLICK from the client"); + Log(" WindowID = %d", WindowID); + Log(" SlotNum = %d", SlotNum); + Log(" IsRclk = %d, IsShift = %d", IsRightClick, IsShiftClick); + Log(" TransactionID = 0x%x", TransactionID); + Log(" ClickedItem = %s", Item.c_str()); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleClientWindowClose(void) +{ + HANDLE_CLIENT_PACKET_READ(ReadChar, char, WindowID); + Log("Received a PACKET_WINDOW_CLOSE from the client:"); + Log(" WindowID = %d", WindowID); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleServerAttachEntity(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, VehicleID); + HANDLE_SERVER_PACKET_READ(ReadBool, bool, Leash); + Log("Received a PACKET_ATTACH_ENTITY from the server:"); + Log(" EntityID = %d (0x%x)", EntityID, EntityID); + Log(" VehicleID = %d (0x%x)", VehicleID, VehicleID); + Log(" Leash = %s", Leash ? "true" : "false"); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerBlockAction(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Byte1); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Byte2); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockID); + Log("Received a PACKET_BLOCK_ACTION from the server:"); + Log(" Pos = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Bytes = (%d, %d) == (0x%x, 0x%x)", Byte1, Byte2, Byte1, Byte2); + Log(" BlockID = %d", BlockID); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerBlockChange(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, BlockY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockType); + HANDLE_SERVER_PACKET_READ(ReadChar, char, BlockMeta); + Log("Received a PACKET_BLOCK_CHANGE from the server"); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerChangeGameState(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, Reason); + HANDLE_SERVER_PACKET_READ(ReadChar, char, Data); + Log("Received a PACKET_CHANGE_GAME_STATE from the server:"); + Log(" Reason = %d", Reason); + Log(" Data = %d", Data); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerChatMessage(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Message); + Log("Received a PACKET_CHAT_MESSAGE from the server:"); + Log(" Message = \"%s\"", Message.c_str()); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerCollectPickup(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CollectedID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CollectorID); + Log("Received a PACKET_COLLECT_PICKUP from the server:"); + Log(" CollectedID = %d", CollectedID); + Log(" CollectorID = %d", CollectorID); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerCompass(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, SpawnX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, SpawnY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, SpawnZ); + Log("Received PACKET_COMPASS from the server:"); + Log(" Spawn = {%d, %d, %d}", SpawnX, SpawnY, SpawnZ); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerDestroyEntities(void) +{ + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, NumEntities); + if (!m_ServerBuffer.SkipRead((int)NumEntities * 4)) + { + return false; + } + Log("Received PACKET_DESTROY_ENTITIES from the server:"); + Log(" NumEntities = %d", NumEntities); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEncryptionKeyRequest(void) +{ + // Read the packet from the server: + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ServerID); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PublicKeyLength); + AString PublicKey; + if (!m_ServerBuffer.ReadString(PublicKey, PublicKeyLength)) + { + return false; + } + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NonceLength); + AString Nonce; + if (!m_ServerBuffer.ReadString(Nonce, NonceLength)) + { + return false; + } + Log("Got PACKET_ENCRYPTION_KEY_REQUEST from the SERVER:"); + Log(" ServerID = %s", ServerID.c_str()); + + // Reply to the server: + SendEncryptionKeyResponse(PublicKey, Nonce); + + // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD to the client, using our own key: + Log("Sending PACKET_ENCRYPTION_KEY_REQUEST to the CLIENT"); + AString key; + StringSink sink(key); // GCC won't allow inline instantiation in the following line, damned temporary refs + m_Server.GetPublicKey().Save(sink); + cByteBuffer ToClient(512); + ToClient.WriteByte (PACKET_ENCRYPTION_KEY_REQUEST); + ToClient.WriteBEUTF16String16(ServerID); + ToClient.WriteBEShort ((short)key.size()); + ToClient.WriteBuf (key.data(), key.size()); + ToClient.WriteBEShort (4); + ToClient.WriteBEInt (m_Nonce); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :) + CLIENTSEND(ToClient); + return true; +} + + + + + +bool cConnection::HandleServerEntity(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + Log("Received a PACKET_ENTITY from the server:"); + Log(" EntityID = %d", EntityID); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityEquipment(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SlotNum); + AString Item; + if (!ParseSlot(m_ServerBuffer, Item)) + { + return false; + } + Log("Received a PACKET_ENTITY_EQUIPMENT from the server:"); + Log(" EntityID = %d", EntityID); + Log(" SlotNum = %d", SlotNum); + Log(" Item = %s", Item.c_str()); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityHeadLook(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, HeadYaw); + Log("Received a PACKET_ENTITY_HEAD_LOOK from the server:"); + Log(" EntityID = %d", EntityID); + Log(" HeadYaw = %d", HeadYaw); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityLook(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + Log("Received a PACKET_ENTITY_LOOK from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Yaw = %d", Yaw); + Log(" Pitch = %d", Pitch); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityMetadata(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + AString Metadata; + if (!ParseMetadata(m_ServerBuffer, Metadata)) + { + return false; + } + AString HexDump; + CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32); + Log("Received a PACKET_ENTITY_METADATA from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Metadata, length = %d (0x%x):\n%s", Metadata.length(), Metadata.length(), HexDump.c_str()); + LogMetadata(Metadata, 4); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityProperties(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Count); + Log("Received a PACKET_ENTITY_PROPERTIES from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Count = %d", Count); + + for (int i = 0; i < Count; i++) + { + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Key); + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, Value); + Log(" \"%s\" = %f", Key.c_str(), Value); + } // for i + + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, ListLength); + Log(" ListLength = %d", ListLength); + for (int i = 0; i < ListLength; i++) + { + HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, UUIDHi); + HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, UUIDLo); + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, DblVal); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, ByteVal); + Log(" [%d] = {0x%08llx%08llx, %f, %i}", i, UUIDHi, UUIDLo, DblVal, ByteVal); + } // for i + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityRelativeMove(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dx); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dy); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dz); + Log("Received a PACKET_ENTITY_RELATIVE_MOVE from the server:"); + Log(" EntityID = %d", EntityID); + Log(" RelMove = <%d, %d, %d>", dx, dy, dz); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityRelativeMoveLook(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dx); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dy); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, dz); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + Log("Received a PACKET_ENTITY_RELATIVE_MOVE_LOOK from the server:"); + Log(" EntityID = %d", EntityID); + Log(" RelMove = <%d, %d, %d>", dx, dy, dz); + Log(" Yaw = %d", Yaw); + Log(" Pitch = %d", Pitch); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityStatus(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Status); + Log("Received a PACKET_ENTITY_STATUS from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Status = %d", Status); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityTeleport(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + Log("Received a PACKET_ENTITY_TELEPORT from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Pos = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Yaw = %d", Yaw); + Log(" Pitch = %d", Pitch); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerEntityVelocity(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityY); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityZ); + Log("Received a PACKET_ENTITY_VELOCITY from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerIncrementStatistic(void) +{ + // 0xc8 + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, StatisticID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Amount); + Log("Received a PACKET_INCREMENT_STATISTIC from the server:"); + Log(" StatisticID = %d (0x%x)", StatisticID, StatisticID); + Log(" Amount = %d", Amount); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerKeepAlive(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PingID); + Log("Received a PACKET_KEEP_ALIVE from the server:"); + Log(" ID = %d", PingID); + COPY_TO_CLIENT() + return true; +} + + + + + +bool cConnection::HandleServerEncryptionKeyResponse(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Lengths); + if (Lengths != 0) + { + Log("Lengths are not zero!"); + return true; + } + Log("Server communication is now encrypted"); + m_ServerState = csEncryptedUnderstood; + DataLog(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size(), "Sending the queued data to server (%u bytes):", m_ServerEncryptionBuffer.size()); + SERVERENCRYPTSEND(m_ServerEncryptionBuffer.data(), m_ServerEncryptionBuffer.size()); + m_ServerEncryptionBuffer.clear(); + return true; +} + + + + + +bool cConnection::HandleServerKick(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Reason); + Log("Received PACKET_KICK from the SERVER:"); + if (m_HasClientPinged) + { + Log(" This was a std reply to client's PING"); + AStringVector Split; + + // Split by NULL chars (StringSplit() won't work here): + size_t Last = 0; + for (size_t i = 0; i < Reason.size(); i++) + { + if (Reason[i] == 0) + { + Split.push_back(Reason.substr(Last, i - Last)); + Last = i + 1; + } + } + + if (Split.size() == 5) + { + Log(" Protocol version: \"%s\"", Split[0].c_str()); + Log(" Server version: \"%s\"", Split[1].c_str()); + Log(" MOTD: \"%s\"", Split[2].c_str()); + Log(" Cur players: \"%s\"", Split[3].c_str()); + Log(" Max players: \"%s\"", Split[4].c_str()); + } + else + { + DataLog(Reason.data(), Reason.size(), " Unknown reply format, dumping hex:"); + } + } + else + { + Log(" Reason = \"%s\"", Reason.c_str()); + } + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerLogin(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, LevelType); + HANDLE_SERVER_PACKET_READ(ReadChar, char, GameMode); + HANDLE_SERVER_PACKET_READ(ReadChar, char, Dimension); + HANDLE_SERVER_PACKET_READ(ReadChar, char, Difficulty); + HANDLE_SERVER_PACKET_READ(ReadChar, char, Unused); + HANDLE_SERVER_PACKET_READ(ReadChar, char, MaxPlayers); + Log("Received a PACKET_LOGIN from the server:"); + Log(" EntityID = %d", EntityID); + Log(" LevelType = \"%s\"", LevelType.c_str()); + Log(" GameMode = %d", GameMode); + Log(" Dimension = %d", Dimension); + Log(" Difficulty = %d", Difficulty); + Log(" Unused = %d", Unused); + Log(" MaxPlayers = %d", MaxPlayers); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerMapChunk(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ); + HANDLE_SERVER_PACKET_READ(ReadChar, char, IsContiguous); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, PrimaryBitmap); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, AdditionalBitmap); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CompressedSize); + AString CompressedData; + if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize)) + { + return false; + } + Log("Received a PACKET_MAP_CHUNK from the server:"); + Log(" ChunkPos = [%d, %d]", ChunkX, ChunkZ); + Log(" Compressed size = %d (0x%x)", CompressedSize, CompressedSize); + + // TODO: Save the compressed data into a file for later analysis + + COPY_TO_CLIENT() + return true; +} + + + + + +bool cConnection::HandleServerMapChunkBulk(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, ChunkCount); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, CompressedSize); + HANDLE_SERVER_PACKET_READ(ReadBool, bool, IsSkyLightSent); + AString CompressedData; + if (!m_ServerBuffer.ReadString(CompressedData, CompressedSize)) + { + return false; + } + AString Meta; + if (!m_ServerBuffer.ReadString(Meta, ChunkCount * 12)) + { + return false; + } + Log("Received a PACKET_MAP_CHUNK_BULK from the server:"); + Log(" ChunkCount = %d", ChunkCount); + Log(" Compressed size = %d (0x%x)", CompressedSize, CompressedSize); + Log(" IsSkyLightSent = %s", IsSkyLightSent ? "true" : "false"); + + // TODO: Save the compressed data into a file for later analysis + + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerMultiBlockChange(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, ChunkZ); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NumBlocks); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, DataSize); + AString BlockChangeData; + if (!m_ServerBuffer.ReadString(BlockChangeData, DataSize)) + { + return false; + } + Log("Received a PACKET_MULTI_BLOCK_CHANGE packet from the server:"); + Log(" Chunk = [%d, %d]", ChunkX, ChunkZ); + Log(" NumBlocks = %d", NumBlocks); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerNamedSoundEffect(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, SoundName); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Volume); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + Log("Received a PACKET_NAMED_SOUND_EFFECT from the server:"); + Log(" SoundName = \"%s\"", SoundName.c_str()); + Log(" Pos = (%d, %d, %d) ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 8, PosY / 8, PosZ / 8); + Log(" Volume = %f", Volume); + Log(" Pitch = %d", Pitch); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerPlayerAbilities(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, Flags); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, FlyingSpeed); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, WalkingSpeed); + Log("Received a PACKET_PLAYER_ABILITIES from the server:"); + Log(" Flags = %d (0x%02x)", Flags, Flags); + Log(" FlyingSpeed = %f", FlyingSpeed); + Log(" WalkingSpeed = %f", WalkingSpeed); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerPlayerAnimation(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PlayerID); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, AnimationID); + Log("Received a PACKET_PLAYER_ANIMATION from the server:"); + Log(" PlayerID: %d (0x%x)", PlayerID, PlayerID); + Log(" Animation: %d", AnimationID); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerPlayerListItem(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, PlayerName); + HANDLE_SERVER_PACKET_READ(ReadChar, char, IsOnline); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Ping); + Log("Received a PACKET_PLAYERLIST_ITEM from the server:"); + Log(" PlayerName = \"%s\"", PlayerName.c_str()); + Log(" Ping = %d", Ping); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerPlayerPositionLook(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, Stance); + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEDouble, double, PosZ); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Yaw); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Pitch); + HANDLE_SERVER_PACKET_READ(ReadChar, char, IsOnGround); + Log("Received a PACKET_PLAYER_POSITION_LOOK from the server"); + + // TODO: list packet contents + + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerPluginMessage(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ChannelName); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Length); + AString Data; + if (!m_ServerBuffer.ReadString(Data, Length)) + { + return false; + } + Log("Received a PACKET_PLUGIN_MESSAGE from the server"); + Log(" ChannelName = \"%s\"", ChannelName.c_str()); + DataLog(Data.data(), Length, " Data: %d bytes", Length); + COPY_TO_SERVER(); + return true; +} + + + + + +bool cConnection::HandleServerSetExperience(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, ExperienceBar); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Level); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, TotalExperience); + Log("Received a PACKET_SET_EXPERIENCE from the server:"); + Log(" ExperienceBar = %.05f", ExperienceBar); + Log(" Level = %d", Level); + Log(" TotalExperience = %d", TotalExperience); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSetSlot(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SlotNum); + AString Item; + if (!ParseSlot(m_ServerBuffer, Item)) + { + return false; + } + Log("Received a PACKET_SET_SLOT from the server:"); + Log(" WindowID = %d", WindowID); + Log(" SlotNum = %d", SlotNum); + Log(" Item = %s", Item.c_str()); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSlotSelect(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SlotNum); + Log("Received a PACKET_SLOT_SELECT from the server:"); + Log(" SlotNum = %d", SlotNum); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSoundEffect(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EffectID); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Data); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, NoVolumeDecrease); + Log("Received a PACKET_SOUND_EFFECT from the server:"); + Log(" EffectID = %d", EffectID); + Log(" Pos = {%d, %d, %d}", PosX, PosY, PosZ); + Log(" Data = %d", Data); + Log(" NoVolumeDecrease = %d", NoVolumeDecrease); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSpawnMob(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadChar, char, MobType); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, HeadYaw); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityY); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, VelocityZ); + AString Metadata; + if (!ParseMetadata(m_ServerBuffer, Metadata)) + { + return false; + } + AString HexDump; + CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32); + Log("Received a PACKET_SPAWN_MOB from the server:"); + Log(" EntityID = %d", EntityID); + Log(" MobType = %d", MobType); + Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); + Log(" Angles = [%d, %d, %d]", Yaw, Pitch, HeadYaw); + Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ); + Log(" Metadata, length = %d (0x%x):\n%s", Metadata.length(), Metadata.length(), HexDump.c_str()); + LogMetadata(Metadata, 4); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSpawnNamedEntity(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, EntityName); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, CurrentItem); + AString Metadata; + if (!ParseMetadata(m_ServerBuffer, Metadata)) + { + return false; + } + AString HexDump; + CreateHexDump(HexDump, Metadata.data(), Metadata.size(), 32); + Log("Received a PACKET_SPAWN_NAMED_ENTITY from the server:"); + Log(" EntityID = %d (0x%x)", EntityID, EntityID); + Log(" Name = %s", EntityName.c_str()); + Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); + Log(" Rotation = ", Yaw, Pitch); + Log(" CurrentItem = %d", CurrentItem); + Log(" Metadata, length = %d (0x%x):\n%s", Metadata.length(), Metadata.length(), HexDump.c_str()); + LogMetadata(Metadata, 4); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSpawnObjectVehicle(void) +{ + #ifdef _DEBUG + // DEBUG: + // This packet is still troublesome when DataIndicator != 0 + AString Buffer; + m_ServerBuffer.ResetRead(); + m_ServerBuffer.ReadAll(Buffer); + m_ServerBuffer.ResetRead(); + m_ServerBuffer.SkipRead(1); + if (Buffer.size() > 128) + { + // Only log up to 128 bytes + Buffer.erase(128, AString::npos); + } + DataLog(Buffer.data(), Buffer.size(), "Buffer while parsing the PACKET_SPAWN_OBJECT_VEHICLE packet (%d bytes):", Buffer.size()); + #endif // _DEBUG + + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadChar, char, ObjType); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Yaw); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, DataIndicator); + AString ExtraData; + short VelocityX, VelocityY, VelocityZ; + if (DataIndicator != 0) + { + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SpeedX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SpeedY); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, SpeedZ); + VelocityX = SpeedX; VelocityY = SpeedY; VelocityZ = SpeedZ; // Speed vars are local to this scope, but we need them available later + /* + // This doesn't seem to work - for a falling block I'm getting no extra data at all + int ExtraLen = 0; + switch (ObjType) + { + case OBJECT_FALLING_BLOCK: ExtraLen = 4; break; // int: BlockType | (BlockMeta << 12) + case OBJECT_ARROW: + case OBJECT_SNOWBALL: + case OBJECT_EGG: + case OBJECT_EYE_OF_ENDER: + case OBJECT_DRAGON_EGG: + case OBJECT_FISHING_FLOAT: + { + ExtraLen = 4; break; // int: EntityID of the thrower + } + // TODO: Splash potions + } + if ((ExtraLen > 0) && !m_ServerBuffer.ReadString(ExtraData, ExtraLen)) + { + return false; + } + */ + } + Log("Received a PACKET_SPAWN_OBJECT_VEHICLE from the server:"); + Log(" EntityID = %d (0x%x)", EntityID, EntityID); + Log(" ObjType = %d (0x%x)", ObjType, ObjType); + Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); + Log(" Rotation = ", Yaw, Pitch); + Log(" DataIndicator = %d (0x%x)", DataIndicator, DataIndicator); + if (DataIndicator != 0) + { + Log(" Velocity = <%d, %d, %d>", VelocityX, VelocityY, VelocityZ); + DataLog(ExtraData.data(), ExtraData.size(), " ExtraData size = %d:", ExtraData.size()); + } + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSpawnPainting(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, ImageName); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, Direction); + Log("Received a PACKET_SPAWN_PAINTING from the server:"); + Log(" EntityID = %d", EntityID); + Log(" ImageName = \"%s\"", ImageName.c_str()); + Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); + Log(" Direction = %d", Direction); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerSpawnPickup(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, EntityID); + AString ItemDesc; + if (!ParseSlot(m_ServerBuffer, ItemDesc)) + { + return false; + } + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosX); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, PosZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Rotation); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Pitch); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Roll); + Log("Received a PACKET_SPAWN_PICKUP from the server:"); + Log(" EntityID = %d", EntityID); + Log(" Item = %s", ItemDesc.c_str()); + Log(" Pos = <%d, %d, %d> ~ {%d, %d, %d}", PosX, PosY, PosZ, PosX / 32, PosY / 32, PosZ / 32); + Log(" Angles = [%d, %d, %d]", Rotation, Pitch, Roll); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerTimeUpdate(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, WorldAge); + HANDLE_SERVER_PACKET_READ(ReadBEInt64, Int64, TimeOfDay); + Log("Received a PACKET_TIME_UPDATE from the server"); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerUpdateHealth(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Health); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, Food); + HANDLE_SERVER_PACKET_READ(ReadBEFloat, float, Saturation); + Log("Received a PACKET_UPDATE_HEALTH from the server"); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerUpdateSign(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line1); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line2); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line3); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Line4); + Log("Received a PACKET_UPDATE_SIGN from the server:"); + Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Lines = \"%s\", \"%s\", \"%s\", \"%s\"", Line1.c_str(), Line2.c_str(), Line3.c_str(), Line4.c_str()); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerUpdateTileEntity(void) +{ + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockX); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, BlockY); + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, BlockZ); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, Action); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, DataLength); + AString Data; + if ((DataLength > 0) && !m_ServerBuffer.ReadString(Data, DataLength)) + { + return false; + } + Log("Received a PACKET_UPDATE_TILE_ENTITY from the server:"); + Log(" Block = {%d, %d, %d}", BlockX, BlockY, BlockZ); + Log(" Action = %d", Action); + DataLog(Data.data(), Data.size(), " Data (%d bytes)", Data.size()); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerWindowClose(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); + Log("Received a PACKET_WINDOW_CLOSE from the server:"); + Log(" WindowID = %d", WindowID); + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerWindowContents(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); + HANDLE_SERVER_PACKET_READ(ReadBEShort, short, NumSlots); + Log("Received a PACKET_WINDOW_CONTENTS from the server:"); + Log(" WindowID = %d", WindowID); + Log(" NumSlots = %d", NumSlots); + AStringVector Items; + for (short i = 0; i < NumSlots; i++) + { + AString Item; + if (!ParseSlot(m_ServerBuffer, Item)) + { + return false; + } + Log(" %d: %s", i, Item.c_str()); + } + + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::HandleServerWindowOpen(void) +{ + HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowID); + HANDLE_SERVER_PACKET_READ(ReadChar, char, WindowType); + HANDLE_SERVER_PACKET_READ(ReadBEUTF16String16, AString, Title); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, NumSlots); + HANDLE_SERVER_PACKET_READ(ReadByte, Byte, UseProvidedTitle); + int HorseInt = 0; + if (WindowType == 11) // Horse / Donkey / Mule + { + HANDLE_SERVER_PACKET_READ(ReadBEInt, int, intHorseInt); + HorseInt = intHorseInt; + } + Log("Received a PACKET_WINDOW_OPEN from the server:"); + Log(" WindowID = %d", WindowID); + Log(" WindowType = %d", WindowType); + Log(" Title = \"%s\", Use = %d", Title.c_str(), UseProvidedTitle); + Log(" NumSlots = %d", NumSlots); + if (WindowType == 11) + { + Log(" HorseInt = %d (0x%08x)", HorseInt, HorseInt); + } + COPY_TO_CLIENT(); + return true; +} + + + + + +bool cConnection::ParseSlot(cByteBuffer & a_Buffer, AString & a_ItemDesc) +{ + short ItemType; + if (!a_Buffer.ReadBEShort(ItemType)) + { + return false; + } + if (ItemType <= 0) + { + a_ItemDesc = ""; + return true; + } + if (!a_Buffer.CanReadBytes(5)) + { + return false; + } + char ItemCount; + short ItemDamage; + short MetadataLength; + a_Buffer.ReadChar(ItemCount); + a_Buffer.ReadBEShort(ItemDamage); + a_Buffer.ReadBEShort(MetadataLength); + Printf(a_ItemDesc, "%d:%d * %d", ItemType, ItemDamage, ItemCount); + if (MetadataLength <= 0) + { + return true; + } + AString Metadata; + Metadata.resize(MetadataLength); + if (!a_Buffer.ReadBuf((void *)Metadata.data(), MetadataLength)) + { + return false; + } + AString MetaHex; + CreateHexDump(MetaHex, Metadata.data(), Metadata.size(), 16); + AppendPrintf(a_ItemDesc, "; %d bytes of meta:\n%s", MetadataLength, MetaHex.c_str()); + + // Save metadata to a file: + AString fnam; + Printf(fnam, "%s_item_%08x.nbt", m_LogNameBase.c_str(), m_ItemIdx++); + FILE * f = fopen(fnam.c_str(), "wb"); + if (f != NULL) + { + fwrite(Metadata.data(), 1, Metadata.size(), f); + fclose(f); + AppendPrintf(a_ItemDesc, "\n (saved to file \"%s\")", fnam.c_str()); + } + + return true; +} + + + + + +bool cConnection::ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata) +{ + char x; + if (!a_Buffer.ReadChar(x)) + { + return false; + } + a_Metadata.push_back(x); + while (x != 0x7f) + { + int Index = ((unsigned)((unsigned char)x)) & 0x1f; // Lower 5 bits = index + int Type = ((unsigned)((unsigned char)x)) >> 5; // Upper 3 bits = type + int Length = 0; + switch (Type) + { + case 0: Length = 1; break; // byte + case 1: Length = 2; break; // short + case 2: Length = 4; break; // int + case 3: Length = 4; break; // float + case 4: // string16 + { + short Len = 0; + if (!a_Buffer.ReadBEShort(Len)) + { + return false; + } + short NetLen = htons(Len); + a_Metadata.append((char *)&NetLen, 2); + Length = Len; + break; + } + case 5: + { + int Before = a_Buffer.GetReadableSpace(); + AString ItemDesc; + if (!ParseSlot(a_Buffer, ItemDesc)) + { + return false; + } + int After = a_Buffer.GetReadableSpace(); + a_Buffer.ResetRead(); + a_Buffer.SkipRead(a_Buffer.GetReadableSpace() - Before); + Length = Before - After; + break; + } + case 6: Length = 12; break; // 3 * int + default: + { + ASSERT(!"Unknown metadata type"); + break; + } + } // switch (Type) + AString data; + if (!a_Buffer.ReadString(data, Length)) + { + return false; + } + a_Metadata.append(data); + if (!a_Buffer.ReadChar(x)) + { + return false; + } + a_Metadata.push_back(x); + } // while (x != 0x7f) + return true; +} + + + + + +void cConnection::LogMetadata(const AString & a_Metadata, size_t a_IndentCount) +{ + AString Indent(a_IndentCount, ' '); + int pos = 0; + while (a_Metadata[pos] != 0x7f) + { + int Index = ((unsigned)((unsigned char)a_Metadata[pos])) & 0x1f; // Lower 5 bits = index + int Type = ((unsigned)((unsigned char)a_Metadata[pos])) >> 5; // Upper 3 bits = type + int Length = 0; + switch (Type) + { + case 0: + { + Log("%sbyte[%d] = %d", Indent.c_str(), Index, a_Metadata[pos + 1]); + pos += 1; + break; + } + case 1: + { + Log("%sshort[%d] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 8) | a_Metadata[pos + 2]); + pos += 2; + break; + } + case 2: + { + Log("%sint[%d] = %d", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]); + pos += 4; + break; + } + case 3: + { + Log("%sfloat[%d] = 0x%x", Indent.c_str(), Index, (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4]); + pos += 4; + break; + } + case 4: // string16 + { + short Length = (a_Metadata[pos + 1] << 8) | a_Metadata[pos + 2]; + Log("%sstring[%d] = \"%*s\"", Indent.c_str(), Index, Length, a_Metadata.c_str() + pos + 3); + pos += Length + 2; + break; + } + case 5: + { + int BytesLeft = a_Metadata.size() - pos - 1; + cByteBuffer bb(BytesLeft); + bb.Write(a_Metadata.data() + pos + 1, BytesLeft); + AString ItemDesc; + if (!ParseSlot(bb, ItemDesc)) + { + ASSERT(!"Cannot parse item description from metadata"); + return; + } + int After = bb.GetReadableSpace(); + int BytesConsumed = BytesLeft - bb.GetReadableSpace(); + + Log("%sslot[%d] = %s (%d bytes)", Indent.c_str(), Index, ItemDesc.c_str(), BytesConsumed); + pos += BytesConsumed; + break; + } + case 6: + { + Log("%spos[%d] = <%d, %d, %d>", Indent.c_str(), Index, + (a_Metadata[pos + 1] << 24) | (a_Metadata[pos + 2] << 16) | (a_Metadata[pos + 3] << 8) | a_Metadata[pos + 4], + (a_Metadata[pos + 5] << 24) | (a_Metadata[pos + 6] << 16) | (a_Metadata[pos + 7] << 8) | a_Metadata[pos + 8], + (a_Metadata[pos + 9] << 24) | (a_Metadata[pos + 10] << 16) | (a_Metadata[pos + 11] << 8) | a_Metadata[pos + 12] + ); + pos += 12; + break; + } + default: + { + ASSERT(!"Unknown metadata type"); + break; + } + } // switch (Type) + pos += 1; + } // while (x != 0x7f) +} + + + + + +void cConnection::SendEncryptionKeyResponse(const AString & a_ServerPublicKey, const AString & a_Nonce) +{ + // Generate the shared secret and encrypt using the server's public key + byte SharedSecret[16]; + byte EncryptedSecret[128]; + memset(SharedSecret, 0, sizeof(SharedSecret)); // Use all zeroes for the initial secret + RSA::PublicKey pk; + CryptoPP::StringSource src(a_ServerPublicKey, true); + ByteQueue bq; + src.TransferTo(bq); + bq.MessageEnd(); + pk.Load(bq); + RSAES::Encryptor rsaEncryptor(pk); + RandomPool rng; + time_t CurTime = time(NULL); + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + int EncryptedLength = rsaEncryptor.FixedCiphertextLength(); + ASSERT(EncryptedLength <= sizeof(EncryptedSecret)); + rsaEncryptor.Encrypt(rng, SharedSecret, sizeof(SharedSecret), EncryptedSecret); + m_ServerEncryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); + m_ServerDecryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); + + // Encrypt the nonce: + byte EncryptedNonce[128]; + rsaEncryptor.Encrypt(rng, (const byte *)(a_Nonce.data()), a_Nonce.size(), EncryptedNonce); + + // Send the packet to the server: + Log("Sending PACKET_ENCRYPTION_KEY_RESPONSE to the SERVER"); + cByteBuffer ToServer(1024); + ToServer.WriteByte(PACKET_ENCRYPTION_KEY_RESPONSE); + ToServer.WriteBEShort(EncryptedLength); + ToServer.WriteBuf(EncryptedSecret, EncryptedLength); + ToServer.WriteBEShort(EncryptedLength); + ToServer.WriteBuf(EncryptedNonce, EncryptedLength); + SERVERSEND(ToServer); + m_ServerState = csWaitingForEncryption; +} + + + + + +void cConnection::StartClientEncryption(const AString & a_EncKey, const AString & a_EncNonce) +{ + // Decrypt EncNonce using privkey + RSAES::Decryptor rsaDecryptor(m_Server.GetPrivateKey()); + time_t CurTime = time(NULL); + RandomPool rng; + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + byte DecryptedNonce[MAX_ENC_LEN]; + DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce); + if (!res.isValidCoding || (res.messageLength != 4)) + { + Log("Client: Bad nonce length"); + return; + } + if (ntohl(*((int *)DecryptedNonce)) != m_Nonce) + { + Log("Bad nonce value"); + return; + } + + // Decrypt the symmetric encryption key using privkey: + byte SharedSecret[MAX_ENC_LEN]; + res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), SharedSecret); + if (!res.isValidCoding || (res.messageLength != 16)) + { + Log("Bad key length"); + return; + } + + // Send encryption key response: + cByteBuffer ToClient(6); + ToClient.WriteByte((char)0xfc); + ToClient.WriteBEShort(0); + ToClient.WriteBEShort(0); + CLIENTSEND(ToClient); + + // Start the encryption: + m_ClientEncryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); + m_ClientDecryptor.SetKey(SharedSecret, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(SharedSecret, 16))(Name::FeedbackSize(), 1)); + Log("Client connection is now encrypted"); + m_ClientState = csEncryptedUnderstood; + + // Send the queued data: + DataLog(m_ClientEncryptionBuffer.data(), m_ClientEncryptionBuffer.size(), "Sending the queued data to client (%u bytes):", m_ClientEncryptionBuffer.size()); + CLIENTENCRYPTSEND(m_ClientEncryptionBuffer.data(), m_ClientEncryptionBuffer.size()); + m_ClientEncryptionBuffer.clear(); + + // Handle all postponed server data + DecodeServersPackets(NULL, 0); +} + + + + diff --git a/Tools/ProtoProxy/Connection.h b/Tools/ProtoProxy/Connection.h index dafc1b36b..942ee6e06 100644 --- a/Tools/ProtoProxy/Connection.h +++ b/Tools/ProtoProxy/Connection.h @@ -1,206 +1,206 @@ - -// Connection.h - -// Interfaces to the cConnection class representing a single pair of connected sockets - - - - - -#pragma once - -#include -#include "ByteBuffer.h" - - - - - -class cServer; - - - - - -class cConnection -{ - AString m_LogNameBase; ///< Base for the log filename and all files connected to this log - - int m_ItemIdx; ///< Index for the next file into which item metadata should be written (ParseSlot() function) - - cCriticalSection m_CSLog; - FILE * m_LogFile; - - cServer & m_Server; - SOCKET m_ClientSocket; - SOCKET m_ServerSocket; - - clock_t m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime()) - - enum eConnectionState - { - csUnencrypted, // The connection is not encrypted. Packets must be decoded in order to be able to start decryption. - csEncryptedUnderstood, // The communication is encrypted and so far all packets have been understood, so they can be still decoded - csEncryptedUnknown, // The communication is encrypted, but an unknown packet has been received, so packets cannot be decoded anymore - csWaitingForEncryption, // The communication is waiting for the other line to establish encryption - }; - - eConnectionState m_ClientState; - eConnectionState m_ServerState; - - int m_Nonce; - -public: - cConnection(SOCKET a_ClientSocket, cServer & a_Server); - ~cConnection(); - - void Run(void); - - void Log(const char * a_Format, ...); - void DataLog(const void * a_Data, int a_Size, const char * a_Format, ...); - void LogFlush(void); - -protected: - typedef CFB_Mode::Encryption Encryptor; - typedef CFB_Mode::Decryption Decryptor; - - cByteBuffer m_ClientBuffer; - cByteBuffer m_ServerBuffer; - - Decryptor m_ServerDecryptor; - Encryptor m_ServerEncryptor; - - Decryptor m_ClientDecryptor; - Encryptor m_ClientEncryptor; - - AString m_ClientEncryptionBuffer; // Buffer for the data to be sent to the client once encryption is established - AString m_ServerEncryptionBuffer; // Buffer for the data to be sent to the server once encryption is established - - /// Set to true when PACKET_PING is received from the client; will cause special parsing for server kick - bool m_HasClientPinged; - - bool ConnectToServer(void); - - /// Relays data from server to client; returns false if connection aborted - bool RelayFromServer(void); - - /// Relays data from client to server; returns false if connection aborted - bool RelayFromClient(void); - - /// Returns the time relative to the first call of this function, in the fractional seconds elapsed - double GetRelativeTime(void); - - /// Sends data to the specified socket. If sending fails, prints a fail message using a_Peer and returns false. - bool SendData(SOCKET a_Socket, const char * a_Data, int a_Size, const char * a_Peer); - - /// Sends data to the specified socket. If sending fails, prints a fail message using a_Peer and returns false. - bool SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer); - - /// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false - bool SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, const char * a_Data, int a_Size, const char * a_Peer); - - /// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false - bool SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer); - - /// Decodes packets coming from the client, sends appropriate counterparts to the server; returns false if the connection is to be dropped - bool DecodeClientsPackets(const char * a_Data, int a_Size); - - /// Decodes packets coming from the server, sends appropriate counterparts to the client; returns false if the connection is to be dropped - bool DecodeServersPackets(const char * a_Data, int a_Size); - - // Packet handling, client-side: - bool HandleClientAnimation(void); - bool HandleClientBlockDig(void); - bool HandleClientBlockPlace(void); - bool HandleClientChatMessage(void); - bool HandleClientClientStatuses(void); - bool HandleClientCreativeInventoryAction(void); - bool HandleClientEncryptionKeyResponse(void); - bool HandleClientEntityAction(void); - bool HandleClientHandshake(void); - bool HandleClientKeepAlive(void); - bool HandleClientLocaleAndView(void); - bool HandleClientPing(void); - bool HandleClientPlayerAbilities(void); - bool HandleClientPlayerLook(void); - bool HandleClientPlayerOnGround(void); - bool HandleClientPlayerPosition(void); - bool HandleClientPlayerPositionLook(void); - bool HandleClientPluginMessage(void); - bool HandleClientSlotSelect(void); - bool HandleClientUpdateSign(void); - bool HandleClientUseEntity(void); - bool HandleClientWindowClick(void); - bool HandleClientWindowClose(void); - - // Packet handling, server-side: - bool HandleServerAttachEntity(void); - bool HandleServerBlockAction(void); - bool HandleServerBlockChange(void); - bool HandleServerChangeGameState(void); - bool HandleServerChatMessage(void); - bool HandleServerCollectPickup(void); - bool HandleServerCompass(void); - bool HandleServerDestroyEntities(void); - bool HandleServerEncryptionKeyRequest(void); - bool HandleServerEncryptionKeyResponse(void); - bool HandleServerEntity(void); - bool HandleServerEntityEquipment(void); - bool HandleServerEntityHeadLook(void); - bool HandleServerEntityLook(void); - bool HandleServerEntityMetadata(void); - bool HandleServerEntityProperties(void); - bool HandleServerEntityRelativeMove(void); - bool HandleServerEntityRelativeMoveLook(void); - bool HandleServerEntityStatus(void); - bool HandleServerEntityTeleport(void); - bool HandleServerEntityVelocity(void); - bool HandleServerIncrementStatistic(void); - bool HandleServerKeepAlive(void); - bool HandleServerKick(void); - bool HandleServerLogin(void); - bool HandleServerMapChunk(void); - bool HandleServerMapChunkBulk(void); - bool HandleServerMultiBlockChange(void); - bool HandleServerNamedSoundEffect(void); - bool HandleServerPlayerAbilities(void); - bool HandleServerPlayerAnimation(void); - bool HandleServerPlayerListItem(void); - bool HandleServerPlayerPositionLook(void); - bool HandleServerPluginMessage(void); - bool HandleServerSetExperience(void); - bool HandleServerSetSlot(void); - bool HandleServerSlotSelect(void); - bool HandleServerSoundEffect(void); - bool HandleServerSpawnMob(void); - bool HandleServerSpawnNamedEntity(void); - bool HandleServerSpawnObjectVehicle(void); - bool HandleServerSpawnPainting(void); - bool HandleServerSpawnPickup(void); - bool HandleServerTimeUpdate(void); - bool HandleServerUpdateHealth(void); - bool HandleServerUpdateSign(void); - bool HandleServerUpdateTileEntity(void); - bool HandleServerWindowClose(void); - bool HandleServerWindowContents(void); - bool HandleServerWindowOpen(void); - - /// Parses the slot data in a_Buffer into item description; returns true if successful, false if not enough data - bool ParseSlot(cByteBuffer & a_Buffer, AString & a_ItemDesc); - - /// Parses the metadata in a_Buffer into raw metadata in an AString; returns true if successful, false if not enough data - bool ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata); - - /// Logs the contents of the metadata in the AString, using Log(). Assumes a_Metadata is valid (parsed by ParseMetadata()). The log is indented by a_IndentCount spaces - void LogMetadata(const AString & a_Metadata, size_t a_IndentCount); - - /// Send EKResp to the server: - void SendEncryptionKeyResponse(const AString & a_ServerPublicKey, const AString & a_Nonce); - - /// Starts client encryption based on the parameters received - void StartClientEncryption(const AString & a_EncryptedSecret, const AString & a_EncryptedNonce); -} ; - - - - + +// Connection.h + +// Interfaces to the cConnection class representing a single pair of connected sockets + + + + + +#pragma once + +#include +#include "ByteBuffer.h" + + + + + +class cServer; + + + + + +class cConnection +{ + AString m_LogNameBase; ///< Base for the log filename and all files connected to this log + + int m_ItemIdx; ///< Index for the next file into which item metadata should be written (ParseSlot() function) + + cCriticalSection m_CSLog; + FILE * m_LogFile; + + cServer & m_Server; + SOCKET m_ClientSocket; + SOCKET m_ServerSocket; + + clock_t m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime()) + + enum eConnectionState + { + csUnencrypted, // The connection is not encrypted. Packets must be decoded in order to be able to start decryption. + csEncryptedUnderstood, // The communication is encrypted and so far all packets have been understood, so they can be still decoded + csEncryptedUnknown, // The communication is encrypted, but an unknown packet has been received, so packets cannot be decoded anymore + csWaitingForEncryption, // The communication is waiting for the other line to establish encryption + }; + + eConnectionState m_ClientState; + eConnectionState m_ServerState; + + int m_Nonce; + +public: + cConnection(SOCKET a_ClientSocket, cServer & a_Server); + ~cConnection(); + + void Run(void); + + void Log(const char * a_Format, ...); + void DataLog(const void * a_Data, int a_Size, const char * a_Format, ...); + void LogFlush(void); + +protected: + typedef CFB_Mode::Encryption Encryptor; + typedef CFB_Mode::Decryption Decryptor; + + cByteBuffer m_ClientBuffer; + cByteBuffer m_ServerBuffer; + + Decryptor m_ServerDecryptor; + Encryptor m_ServerEncryptor; + + Decryptor m_ClientDecryptor; + Encryptor m_ClientEncryptor; + + AString m_ClientEncryptionBuffer; // Buffer for the data to be sent to the client once encryption is established + AString m_ServerEncryptionBuffer; // Buffer for the data to be sent to the server once encryption is established + + /// Set to true when PACKET_PING is received from the client; will cause special parsing for server kick + bool m_HasClientPinged; + + bool ConnectToServer(void); + + /// Relays data from server to client; returns false if connection aborted + bool RelayFromServer(void); + + /// Relays data from client to server; returns false if connection aborted + bool RelayFromClient(void); + + /// Returns the time relative to the first call of this function, in the fractional seconds elapsed + double GetRelativeTime(void); + + /// Sends data to the specified socket. If sending fails, prints a fail message using a_Peer and returns false. + bool SendData(SOCKET a_Socket, const char * a_Data, int a_Size, const char * a_Peer); + + /// Sends data to the specified socket. If sending fails, prints a fail message using a_Peer and returns false. + bool SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer); + + /// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false + bool SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, const char * a_Data, int a_Size, const char * a_Peer); + + /// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false + bool SendEncryptedData(SOCKET a_Socket, Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer); + + /// Decodes packets coming from the client, sends appropriate counterparts to the server; returns false if the connection is to be dropped + bool DecodeClientsPackets(const char * a_Data, int a_Size); + + /// Decodes packets coming from the server, sends appropriate counterparts to the client; returns false if the connection is to be dropped + bool DecodeServersPackets(const char * a_Data, int a_Size); + + // Packet handling, client-side: + bool HandleClientAnimation(void); + bool HandleClientBlockDig(void); + bool HandleClientBlockPlace(void); + bool HandleClientChatMessage(void); + bool HandleClientClientStatuses(void); + bool HandleClientCreativeInventoryAction(void); + bool HandleClientEncryptionKeyResponse(void); + bool HandleClientEntityAction(void); + bool HandleClientHandshake(void); + bool HandleClientKeepAlive(void); + bool HandleClientLocaleAndView(void); + bool HandleClientPing(void); + bool HandleClientPlayerAbilities(void); + bool HandleClientPlayerLook(void); + bool HandleClientPlayerOnGround(void); + bool HandleClientPlayerPosition(void); + bool HandleClientPlayerPositionLook(void); + bool HandleClientPluginMessage(void); + bool HandleClientSlotSelect(void); + bool HandleClientUpdateSign(void); + bool HandleClientUseEntity(void); + bool HandleClientWindowClick(void); + bool HandleClientWindowClose(void); + + // Packet handling, server-side: + bool HandleServerAttachEntity(void); + bool HandleServerBlockAction(void); + bool HandleServerBlockChange(void); + bool HandleServerChangeGameState(void); + bool HandleServerChatMessage(void); + bool HandleServerCollectPickup(void); + bool HandleServerCompass(void); + bool HandleServerDestroyEntities(void); + bool HandleServerEncryptionKeyRequest(void); + bool HandleServerEncryptionKeyResponse(void); + bool HandleServerEntity(void); + bool HandleServerEntityEquipment(void); + bool HandleServerEntityHeadLook(void); + bool HandleServerEntityLook(void); + bool HandleServerEntityMetadata(void); + bool HandleServerEntityProperties(void); + bool HandleServerEntityRelativeMove(void); + bool HandleServerEntityRelativeMoveLook(void); + bool HandleServerEntityStatus(void); + bool HandleServerEntityTeleport(void); + bool HandleServerEntityVelocity(void); + bool HandleServerIncrementStatistic(void); + bool HandleServerKeepAlive(void); + bool HandleServerKick(void); + bool HandleServerLogin(void); + bool HandleServerMapChunk(void); + bool HandleServerMapChunkBulk(void); + bool HandleServerMultiBlockChange(void); + bool HandleServerNamedSoundEffect(void); + bool HandleServerPlayerAbilities(void); + bool HandleServerPlayerAnimation(void); + bool HandleServerPlayerListItem(void); + bool HandleServerPlayerPositionLook(void); + bool HandleServerPluginMessage(void); + bool HandleServerSetExperience(void); + bool HandleServerSetSlot(void); + bool HandleServerSlotSelect(void); + bool HandleServerSoundEffect(void); + bool HandleServerSpawnMob(void); + bool HandleServerSpawnNamedEntity(void); + bool HandleServerSpawnObjectVehicle(void); + bool HandleServerSpawnPainting(void); + bool HandleServerSpawnPickup(void); + bool HandleServerTimeUpdate(void); + bool HandleServerUpdateHealth(void); + bool HandleServerUpdateSign(void); + bool HandleServerUpdateTileEntity(void); + bool HandleServerWindowClose(void); + bool HandleServerWindowContents(void); + bool HandleServerWindowOpen(void); + + /// Parses the slot data in a_Buffer into item description; returns true if successful, false if not enough data + bool ParseSlot(cByteBuffer & a_Buffer, AString & a_ItemDesc); + + /// Parses the metadata in a_Buffer into raw metadata in an AString; returns true if successful, false if not enough data + bool ParseMetadata(cByteBuffer & a_Buffer, AString & a_Metadata); + + /// Logs the contents of the metadata in the AString, using Log(). Assumes a_Metadata is valid (parsed by ParseMetadata()). The log is indented by a_IndentCount spaces + void LogMetadata(const AString & a_Metadata, size_t a_IndentCount); + + /// Send EKResp to the server: + void SendEncryptionKeyResponse(const AString & a_ServerPublicKey, const AString & a_Nonce); + + /// Starts client encryption based on the parameters received + void StartClientEncryption(const AString & a_EncryptedSecret, const AString & a_EncryptedNonce); +} ; + + + + diff --git a/Tools/ProtoProxy/Globals.cpp b/Tools/ProtoProxy/Globals.cpp index 2c60fd698..13c6ae709 100644 --- a/Tools/ProtoProxy/Globals.cpp +++ b/Tools/ProtoProxy/Globals.cpp @@ -1,10 +1,10 @@ - -// Globals.cpp - -// This file is used for precompiled header generation in MSVC environments - -#include "Globals.h" - - - - + +// Globals.cpp + +// This file is used for precompiled header generation in MSVC environments + +#include "Globals.h" + + + + diff --git a/Tools/ProtoProxy/Globals.h b/Tools/ProtoProxy/Globals.h index 404044a5f..f2c47e96f 100644 --- a/Tools/ProtoProxy/Globals.h +++ b/Tools/ProtoProxy/Globals.h @@ -1,221 +1,221 @@ - -// 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 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; - - - - - -// 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 - #include - - // 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 - #include // for mkdir - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - #include - #include - #include - #include -#if !defined(ANDROID_NDK) - #include -#endif -#endif - -#if !defined(ANDROID_NDK) - #define USE_SQUIRREL -#endif - -#if defined(ANDROID_NDK) - #define FILE_IO_PREFIX "/sdcard/mcserver/" -#else - #define FILE_IO_PREFIX "" -#endif - - - - - -// CRT stuff: -#include -#include -#include -#include - - - - - -// STL stuff: -#include -#include -#include -#include -#include -#include -#include - - - - - -// Common headers (part 1, without macros): -#include "StringUtils.h" -#include "OSSupport/CriticalSection.h" - - - - - -// Common definitions: - -/// 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 - -/// 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 NDEBUG - #define ASSERT(x) ((void)0) -#else - #define ASSERT assert -#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 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; -} ; - - - - - -#include "CryptoPP/randpool.h" -#include "CryptoPP/aes.h" -#include "CryptoPP/rsa.h" -#include "CryptoPP/modes.h" - -using namespace CryptoPP; - - - - -#define LOGERROR printf -#define LOGINFO printf + +// 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 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; + + + + + +// 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 + #include + + // 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 + #include // for mkdir + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include +#if !defined(ANDROID_NDK) + #include +#endif +#endif + +#if !defined(ANDROID_NDK) + #define USE_SQUIRREL +#endif + +#if defined(ANDROID_NDK) + #define FILE_IO_PREFIX "/sdcard/mcserver/" +#else + #define FILE_IO_PREFIX "" +#endif + + + + + +// CRT stuff: +#include +#include +#include +#include + + + + + +// STL stuff: +#include +#include +#include +#include +#include +#include +#include + + + + + +// Common headers (part 1, without macros): +#include "StringUtils.h" +#include "OSSupport/CriticalSection.h" + + + + + +// Common definitions: + +/// 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 + +/// 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 NDEBUG + #define ASSERT(x) ((void)0) +#else + #define ASSERT assert +#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 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; +} ; + + + + + +#include "CryptoPP/randpool.h" +#include "CryptoPP/aes.h" +#include "CryptoPP/rsa.h" +#include "CryptoPP/modes.h" + +using namespace CryptoPP; + + + + +#define LOGERROR printf +#define LOGINFO printf #define LOGWARNING printf \ No newline at end of file diff --git a/Tools/ProtoProxy/ProtoProxy.cpp b/Tools/ProtoProxy/ProtoProxy.cpp index 0d61835e7..2724ef704 100644 --- a/Tools/ProtoProxy/ProtoProxy.cpp +++ b/Tools/ProtoProxy/ProtoProxy.cpp @@ -1,32 +1,32 @@ - -// ProtoProxy.cpp - -// Implements the main app entrypoint - -#include "Globals.h" -#include "Server.h" - - - - - -int main(int argc, char ** argv) -{ - int ListenPort = (argc > 1) ? atoi(argv[1]) : 25564; - int ConnectPort = (argc > 2) ? atoi(argv[2]) : 25565; - cServer Server; - int res = Server.Init(ListenPort, ConnectPort); - if (res != 0) - { - printf("Server initialization failed: %d", res); - return res; - } - - Server.Run(); - - return 0; -} - - - - + +// ProtoProxy.cpp + +// Implements the main app entrypoint + +#include "Globals.h" +#include "Server.h" + + + + + +int main(int argc, char ** argv) +{ + int ListenPort = (argc > 1) ? atoi(argv[1]) : 25564; + int ConnectPort = (argc > 2) ? atoi(argv[2]) : 25565; + cServer Server; + int res = Server.Init(ListenPort, ConnectPort); + if (res != 0) + { + printf("Server initialization failed: %d", res); + return res; + } + + Server.Run(); + + return 0; +} + + + + diff --git a/Tools/ProtoProxy/ProtoProxy.sln b/Tools/ProtoProxy/ProtoProxy.sln index 5488abd41..1c8c1a2a7 100644 --- a/Tools/ProtoProxy/ProtoProxy.sln +++ b/Tools/ProtoProxy/ProtoProxy.sln @@ -1,29 +1,29 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual C++ Express 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProtoProxy", "ProtoProxy.vcproj", "{EFEC8F76-1397-49A4-885B-314CB4244231}" - ProjectSection(ProjectDependencies) = postProject - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF} = {3423EC9A-52E4-4A4D-9753-EDEBC38785EF} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CryptoPP", "..\..\VC2008\CryptoPP.vcproj", "{3423EC9A-52E4-4A4D-9753-EDEBC38785EF}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EFEC8F76-1397-49A4-885B-314CB4244231}.Debug|Win32.ActiveCfg = Debug|Win32 - {EFEC8F76-1397-49A4-885B-314CB4244231}.Debug|Win32.Build.0 = Debug|Win32 - {EFEC8F76-1397-49A4-885B-314CB4244231}.Release|Win32.ActiveCfg = Release|Win32 - {EFEC8F76-1397-49A4-885B-314CB4244231}.Release|Win32.Build.0 = Release|Win32 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|Win32.ActiveCfg = Debug|Win32 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|Win32.Build.0 = Debug|Win32 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|Win32.ActiveCfg = Release|Win32 - {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ProtoProxy", "ProtoProxy.vcproj", "{EFEC8F76-1397-49A4-885B-314CB4244231}" + ProjectSection(ProjectDependencies) = postProject + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF} = {3423EC9A-52E4-4A4D-9753-EDEBC38785EF} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CryptoPP", "..\..\VC2008\CryptoPP.vcproj", "{3423EC9A-52E4-4A4D-9753-EDEBC38785EF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EFEC8F76-1397-49A4-885B-314CB4244231}.Debug|Win32.ActiveCfg = Debug|Win32 + {EFEC8F76-1397-49A4-885B-314CB4244231}.Debug|Win32.Build.0 = Debug|Win32 + {EFEC8F76-1397-49A4-885B-314CB4244231}.Release|Win32.ActiveCfg = Release|Win32 + {EFEC8F76-1397-49A4-885B-314CB4244231}.Release|Win32.Build.0 = Release|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|Win32.ActiveCfg = Debug|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Debug|Win32.Build.0 = Debug|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|Win32.ActiveCfg = Release|Win32 + {3423EC9A-52E4-4A4D-9753-EDEBC38785EF}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Tools/ProtoProxy/ProtoProxy.txt b/Tools/ProtoProxy/ProtoProxy.txt index d232d41fd..e25d513f3 100644 --- a/Tools/ProtoProxy/ProtoProxy.txt +++ b/Tools/ProtoProxy/ProtoProxy.txt @@ -1,31 +1,31 @@ - -// ProtoProxy.txt - -// A readme for the project - -/* -ProtoProxy -========== - -This is a project to create a proxy for the MineCraft protocol, allowing anyone to view the data sent over a network connection between a client and a server. This, in fact, performs a kind of Man-In-The-Middle (MITM) attack on the protocol by tapping in between the connection points and providing a decrypter and an encrypter for each. - -In order to catch the encryption parameters, the MC protocol needs to be understood at least a little bit at the beginning, when the cryptography parameters are exchanged. - -This project is currently Windows-only and I don't plan on making it multi-platform, although the effort needed for doing so should be minimal. - -The proxy only works on the localhost connection. It listens on port 25564 and expects the underlying MC server to run on port 25565. Ports can be changed by cmdline args: ProtoProxy . - -You need to set the server *not* to verify usernames ("online-mode=false" in server.properties) in order to be able to connect through ProtoProxy - since the full server name, including the port, is used for verification, the client uses different servername than the server and thus the verification fails. - - - -ProtoProxy is not much dependent on the protocol - it will work with unknown packets, it just won't parse them into human-readable format. -The latest protocol which has been tested is 1.6.1 (#73). - - -*/ - - - - - + +// ProtoProxy.txt + +// A readme for the project + +/* +ProtoProxy +========== + +This is a project to create a proxy for the MineCraft protocol, allowing anyone to view the data sent over a network connection between a client and a server. This, in fact, performs a kind of Man-In-The-Middle (MITM) attack on the protocol by tapping in between the connection points and providing a decrypter and an encrypter for each. + +In order to catch the encryption parameters, the MC protocol needs to be understood at least a little bit at the beginning, when the cryptography parameters are exchanged. + +This project is currently Windows-only and I don't plan on making it multi-platform, although the effort needed for doing so should be minimal. + +The proxy only works on the localhost connection. It listens on port 25564 and expects the underlying MC server to run on port 25565. Ports can be changed by cmdline args: ProtoProxy . + +You need to set the server *not* to verify usernames ("online-mode=false" in server.properties) in order to be able to connect through ProtoProxy - since the full server name, including the port, is used for verification, the client uses different servername than the server and thus the verification fails. + + + +ProtoProxy is not much dependent on the protocol - it will work with unknown packets, it just won't parse them into human-readable format. +The latest protocol which has been tested is 1.6.1 (#73). + + +*/ + + + + + diff --git a/Tools/ProtoProxy/ProtoProxy.vcproj b/Tools/ProtoProxy/ProtoProxy.vcproj index b7384ee8a..695e01e76 100644 --- a/Tools/ProtoProxy/ProtoProxy.vcproj +++ b/Tools/ProtoProxy/ProtoProxy.vcproj @@ -1,267 +1,267 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/ProtoProxy/Server.cpp b/Tools/ProtoProxy/Server.cpp index 3d4913355..35732764c 100644 --- a/Tools/ProtoProxy/Server.cpp +++ b/Tools/ProtoProxy/Server.cpp @@ -1,82 +1,82 @@ - -// Server.cpp - -// Interfaces to the cServer class encapsulating the entire "server" - -#include "Globals.h" -#include "Server.h" -#include "Connection.h" - - - - - -cServer::cServer(void) -{ -} - - - - - -int cServer::Init(short a_ListenPort, short a_ConnectPort) -{ - m_ConnectPort = a_ConnectPort; - WSAData wsa; - int res = WSAStartup(0x0202, &wsa); - if (res != 0) - { - printf("Cannot initialize WinSock: %d\n", res); - return res; - } - - printf("Generating protocol encryption keypair...\n"); - time_t CurTime = time(NULL); - RandomPool rng; - rng.Put((const byte *)&CurTime, sizeof(CurTime)); - m_PrivateKey.GenerateRandomWithKeySize(rng, 1024); - RSA::PublicKey pk(m_PrivateKey); - m_PublicKey = pk; - - m_ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - sockaddr_in local; - memset(&local, 0, sizeof(local)); - local.sin_family = AF_INET; - local.sin_addr.s_addr = 0; // All interfaces - local.sin_port = htons(a_ListenPort); - bind(m_ListenSocket, (sockaddr *)&local, sizeof(local)); - listen(m_ListenSocket, 1); - - printf("Listening on port %d, connecting to localhost:%d\n", a_ListenPort, a_ConnectPort); - - return 0; -} - - - - - -void cServer::Run(void) -{ - printf("Server running.\n"); - while (true) - { - sockaddr_in Addr; - ZeroMemory(&Addr, sizeof(Addr)); - int AddrSize = sizeof(Addr); - SOCKET client = accept(m_ListenSocket, (sockaddr *)&Addr, &AddrSize); - if (client == INVALID_SOCKET) - { - printf("accept returned an error: %d; bailing out.\n", WSAGetLastError()); - return; - } - printf("Client connected, proxying...\n"); - cConnection Connection(client, *this); - Connection.Run(); - printf("Client disconnected. Ready for another connection.\n"); - } -} - - - - + +// Server.cpp + +// Interfaces to the cServer class encapsulating the entire "server" + +#include "Globals.h" +#include "Server.h" +#include "Connection.h" + + + + + +cServer::cServer(void) +{ +} + + + + + +int cServer::Init(short a_ListenPort, short a_ConnectPort) +{ + m_ConnectPort = a_ConnectPort; + WSAData wsa; + int res = WSAStartup(0x0202, &wsa); + if (res != 0) + { + printf("Cannot initialize WinSock: %d\n", res); + return res; + } + + printf("Generating protocol encryption keypair...\n"); + time_t CurTime = time(NULL); + RandomPool rng; + rng.Put((const byte *)&CurTime, sizeof(CurTime)); + m_PrivateKey.GenerateRandomWithKeySize(rng, 1024); + RSA::PublicKey pk(m_PrivateKey); + m_PublicKey = pk; + + m_ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + sockaddr_in local; + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + local.sin_addr.s_addr = 0; // All interfaces + local.sin_port = htons(a_ListenPort); + bind(m_ListenSocket, (sockaddr *)&local, sizeof(local)); + listen(m_ListenSocket, 1); + + printf("Listening on port %d, connecting to localhost:%d\n", a_ListenPort, a_ConnectPort); + + return 0; +} + + + + + +void cServer::Run(void) +{ + printf("Server running.\n"); + while (true) + { + sockaddr_in Addr; + ZeroMemory(&Addr, sizeof(Addr)); + int AddrSize = sizeof(Addr); + SOCKET client = accept(m_ListenSocket, (sockaddr *)&Addr, &AddrSize); + if (client == INVALID_SOCKET) + { + printf("accept returned an error: %d; bailing out.\n", WSAGetLastError()); + return; + } + printf("Client connected, proxying...\n"); + cConnection Connection(client, *this); + Connection.Run(); + printf("Client disconnected. Ready for another connection.\n"); + } +} + + + + diff --git a/Tools/ProtoProxy/Server.h b/Tools/ProtoProxy/Server.h index da64036b4..e69dbb5e0 100644 --- a/Tools/ProtoProxy/Server.h +++ b/Tools/ProtoProxy/Server.h @@ -1,38 +1,38 @@ - -// Server.h - -// Interfaces to the cServer class encapsulating the entire "server" - - - - - -#pragma once - - - - - - -class cServer -{ - SOCKET m_ListenSocket; - RSA::PrivateKey m_PrivateKey; - RSA::PublicKey m_PublicKey; - short m_ConnectPort; - -public: - cServer(void); - - int Init(short a_ListenPort, short a_ConnectPort); - void Run(void); - - RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; } - RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; } - - short GetConnectPort(void) const { return m_ConnectPort; } -} ; - - - - + +// Server.h + +// Interfaces to the cServer class encapsulating the entire "server" + + + + + +#pragma once + + + + + + +class cServer +{ + SOCKET m_ListenSocket; + RSA::PrivateKey m_PrivateKey; + RSA::PublicKey m_PublicKey; + short m_ConnectPort; + +public: + cServer(void); + + int Init(short a_ListenPort, short a_ConnectPort); + void Run(void); + + RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; } + RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; } + + short GetConnectPort(void) const { return m_ConnectPort; } +} ; + + + + diff --git a/Tools/ToLuaDoxy/Globals.cpp b/Tools/ToLuaDoxy/Globals.cpp index 31440fd44..d73265a60 100644 --- a/Tools/ToLuaDoxy/Globals.cpp +++ b/Tools/ToLuaDoxy/Globals.cpp @@ -1,10 +1,10 @@ - -// Globals.cpp - -// Used for precompiled header generation in MSVC - -#include "Globals.h" - - - - + +// Globals.cpp + +// Used for precompiled header generation in MSVC + +#include "Globals.h" + + + + diff --git a/Tools/ToLuaDoxy/Globals.h b/Tools/ToLuaDoxy/Globals.h index 1b92a1349..b9100a297 100644 --- a/Tools/ToLuaDoxy/Globals.h +++ b/Tools/ToLuaDoxy/Globals.h @@ -1,101 +1,101 @@ - -// Globals.h - -// This file is used for precompiled header generation in MSVC - - - - -// OS-dependent stuff: -#ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - - #define _WIN32_WINNT 0x501 // We want to target WinXP and higher - - #include - #include - #include // IPv6 stuff - - // 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 - #include // for mkdir - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include - #include - #include - #include - #include - - #if !defined(ANDROID_NDK) - #include - #endif -#endif - - - - - -// CRT stuff: -#include -#include -#include -#include - - - - - -// STL stuff: -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - - - -// Common headers (part 1, without macros): -#include "../../source/StringUtils.h" - - - - - -// Common definitions: - -/// 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 -#define MiB * 1024 * 1024 - -#define ASSERT assert - - - - + +// Globals.h + +// This file is used for precompiled header generation in MSVC + + + + +// OS-dependent stuff: +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + + #define _WIN32_WINNT 0x501 // We want to target WinXP and higher + + #include + #include + #include // IPv6 stuff + + // 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 + #include // for mkdir + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include + + #if !defined(ANDROID_NDK) + #include + #endif +#endif + + + + + +// CRT stuff: +#include +#include +#include +#include + + + + + +// STL stuff: +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + + + +// Common headers (part 1, without macros): +#include "../../source/StringUtils.h" + + + + + +// Common definitions: + +/// 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 +#define MiB * 1024 * 1024 + +#define ASSERT assert + + + + diff --git a/Tools/ToLuaDoxy/ToLuaDoxy.cpp b/Tools/ToLuaDoxy/ToLuaDoxy.cpp index cadb4b32e..8b692bb75 100644 --- a/Tools/ToLuaDoxy/ToLuaDoxy.cpp +++ b/Tools/ToLuaDoxy/ToLuaDoxy.cpp @@ -1,222 +1,222 @@ - -// ToLuaDoxy.cpp - -// Implements the main app entrypoint - -#include "Globals.h" -#include -#include - - - - - -typedef std::vector AStrings; - - - - - -class cProcessor -{ -public: - cProcessor(const AString & a_FileOut) : - m_Out(a_FileOut.c_str(), std::ios::out), - m_IsInToLua(false), - m_IsInComment(false) - { - } - - - bool IsGood(void) const - { - return !m_Out.fail(); - } - - - void ProcessFile(const AString & a_FileIn) - { - std::ifstream In(a_FileIn.c_str()); - if (In.fail()) - { - std::cerr << "Cannot open input file " << a_FileIn << "." << std::endl; - return; - } - while (!In.eof()) - { - AString Line; - std::getline(In, Line); - PushLine(Line); - } - } - -protected: - std::ofstream m_Out; - bool m_IsInToLua; ///< Set to true if inside a tolua_begin .. tolua_end block - bool m_IsInComment; ///< Set to true if previous line has started a multiline comment; only outside tolua blocks - AString m_LastComment; ///< Accumulator for a multiline comment preceding a tolua block - - - void PushLine(const AString & a_Line) - { - if (m_IsInToLua) - { - // Inside a tolua block - if (TrimString(a_Line) == "// tolua_end") - { - // End of a tolua block - m_IsInToLua = false; - return; - } - m_Out << a_Line << std::endl; - return; - } - - if (m_IsInComment) - { - // Inside a multiline comment block, outside of a tolua block; accumulate m_LastComment - m_LastComment += a_Line + "\n"; - m_IsInComment = (a_Line.find("*/") == AString::npos); - return; - } - - AString Trimmed(TrimString(a_Line)); - - if (Trimmed == "// tolua_begin") - { - // Beginning of a tolua block - m_IsInToLua = true; - if (!m_LastComment.empty()) - { - m_Out << m_LastComment << std::endl; - m_LastComment.clear(); - } - return; - } - - size_t CommentBegin = a_Line.find("/*"); - if (CommentBegin != AString::npos) - { - m_IsInComment = (a_Line.find("*/", CommentBegin) == AString::npos); - m_LastComment = a_Line; - } - - size_t ExportIdx = a_Line.find("// tolua_export"); - if (ExportIdx != AString::npos) - { - // Single-line tolua block - - // Strip the export comment and right-trim the line: - AString Stripped(a_Line.substr(0, ExportIdx)); - int End = Stripped.length() - 1; - while ((End > 0) && (Stripped[End] <= 32)) - { - End--; - } - Stripped.erase(End + 1); - - if (!m_LastComment.empty()) - { - m_Out << m_LastComment << std::endl; - m_LastComment.clear(); - } - m_Out << Stripped << std::endl; - return; - } - - if (!m_IsInComment) - { - m_LastComment.clear(); - } - } -} ; - - - - - -/** Parses the specified package file into a list of $cfile-included files and all the other contents -Returns true if successful. -Returns false and prints error if unsuccessful -*/ -bool ParsePackageFile(const AString & a_FileName, AStrings & a_CFiles, AStrings & a_DirectContentsLines) -{ - std::ifstream PkgFile(a_FileName.c_str()); - if (PkgFile.fail()) - { - std::cerr << "Cannot open the package file " << a_FileName << "." << std::endl; - return false; - } - - while (!PkgFile.eof()) - { - AString Line; - std::getline(PkgFile, Line); - Line = TrimString(Line); - if (strncmp(Line.c_str(), "$cfile \"", 8) == 0) - { - a_CFiles.push_back(Line.substr(8, Line.length() - 9)); - } - else - { - a_DirectContentsLines.push_back(Line); - } - } - return true; -} - - - - - -/// Processes the specified input header file into the output file -void ProcessCFile(const AString & a_CFileIn, const AString & a_CFileOut) -{ - cProcessor p(a_CFileOut); - if (!p.IsGood()) - { - std::cerr << "Cannot open output file " << a_CFileOut << "." << std::endl; - return; - } - p.ProcessFile(a_CFileIn); -} - - - - - -int main(int argc, char * argv[]) -{ - AString BaseDir = (argc > 1) ? argv[1] : "."; - AString OutDir = (argc > 2) ? argv[2] : "Out"; - - // Create the output directory: - #ifdef _WIN32 - CreateDirectory(OutDir.c_str(), NULL); - #else - mkdir(OutDir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); - #endif - - // Parse the package file - AStrings CFiles; - AStrings DirectLines; - if (!ParsePackageFile(Printf("%s/AllToLua.pkg", BaseDir.c_str()), CFiles, DirectLines)) - { - return 1; - } - - // Process header files: - for (AStrings::const_iterator itr = CFiles.begin(), end = CFiles.end(); itr != end; ++itr) - { - static int cnt = 0; - AString In = Printf("%s/%s", BaseDir.c_str(), itr->c_str()); - AString Out = Printf("%s/%04x.h", OutDir.c_str(), cnt++); - ProcessCFile(In, Out); - } // for itr - CFiles[] - - return 0; -} - - - - + +// ToLuaDoxy.cpp + +// Implements the main app entrypoint + +#include "Globals.h" +#include +#include + + + + + +typedef std::vector AStrings; + + + + + +class cProcessor +{ +public: + cProcessor(const AString & a_FileOut) : + m_Out(a_FileOut.c_str(), std::ios::out), + m_IsInToLua(false), + m_IsInComment(false) + { + } + + + bool IsGood(void) const + { + return !m_Out.fail(); + } + + + void ProcessFile(const AString & a_FileIn) + { + std::ifstream In(a_FileIn.c_str()); + if (In.fail()) + { + std::cerr << "Cannot open input file " << a_FileIn << "." << std::endl; + return; + } + while (!In.eof()) + { + AString Line; + std::getline(In, Line); + PushLine(Line); + } + } + +protected: + std::ofstream m_Out; + bool m_IsInToLua; ///< Set to true if inside a tolua_begin .. tolua_end block + bool m_IsInComment; ///< Set to true if previous line has started a multiline comment; only outside tolua blocks + AString m_LastComment; ///< Accumulator for a multiline comment preceding a tolua block + + + void PushLine(const AString & a_Line) + { + if (m_IsInToLua) + { + // Inside a tolua block + if (TrimString(a_Line) == "// tolua_end") + { + // End of a tolua block + m_IsInToLua = false; + return; + } + m_Out << a_Line << std::endl; + return; + } + + if (m_IsInComment) + { + // Inside a multiline comment block, outside of a tolua block; accumulate m_LastComment + m_LastComment += a_Line + "\n"; + m_IsInComment = (a_Line.find("*/") == AString::npos); + return; + } + + AString Trimmed(TrimString(a_Line)); + + if (Trimmed == "// tolua_begin") + { + // Beginning of a tolua block + m_IsInToLua = true; + if (!m_LastComment.empty()) + { + m_Out << m_LastComment << std::endl; + m_LastComment.clear(); + } + return; + } + + size_t CommentBegin = a_Line.find("/*"); + if (CommentBegin != AString::npos) + { + m_IsInComment = (a_Line.find("*/", CommentBegin) == AString::npos); + m_LastComment = a_Line; + } + + size_t ExportIdx = a_Line.find("// tolua_export"); + if (ExportIdx != AString::npos) + { + // Single-line tolua block + + // Strip the export comment and right-trim the line: + AString Stripped(a_Line.substr(0, ExportIdx)); + int End = Stripped.length() - 1; + while ((End > 0) && (Stripped[End] <= 32)) + { + End--; + } + Stripped.erase(End + 1); + + if (!m_LastComment.empty()) + { + m_Out << m_LastComment << std::endl; + m_LastComment.clear(); + } + m_Out << Stripped << std::endl; + return; + } + + if (!m_IsInComment) + { + m_LastComment.clear(); + } + } +} ; + + + + + +/** Parses the specified package file into a list of $cfile-included files and all the other contents +Returns true if successful. +Returns false and prints error if unsuccessful +*/ +bool ParsePackageFile(const AString & a_FileName, AStrings & a_CFiles, AStrings & a_DirectContentsLines) +{ + std::ifstream PkgFile(a_FileName.c_str()); + if (PkgFile.fail()) + { + std::cerr << "Cannot open the package file " << a_FileName << "." << std::endl; + return false; + } + + while (!PkgFile.eof()) + { + AString Line; + std::getline(PkgFile, Line); + Line = TrimString(Line); + if (strncmp(Line.c_str(), "$cfile \"", 8) == 0) + { + a_CFiles.push_back(Line.substr(8, Line.length() - 9)); + } + else + { + a_DirectContentsLines.push_back(Line); + } + } + return true; +} + + + + + +/// Processes the specified input header file into the output file +void ProcessCFile(const AString & a_CFileIn, const AString & a_CFileOut) +{ + cProcessor p(a_CFileOut); + if (!p.IsGood()) + { + std::cerr << "Cannot open output file " << a_CFileOut << "." << std::endl; + return; + } + p.ProcessFile(a_CFileIn); +} + + + + + +int main(int argc, char * argv[]) +{ + AString BaseDir = (argc > 1) ? argv[1] : "."; + AString OutDir = (argc > 2) ? argv[2] : "Out"; + + // Create the output directory: + #ifdef _WIN32 + CreateDirectory(OutDir.c_str(), NULL); + #else + mkdir(OutDir.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); + #endif + + // Parse the package file + AStrings CFiles; + AStrings DirectLines; + if (!ParsePackageFile(Printf("%s/AllToLua.pkg", BaseDir.c_str()), CFiles, DirectLines)) + { + return 1; + } + + // Process header files: + for (AStrings::const_iterator itr = CFiles.begin(), end = CFiles.end(); itr != end; ++itr) + { + static int cnt = 0; + AString In = Printf("%s/%s", BaseDir.c_str(), itr->c_str()); + AString Out = Printf("%s/%04x.h", OutDir.c_str(), cnt++); + ProcessCFile(In, Out); + } // for itr - CFiles[] + + return 0; +} + + + + diff --git a/Tools/ToLuaDoxy/ToLuaDoxy.sln b/Tools/ToLuaDoxy/ToLuaDoxy.sln index 8df530779..2d8e78b1b 100644 --- a/Tools/ToLuaDoxy/ToLuaDoxy.sln +++ b/Tools/ToLuaDoxy/ToLuaDoxy.sln @@ -1,20 +1,20 @@ - -Microsoft Visual Studio Solution File, Format Version 10.00 -# Visual C++ Express 2008 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ToLuaDoxy", "ToLuaDoxy.vcproj", "{137140AF-4E9D-404F-BC87-982A6517AC12}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {137140AF-4E9D-404F-BC87-982A6517AC12}.Debug|Win32.ActiveCfg = Debug|Win32 - {137140AF-4E9D-404F-BC87-982A6517AC12}.Debug|Win32.Build.0 = Debug|Win32 - {137140AF-4E9D-404F-BC87-982A6517AC12}.Release|Win32.ActiveCfg = Release|Win32 - {137140AF-4E9D-404F-BC87-982A6517AC12}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual C++ Express 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ToLuaDoxy", "ToLuaDoxy.vcproj", "{137140AF-4E9D-404F-BC87-982A6517AC12}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {137140AF-4E9D-404F-BC87-982A6517AC12}.Debug|Win32.ActiveCfg = Debug|Win32 + {137140AF-4E9D-404F-BC87-982A6517AC12}.Debug|Win32.Build.0 = Debug|Win32 + {137140AF-4E9D-404F-BC87-982A6517AC12}.Release|Win32.ActiveCfg = Release|Win32 + {137140AF-4E9D-404F-BC87-982A6517AC12}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Tools/ToLuaDoxy/ToLuaDoxy.vcproj b/Tools/ToLuaDoxy/ToLuaDoxy.vcproj index 0c9f4ec5b..cd4c89cee 100644 --- a/Tools/ToLuaDoxy/ToLuaDoxy.vcproj +++ b/Tools/ToLuaDoxy/ToLuaDoxy.vcproj @@ -1,221 +1,221 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3