From 4b962fcb21f92da99776786e72e36845cff0dfc6 Mon Sep 17 00:00:00 2001 From: piebot Date: Sat, 29 Aug 2026 14:01:24 +0300 Subject: [PATCH] feat: add smartcmd dedicated server major bug: when connecting to the server a second time, it gets softlocked and keeps repeating ticking connection A\n in debug logs until it times out. --- .gitignore | 9 + Minecraft.Client/ClientConnection.cpp | 4 + Minecraft.Client/Common/Console_Utils.cpp | 14 +- Minecraft.Client/Common/Consoles_App.cpp | 47 +- .../Common/Network/GameNetworkManager.cpp | 286 +- .../Common/Network/NetworkSocketLayer.cpp | 132 +- .../Common/Network/NetworkSocketLayer.h | 12 +- .../Common/Network/PlatformNetworkManager.cpp | 114 +- .../Common/Network/PlatformNetworkManager.h | 2 + .../Common/Postbuilds/Server-postbuild.ps1 | 82 + Minecraft.Client/Minecraft.Client.vcxproj | 829 ++++- Minecraft.Client/MinecraftServer.cpp | 476 ++- Minecraft.Client/MinecraftServer.h | 3 + Minecraft.Client/PendingConnection.cpp | 144 +- Minecraft.Client/PlayerConnection.cpp | 94 +- Minecraft.Client/PlayerConnection.h | 5 + Minecraft.Client/PlayerList.cpp | 11 + Minecraft.Client/ServerPlayerGameMode.cpp | 27 +- Minecraft.Server/Access/Access.cpp | 460 +++ Minecraft.Server/Access/Access.h | 47 + Minecraft.Server/Access/BanManager.cpp | 682 ++++ Minecraft.Server/Access/BanManager.h | 106 + Minecraft.Server/Access/WhitelistManager.cpp | 318 ++ Minecraft.Server/Access/WhitelistManager.h | 64 + Minecraft.Server/Common/AccessStorageUtils.h | 105 + Minecraft.Server/Common/FileUtils.cpp | 146 + Minecraft.Server/Common/FileUtils.h | 25 + Minecraft.Server/Common/NetworkUtils.h | 29 + Minecraft.Server/Common/StringUtils.cpp | 212 ++ Minecraft.Server/Common/StringUtils.h | 23 + Minecraft.Server/Console/ServerCli.cpp | 44 + Minecraft.Server/Console/ServerCli.h | 50 + Minecraft.Server/Console/ServerCliEngine.cpp | 395 ++ Minecraft.Server/Console/ServerCliEngine.h | 127 + Minecraft.Server/Console/ServerCliInput.cpp | 285 ++ Minecraft.Server/Console/ServerCliInput.h | 63 + Minecraft.Server/Console/ServerCliParser.cpp | 116 + Minecraft.Server/Console/ServerCliParser.h | 63 + .../Console/ServerCliRegistry.cpp | 99 + Minecraft.Server/Console/ServerCliRegistry.h | 58 + .../Console/commands/CommandParsing.h | 39 + .../Console/commands/IServerCliCommand.h | 49 + .../commands/ban-ip/CliCommandBanIp.cpp | 171 + .../Console/commands/ban-ip/CliCommandBanIp.h | 19 + .../commands/ban-list/CliCommandBanList.cpp | 136 + .../commands/ban-list/CliCommandBanList.h | 23 + .../Console/commands/ban/CliCommandBan.cpp | 145 + .../Console/commands/ban/CliCommandBan.h | 20 + .../CliCommandDefaultGamemode.cpp | 117 + .../CliCommandDefaultGamemode.h | 16 + .../commands/enchant/CliCommandEnchant.cpp | 87 + .../commands/enchant/CliCommandEnchant.h | 16 + .../experience/CliCommandExperience.cpp | 192 + .../experience/CliCommandExperience.h | 17 + .../commands/gamemode/CliCommandGamemode.cpp | 111 + .../commands/gamemode/CliCommandGamemode.h | 18 + .../Console/commands/give/CliCommandGive.cpp | 103 + .../Console/commands/give/CliCommandGive.h | 16 + .../Console/commands/help/CliCommandHelp.cpp | 49 + .../Console/commands/help/CliCommandHelp.h | 17 + .../Console/commands/kill/CliCommandKill.cpp | 64 + .../Console/commands/kill/CliCommandKill.h | 16 + .../Console/commands/list/CliCommandList.cpp | 50 + .../Console/commands/list/CliCommandList.h | 16 + .../commands/pardon-ip/CliCommandPardonIp.cpp | 98 + .../commands/pardon-ip/CliCommandPardonIp.h | 19 + .../commands/pardon/CliCommandPardon.cpp | 173 + .../commands/pardon/CliCommandPardon.h | 19 + .../Console/commands/stop/CliCommandStop.cpp | 33 + .../Console/commands/stop/CliCommandStop.h | 16 + .../Console/commands/time/CliCommandTime.cpp | 118 + .../Console/commands/time/CliCommandTime.h | 16 + .../Console/commands/tp/CliCommandTp.cpp | 84 + .../Console/commands/tp/CliCommandTp.h | 18 + .../commands/weather/CliCommandWeather.cpp | 49 + .../commands/weather/CliCommandWeather.h | 15 + .../whitelist/CliCommandWhitelist.cpp | 285 ++ .../commands/whitelist/CliCommandWhitelist.h | 17 + Minecraft.Server/Minecraft.Server.vcxproj | 308 ++ .../Minecraft.Server.vcxproj.filters | 529 +++ Minecraft.Server/ServerLogManager.cpp | 401 ++ Minecraft.Server/ServerLogManager.h | 127 + Minecraft.Server/ServerLogger.cpp | 258 ++ Minecraft.Server/ServerLogger.h | 44 + Minecraft.Server/ServerProperties.cpp | 930 +++++ Minecraft.Server/ServerProperties.h | 105 + Minecraft.Server/ServerShutdown.h | 6 + .../Windows64/Iggy/lib/iggy_w32.lib | Bin 0 -> 49766 bytes .../Windows64/Iggy/lib/iggy_w64.lib | Bin 0 -> 51572 bytes .../Windows64/Iggy/lib/iggyexpruntime_w32.lib | Bin 0 -> 2430 bytes .../Windows64/Iggy/lib/iggyexpruntime_w64.lib | Bin 0 -> 2386 bytes .../Windows64/Iggy/lib/iggyperfmon_w32.lib | Bin 0 -> 2436 bytes .../Windows64/Iggy/lib/iggyperfmon_w64.lib | Bin 0 -> 2390 bytes .../Windows64/Miles/lib/mss64.lib | Bin 0 -> 94326 bytes Minecraft.Server/Windows64/ServerMain.cpp | 730 ++++ Minecraft.Server/WorldManager.cpp | 641 ++++ Minecraft.Server/WorldManager.h | 92 + Minecraft.Server/docs/DEVELOPMENT.en.md | 284 ++ Minecraft.Server/docs/DEVELOPMENT.ja.md | 286 ++ Minecraft.Server/stdafx.cpp | 8 + Minecraft.Server/vendor/linenoise/LICENSE | 25 + Minecraft.Server/vendor/linenoise/linenoise.c | 652 ++++ Minecraft.Server/vendor/linenoise/linenoise.h | 37 + .../vendor/rapidjson/allocators.h | 693 ++++ .../vendor/rapidjson/cursorstreamwrapper.h | 78 + Minecraft.Server/vendor/rapidjson/document.h | 3044 +++++++++++++++ .../vendor/rapidjson/encodedstream.h | 299 ++ Minecraft.Server/vendor/rapidjson/encodings.h | 716 ++++ Minecraft.Server/vendor/rapidjson/error/en.h | 176 + .../vendor/rapidjson/error/error.h | 285 ++ .../vendor/rapidjson/filereadstream.h | 99 + .../vendor/rapidjson/filewritestream.h | 104 + Minecraft.Server/vendor/rapidjson/fwd.h | 151 + .../vendor/rapidjson/internal/biginteger.h | 297 ++ .../vendor/rapidjson/internal/clzll.h | 71 + .../vendor/rapidjson/internal/diyfp.h | 261 ++ .../vendor/rapidjson/internal/dtoa.h | 249 ++ .../vendor/rapidjson/internal/ieee754.h | 78 + .../vendor/rapidjson/internal/itoa.h | 308 ++ .../vendor/rapidjson/internal/meta.h | 186 + .../vendor/rapidjson/internal/pow10.h | 55 + .../vendor/rapidjson/internal/regex.h | 739 ++++ .../vendor/rapidjson/internal/stack.h | 232 ++ .../vendor/rapidjson/internal/strfunc.h | 83 + .../vendor/rapidjson/internal/strtod.h | 293 ++ .../vendor/rapidjson/internal/swap.h | 46 + .../vendor/rapidjson/istreamwrapper.h | 128 + .../vendor/rapidjson/memorybuffer.h | 70 + .../vendor/rapidjson/memorystream.h | 71 + .../vendor/rapidjson/msinttypes/inttypes.h | 316 ++ .../vendor/rapidjson/msinttypes/stdint.h | 300 ++ .../vendor/rapidjson/ostreamwrapper.h | 81 + Minecraft.Server/vendor/rapidjson/pointer.h | 1482 ++++++++ .../vendor/rapidjson/prettywriter.h | 277 ++ Minecraft.Server/vendor/rapidjson/rapidjson.h | 741 ++++ Minecraft.Server/vendor/rapidjson/reader.h | 2246 ++++++++++++ Minecraft.Server/vendor/rapidjson/schema.h | 3261 +++++++++++++++++ Minecraft.Server/vendor/rapidjson/stream.h | 223 ++ .../vendor/rapidjson/stringbuffer.h | 121 + Minecraft.Server/vendor/rapidjson/uri.h | 481 +++ Minecraft.Server/vendor/rapidjson/writer.h | 721 ++++ Minecraft.World/MapItemSavedData.cpp | 28 +- Minecraft.World/Minecraft.World.vcxproj | 86 +- Minecraft.World/Packet.cpp | 12 +- Minecraft.World/PreLoginPacket.cpp | 2 +- MinecraftConsoles.sln | 181 +- 146 files changed, 32837 insertions(+), 272 deletions(-) create mode 100644 Minecraft.Client/Common/Postbuilds/Server-postbuild.ps1 create mode 100644 Minecraft.Server/Access/Access.cpp create mode 100644 Minecraft.Server/Access/Access.h create mode 100644 Minecraft.Server/Access/BanManager.cpp create mode 100644 Minecraft.Server/Access/BanManager.h create mode 100644 Minecraft.Server/Access/WhitelistManager.cpp create mode 100644 Minecraft.Server/Access/WhitelistManager.h create mode 100644 Minecraft.Server/Common/AccessStorageUtils.h create mode 100644 Minecraft.Server/Common/FileUtils.cpp create mode 100644 Minecraft.Server/Common/FileUtils.h create mode 100644 Minecraft.Server/Common/NetworkUtils.h create mode 100644 Minecraft.Server/Common/StringUtils.cpp create mode 100644 Minecraft.Server/Common/StringUtils.h create mode 100644 Minecraft.Server/Console/ServerCli.cpp create mode 100644 Minecraft.Server/Console/ServerCli.h create mode 100644 Minecraft.Server/Console/ServerCliEngine.cpp create mode 100644 Minecraft.Server/Console/ServerCliEngine.h create mode 100644 Minecraft.Server/Console/ServerCliInput.cpp create mode 100644 Minecraft.Server/Console/ServerCliInput.h create mode 100644 Minecraft.Server/Console/ServerCliParser.cpp create mode 100644 Minecraft.Server/Console/ServerCliParser.h create mode 100644 Minecraft.Server/Console/ServerCliRegistry.cpp create mode 100644 Minecraft.Server/Console/ServerCliRegistry.h create mode 100644 Minecraft.Server/Console/commands/CommandParsing.h create mode 100644 Minecraft.Server/Console/commands/IServerCliCommand.h create mode 100644 Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp create mode 100644 Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.h create mode 100644 Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp create mode 100644 Minecraft.Server/Console/commands/ban-list/CliCommandBanList.h create mode 100644 Minecraft.Server/Console/commands/ban/CliCommandBan.cpp create mode 100644 Minecraft.Server/Console/commands/ban/CliCommandBan.h create mode 100644 Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.cpp create mode 100644 Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.h create mode 100644 Minecraft.Server/Console/commands/enchant/CliCommandEnchant.cpp create mode 100644 Minecraft.Server/Console/commands/enchant/CliCommandEnchant.h create mode 100644 Minecraft.Server/Console/commands/experience/CliCommandExperience.cpp create mode 100644 Minecraft.Server/Console/commands/experience/CliCommandExperience.h create mode 100644 Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.cpp create mode 100644 Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.h create mode 100644 Minecraft.Server/Console/commands/give/CliCommandGive.cpp create mode 100644 Minecraft.Server/Console/commands/give/CliCommandGive.h create mode 100644 Minecraft.Server/Console/commands/help/CliCommandHelp.cpp create mode 100644 Minecraft.Server/Console/commands/help/CliCommandHelp.h create mode 100644 Minecraft.Server/Console/commands/kill/CliCommandKill.cpp create mode 100644 Minecraft.Server/Console/commands/kill/CliCommandKill.h create mode 100644 Minecraft.Server/Console/commands/list/CliCommandList.cpp create mode 100644 Minecraft.Server/Console/commands/list/CliCommandList.h create mode 100644 Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.cpp create mode 100644 Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.h create mode 100644 Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp create mode 100644 Minecraft.Server/Console/commands/pardon/CliCommandPardon.h create mode 100644 Minecraft.Server/Console/commands/stop/CliCommandStop.cpp create mode 100644 Minecraft.Server/Console/commands/stop/CliCommandStop.h create mode 100644 Minecraft.Server/Console/commands/time/CliCommandTime.cpp create mode 100644 Minecraft.Server/Console/commands/time/CliCommandTime.h create mode 100644 Minecraft.Server/Console/commands/tp/CliCommandTp.cpp create mode 100644 Minecraft.Server/Console/commands/tp/CliCommandTp.h create mode 100644 Minecraft.Server/Console/commands/weather/CliCommandWeather.cpp create mode 100644 Minecraft.Server/Console/commands/weather/CliCommandWeather.h create mode 100644 Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.cpp create mode 100644 Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.h create mode 100644 Minecraft.Server/Minecraft.Server.vcxproj create mode 100644 Minecraft.Server/Minecraft.Server.vcxproj.filters create mode 100644 Minecraft.Server/ServerLogManager.cpp create mode 100644 Minecraft.Server/ServerLogManager.h create mode 100644 Minecraft.Server/ServerLogger.cpp create mode 100644 Minecraft.Server/ServerLogger.h create mode 100644 Minecraft.Server/ServerProperties.cpp create mode 100644 Minecraft.Server/ServerProperties.h create mode 100644 Minecraft.Server/ServerShutdown.h create mode 100644 Minecraft.Server/Windows64/Iggy/lib/iggy_w32.lib create mode 100644 Minecraft.Server/Windows64/Iggy/lib/iggy_w64.lib create mode 100644 Minecraft.Server/Windows64/Iggy/lib/iggyexpruntime_w32.lib create mode 100644 Minecraft.Server/Windows64/Iggy/lib/iggyexpruntime_w64.lib create mode 100644 Minecraft.Server/Windows64/Iggy/lib/iggyperfmon_w32.lib create mode 100644 Minecraft.Server/Windows64/Iggy/lib/iggyperfmon_w64.lib create mode 100644 Minecraft.Server/Windows64/Miles/lib/mss64.lib create mode 100644 Minecraft.Server/Windows64/ServerMain.cpp create mode 100644 Minecraft.Server/WorldManager.cpp create mode 100644 Minecraft.Server/WorldManager.h create mode 100644 Minecraft.Server/docs/DEVELOPMENT.en.md create mode 100644 Minecraft.Server/docs/DEVELOPMENT.ja.md create mode 100644 Minecraft.Server/stdafx.cpp create mode 100644 Minecraft.Server/vendor/linenoise/LICENSE create mode 100644 Minecraft.Server/vendor/linenoise/linenoise.c create mode 100644 Minecraft.Server/vendor/linenoise/linenoise.h create mode 100644 Minecraft.Server/vendor/rapidjson/allocators.h create mode 100644 Minecraft.Server/vendor/rapidjson/cursorstreamwrapper.h create mode 100644 Minecraft.Server/vendor/rapidjson/document.h create mode 100644 Minecraft.Server/vendor/rapidjson/encodedstream.h create mode 100644 Minecraft.Server/vendor/rapidjson/encodings.h create mode 100644 Minecraft.Server/vendor/rapidjson/error/en.h create mode 100644 Minecraft.Server/vendor/rapidjson/error/error.h create mode 100644 Minecraft.Server/vendor/rapidjson/filereadstream.h create mode 100644 Minecraft.Server/vendor/rapidjson/filewritestream.h create mode 100644 Minecraft.Server/vendor/rapidjson/fwd.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/biginteger.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/clzll.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/diyfp.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/dtoa.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/ieee754.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/itoa.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/meta.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/pow10.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/regex.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/stack.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/strfunc.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/strtod.h create mode 100644 Minecraft.Server/vendor/rapidjson/internal/swap.h create mode 100644 Minecraft.Server/vendor/rapidjson/istreamwrapper.h create mode 100644 Minecraft.Server/vendor/rapidjson/memorybuffer.h create mode 100644 Minecraft.Server/vendor/rapidjson/memorystream.h create mode 100644 Minecraft.Server/vendor/rapidjson/msinttypes/inttypes.h create mode 100644 Minecraft.Server/vendor/rapidjson/msinttypes/stdint.h create mode 100644 Minecraft.Server/vendor/rapidjson/ostreamwrapper.h create mode 100644 Minecraft.Server/vendor/rapidjson/pointer.h create mode 100644 Minecraft.Server/vendor/rapidjson/prettywriter.h create mode 100644 Minecraft.Server/vendor/rapidjson/rapidjson.h create mode 100644 Minecraft.Server/vendor/rapidjson/reader.h create mode 100644 Minecraft.Server/vendor/rapidjson/schema.h create mode 100644 Minecraft.Server/vendor/rapidjson/stream.h create mode 100644 Minecraft.Server/vendor/rapidjson/stringbuffer.h create mode 100644 Minecraft.Server/vendor/rapidjson/uri.h create mode 100644 Minecraft.Server/vendor/rapidjson/writer.h diff --git a/.gitignore b/.gitignore index 76482abb..845e509a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ /Minecraft.Client/x64_* /Minecraft.Client/*Debug /Minecraft.Client/*Release* +/Minecraft.Client/*Server* /Minecraft.Client/*ContentPackage* /Minecraft.Client/Windows64/GameHDD @@ -28,6 +29,14 @@ /Minecraft.Client/PS3/SPU_Tasks/*/Release /Minecraft.Client/PS3/SPU_Tasks/*/ContentPackage +# Minecraft.Server +/Minecraft.Server/x64 +/Minecraft.Server/x64_* +/Minecraft.Server/*Debug +/Minecraft.Server/*Release* +/Minecraft.Server/*Server* +/Minecraft.Server/*ContentPackage* + # Misc *.ppu.o *.pkg diff --git a/Minecraft.Client/ClientConnection.cpp b/Minecraft.Client/ClientConnection.cpp index b7e7f6dd..9afb35de 100644 --- a/Minecraft.Client/ClientConnection.cpp +++ b/Minecraft.Client/ClientConnection.cpp @@ -303,6 +303,10 @@ void ClientConnection::handleLogin(shared_ptr packet) level->isClientSide = true; minecraft->setLevel(level); } + else + { + level = (MultiPlayerLevel *)dimensionLevel; + } minecraft->player->setPlayerIndex( packet->m_playerIndex ); minecraft->player->setCustomSkin( app.GetPlayerSkinId(m_userIndex) ); diff --git a/Minecraft.Client/Common/Console_Utils.cpp b/Minecraft.Client/Common/Console_Utils.cpp index 96aa38f3..a0eb45ae 100644 --- a/Minecraft.Client/Common/Console_Utils.cpp +++ b/Minecraft.Client/Common/Console_Utils.cpp @@ -1,15 +1,27 @@ #include "stdafx.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "../../Minecraft.Server/ServerLogManager.h" +#endif + //-------------------------------------------------------------------------------------- // Name: DebugSpewV() // Desc: Internal helper function //-------------------------------------------------------------------------------------- #ifndef _CONTENT_PACKAGE -static VOID DebugSpewV( const CHAR* strFormat, const va_list pArgList ) +static VOID DebugSpewV( const CHAR* strFormat, va_list pArgList ) { #if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ assert(0); #else +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + // Dedicated server routes legacy debug spew through ServerLogger to preserve CLI prompt handling. + if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs()) + { + ServerRuntime::ServerLogManager::ForwardClientDebugSpewLogV(strFormat, pArgList); + return; + } +#endif CHAR str[2048]; // Use the secure CRT to avoid buffer overruns. Specify a count of // _TRUNCATE so that too long strings will be silently truncated diff --git a/Minecraft.Client/Common/Consoles_App.cpp b/Minecraft.Client/Common/Consoles_App.cpp index 56a374d7..eec51e73 100644 --- a/Minecraft.Client/Common/Consoles_App.cpp +++ b/Minecraft.Client/Common/Consoles_App.cpp @@ -30,6 +30,9 @@ #include "GameRules\ConsoleGameRules.h" #include "GameRules\ConsoleSchematicFile.h" #include "..\User.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\..\Minecraft.Server\ServerLogManager.h" +#endif #include "..\\EntityRenderDispatcher.h" #include "..\TexturePackRepository.h" #include "..\DLCTexturePack.h" @@ -230,12 +233,21 @@ void CMinecraftApp::DebugPrintf(const char *szFormat, ...) { #ifndef _FINAL_BUILD - char buf[1024]; - va_list ap; - va_start(ap, szFormat); - vsnprintf(buf, sizeof(buf), szFormat, ap); - va_end(ap); - OutputDebugStringA(buf); + va_list ap; + va_start(ap, szFormat); +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + // Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe. + if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs()) + { + ServerRuntime::ServerLogManager::ForwardClientAppDebugLogV(szFormat, ap); + va_end(ap); + return; + } +#endif + char buf[1024]; + vsnprintf(buf, sizeof(buf), szFormat, ap); + va_end(ap); + OutputDebugStringA(buf); #endif } @@ -243,13 +255,22 @@ void CMinecraftApp::DebugPrintf(const char *szFormat, ...) void CMinecraftApp::DebugPrintf(int user, const char *szFormat, ...) { #ifndef _FINAL_BUILD - if(user == USER_NONE) - return; - char buf[1024]; - va_list ap; - va_start(ap, szFormat); - vsnprintf(buf, sizeof(buf), szFormat, ap); - va_end(ap); + if(user == USER_NONE) + return; + va_list ap; + va_start(ap, szFormat); +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + // Dedicated server routes client debug spew through ServerLogger so CLI output stays prompt-safe. + if (ServerRuntime::ServerLogManager::ShouldForwardClientDebugLogs()) + { + ServerRuntime::ServerLogManager::ForwardClientUserDebugLogV(user, szFormat, ap); + va_end(ap); + return; + } +#endif + char buf[1024]; + vsnprintf(buf, sizeof(buf), szFormat, ap); + va_end(ap); #ifdef __PS3__ unsigned int writelen; sys_tty_write(SYS_TTYP_USER1 + ( user - 1 ), buf, strlen(buf), &writelen ); diff --git a/Minecraft.Client/Common/Network/GameNetworkManager.cpp b/Minecraft.Client/Common/Network/GameNetworkManager.cpp index f422c3a0..58a29617 100644 --- a/Minecraft.Client/Common/Network/GameNetworkManager.cpp +++ b/Minecraft.Client/Common/Network/GameNetworkManager.cpp @@ -358,12 +358,24 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame // PRIMARY PLAYER vector createdConnections; - ClientConnection *connection; + ClientConnection *connection = NULL; if( g_NetworkManager.IsHost() ) +#ifdef MINECRAFT_SERVER_BUILD + { + app.DebugPrintf("Dedicated server mode: skipping local host client connection\n"); + + // Keep telemetry behavior consistent with the host path. + INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId(); + TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId); + + app.SetGameMode( eMode_Multiplayer ); + } +#else { connection = new ClientConnection(minecraft, NULL); } +#endif else { INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(ProfileManager.GetLockedProfile()); @@ -389,155 +401,159 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame connection = new ClientConnection(minecraft, socket); } - if( !connection->createdOk ) + if (connection != NULL) { - assert(false); - delete connection; - connection = NULL; - MinecraftServer::HaltServer(); - return false; - } - - connection->send( shared_ptr( new PreLoginPacket(minecraft->user->name) ) ); - - // Tick connection until we're ready to go. The stages involved in this are: - // (1) Creating the ClientConnection sends a prelogin packet to the server - // (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet - // (3) the server sends a login back, which is handled by the client connection to start the game - if( !g_NetworkManager.IsHost() ) - { - Minecraft::GetInstance()->progressRenderer->progressStart(IDS_PROGRESS_CONNECTING); - } - else - { - // 4J Stu - Host needs to generate a unique multiplayer id for sentient telemetry reporting - INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId(); - TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId); - } - TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); - do - { - app.DebugPrintf("ticking connection A\n"); - connection->tick(); - - // 4J Stu - We were ticking this way too fast which could cause the connection to time out - // The connections should tick at 20 per second - Sleep(50); - } while ( (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame()) || tPack->isLoadingData() || (Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()) ); - ui.CleanUpSkinReload(); - - // 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash - // We need to break out of the above loop if m_bLeavingGame is set, and close the connection - if( g_NetworkManager.IsLeavingGame() || !IsInSession() ) - { - connection->close(); - } - - if( connection->isStarted() && !connection->isClosed() ) - { - createdConnections.push_back( connection ); - - int primaryPad = ProfileManager.GetPrimaryPad(); - app.SetRichPresenceContext(primaryPad,CONTEXT_GAME_STATE_BLANK); - if (GetPlayerCount() > 1) // Are we offline or online, and how many players are there + if( !connection->createdOk ) { - if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false); - else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER,false); + assert(false); + delete connection; + connection = NULL; + MinecraftServer::HaltServer(); + return false; + } + + connection->send( shared_ptr( new PreLoginPacket(minecraft->user->name) ) ); + + // Tick connection until we're ready to go. The stages involved in this are: + // (1) Creating the ClientConnection sends a prelogin packet to the server + // (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet + // (3) the server sends a login back, which is handled by the client connection to start the game + if( !g_NetworkManager.IsHost() ) + { + Minecraft::GetInstance()->progressRenderer->progressStart(IDS_PROGRESS_CONNECTING); } else { - if(IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,false); - else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1P,false); + // 4J Stu - Host needs to generate a unique multiplayer id for sentient telemetry reporting + INT multiplayerInstanceId = TelemetryManager->GenerateMultiplayerInstanceId(); + TelemetryManager->SetMultiplayerInstanceId(multiplayerInstanceId); + } + TexturePack *tPack = Minecraft::GetInstance()->skins->getSelected(); + do + { + app.DebugPrintf("ticking connection A\n"); + connection->tick(); + + // 4J Stu - We were ticking this way too fast which could cause the connection to time out + // The connections should tick at 20 per second + Sleep(50); + } while ( (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame()) || tPack->isLoadingData() || (Minecraft::GetInstance()->skins->needsUIUpdate() || ui.IsReloadingSkin()) ); + ui.CleanUpSkinReload(); + + // 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash + // We need to break out of the above loop if m_bLeavingGame is set, and close the connection + if( g_NetworkManager.IsLeavingGame() || !IsInSession() ) + { + connection->close(); } - - // ALL OTHER LOCAL PLAYERS - for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + if( connection->isStarted() && !connection->isClosed() ) { - // Already have setup the primary pad - if(idx == ProfileManager.GetPrimaryPad() ) continue; - - if( GetLocalPlayerByUserIndex(idx) != NULL && !ProfileManager.IsSignedIn(idx) ) - { - INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx); - Socket *socket = pNetworkPlayer->GetSocket(); - app.DebugPrintf("Closing socket due to player %d not being signed in any more\n"); - if( !socket->close(false) ) socket->close(true); - - continue; - } - - // By default when we host we only have the local player, but currently allow multiple local players to join - // when joining any other way, so just because they are signed in doesn't mean they are in the session - // 4J Stu - If they are in the session, then we should add them to the game. Otherwise we won't be able to add them later - INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx); - if( pNetworkPlayer == NULL ) - continue; - - ClientConnection *connection; - - Socket *socket = pNetworkPlayer->GetSocket(); - connection = new ClientConnection(minecraft, socket, idx); - - minecraft->addPendingLocalConnection(idx, connection); - //minecraft->createExtraLocalPlayer(idx, (convStringToWstring( ProfileManager.GetGamertag(idx) )).c_str(), idx, connection); - - // Open the socket on the server end to accept incoming data - Socket::addIncomingSocket(socket); - - connection->send( shared_ptr( new PreLoginPacket(convStringToWstring( ProfileManager.GetGamertag(idx) )) ) ); - createdConnections.push_back( connection ); - // Tick connection until we're ready to go. The stages involved in this are: - // (1) Creating the ClientConnection sends a prelogin packet to the server - // (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet - // (3) the server sends a login back, which is handled by the client connection to start the game - do + int primaryPad = ProfileManager.GetPrimaryPad(); + app.SetRichPresenceContext(primaryPad,CONTEXT_GAME_STATE_BLANK); + if (GetPlayerCount() > 1) // Are we offline or online, and how many players are there { - // We need to keep ticking the connections for players that already logged in - for(AUTO_VAR(it, createdConnections.begin()); it < createdConnections.end(); ++it) - { - (*it)->tick(); - } - - // 4J Stu - We were ticking this way too fast which could cause the connection to time out - // The connections should tick at 20 per second - Sleep(50); - app.DebugPrintf("<***> %d %d %d %d %d\n",IsInSession(), !connection->isStarted(),!connection->isClosed(),ProfileManager.IsSignedIn(idx),!g_NetworkManager.IsLeavingGame()); -#if defined _XBOX || defined __PS3__ && !defined DISABLE_PSN - } while (IsInSession() && !connection->isStarted() && !connection->isClosed() && ProfileManager.IsSignedIn(idx) && !g_NetworkManager.IsLeavingGame() ); -#else - // TODO - This SHOULD be something just like the code above but temporarily changing here so that we don't have to depend on the profilemanager behaviour - } while (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame() ); -#endif - - // 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash - // We need to break out of the above loop if m_bLeavingGame is set, and stop creating new connections - // The connections in the createdConnections vector get closed at the end of the thread - if( g_NetworkManager.IsLeavingGame() || !IsInSession() ) break; - - if( ProfileManager.IsSignedIn(idx) && !connection->isClosed() ) - { - app.SetRichPresenceContext(idx,CONTEXT_GAME_STATE_BLANK); - if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false); - else ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYER,false); + if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false); + else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER,false); } else { - connection->close(); - AUTO_VAR(it, find( createdConnections.begin(), createdConnections.end(), connection )); - if(it != createdConnections.end() ) createdConnections.erase( it ); + if(IsLocalGame()) ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1POFFLINE,false); + else ProfileManager.SetCurrentGameActivity(primaryPad,CONTEXT_PRESENCE_MULTIPLAYER_1P,false); } - } - app.SetGameMode( eMode_Multiplayer ); - } - else if ( connection->isClosed() || !IsInSession()) - { -// assert(false); - MinecraftServer::HaltServer(); - return false; + + // ALL OTHER LOCAL PLAYERS + for(int idx = 0; idx < XUSER_MAX_COUNT; ++idx) + { + // Already have setup the primary pad + if(idx == ProfileManager.GetPrimaryPad() ) continue; + + if( GetLocalPlayerByUserIndex(idx) != NULL && !ProfileManager.IsSignedIn(idx) ) + { + INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx); + Socket *socket = pNetworkPlayer->GetSocket(); + app.DebugPrintf("Closing socket due to player %d not being signed in any more\n"); + if( !socket->close(false) ) socket->close(true); + + continue; + } + + // By default when we host we only have the local player, but currently allow multiple local players to join + // when joining any other way, so just because they are signed in doesn't mean they are in the session + // 4J Stu - If they are in the session, then we should add them to the game. Otherwise we won't be able to add them later + INetworkPlayer *pNetworkPlayer = g_NetworkManager.GetLocalPlayerByUserIndex(idx); + if( pNetworkPlayer == NULL ) + continue; + + ClientConnection *connection; + + Socket *socket = pNetworkPlayer->GetSocket(); + connection = new ClientConnection(minecraft, socket, idx); + + minecraft->addPendingLocalConnection(idx, connection); + //minecraft->createExtraLocalPlayer(idx, (convStringToWstring( ProfileManager.GetGamertag(idx) )).c_str(), idx, connection); + + // Open the socket on the server end to accept incoming data + Socket::addIncomingSocket(socket); + + connection->send( shared_ptr( new PreLoginPacket(convStringToWstring( ProfileManager.GetGamertag(idx) )) ) ); + + createdConnections.push_back( connection ); + + // Tick connection until we're ready to go. The stages involved in this are: + // (1) Creating the ClientConnection sends a prelogin packet to the server + // (2) the server sends a prelogin back, which is handled by the clientConnection, and returns a login packet + // (3) the server sends a login back, which is handled by the client connection to start the game + do + { + // We need to keep ticking the connections for players that already logged in + for(AUTO_VAR(it, createdConnections.begin()); it < createdConnections.end(); ++it) + { + (*it)->tick(); + } + + // 4J Stu - We were ticking this way too fast which could cause the connection to time out + // The connections should tick at 20 per second + Sleep(50); + app.DebugPrintf("<***> %d %d %d %d %d\n",IsInSession(), !connection->isStarted(),!connection->isClosed(),ProfileManager.IsSignedIn(idx),!g_NetworkManager.IsLeavingGame()); + #if defined _XBOX || defined __PS3__ && !defined DISABLE_PSN + } while (IsInSession() && !connection->isStarted() && !connection->isClosed() && ProfileManager.IsSignedIn(idx) && !g_NetworkManager.IsLeavingGame() ); + #else + // TODO - This SHOULD be something just like the code above but temporarily changing here so that we don't have to depend on the profilemanager behaviour + } while (IsInSession() && !connection->isStarted() && !connection->isClosed() && !g_NetworkManager.IsLeavingGame() ); + #endif + + // 4J Stu - Fix for #11279 - CRASH: TCR 001: BAS Game Stability: Signing out of game will cause title to crash + // We need to break out of the above loop if m_bLeavingGame is set, and stop creating new connections + // The connections in the createdConnections vector get closed at the end of the thread + if( g_NetworkManager.IsLeavingGame() || !IsInSession() ) break; + + if( ProfileManager.IsSignedIn(idx) && !connection->isClosed() ) + { + app.SetRichPresenceContext(idx,CONTEXT_GAME_STATE_BLANK); + if (IsLocalGame()) ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYEROFFLINE,false); + else ProfileManager.SetCurrentGameActivity(idx,CONTEXT_PRESENCE_MULTIPLAYER,false); + } + else + { + connection->close(); + AUTO_VAR(it, find( createdConnections.begin(), createdConnections.end(), connection )); + if(it != createdConnections.end() ) createdConnections.erase( it ); + } + } + + app.SetGameMode( eMode_Multiplayer ); + } + else if ( connection->isClosed() || !IsInSession()) + { + // assert(false); + + MinecraftServer::HaltServer(); + return false; + } } @@ -547,7 +563,7 @@ bool CGameNetworkManager::StartNetworkGame(Minecraft *minecraft, LPVOID lpParame { (*it)->close(); } -// assert(false); + //assert(false); MinecraftServer::HaltServer(); return false; } diff --git a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp index fba5a944..af499760 100644 --- a/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp +++ b/Minecraft.Client/Common/Network/NetworkSocketLayer.cpp @@ -1,8 +1,17 @@ #include "stdafx.h" +#include #include "NetworkSocketLayer.h" #include "..\..\Common\Network\PlatformNetworkManager.h" #include "..\..\..\Minecraft.World\Socket.h" +#if defined(MINECRAFT_SERVER_BUILD) +#include "..\..\..\Minecraft.Server\Access\Access.h" +#include "..\..\..\Minecraft.Server\ServerLogManager.h" +#endif + +#if defined(MINECRAFT_SERVER_BUILD) +static bool TryGetNumericRemoteIp(const sockaddr_in &remoteAddress, std::string *outIp); +#endif SOCKET NetworkSocketLayer::s_listenSocket = INVALID_SOCKET; SOCKET NetworkSocketLayer::s_hostConnectionSocket = INVALID_SOCKET; @@ -55,23 +64,26 @@ std::vector NetworkSocketLayer::s_discoveredSessions; CRITICAL_SECTION NetworkSocketLayer::s_disconnectLock; std::vector NetworkSocketLayer::s_disconnectedSmallIds; -CRITICAL_SECTION NetworkSocketLayer::s_pendingJoinLock; -std::vector NetworkSocketLayer::s_pendingJoinSmallIds; - CRITICAL_SECTION NetworkSocketLayer::s_freeSmallIdLock; std::vector NetworkSocketLayer::s_freeSmallIds; // only goes true on a successful Initialize(), and a failed one gets retried static bool s_locksCreated = false; -#if defined _WINDOWS64 +#if !defined _WINDOWS64 bool g_MultiplayerHost = true; bool g_MultiplayerJoin = false; #else bool g_MultiplayerHost = false; bool g_MultiplayerJoin = true; #endif + +#ifdef MINECRAFT_SERVER_BUILD +int g_DedicatedServerPort = NETWORK_LAN_DEFAULT_PORT; +char g_DedicatedServerBindIP[256] = ""; +bool g_DedicatedServerLanAdvertise = true; +#endif int g_MultiplayerPort = NETWORK_LAN_DEFAULT_PORT; -char g_MultiplayerIP[256] = "192.168.0.69"; +char g_MultiplayerIP[256] = "192.168.8.27"; bool NetworkSocketLayer::Initialize() @@ -86,7 +98,6 @@ bool NetworkSocketLayer::Initialize() InitializeCriticalSection(&s_advertiseLock); InitializeCriticalSection(&s_discoveryLock); InitializeCriticalSection(&s_disconnectLock); - InitializeCriticalSection(&s_pendingJoinLock); InitializeCriticalSection(&s_freeSmallIdLock); for (int i = 0; i < NETWORK_LAN_MAX_CLIENTS + 1; i++) @@ -139,7 +150,10 @@ bool NetworkSocketLayer::Initialize() s_initialized = true; + // Dedicated Server does not use LAN session discovery and therefore does not initiate discovery. +#ifndef MINECRAFT_SERVER_BUILD StartDiscovery(); +#endif return true; } @@ -224,8 +238,6 @@ void NetworkSocketLayer::Shutdown() DeleteCriticalSection(&s_discoveryLock); DeleteCriticalSection(&s_disconnectLock); s_disconnectedSmallIds.clear(); - DeleteCriticalSection(&s_pendingJoinLock); - s_pendingJoinSmallIds.clear(); DeleteCriticalSection(&s_freeSmallIdLock); s_freeSmallIds.clear(); s_locksCreated = false; @@ -799,6 +811,12 @@ bool NetworkSocketLayer::JoinGame(const char *ip, int port) { return false; } + + // Clear the recv timeout now that the handshake is complete. + // The recv thread should block indefinitely waiting for data. + DWORD noTimeout = 0; + setsockopt(s_hostConnectionSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&noTimeout, sizeof(noTimeout)); + s_localSmallId = assignedSmallId; app.DebugPrintf("LAN: Connected to %s:%d, assigned smallId=%d\n", ip, port, s_localSmallId); @@ -907,6 +925,20 @@ SOCKET NetworkSocketLayer::GetSocketForSmallId(BYTE smallId) #endif } +// Send reject handshake: sentinel 0xFF + DisconnectPacket wire format (1 byte id 255 + 4 byte big-endian reason). Then caller closes socket. +static void SendRejectWithReason(SOCKET clientSocket, DisconnectPacket::eDisconnectReason reason) +{ + BYTE buf[6]; + buf[0] = NETWORK_LAN_SMALLID_REJECT; + buf[1] = (BYTE)255; // DisconnectPacket packet id + int r = (int)reason; + buf[2] = (BYTE)((r >> 24) & 0xff); + buf[3] = (BYTE)((r >> 16) & 0xff); + buf[4] = (BYTE)((r >> 8) & 0xff); + buf[5] = (BYTE)(r & 0xff); + send(clientSocket, (const char*)buf, sizeof(buf), 0); +} + static bool RecvExact(SOCKET sock, BYTE *buf, int len) { int totalRecv = 0; @@ -919,6 +951,27 @@ static bool RecvExact(SOCKET sock, BYTE *buf, int len) return true; } +#if defined(MINECRAFT_SERVER_BUILD) +static bool TryGetNumericRemoteIp(const sockaddr_in &remoteAddress, std::string *outIp) +{ + if (outIp == nullptr) + { + return false; + } + + outIp->clear(); + char ipBuffer[64] = {}; + const char *ip = inet_ntop(AF_INET, (void *)&remoteAddress.sin_addr, ipBuffer, sizeof(ipBuffer)); + if (ip == nullptr || ip[0] == 0) + { + return false; + } + + *outIp = ip; + return true; +} +#endif + void NetworkSocketLayer::HandleDataReceived(BYTE fromSmallId, BYTE toSmallId, unsigned char *data, unsigned int dataSize) { INetworkPlayer *pPlayerFrom = g_NetworkManager.GetPlayerBySmallId(fromSmallId); @@ -951,9 +1004,15 @@ int NetworkSocketLayer::AcceptThreadProc(LPVOID param) while (s_active) { #ifdef __PSVITA__ - SOCKET clientSocket = accept(s_listenSocket, (SceNetSockaddr *)NULL, NULL); + sockaddr_in remoteAddress; + ZeroMemory(&remoteAddress, sizeof(remoteAddress)); + int remoteAddressLength = sizeof(remoteAddress); + SOCKET clientSocket = accept(s_listenSocket, (SceNetSockaddr *)&remoteAddress, &remoteAddressLength); #else - SOCKET clientSocket = accept(s_listenSocket, NULL, NULL); + sockaddr_in remoteAddress; + ZeroMemory(&remoteAddress, sizeof(remoteAddress)); + int remoteAddressLength = sizeof(remoteAddress); + SOCKET clientSocket = accept(s_listenSocket, (sockaddr*)&remoteAddress, &remoteAddressLength); #endif #if defined _WINDOWS64 || defined _XBOX if (clientSocket == INVALID_SOCKET) @@ -974,10 +1033,28 @@ int NetworkSocketLayer::AcceptThreadProc(LPVOID param) int noDelay = 1; setsockopt(clientSocket, IPPROTO_TCP, TCP_NODELAY, (const char *)&noDelay, sizeof(noDelay)); - extern QNET_STATE _iQNetStubState; - if (_iQNetStubState != QNET_STATE_GAME_PLAY && _iQNetStubState != QNET_STATE_SESSION_STARTING) +#if defined(MINECRAFT_SERVER_BUILD) + std::string remoteIp; + const bool hasRemoteIp = TryGetNumericRemoteIp(remoteAddress, &remoteIp); + const char *remoteIpForLog = hasRemoteIp ? remoteIp.c_str() : "unknown"; + ServerRuntime::ServerLogManager::OnIncomingTcpConnection(remoteIpForLog); + if (hasRemoteIp && ServerRuntime::Access::IsIpBanned(remoteIp)) { + ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_BannedIp); + SendRejectWithReason(clientSocket, DisconnectPacket::eDisconnect_Banned); + closesocket(clientSocket); + continue; + } +#endif + + extern QNET_STATE _iQNetStubState; + if (_iQNetStubState != QNET_STATE_GAME_PLAY) + { +#if defined(MINECRAFT_SERVER_BUILD) + ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_GameNotReady); +#else app.DebugPrintf("Win64 LAN: Rejecting connection, game not ready\n"); +#endif closesocket(clientSocket); continue; } @@ -996,7 +1073,11 @@ int NetworkSocketLayer::AcceptThreadProc(LPVOID param) else { LeaveCriticalSection(&s_freeSmallIdLock); +#if defined(MINECRAFT_SERVER_BUILD) + ServerRuntime::ServerLogManager::OnRejectedTcpConnection(remoteIpForLog, ServerRuntime::ServerLogManager::eTcpRejectReason_ServerFull); +#else app.DebugPrintf("Win64 LAN: Server full, rejecting connection\n"); +#endif closesocket(clientSocket); continue; } @@ -1026,16 +1107,19 @@ int NetworkSocketLayer::AcceptThreadProc(LPVOID param) conn.active = true; LeaveCriticalSection(&s_connectionsLock); +#if defined(MINECRAFT_SERVER_BUILD) + ServerRuntime::ServerLogManager::OnAcceptedTcpConnection(assignedSmallId, remoteIpForLog); +#else app.DebugPrintf("Win64 LAN: Client connected, assigned smallId=%d\n", assignedSmallId); +#endif - IQNetPlayer *qnetPlayer = &IQNet::m_player[assignedSmallId]; + IQNetPlayer* qnetPlayer = &IQNet::m_player[assignedSmallId]; - extern void Win64_SetupRemoteQNetPlayer(IQNetPlayer *player, BYTE smallId, bool isHost, bool isLocal); + extern void Win64_SetupRemoteQNetPlayer(IQNetPlayer * player, BYTE smallId, bool isHost, bool isLocal); Win64_SetupRemoteQNetPlayer(qnetPlayer, assignedSmallId, false, false); - EnterCriticalSection(&s_pendingJoinLock); - s_pendingJoinSmallIds.push_back(assignedSmallId); - LeaveCriticalSection(&s_pendingJoinLock); + extern CPlatformNetworkManagerStub* g_pPlatformNetworkManager; + g_pPlatformNetworkManager->NotifyPlayerJoined(qnetPlayer); DWORD *threadParam = new DWORD; *threadParam = assignedSmallId; @@ -1142,20 +1226,6 @@ void NetworkSocketLayer::PushFreeSmallId(BYTE smallId) LeaveCriticalSection(&s_freeSmallIdLock); } -bool NetworkSocketLayer::PopPendingJoinSmallId(BYTE *outSmallId) -{ - bool found = false; - EnterCriticalSection(&s_pendingJoinLock); - if (!s_pendingJoinSmallIds.empty()) - { - *outSmallId = s_pendingJoinSmallIds.back(); - s_pendingJoinSmallIds.pop_back(); - found = true; - } - LeaveCriticalSection(&s_pendingJoinLock); - return found; -} - bool NetworkSocketLayer::IsSmallIdConnected(BYTE smallId) { if (smallId >= NETWORK_LAN_MAX_CLIENTS + 1) return false; diff --git a/Minecraft.Client/Common/Network/NetworkSocketLayer.h b/Minecraft.Client/Common/Network/NetworkSocketLayer.h index 908a4b61..bb798905 100644 --- a/Minecraft.Client/Common/Network/NetworkSocketLayer.h +++ b/Minecraft.Client/Common/Network/NetworkSocketLayer.h @@ -34,6 +34,7 @@ #define NETWORK_LAN_DEFAULT_PORT 25565 #define NETWORK_LAN_MAX_CLIENTS 7 +#define NETWORK_LAN_SMALLID_REJECT 0xFF #define NETWORK_LAN_RECV_BUFFER_SIZE 65536 #define NETWORK_LAN_MAX_PACKET_SIZE (3 * 1024 * 1024) #define NETWORK_LAN_DISCOVERY_PORT 25566 @@ -172,8 +173,6 @@ public: static void PushFreeSmallId(BYTE smallId); static void CloseConnectionBySmallId(BYTE smallId); - static bool PopPendingJoinSmallId(BYTE *outSmallId); - static bool IsSmallIdConnected(BYTE smallId); static bool StartAdvertising(int gamePort, const wchar_t *hostName, unsigned int gameSettings, unsigned int texPackId, unsigned char subTexId, unsigned short netVer); @@ -230,9 +229,6 @@ private: static CRITICAL_SECTION s_disconnectLock; static std::vector s_disconnectedSmallIds; - static CRITICAL_SECTION s_pendingJoinLock; - static std::vector s_pendingJoinSmallIds; - static CRITICAL_SECTION s_freeSmallIdLock; static std::vector s_freeSmallIds; }; @@ -241,3 +237,9 @@ extern bool g_MultiplayerHost; extern bool g_MultiplayerJoin; extern int g_MultiplayerPort; extern char g_MultiplayerIP[256]; + +#ifdef MINECRAFT_SERVER_BUILD +extern int g_DedicatedServerPort; +extern char g_DedicatedServerBindIP[256]; +extern bool g_DedicatedServerLanAdvertise; +#endif \ No newline at end of file diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp b/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp index 968ff578..84d51204 100644 --- a/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp +++ b/Minecraft.Client/Common/Network/PlatformNetworkManager.cpp @@ -171,6 +171,7 @@ bool CPlatformNetworkManagerStub::Initialise(CGameNetworkManager *pGameNetworkMa m_bLeavingGame = false; m_bLeaveGameOnTick = false; + m_bLeaveGameOnTick = false; m_bHostChanged = false; m_bSearchResultsReady = false; @@ -233,53 +234,56 @@ void CPlatformNetworkManagerStub::DoWork() BYTE disconnectedSmallId; while (NetworkSocketLayer::PopDisconnectedSmallId(&disconnectedSmallId)) { - if (disconnectedSmallId == 0) continue; - if (NetworkSocketLayer::IsSmallIdConnected(disconnectedSmallId)) continue; - IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(disconnectedSmallId); - if (qnetPlayer != NULL && qnetPlayer->m_smallId == disconnectedSmallId) + IQNetPlayer* qnetPlayer = m_pIQNet->GetPlayerBySmallId(disconnectedSmallId); + if (qnetPlayer != nullptr && qnetPlayer->m_smallId == disconnectedSmallId) { NotifyPlayerLeaving(qnetPlayer); qnetPlayer->m_smallId = 0; qnetPlayer->m_isRemote = false; qnetPlayer->m_isHostPlayer = false; + //qnetPlayer->m_resolvedXuid = INVALID_XUID; // TODO: implement qnetPlayer->m_gamertag[0] = 0; qnetPlayer->SetCustomDataValue(0); - NetworkSocketLayer::PushFreeSmallId(disconnectedSmallId); - if (IQNet::s_playerCount > 1) + // Recalculate s_playerCount as the highest active slot + 1. + // A blind decrement would hide players at higher-indexed slots when a + // lower-indexed player disconnects first: GetPlayerBySmallId scans + // [0, s_playerCount) so any slot at or above the decremented count + // becomes invisible, causing its disconnect to be missed (ghost player). + while (IQNet::s_playerCount > 1 && + IQNet::m_player[IQNet::s_playerCount - 1].GetCustomDataValue() == 0) IQNet::s_playerCount--; } - } - - BYTE joinedSmallId; - while (NetworkSocketLayer::PopPendingJoinSmallId(&joinedSmallId)) - { - IQNetPlayer *qnetPlayer = m_pIQNet->GetPlayerBySmallId(joinedSmallId); - if (qnetPlayer != NULL && qnetPlayer->m_smallId == joinedSmallId) - { - NotifyPlayerJoined(qnetPlayer); - } - } - - for (int i = 1; i < MINECRAFT_NET_MAX_PLAYERS; i++) - { - IQNetPlayer *qp = &IQNet::m_player[i]; - if (qp->GetCustomDataValue() == 0) - continue; - INetworkPlayer *np = (INetworkPlayer *)qp->GetCustomDataValue(); - Socket *sock = np->GetSocket(); - if (sock != NULL && sock->isClosing()) - { - NetworkSocketLayer::CloseConnectionBySmallId((BYTE)i); - } + // NOTE: Do NOT call PushFreeSmallId here. The old PlayerConnection's + // write thread may still be alive (it dies in PlayerList::tick when + // m_smallIdsToClose is processed). If we recycle the smallId now, + // AcceptThread can reuse it for a new connection, and the old write + // thread's getPlayer() lookup will resolve to the NEW player, sending + // stale game packets to the new client's TCP socket — corrupting its + // login handshake (bad packet id crash). PushFreeSmallId and + // ClearSocketForSmallId are called from PlayerList::tick after the + // old Connection threads are dead. + // + // Clear chunk visibility flags for this system so rejoin gets fresh chunk state. + SystemFlagRemoveBySmallId(disconnectedSmallId); } } - if (_iQNetStubState == QNET_STATE_GAME_PLAY && !m_pIQNet->IsHost()) + + // Client-side host disconnect detection: + // if TCP is gone, propagate through normal network-disconnect flow so UI returns to menus. + // The processing from the Xbox version will be reused. + if (_iQNetStubState == QNET_STATE_GAME_PLAY && !m_pIQNet->IsHost() && !m_bLeavingGame) { - if (!NetworkSocketLayer::IsConnected() && !g_NetworkManager.IsLeavingGame()) + if (!NetworkSocketLayer::IsConnected()) { - if (app.GetDisconnectReason() == DisconnectPacket::eDisconnect_None) - app.SetDisconnectReason(DisconnectPacket::eDisconnect_Quitting); - app.SetAction(ProfileManager.GetPrimaryPad(), eAppAction_ExitWorld, (void *)TRUE); + if (!m_bLeaveGameOnTick) + { + m_bLeaveGameOnTick = true; + g_NetworkManager.HandleDisconnect(false); + } + } + else + { + m_bLeaveGameOnTick = false; } } } @@ -354,6 +358,7 @@ bool CPlatformNetworkManagerStub::LeaveGame(bool bMigrateHost) if( m_bLeavingGame ) return true; m_bLeavingGame = true; + m_bLeaveGameOnTick = false; NetworkSocketLayer::StopAdvertising(); // If we are the host wait for the game server to end @@ -397,6 +402,7 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame, localUsersMask |= GetLocalPlayerMask( g_NetworkManager.GetPrimaryPad() ); m_bLeavingGame = false; + m_bLeaveGameOnTick = false; m_pIQNet->HostGame(); @@ -418,9 +424,25 @@ void CPlatformNetworkManagerStub::HostGame(int localUsersMask, bool bOnlineGame, if (!NetworkSocketLayer::IsActive()) NetworkSocketLayer::HostGame(port); - const wchar_t *hostName = IQNet::m_player[0].m_gamertag; - unsigned int settings = app.GetGameHostOption(eGameHostOption_All); - NetworkSocketLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION); + if (NetworkSocketLayer::IsActive()) + { + // For Dedicated Server, refer to `lan-advertise` in `server.properties` + bool enableLanAdvertising = true; +#ifdef MINECRAFT_SERVER_BUILD + enableLanAdvertising = g_DedicatedServerLanAdvertise; +#endif + + if (enableLanAdvertising) + { + const wchar_t* hostName = IQNet::m_player[0].m_gamertag; + unsigned int settings = app.GetGameHostOption(eGameHostOption_All); + NetworkSocketLayer::StartAdvertising(port, hostName, settings, 0, 0, MINECRAFT_NET_VERSION); + } + else + { + NetworkSocketLayer::StopAdvertising(); + } + } //#endif } @@ -626,6 +648,7 @@ CPlatformNetworkManagerStub::PlayerFlags::PlayerFlags(INetworkPlayer *pNetworkPl this->flags = new unsigned char [ count / 8 ]; memset( this->flags, 0, count / 8 ); this->count = count; + this->m_smallId = (pNetworkPlayer && pNetworkPlayer->IsLocal()) ? 256 : (pNetworkPlayer ? (int)pNetworkPlayer->GetSmallId() : -1); } CPlatformNetworkManagerStub::PlayerFlags::~PlayerFlags() { @@ -663,6 +686,23 @@ void CPlatformNetworkManagerStub::SystemFlagRemovePlayer(INetworkPlayer *pNetwor } } +// Clear chunk flags for a system when they disconnect (by smallId). Call even when we don't find the player, +// so we always clear and the smallId can be reused without stale "chunk seen" state. +void CPlatformNetworkManagerStub::SystemFlagRemoveBySmallId(int smallId) +{ + if (smallId < 0) return; + for (unsigned int i = 0; i < m_playerFlags.size(); i++) + { + if (m_playerFlags[i]->m_smallId == smallId) + { + delete m_playerFlags[i]; + m_playerFlags[i] = m_playerFlags.back(); + m_playerFlags.pop_back(); + return; + } + } +} + void CPlatformNetworkManagerStub::SystemFlagReset() { for( unsigned int i = 0; i < m_playerFlags.size(); i++ ) diff --git a/Minecraft.Client/Common/Network/PlatformNetworkManager.h b/Minecraft.Client/Common/Network/PlatformNetworkManager.h index 3db399e0..dc7653ec 100644 --- a/Minecraft.Client/Common/Network/PlatformNetworkManager.h +++ b/Minecraft.Client/Common/Network/PlatformNetworkManager.h @@ -99,12 +99,14 @@ private: INetworkPlayer *m_pNetworkPlayer; unsigned char *flags; unsigned int count; + int m_smallId; PlayerFlags(INetworkPlayer *pNetworkPlayer, unsigned int count); ~PlayerFlags(); }; vector m_playerFlags; void SystemFlagAddPlayer(INetworkPlayer *pNetworkPlayer); void SystemFlagRemovePlayer(INetworkPlayer *pNetworkPlayer); + void SystemFlagRemoveBySmallId(int smallId); void SystemFlagReset(); public: virtual void SystemFlagSet(INetworkPlayer *pNetworkPlayer, int index); diff --git a/Minecraft.Client/Common/Postbuilds/Server-postbuild.ps1 b/Minecraft.Client/Common/Postbuilds/Server-postbuild.ps1 new file mode 100644 index 00000000..d5d4e660 --- /dev/null +++ b/Minecraft.Client/Common/Postbuilds/Server-postbuild.ps1 @@ -0,0 +1,82 @@ +param( + [string]$OutDir, + [string]$ProjectDir +) + +Write-Host "Server Postbuild: script started. Output Directory: $OutDir, Project Directory: $ProjectDir" + +$directories = @( + "Windows64\GameHDD", + "Common\Media", + "Common\res", + "redist64" +) + +foreach ($dir in $directories) { + New-Item -ItemType Directory -Path (Join-Path $OutDir $dir) -Force | Out-Null +} + +$folderCopies = @( + @{ Source = "Common\Media"; Dest = "Common\Media" }, + @{ Source = "Common\res"; Dest = "Common\res" }, + @{ Source = "Common\Postbuilds\Contents\Windows64\redist64"; Dest = "redist64" } +) + +foreach ($copy in $folderCopies) { + $src = Join-Path "$ProjectDir\..\Minecraft.Client" $copy.Source + $dst = Join-Path $OutDir $copy.Dest + + if (Test-Path $src) { + xcopy /q /y /i /s /e /d "$src" "$dst" 2>$null + } +} + +$fileCopies = @( + @{ Source = "Common\Postbuilds\Contents\Windows64\Effects.msscmp"; Dest = "Effects.msscmp" }, + @{ Source = "Common\Postbuilds\Contents\Windows64\iggy_w64.dll"; Dest = "iggy_w64.dll" }, + @{ Source = "Common\Postbuilds\Contents\Windows64\mss64.dll"; Dest = "mss64.dll" } +) + +foreach ($copy in $fileCopies) { + $src = Join-Path "$ProjectDir\..\Minecraft.Client" $copy.Source + $dst = Join-Path $OutDir $copy.Dest + if (Test-Path $src) { + Copy-Item -Path $src -Destination $dst -Force + } +} + +$deleteDirs = @( + "Common\Media\Sound", + "Common\Media\Graphics", + "Common\Media\de-DE", + "Common\Media\es-ES", + "Common\Media\fr-FR", + "Common\Media\it-IT", + "Common\Media\ja-JP", + "Common\Media\ko-KR", + "Common\Media\pt-BR", + "Common\Media\pt-PT", + "Common\Media\zh-CHT" +) + +foreach ($dir in $deleteDirs) { + $path = Join-Path $OutDir $dir + if (Test-Path $path) { + Remove-Item $path -Recurse -Force -ErrorAction SilentlyContinue + } +} + +$delDirs = @( + "Common\Media", + "Common\Media\font" +) + +foreach ($dir in $delDirs) { + if ($delDirs -contains $dir) { + $path = Join-Path $OutDir $dir + if (Test-Path $path) { + Get-ChildItem -Path $path -Recurse -Include *.swf, *.txt, *.resx, *.xml, *.loc, *.lang, *.col, *.h, *.xui, *.abc, "Mojang Font_7.ttf", "Mojang Font_11.ttf", MediaDurango.arc, MediaOrbis.arc, MediaPS3.arc, MediaPSVita.arc -File | + Remove-Item -Force -ErrorAction SilentlyContinue + } + } +} \ No newline at end of file diff --git a/Minecraft.Client/Minecraft.Client.vcxproj b/Minecraft.Client/Minecraft.Client.vcxproj index df0dcdbb..9cfcd20f 100644 --- a/Minecraft.Client/Minecraft.Client.vcxproj +++ b/Minecraft.Client/Minecraft.Client.vcxproj @@ -145,6 +145,30 @@ Release Xbox 360 + + Server + Durango + + + Server + ORBIS + + + Server + PS3 + + + Server + PSVita + + + Server + x64 + + + Server + Xbox 360 + en-US @@ -350,8 +374,25 @@ Clang + v120 + + + SNC + + + + + Clang + + v110 + MultiByte + StaticLibrary + + + SNC + @@ -682,6 +723,12 @@ false false + + $(ProjectDir)\..\Minecraft.World\x64headers;$(ProjectDir)\Windows64\Sentient\Include;$(IncludePath) + true + $(ProjectDir)\$(Platform)_$(Configuration)\ + $(Platform)_$(Configuration)\ + Use @@ -2279,6 +2326,27 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CURun PSVita postbuild script + + + Windows64\Iggy\include;$(ProjectDir);%(AdditionalIncludeDirectories) + Level3 + true + Full + Speed + MINECRAFT_SERVER_BUILD;_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;%(PreprocessorDefinitions) + MultiThreaded + false + true + Use + $(OutDir)$(ProjectName).pch + + + false + d3d11.lib;..\Minecraft.World\x64_$(Configuration)\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;;XInput9_1_0.lib;..\Minecraft.Client\Windows64\Miles\Lib\mss64.lib;wsock32.lib;%(AdditionalDependencies) + true + $(OutDir)$(ProjectName).pdb + + @@ -2352,6 +2420,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -2395,6 +2464,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -2438,6 +2508,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -2482,6 +2553,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2525,6 +2597,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2568,6 +2641,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2612,6 +2686,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2655,6 +2730,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2699,6 +2775,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2732,6 +2809,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2765,6 +2843,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2802,6 +2881,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2840,6 +2920,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2878,6 +2959,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2914,6 +2996,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2950,6 +3033,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -2986,6 +3070,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3022,6 +3107,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3058,6 +3144,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3094,6 +3181,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3130,6 +3218,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3166,6 +3255,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3202,6 +3292,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3238,6 +3329,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3274,6 +3366,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -3312,6 +3405,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3348,6 +3442,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -3450,6 +3545,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3486,6 +3582,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3590,6 +3687,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3648,6 +3746,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3684,6 +3783,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -3728,6 +3828,8 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true + true @@ -3845,6 +3947,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -3878,6 +3981,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -3937,6 +4041,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3961,6 +4066,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -3982,6 +4088,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -4004,6 +4111,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -4033,6 +4141,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -4054,6 +4163,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -4083,6 +4193,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -4488,6 +4599,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -4741,6 +4853,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -5825,6 +5938,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -5867,6 +5981,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -5903,6 +6018,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -5939,6 +6055,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -5971,6 +6088,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -6013,6 +6131,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -6055,6 +6174,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -6097,6 +6217,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -6139,6 +6260,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -6171,6 +6293,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -6207,6 +6330,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -6239,6 +6363,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -6270,6 +6395,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -6528,6 +6654,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -6572,6 +6699,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -6617,6 +6745,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -6639,6 +6768,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -6680,6 +6810,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -6715,6 +6846,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -6750,6 +6882,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -6785,8 +6918,11 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true + + + true - true true @@ -6829,6 +6965,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -6866,6 +7003,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -6942,6 +7080,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -6977,6 +7116,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7012,6 +7152,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7047,6 +7188,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7082,6 +7224,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7117,6 +7260,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -7159,6 +7303,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -7201,6 +7346,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -7235,6 +7381,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7267,6 +7414,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -7301,6 +7449,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -7335,6 +7484,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -7369,6 +7519,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -7399,6 +7550,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7434,6 +7586,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7462,6 +7615,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7490,6 +7644,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7518,6 +7673,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7546,6 +7702,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7574,6 +7731,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7602,6 +7760,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -7631,6 +7790,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7674,6 +7834,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -7696,6 +7857,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -7738,6 +7900,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -7810,6 +7973,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -7871,6 +8035,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -7979,6 +8144,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8012,6 +8178,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8045,6 +8212,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8078,6 +8246,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8111,6 +8280,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8144,6 +8314,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8177,6 +8348,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8210,6 +8382,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8254,6 +8427,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8291,6 +8465,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8328,6 +8503,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8372,6 +8548,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8415,6 +8592,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8450,6 +8628,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8480,6 +8659,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8516,6 +8696,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8559,6 +8740,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8594,6 +8776,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8633,6 +8816,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8693,6 +8877,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8737,6 +8922,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8781,6 +8967,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8825,6 +9012,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8858,6 +9046,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8902,6 +9091,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8931,6 +9121,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -8969,6 +9160,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9013,6 +9205,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9057,6 +9250,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9101,6 +9295,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9145,6 +9340,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9189,6 +9385,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9233,6 +9430,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9277,6 +9475,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9321,6 +9520,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -9335,6 +9535,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -9361,6 +9562,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9385,6 +9587,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9409,6 +9612,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9433,6 +9637,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9462,6 +9667,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9500,6 +9706,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9586,6 +9793,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9622,6 +9830,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9658,6 +9867,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9694,6 +9904,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9722,6 +9933,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9753,6 +9965,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9788,6 +10001,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9816,6 +10030,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9843,6 +10058,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9856,6 +10072,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9893,6 +10110,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9929,6 +10147,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -9968,6 +10187,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse true true + true true @@ -10006,6 +10226,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10044,6 +10265,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10080,6 +10302,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10116,6 +10339,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -10192,6 +10416,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10242,6 +10467,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10269,6 +10495,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10307,6 +10534,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10343,6 +10571,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10373,6 +10602,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10409,6 +10639,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10445,6 +10676,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10481,6 +10713,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10518,6 +10751,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10555,6 +10789,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10592,6 +10827,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10629,6 +10865,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10666,6 +10903,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10703,6 +10941,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10740,6 +10979,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10777,6 +11017,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10814,6 +11055,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10851,6 +11093,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10888,6 +11131,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10925,6 +11169,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10962,6 +11207,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -10999,6 +11245,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11036,6 +11283,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11073,6 +11321,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11110,6 +11359,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11147,6 +11397,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11184,6 +11435,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11221,6 +11473,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11258,6 +11511,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11295,6 +11549,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11332,6 +11587,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11369,6 +11625,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11406,6 +11663,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11443,6 +11701,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11480,6 +11739,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11517,6 +11777,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11554,6 +11815,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11591,6 +11853,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11628,6 +11891,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11665,6 +11929,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11702,6 +11967,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11739,6 +12005,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11776,6 +12043,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11813,6 +12081,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11850,6 +12119,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11887,6 +12157,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11924,6 +12195,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11961,6 +12233,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -11998,6 +12271,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12035,6 +12309,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12072,6 +12347,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12109,6 +12385,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12146,6 +12423,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12183,6 +12461,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12220,6 +12499,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12257,6 +12537,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12294,6 +12575,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12331,6 +12613,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12368,6 +12651,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12405,6 +12689,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12442,6 +12727,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12479,6 +12765,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12516,6 +12803,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12553,6 +12841,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12590,6 +12879,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12627,6 +12917,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12664,6 +12955,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12701,6 +12993,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12738,6 +13031,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12775,6 +13069,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12812,6 +13107,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12849,6 +13145,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12886,6 +13183,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12923,6 +13221,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12960,6 +13259,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -12997,6 +13297,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13034,6 +13335,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13071,6 +13373,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13108,6 +13411,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13145,6 +13449,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13182,6 +13487,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13219,6 +13525,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13256,6 +13563,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13293,6 +13601,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13330,6 +13639,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13367,6 +13677,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13404,6 +13715,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13441,6 +13753,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13478,6 +13791,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13515,6 +13829,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13552,6 +13867,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13586,6 +13902,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13623,6 +13940,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13660,6 +13978,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13697,6 +14016,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13734,6 +14054,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -13772,6 +14093,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13809,6 +14131,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13846,6 +14169,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13883,6 +14207,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13920,6 +14245,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13957,6 +14283,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -13994,6 +14321,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14032,6 +14360,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14075,6 +14404,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14118,6 +14448,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14155,6 +14486,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14192,6 +14524,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14212,6 +14545,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14232,6 +14566,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14275,6 +14610,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14318,6 +14654,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14361,6 +14698,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14387,6 +14725,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14424,6 +14763,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14461,6 +14801,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14498,6 +14839,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14535,6 +14877,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14572,6 +14915,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14609,6 +14953,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14646,6 +14991,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14683,6 +15029,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14720,6 +15067,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14757,6 +15105,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14794,6 +15143,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14831,8 +15181,11 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true + + + true - true true @@ -14875,6 +15228,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14912,6 +15266,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14949,6 +15304,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -14986,6 +15342,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -15023,6 +15380,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -15060,6 +15418,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -15097,6 +15456,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -15107,6 +15467,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -15284,6 +15645,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -15344,6 +15706,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -15747,6 +16110,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -16306,6 +16670,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -16343,6 +16708,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -16380,6 +16746,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -16417,6 +16784,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -16450,6 +16818,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -16479,6 +16848,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -16508,6 +16878,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -16545,6 +16916,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -16585,6 +16957,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -16658,6 +17031,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -16697,6 +17071,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17176,6 +17551,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17205,6 +17581,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17234,6 +17611,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17263,6 +17641,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17292,6 +17671,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17329,6 +17709,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17383,6 +17764,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17420,6 +17802,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -17457,6 +17840,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17494,6 +17878,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17535,6 +17920,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17576,6 +17962,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17617,6 +18004,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17658,6 +18046,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17699,6 +18088,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17740,6 +18130,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17781,6 +18172,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17822,6 +18214,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17863,6 +18256,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17904,6 +18298,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17945,6 +18340,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -17986,6 +18382,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18027,6 +18424,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18068,6 +18466,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18109,6 +18508,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18150,6 +18550,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18191,6 +18592,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18232,6 +18634,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18273,6 +18676,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18314,6 +18718,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18355,6 +18760,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18396,6 +18802,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18437,6 +18844,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18478,6 +18886,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18519,6 +18928,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18560,6 +18970,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18601,6 +19012,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18642,6 +19054,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18683,6 +19096,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18724,6 +19138,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18765,6 +19180,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18806,6 +19222,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18847,6 +19264,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18888,6 +19306,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18929,6 +19348,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -18970,6 +19390,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19011,6 +19432,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19052,6 +19474,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19093,6 +19516,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19134,6 +19558,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19175,6 +19600,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19216,6 +19642,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19257,6 +19684,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19298,6 +19726,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19339,6 +19768,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19380,6 +19810,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19421,6 +19852,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19462,6 +19894,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19503,6 +19936,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19544,6 +19978,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19585,6 +20020,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19626,6 +20062,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19667,6 +20104,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19708,6 +20146,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19749,6 +20188,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19790,6 +20230,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19831,6 +20272,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19872,6 +20314,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19913,6 +20356,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19954,6 +20398,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -19995,6 +20440,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20036,6 +20482,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20077,6 +20524,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20118,6 +20566,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20159,6 +20608,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20200,6 +20650,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20241,6 +20692,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20282,6 +20734,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20323,6 +20776,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20364,6 +20818,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20405,6 +20860,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20446,6 +20902,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20487,6 +20944,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20528,6 +20986,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20569,6 +21028,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20610,6 +21070,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20651,6 +21112,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20692,6 +21154,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20729,6 +21192,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -20770,6 +21234,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20811,6 +21276,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -20852,6 +21318,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -20899,6 +21366,8 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true + true @@ -21030,6 +21499,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -21062,6 +21532,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -21118,6 +21589,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -21142,6 +21614,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -21164,6 +21637,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -21193,6 +21667,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -21236,6 +21711,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -21265,6 +21741,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -21668,6 +22145,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -21921,6 +22399,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -22981,6 +23460,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -23024,6 +23504,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -23061,6 +23542,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -23098,6 +23580,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -23131,6 +23614,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -23174,6 +23658,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -23217,6 +23702,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -23260,6 +23746,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -23303,6 +23790,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -23336,6 +23824,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -23373,6 +23862,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -23406,6 +23896,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -23442,6 +23933,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -23500,6 +23992,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU NotUsing + NotUsing false @@ -23556,6 +24049,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse NotUsing false + NotUsing false @@ -23612,6 +24106,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse NotUsing false + NotUsing false @@ -23668,6 +24163,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse NotUsing false + NotUsing false @@ -23731,6 +24227,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue NotUsing false + NotUsing false @@ -23794,6 +24291,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue NotUsing false + NotUsing false @@ -23857,6 +24355,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue NotUsing false + NotUsing false @@ -23920,6 +24419,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue NotUsing false + NotUsing false @@ -23976,6 +24476,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse NotUsing false + NotUsing false @@ -24032,6 +24533,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse NotUsing false + NotUsing false @@ -24088,6 +24590,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse NotUsing false + NotUsing false @@ -24144,6 +24647,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse NotUsing false + NotUsing false @@ -24200,6 +24704,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse NotUsing false + NotUsing false @@ -24256,6 +24761,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse NotUsing false + NotUsing false @@ -24312,6 +24818,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse NotUsing false + NotUsing @@ -24322,6 +24829,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -24366,6 +24874,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -24412,6 +24921,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -24464,6 +24974,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -24475,6 +24986,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -24517,6 +25029,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -24561,6 +25074,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -24599,6 +25113,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -24637,6 +25152,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -24675,6 +25191,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -24711,6 +25228,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -24755,6 +25273,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -24791,6 +25310,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -24827,6 +25347,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -24863,6 +25384,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -24906,6 +25428,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -24949,6 +25472,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -24985,6 +25509,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -25028,6 +25553,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -25071,6 +25597,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -25114,6 +25641,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -25157,6 +25685,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -25190,6 +25719,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -25225,6 +25755,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -25260,6 +25791,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -25295,6 +25827,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -25331,6 +25864,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -25374,6 +25908,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -25455,6 +25990,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -25521,6 +26057,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -25644,6 +26181,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -25688,6 +26226,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -25731,6 +26270,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -25760,6 +26300,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -25789,6 +26330,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -25825,6 +26367,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -25868,6 +26411,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -25903,6 +26447,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -25942,6 +26487,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -25986,6 +26532,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26030,6 +26577,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26074,6 +26622,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26118,6 +26667,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26147,6 +26697,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26185,6 +26736,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26221,6 +26773,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26257,6 +26810,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26293,6 +26847,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -26348,6 +26903,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26388,6 +26944,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26427,6 +26984,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26466,6 +27024,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26497,6 +27056,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26529,6 +27089,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26565,6 +27126,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26596,6 +27158,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26624,6 +27187,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26662,6 +27226,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26699,6 +27264,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26739,6 +27305,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse true true + true true @@ -26778,6 +27345,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26815,6 +27383,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -26892,6 +27461,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26929,6 +27499,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26966,6 +27537,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -26994,6 +27566,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27033,6 +27606,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27070,6 +27644,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27107,6 +27682,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27144,6 +27720,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27181,6 +27758,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27218,6 +27796,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27255,6 +27834,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27292,6 +27872,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27329,6 +27910,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27366,6 +27948,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27403,6 +27986,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27440,6 +28024,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27477,6 +28062,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27514,6 +28100,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27551,6 +28138,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27588,6 +28176,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27625,6 +28214,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27662,6 +28252,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27696,6 +28287,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27733,6 +28325,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27776,6 +28369,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27819,6 +28413,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27862,6 +28457,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27899,6 +28495,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27936,6 +28533,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -27979,6 +28577,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28022,6 +28621,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28065,6 +28665,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28091,6 +28692,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28128,6 +28730,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28165,6 +28768,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28202,6 +28806,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28245,6 +28850,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28282,6 +28888,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28319,6 +28926,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28356,6 +28964,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28393,6 +29002,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28430,6 +29040,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28473,6 +29084,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28510,6 +29122,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28553,6 +29166,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -28561,6 +29175,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -28706,6 +29321,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CU$(OutDir)$(ProjectName).pch $(IntDir)%(Filename)$(ObjectExt) $(IntDir)%(Filename)$(ObjectExt) + Create @@ -28761,6 +29377,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -28821,6 +29438,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -29079,6 +29697,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29110,6 +29729,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29141,6 +29761,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29172,6 +29793,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29212,6 +29834,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -29285,6 +29908,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29316,6 +29940,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29347,6 +29972,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true NotUsing @@ -29415,6 +30041,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29452,6 +30079,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29489,6 +30117,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29526,6 +30155,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29563,6 +30193,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse false false + true true @@ -29600,6 +30231,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29641,6 +30273,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29682,6 +30315,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29723,6 +30357,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29764,6 +30399,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29805,6 +30441,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29846,6 +30483,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29887,6 +30525,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29928,6 +30567,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -29969,6 +30609,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30010,6 +30651,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30051,6 +30693,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30092,6 +30735,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30133,6 +30777,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30174,6 +30819,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30215,6 +30861,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30256,6 +30903,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30297,6 +30945,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30338,6 +30987,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30379,6 +31029,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30420,6 +31071,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30461,6 +31113,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30502,6 +31155,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30543,6 +31197,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30584,6 +31239,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30625,6 +31281,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30666,6 +31323,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30707,6 +31365,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30748,6 +31407,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30789,6 +31449,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30830,6 +31491,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30871,6 +31533,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30925,6 +31588,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -30966,6 +31630,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31007,6 +31672,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31048,6 +31714,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31089,6 +31756,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31130,6 +31798,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31171,6 +31840,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31212,6 +31882,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31253,6 +31924,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31294,6 +31966,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31335,6 +32008,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31376,6 +32050,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31417,6 +32092,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31458,6 +32134,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31499,6 +32176,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31540,6 +32218,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31581,6 +32260,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31622,6 +32302,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31663,6 +32344,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31704,6 +32386,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31745,6 +32428,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31786,6 +32470,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31827,6 +32512,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31868,6 +32554,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31909,6 +32596,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31950,6 +32638,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -31991,6 +32680,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32032,6 +32722,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32073,6 +32764,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32114,6 +32806,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32155,6 +32848,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32196,6 +32890,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32237,6 +32932,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32278,6 +32974,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32319,6 +33016,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32360,6 +33058,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32401,6 +33100,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32442,6 +33142,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32483,6 +33184,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32524,6 +33226,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32565,6 +33268,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32606,6 +33310,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32647,6 +33352,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32688,6 +33394,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32729,6 +33436,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -32753,6 +33461,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32773,6 +33482,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32816,6 +33526,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -32854,6 +33565,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32892,6 +33604,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32935,6 +33648,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -32967,6 +33681,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33006,6 +33721,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33031,6 +33747,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33056,6 +33773,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -33081,6 +33799,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33092,6 +33811,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33104,6 +33824,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33115,6 +33836,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33127,16 +33849,19 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true true true + true true true true + true true @@ -33180,6 +33905,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33223,6 +33949,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33266,6 +33993,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -33286,6 +34014,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33306,6 +34035,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33326,6 +34056,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33359,6 +34090,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33392,6 +34124,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33425,6 +34158,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33459,6 +34193,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33478,6 +34213,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33511,6 +34247,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33545,6 +34282,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33564,6 +34302,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33597,6 +34336,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33631,6 +34371,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33650,6 +34391,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33683,6 +34425,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33717,6 +34460,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33736,6 +34480,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33755,6 +34500,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33774,6 +34520,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33793,6 +34540,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -33836,6 +34584,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33879,6 +34628,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -33922,6 +34672,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -33965,6 +34716,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34008,6 +34760,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34051,6 +34804,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -34094,6 +34848,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34137,6 +34892,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34180,6 +34936,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true false @@ -34223,6 +34980,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34266,6 +35024,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34309,6 +35068,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34336,6 +35096,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34362,6 +35123,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34389,6 +35151,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34415,6 +35178,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34452,6 +35216,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34489,6 +35254,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34515,6 +35281,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34541,6 +35308,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34567,6 +35335,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34593,6 +35362,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34624,6 +35394,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34655,8 +35426,11 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true + + + true - true true @@ -34700,6 +35474,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34731,6 +35506,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34764,6 +35540,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34797,6 +35574,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -34962,6 +35740,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35005,6 +35784,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35015,6 +35795,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35059,6 +35840,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35102,6 +35884,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35111,6 +35894,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35151,6 +35935,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35194,6 +35979,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35234,6 +36020,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35277,6 +36064,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35286,6 +36074,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35329,6 +36118,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue false true + true true @@ -35372,6 +36162,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUfalse true true + true @@ -35414,6 +36205,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -35453,6 +36245,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true @@ -35536,6 +36329,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35575,6 +36369,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35614,6 +36409,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35660,6 +36456,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35699,6 +36496,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35738,6 +36536,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35777,6 +36576,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35816,6 +36616,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35855,6 +36656,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35894,6 +36696,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35933,6 +36736,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -35972,6 +36776,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36011,6 +36816,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36057,6 +36863,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36096,6 +36903,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36135,6 +36943,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36174,6 +36983,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36213,6 +37023,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36252,6 +37063,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36291,6 +37103,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36329,6 +37142,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36367,6 +37181,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36405,6 +37220,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36449,6 +37265,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36487,6 +37304,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36525,6 +37343,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36563,6 +37382,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36601,6 +37421,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36639,6 +37460,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true true @@ -36677,6 +37499,7 @@ xcopy /q /y /i /s /e $(ProjectDir)Durango\CU $(LayoutDir)Image\Loose\CUtrue true true + true diff --git a/Minecraft.Client/MinecraftServer.cpp b/Minecraft.Client/MinecraftServer.cpp index 626b5d08..62eb19c4 100644 --- a/Minecraft.Client/MinecraftServer.cpp +++ b/Minecraft.Client/MinecraftServer.cpp @@ -27,9 +27,19 @@ #include "..\Minecraft.World\Pos.h" #include "..\Minecraft.World\System.h" #include "..\Minecraft.World\StringHelpers.h" +#include "../Minecraft.World/net.minecraft.world.entity.item.h" +#include "../Minecraft.World/net.minecraft.world.item.h" +#include "../Minecraft.World/net.minecraft.world.item.enchantment.h" +#include "../Minecraft.World/net.minecraft.world.damagesource.h" #ifdef SPLIT_SAVES #include "..\Minecraft.World\ConsoleSaveFileSplit.h" #endif +#if defined(_WINDOWS64) || defined(DISABLE_PSN) || defined(_DISABLE_XBLIVE) +#include "Common/Network/NetworkSocketLayer.h" +#endif +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "../Minecraft.Server/ServerLogger.h" +#endif #include "..\Minecraft.World\ConsoleSaveFileOriginal.h" #include "..\Minecraft.World\Socket.h" #include "..\Minecraft.World\net.minecraft.world.entity.h" @@ -78,6 +88,462 @@ bool MinecraftServer::s_slowQueuePacketSent = false; unordered_map MinecraftServer::ironTimers; +static bool ShouldUseDedicatedServerProperties() +{ +#if defined(MINECRAFT_SERVER_BUILD) + return true; +#else + return false; +#endif +} + +static int GetDedicatedServerInt(Settings *settings, const wchar_t *key, int defaultValue) +{ + return (ShouldUseDedicatedServerProperties() && settings != nullptr) ? settings->getInt(key, defaultValue) : defaultValue; +} + +static bool GetDedicatedServerBool(Settings *settings, const wchar_t *key, bool defaultValue) +{ + return (ShouldUseDedicatedServerProperties() && settings != nullptr) ? settings->getBoolean(key, defaultValue) : defaultValue; +} + +static wstring GetDedicatedServerString(Settings *settings, const wchar_t *key, const wstring &defaultValue) +{ + return (ShouldUseDedicatedServerProperties() && settings != nullptr) ? settings->getString(key, defaultValue) : defaultValue; +} + +static void PrintConsoleLine(const wchar_t *prefix, const wstring &message) +{ + wprintf(L"%ls%ls\n", prefix, message.c_str()); + fflush(stdout); +} + +static bool TryParseIntValue(const wstring &text, int &value) +{ + std::wistringstream stream(text); + stream >> value; + return !stream.fail() && stream.eof(); +} + +static vector SplitConsoleCommand(const wstring &command) +{ + vector tokens; + std::wistringstream stream(command); + wstring token; + while (stream >> token) + { + tokens.push_back(token); + } + return tokens; +} + +static wstring JoinConsoleCommandTokens(const vector &tokens, size_t startIndex) +{ + wstring joined; + for (size_t i = startIndex; i < tokens.size(); ++i) + { + if (!joined.empty()) joined += L" "; + joined += tokens[i]; + } + return joined; +} + +static shared_ptr FindPlayerByName(PlayerList *playerList, const wstring &name) +{ + if (playerList == nullptr) return nullptr; + + for (size_t i = 0; i < playerList->players.size(); ++i) + { + shared_ptr player = playerList->players[i]; + if (player != nullptr && equalsIgnoreCase(player->getName(), name)) + { + return player; + } + } + + return nullptr; +} + +static void SetAllLevelTimes(MinecraftServer *server, int value) +{ + for (unsigned int i = 0; i < server->levels.length; ++i) + { + if (server->levels[i] != nullptr) + { + server->levels[i]->setDayTime(value); + } + } +} + +static bool ExecuteConsoleCommand(MinecraftServer *server, const wstring &rawCommand) +{ + if (server == nullptr) + return false; + + wstring command = trimString(rawCommand); + if (command.empty()) + return true; + + if (command[0] == L'/') + { + command = trimString(command.substr(1)); + } + + vector tokens = SplitConsoleCommand(command); + if (tokens.empty()) + return true; + + const wstring action = toLower(tokens[0]); + PlayerList *playerList = server->getPlayers(); + + if (action == L"help" || action == L"?") + { + server->info(L"Commands: help, stop, list, say , save-all, time , weather [seconds], tp , give [amount] [aux], enchant [level], kill "); + return true; + } + + if (action == L"stop") + { + server->info(L"Stopping server..."); + MinecraftServer::HaltServer(); + return true; + } + + if (action == L"list") + { + wstring playerNames = (playerList != nullptr) ? playerList->getPlayerNames() : L""; + if (playerNames.empty()) playerNames = L"(none)"; + server->info(L"Players (" + std::to_wstring((playerList != nullptr) ? playerList->getPlayerCount() : 0) + L"): " + playerNames); + return true; + } + + if (action == L"say") + { + if (tokens.size() < 2) + { + server->warn(L"Usage: say "); + return false; + } + + wstring message = L"[Server] " + JoinConsoleCommandTokens(tokens, 1); + if (playerList != nullptr) + { + playerList->broadcastAll(std::make_shared(message)); + } + server->info(message); + return true; + } + + if (action == L"save-all") + { + if (playerList != nullptr) + { + playerList->saveAll(nullptr, false); + } + server->info(L"World saved."); + return true; + } + + if (action == L"time") + { + if (tokens.size() < 2) + { + server->warn(L"Usage: time set | time add "); + return false; + } + + if (toLower(tokens[1]) == L"add") + { + if (tokens.size() < 3) + { + server->warn(L"Usage: time add "); + return false; + } + + int delta = 0; + if (!TryParseIntValue(tokens[2], delta)) + { + server->warn(L"Invalid tick value: " + tokens[2]); + return false; + } + + for (unsigned int i = 0; i < server->levels.length; ++i) + { + if (server->levels[i] != nullptr) + { + server->levels[i]->setDayTime(server->levels[i]->getDayTime() + delta); + } + } + + server->info(L"Added " + std::to_wstring(delta) + L" ticks."); + return true; + } + + wstring timeValue = toLower(tokens[1]); + if (timeValue == L"set") + { + if (tokens.size() < 3) + { + server->warn(L"Usage: time set "); + return false; + } + timeValue = toLower(tokens[2]); + } + + int targetTime = 0; + if (timeValue == L"day") + { + targetTime = 0; + } + else if (timeValue == L"night") + { + targetTime = 12500; + } + else if (!TryParseIntValue(timeValue, targetTime)) + { + server->warn(L"Invalid time value: " + timeValue); + return false; + } + + SetAllLevelTimes(server, targetTime); + server->info(L"Time set to " + std::to_wstring(targetTime) + L"."); + return true; + } + + if (action == L"weather") + { + if (tokens.size() < 2) + { + server->warn(L"Usage: weather [seconds]"); + return false; + } + + int durationSeconds = 600; + if (tokens.size() >= 3 && !TryParseIntValue(tokens[2], durationSeconds)) + { + server->warn(L"Invalid duration: " + tokens[2]); + return false; + } + + if (server->levels[0] == nullptr) + { + server->warn(L"The overworld is not loaded."); + return false; + } + + LevelData *levelData = server->levels[0]->getLevelData(); + int duration = durationSeconds * SharedConstants::TICKS_PER_SECOND; + levelData->setRainTime(duration); + levelData->setThunderTime(duration); + + wstring weather = toLower(tokens[1]); + if (weather == L"clear") + { + levelData->setRaining(false); + levelData->setThundering(false); + } + else if (weather == L"rain") + { + levelData->setRaining(true); + levelData->setThundering(false); + } + else if (weather == L"thunder") + { + levelData->setRaining(true); + levelData->setThundering(true); + } + else + { + server->warn(L"Usage: weather [seconds]"); + return false; + } + + server->info(L"Weather set to " + weather + L"."); + return true; + } + + if (action == L"tp" || action == L"teleport") + { + if (tokens.size() < 3) + { + server->warn(L"Usage: tp "); + return false; + } + + shared_ptr subject = FindPlayerByName(playerList, tokens[1]); + shared_ptr destination = FindPlayerByName(playerList, tokens[2]); + if (subject == nullptr) + { + server->warn(L"Unknown player: " + tokens[1]); + return false; + } + if (destination == nullptr) + { + server->warn(L"Unknown player: " + tokens[2]); + return false; + } + if (subject->level->dimension->id != destination->level->dimension->id || !subject->isAlive()) + { + server->warn(L"Teleport failed because the players are not in the same dimension or the source player is dead."); + return false; + } + + subject->ride(nullptr); + subject->connection->teleport(destination->x, destination->y, destination->z, destination->yRot, destination->xRot); + server->info(L"Teleported " + subject->getName() + L" to " + destination->getName() + L"."); + return true; + } + + if (action == L"give") + { + if (tokens.size() < 3) + { + server->warn(L"Usage: give [amount] [aux]"); + return false; + } + + shared_ptr player = FindPlayerByName(playerList, tokens[1]); + if (player == nullptr) + { + server->warn(L"Unknown player: " + tokens[1]); + return false; + } + + int itemId = 0; + int amount = 1; + int aux = 0; + if (!TryParseIntValue(tokens[2], itemId)) + { + server->warn(L"Invalid item id: " + tokens[2]); + return false; + } + if (tokens.size() >= 4 && !TryParseIntValue(tokens[3], amount)) + { + server->warn(L"Invalid amount: " + tokens[3]); + return false; + } + if (tokens.size() >= 5 && !TryParseIntValue(tokens[4], aux)) + { + server->warn(L"Invalid aux value: " + tokens[4]); + return false; + } + if (itemId <= 0 || Item::items[itemId] == nullptr) + { + server->warn(L"Unknown item id: " + std::to_wstring(itemId)); + return false; + } + if (amount <= 0) + { + server->warn(L"Amount must be positive."); + return false; + } + + shared_ptr itemInstance(new ItemInstance(itemId, amount, aux)); + shared_ptr drop = player->drop(itemInstance); + if (drop != nullptr) + { + drop->throwTime = 0; + } + server->info(L"Gave item " + std::to_wstring(itemId) + L" x" + std::to_wstring(amount) + L" to " + player->getName() + L"."); + return true; + } + + if (action == L"enchant") + { + if (tokens.size() < 3) + { + server->warn(L"Usage: enchant [level]"); + return false; + } + + shared_ptr player = FindPlayerByName(playerList, tokens[1]); + if (player == nullptr) + { + server->warn(L"Unknown player: " + tokens[1]); + return false; + } + + int enchantmentId = 0; + int enchantmentLevel = 1; + if (!TryParseIntValue(tokens[2], enchantmentId)) + { + server->warn(L"Invalid enchantment id: " + tokens[2]); + return false; + } + if (tokens.size() >= 4 && !TryParseIntValue(tokens[3], enchantmentLevel)) + { + server->warn(L"Invalid enchantment level: " + tokens[3]); + return false; + } + + shared_ptr selectedItem = player->getSelectedItem(); + if (selectedItem == nullptr) + { + server->warn(L"The player is not holding an item."); + return false; + } + + Enchantment *enchantment = Enchantment::enchantments[enchantmentId]; + if (enchantment == nullptr) + { + server->warn(L"Unknown enchantment id: " + std::to_wstring(enchantmentId)); + return false; + } + if (!enchantment->canEnchant(selectedItem)) + { + server->warn(L"That enchantment cannot be applied to the selected item."); + return false; + } + + if (enchantmentLevel < enchantment->getMinLevel()) enchantmentLevel = enchantment->getMinLevel(); + if (enchantmentLevel > enchantment->getMaxLevel()) enchantmentLevel = enchantment->getMaxLevel(); + + if (selectedItem->hasTag()) + { + ListTag *enchantmentTags = selectedItem->getEnchantmentTags(); + if (enchantmentTags != nullptr) + { + for (int i = 0; i < enchantmentTags->size(); i++) + { + int type = enchantmentTags->get(i)->getShort((wchar_t *)ItemInstance::TAG_ENCH_ID); + if (Enchantment::enchantments[type] != nullptr && !Enchantment::enchantments[type]->isCompatibleWith(enchantment)) + { + server->warn(L"That enchantment conflicts with an existing enchantment on the selected item."); + return false; + } + } + } + } + + selectedItem->enchant(enchantment, enchantmentLevel); + server->info(L"Enchanted " + player->getName() + L"'s held item with " + std::to_wstring(enchantmentId) + L" " + std::to_wstring(enchantmentLevel) + L"."); + return true; + } + + if (action == L"kill") + { + if (tokens.size() < 2) + { + server->warn(L"Usage: kill "); + return false; + } + + shared_ptr player = FindPlayerByName(playerList, tokens[1]); + if (player == nullptr) + { + server->warn(L"Unknown player: " + tokens[1]); + return false; + } + + player->hurt(DamageSource::outOfWorld, 3.4e38f); + server->info(L"Killed " + player->getName() + L"."); + return true; + } + + server->warn(L"Unknown command: " + command); + return false; +} + MinecraftServer::MinecraftServer() { // 4J - added initialisers @@ -105,6 +571,7 @@ MinecraftServer::MinecraftServer() playerIdleTimeout = 0; m_postUpdateThread = NULL; forceGameType = false; + m_spawnProtectionRadius = 0; commandDispatcher = new ServerCommandDispatcher(); @@ -149,6 +616,10 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW logger.info("Loading properties"); #endif settings = new Settings(new File(L"server.properties")); + // Dedicated-only: spawn-protection radius in blocks; 0 disables protection. + m_spawnProtectionRadius = GetDedicatedServerInt(settings, L"spawn-protection", 0); + if (m_spawnProtectionRadius < 0) m_spawnProtectionRadius = 0; + if (m_spawnProtectionRadius > 256) m_spawnProtectionRadius = 256; app.DebugPrintf("\n*** SERVER SETTINGS ***\n"); app.DebugPrintf("ServerSettings: host-friends-only is %s\n",(app.GetGameHostOption(eGameHostOption_FriendsOfFriends)>0)?"on":"off"); @@ -156,6 +627,7 @@ bool MinecraftServer::initServer(__int64 seed, NetworkGameInitData *initData, DW app.DebugPrintf("ServerSettings: pvp is %s\n",(app.GetGameHostOption(eGameHostOption_PvP)>0)?"on":"off"); app.DebugPrintf("ServerSettings: fire spreads is %s\n",(app.GetGameHostOption(eGameHostOption_FireSpreads)>0)?"on":"off"); app.DebugPrintf("ServerSettings: tnt explodes is %s\n",(app.GetGameHostOption(eGameHostOption_TNT)>0)?"on":"off"); + app.DebugPrintf("ServerSettings: spawn protection radius is %d\n", m_spawnProtectionRadius); app.DebugPrintf("\n"); // TODO 4J Stu - Init a load of settings based on data passed as params @@ -1172,7 +1644,9 @@ Level *MinecraftServer::getCommandSenderWorld() int MinecraftServer::getSpawnProtectionRadius() { - return 16; + // Client-host mode must never apply dedicated-server spawn protection settings. + if (!ShouldUseDedicatedServerProperties()) return 0; + return m_spawnProtectionRadius; } bool MinecraftServer::isUnderSpawnProtection(Level *level, int x, int y, int z, shared_ptr player) diff --git a/Minecraft.Client/MinecraftServer.h b/Minecraft.Client/MinecraftServer.h index fb2a8640..422b55fa 100644 --- a/Minecraft.Client/MinecraftServer.h +++ b/Minecraft.Client/MinecraftServer.h @@ -40,6 +40,7 @@ typedef struct _NetworkGameInitData LevelGenerationOptions *levelGen; DWORD texturePackId; bool findSeed; + bool dedicatedNoLocalHostPlayer; unsigned int xzSize; unsigned char hellScale; ESavePlatform savePlatform; @@ -52,6 +53,7 @@ typedef struct _NetworkGameInitData levelGen = NULL; texturePackId = 0; findSeed = false; + dedicatedNoLocalHostPlayer = false; xzSize = LEVEL_LEGACY_WIDTH; hellScale = HELL_LEVEL_LEGACY_SCALE; savePlatform = SAVE_FILE_PLATFORM_LOCAL; @@ -113,6 +115,7 @@ public: int maxBuildHeight; int playerIdleTimeout; bool forceGameType; + int m_spawnProtectionRadius; private: // 4J Added diff --git a/Minecraft.Client/PendingConnection.cpp b/Minecraft.Client/PendingConnection.cpp index 873763e0..e6949949 100644 --- a/Minecraft.Client/PendingConnection.cpp +++ b/Minecraft.Client/PendingConnection.cpp @@ -14,12 +14,37 @@ #include "..\Minecraft.World\net.minecraft.world.item.h" #include "..\Minecraft.World\SharedConstants.h" #include "Settings.h" + +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "../Minecraft.Server/ServerLogManager.h" +#include "../Minecraft.Server/Access/Access.h" +#include "../Minecraft.World/Socket.h" +#include +#endif // #ifdef __PS3__ // #include "PS3\Network\NetworkPlayerSony.h" // #endif Random *PendingConnection::random = new Random(); +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +namespace +{ + static unsigned char GetPendingConnectionSmallId(Connection *connection) + { + if (connection != nullptr) + { + Socket *socket = connection->getSocket(); + if (socket != nullptr) + { + return socket->getSmallId(); + } + } + return 0; + } +} +#endif + PendingConnection::PendingConnection(MinecraftServer *server, Socket *socket, const wstring& id) { // 4J - added initialisers @@ -159,12 +184,123 @@ void PendingConnection::handleLogin(shared_ptr packet) bool sentDisconnect = false; bool nameTaken = false; +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + { + std::string connectionIp = ""; + int connectionPort = 0; + + if (!connection || !connection->getSocket()) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); + return; + } + + unsigned char smallId = connection->getSocket()->getSmallId(); + if (smallId == 0) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); + return; + } + + if (!ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, &connectionIp)) + { + SOCKET sock = NetworkSocketLayer::GetSocketForSmallId(smallId); + if (sock != INVALID_SOCKET) + { + sockaddr_in addr; + int addrLen = sizeof(addr); + if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0) + { + char ipBuf[64] = {}; + if (inet_ntop(AF_INET, &addr.sin_addr, ipBuf, sizeof(ipBuf))) + { + connectionIp = ipBuf; + connectionPort = (int)ntohs(addr.sin_port); + } + } + } + if (connectionIp.empty()) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); + return; + } + } + else { + SOCKET sock = NetworkSocketLayer::GetSocketForSmallId(smallId); + if (sock != INVALID_SOCKET) + { + sockaddr_in addr; + int addrLen = sizeof(addr); + if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0) + connectionPort = (int)ntohs(addr.sin_port); + } + } + } +#endif + + // Use the same Xuid choice as handleAcceptedLogin (offline first, online fallback). + PlayerUID loginXuid = packet->m_offlineXuid; + if (loginXuid == INVALID_XUID) loginXuid = packet->m_onlineXuid; + + bool duplicateXuid = false; + if (loginXuid != INVALID_XUID && server->getPlayers()->getPlayer(loginXuid) != nullptr) + { + duplicateXuid = true; + } + else if (packet->m_onlineXuid != INVALID_XUID && + packet->m_onlineXuid != loginXuid && + server->getPlayers()->getPlayer(packet->m_onlineXuid) != nullptr) + { + duplicateXuid = true; + } + + bool bannedXuid = false; + if (packet->m_offlineXuid != INVALID_XUID) + { + bannedXuid = server->getPlayers()->isXuidBanned(packet->m_offlineXuid); + } + if (!bannedXuid && packet->m_onlineXuid != INVALID_XUID && packet->m_onlineXuid != packet->m_offlineXuid) + { + bannedXuid = server->getPlayers()->isXuidBanned(packet->m_onlineXuid); + } + + bool whitelistSatisfied = true; +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + if (ServerRuntime::Access::IsWhitelistEnabled()) + { + whitelistSatisfied = false; + if (loginXuid != INVALID_XUID) + { + whitelistSatisfied = ServerRuntime::Access::IsPlayerWhitelisted(loginXuid); + } + if (!whitelistSatisfied && packet->m_onlineXuid != INVALID_XUID && packet->m_onlineXuid != loginXuid) + { + whitelistSatisfied = ServerRuntime::Access::IsPlayerWhitelisted(packet->m_onlineXuid); + } + } +#endif + if( sentDisconnect ) { // Do nothing } - else if( server->getPlayers()->isXuidBanned( packet->m_onlineXuid ) ) + else if (bannedXuid) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_BannedXuid); +#endif + disconnect(DisconnectPacket::eDisconnect_Banned); + } + else if (!whitelistSatisfied) + { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_NotWhitelisted); +#endif + disconnect(DisconnectPacket::eDisconnect_Banned); + } + else if (duplicateXuid) + { + // Reject the incoming connection — a player with this UID is already + // on the server. Allowing duplicates causes invisible players and + // other undefined behaviour. + app.DebugPrintf("LOGIN: Rejecting duplicate xuid for name: %ls\n", name.c_str()); disconnect(DisconnectPacket::eDisconnect_Banned); } else @@ -182,6 +318,9 @@ void PendingConnection::handleLogin(shared_ptr packet) if (nameTaken) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + ServerRuntime::ServerLogManager::OnRejectedPlayerLogin(GetPendingConnectionSmallId(connection), name, ServerRuntime::ServerLogManager::eLoginRejectReason_DuplicateName); +#endif app.DebugPrintf("Rejecting duplicate name: %ls\n", name.c_str()); // to do: replace this with a dedicated duplicate name @@ -237,6 +376,9 @@ void PendingConnection::handleAcceptedLogin(shared_ptr packet) shared_ptr playerEntity = server->getPlayers()->getPlayerForLogin(this, name, playerXuid,packet->m_onlineXuid); if (playerEntity != NULL) { +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + ServerRuntime::ServerLogManager::OnAcceptedPlayerLogin(GetPendingConnectionSmallId(connection), name); +#endif server->getPlayers()->placeNewPlayer(connection, playerEntity, packet); connection = NULL; // We've moved responsibility for this over to the new PlayerConnection, NULL so we don't delete our reference to it here in our dtor } diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index 9b5312d8..2ac08340 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -32,6 +32,22 @@ // 4J Added #include "..\Minecraft.World\net.minecraft.world.item.crafting.h" #include "Options.h" +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\ServerLogManager.h" +#endif + +namespace +{ + // Anti-cheat thresholds. Keep server-side checks authoritative even in host mode. + // Base max squared displacement allowed per move packet before speed flags trigger. + const double kMoveBaseAllowanceSq = 100.0; + // Extra squared displacement allowance derived from current server-side velocity. + const double kMoveVelocityAllowanceScale = 100.0; + // Max squared distance for interact/attack when the target is visible (normal reach). + const double kInteractReachSq = 6.0 * 6.0; + // Stricter max squared distance used when LOS is blocked to reduce wall-hit abuse. + const double kInteractBlockedReachSq = 3.0 * 3.0; +} Random PlayerConnection::random; @@ -64,6 +80,13 @@ PlayerConnection::PlayerConnection(MinecraftServer *server, Connection *connecti m_offlineXUID = INVALID_XUID; m_onlineXUID = INVALID_XUID; m_bHasClientTickedOnce = false; + m_logSmallId = 0; + + // Cache the first valid transport smallId because disconnect teardown can clear it before the server logger runs. + if (this->connection != NULL && this->connection->getSocket() != NULL) + { + m_logSmallId = this->connection->getSocket()->getSmallId(); + } setShowOnMaps(app.GetGameHostOption(eGameHostOption_Gamertags)!=0?true:false); } @@ -74,6 +97,17 @@ PlayerConnection::~PlayerConnection() DeleteCriticalSection(&done_cs); } +unsigned char PlayerConnection::getLogSmallId() +{ + // Fall back to the live socket only while the cached value is still empty. + if (m_logSmallId == 0 && connection != NULL && connection->getSocket() != NULL) + { + m_logSmallId = connection->getSocket()->getSmallId(); + } + + return m_logSmallId; +} + void PlayerConnection::tick() { if( done ) return; @@ -116,6 +150,14 @@ void PlayerConnection::disconnect(DisconnectPacket::eDisconnectReason reason) return; } +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + ServerRuntime::ServerLogManager::OnPlayerDisconnected( + getLogSmallId(), + (player != NULL) ? player->name : std::wstring(), + reason, + true); +#endif + app.DebugPrintf("PlayerConnection disconect reason: %d\n", reason ); player->disconnect(); @@ -280,6 +322,20 @@ void PlayerConnection::handleMovePlayer(shared_ptr packet) } */ + // Anti-cheat: reject movement packets that exceed server-authoritative bounds. + double velocitySq = player->xd * player->xd + player->yd * player->yd + player->zd * player->zd; + double maxAllowedSq = kMoveBaseAllowanceSq + (velocitySq * kMoveVelocityAllowanceScale); + if (player->isAllowedToFly() || player->gameMode->isCreative()) + { + // Creative / flight-allowed players can move farther legitimately per tick. + maxAllowedSq *= 1.5; + } + if (dist > maxAllowedSq) + { + disconnect(DisconnectPacket::eDisconnect_MovedTooQuickly); + return; + } + float r = 1 / 16.0f; bool oldOk = level->getCubes(player, player->bb->copy()->shrink(r, r, r))->empty(); @@ -306,8 +362,8 @@ void PlayerConnection::handleMovePlayer(shared_ptr packet) xDist = xt - player->x; yDist = yt - player->y; - // 4J-PB - line below will always be true! - if (yDist > -0.5 || yDist < 0.5) + // Clamp tiny Y drift noise to reduce false positives. + if (yDist > -0.5 && yDist < 0.5) { yDist = 0; } @@ -428,7 +484,8 @@ void PlayerConnection::handlePlayerAction(shared_ptr packet) if (packet->action == PlayerActionPacket::START_DESTROY_BLOCK) { - if (true) player->gameMode->startDestroyBlock(x, y, z, packet->face); // 4J - condition was !server->isUnderSpawnProtection(level, x, y, z, player) (from Java 1.6.4) but putting back to old behaviour + // Anti-cheat: validate spawn protection on the server for mining start. + if (!server->isUnderSpawnProtection(level, x, y, z, player)) player->gameMode->startDestroyBlock(x, y, z, packet->face); else player->connection->send( shared_ptr( new TileUpdatePacket(x, y, z, level) ) ); } @@ -456,8 +513,6 @@ void PlayerConnection::handleUseItem(shared_ptr packet) int face = packet->getFace(); player->resetLastActionTime(); - // 4J Stu - We don't have ops, so just use the levels setting - bool canEditSpawn = level->canEditSpawn; // = level->dimension->id != 0 || server->players->isOp(player->name); if (packet->getFace() == 255) { if (item == NULL) return; @@ -467,7 +522,8 @@ void PlayerConnection::handleUseItem(shared_ptr packet) { if (synched && player->distanceToSqr(x + 0.5, y + 0.5, z + 0.5) < 8 * 8) { - if (true) // 4J - condition was !server->isUnderSpawnProtection(level, x, y, z, player) (from java 1.6.4) but putting back to old behaviour + // Anti-cheat: block placement/use must pass server-side spawn protection. + if (!server->isUnderSpawnProtection(level, x, y, z, player)) { player->gameMode->useItemOn(player, level, item, x, y, z, face, packet->getClickX(), packet->getClickY(), packet->getClickZ()); } @@ -536,7 +592,18 @@ void PlayerConnection::handleUseItem(shared_ptr packet) void PlayerConnection::onDisconnect(DisconnectPacket::eDisconnectReason reason, void *reasonObjects) { EnterCriticalSection(&done_cs); - if( done ) return; + if( done ) + { + LeaveCriticalSection(&done_cs); + return; + } +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + ServerRuntime::ServerLogManager::OnPlayerDisconnected( + getLogSmallId(), + (player != NULL) ? player->name : std::wstring(), + reason, + false); +#endif // logger.info(player.name + " lost connection: " + reason); // 4J-PB - removed, since it needs to be localised in the language the client is in //server->players->broadcastAll( shared_ptr( new ChatPacket(L"§e" + player->name + L" left the game.") ) ); @@ -754,12 +821,13 @@ void PlayerConnection::handleInteract(shared_ptr packet) // even though the ray is blocked. if (target != NULL) // && player->canSee(target) && player->distanceToSqr(target) < 6 * 6) { - //boole canSee = player->canSee(target); - //double maxDist = 6 * 6; - //if (!canSee) - //{ - // maxDist = 3 * 3; - //} + // Anti-cheat: enforce reach and LOS on the server to reject forged hits. + bool canSeeTarget = player->canSee(target); + double maxDistSq = canSeeTarget ? kInteractReachSq : kInteractBlockedReachSq; + if (player->distanceToSqr(target) > maxDistSq) + { + return; + } //if (player->distanceToSqr(target) < maxDist) //{ diff --git a/Minecraft.Client/PlayerConnection.h b/Minecraft.Client/PlayerConnection.h index 39861f8c..7454657a 100644 --- a/Minecraft.Client/PlayerConnection.h +++ b/Minecraft.Client/PlayerConnection.h @@ -37,6 +37,7 @@ private: int dropSpamTickCount; bool m_bHasClientTickedOnce; + unsigned char m_logSmallId; public: PlayerConnection(MinecraftServer *server, Connection *connection, shared_ptr player); @@ -45,6 +46,10 @@ public: void disconnect(DisconnectPacket::eDisconnectReason reason); private: + /** + * Returns the stable network smallId used by dedicated-server logging and refreshes it from the live socket when possible + */ + unsigned char getLogSmallId(); double xLastOk, yLastOk, zLastOk; bool synched; diff --git a/Minecraft.Client/PlayerList.cpp b/Minecraft.Client/PlayerList.cpp index 08b01e62..d05f57c9 100644 --- a/Minecraft.Client/PlayerList.cpp +++ b/Minecraft.Client/PlayerList.cpp @@ -31,6 +31,10 @@ #include "Common\Network\Sony\NetworkPlayerSony.h" #endif +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) +#include "..\Minecraft.Server\Access\Access.h" +#endif + // 4J - this class is fairly substantially altered as there didn't seem any point in porting code for banning, whitelisting, ops etc. PlayerList::PlayerList(MinecraftServer *server) @@ -1626,6 +1630,13 @@ bool PlayerList::isXuidBanned(PlayerUID xuid) } } +#if defined(MINECRAFT_SERVER_BUILD) + if (!banned) + { + banned = ServerRuntime::Access::IsPlayerBanned(xuid); + } +#endif + return banned; } diff --git a/Minecraft.Client/ServerPlayerGameMode.cpp b/Minecraft.Client/ServerPlayerGameMode.cpp index 432e5d7a..abb67cc8 100644 --- a/Minecraft.Client/ServerPlayerGameMode.cpp +++ b/Minecraft.Client/ServerPlayerGameMode.cpp @@ -183,24 +183,25 @@ void ServerPlayerGameMode::stopDestroyBlock(int x, int y, int z) { Tile *tile = Tile::tiles[t]; - // MGH - removed checking for the destroy progress here, it has already been checked on the client before it sent the packet. - // fixes issues with this failing to destroy because of packets bunching up - // float destroyProgress = tile->getDestroyProgress(player, player->level, x, y, z) * (ticksSpentDestroying + 1); - // if (destroyProgress >= .7f || bIgnoreDestroyProgress) + // Anti-cheat: re-check destroy progress on the server for STOP_DESTROY. + int ticksSpentDestroying = gameTicks - destroyProgressStart; + float destroyProgress = tile->getDestroyProgress(player, player->level, x, y, z) * (ticksSpentDestroying + 1); + if (destroyProgress >= 1.0f) { isDestroyingBlock = false; level->destroyTileProgress(player->entityId, x, y, z, -1); destroyBlock(x, y, z); } - // else if (!hasDelayedDestroy) - // { - // isDestroyingBlock = false; - // hasDelayedDestroy = true; - // delayedDestroyX = x; - // delayedDestroyY = y; - // delayedDestroyZ = z; - // delayedTickStart = destroyProgressStart; - // } + else if (!hasDelayedDestroy) + { + // Keep server-authoritative mining while allowing legit latency to finish via delayed tick progression. + isDestroyingBlock = false; + hasDelayedDestroy = true; + delayedDestroyX = x; + delayedDestroyY = y; + delayedDestroyZ = z; + delayedTickStart = destroyProgressStart; + } } } } diff --git a/Minecraft.Server/Access/Access.cpp b/Minecraft.Server/Access/Access.cpp new file mode 100644 index 00000000..8aae7005 --- /dev/null +++ b/Minecraft.Server/Access/Access.cpp @@ -0,0 +1,460 @@ +#include "stdafx.h" + +#include "Access.h" + +#include "../Common/StringUtils.h" +#include "../ServerLogger.h" + +#include +#include + +namespace ServerRuntime +{ + namespace Access + { + namespace + { + /** + * **Access State** + * + * These features are used extensively from various parts of the code, so safe read/write handling is implemented + * Stores the published BAN manager snapshot plus a writer gate for clone-and-publish updates + * 公開中のBanManagerスナップショットと更新直列化用ロックを保持する + */ + struct AccessState + { + std::mutex stateLock; + std::mutex writeLock; + std::shared_ptr banManager; + std::shared_ptr whitelistManager; + bool whitelistEnabled; + }; + + AccessState g_accessState; + + /** + * Copies the currently published manager pointer so readers can work without holding the publish mutex + * 公開中のBanManager共有ポインタを複製取得する + */ + static std::shared_ptr GetBanManagerSnapshot() + { + std::lock_guard stateLock(g_accessState.stateLock); + return g_accessState.banManager; + } + + /** + * Replaces the shared manager pointer with a fully prepared snapshot in one short critical section + * 準備完了したBanManagerスナップショットを短いロックで公開する + */ + static void PublishBanManagerSnapshot(const std::shared_ptr &banManager) + { + std::lock_guard stateLock(g_accessState.stateLock); + g_accessState.banManager = banManager; + } + + static std::shared_ptr GetWhitelistManagerSnapshot() + { + std::lock_guard stateLock(g_accessState.stateLock); + return g_accessState.whitelistManager; + } + + static void PublishWhitelistManagerSnapshot(const std::shared_ptr &whitelistManager) + { + std::lock_guard stateLock(g_accessState.stateLock); + g_accessState.whitelistManager = whitelistManager; + } + } + + std::string FormatXuid(PlayerUID xuid) + { + if (xuid == INVALID_XUID) + { + return ""; + } + + char buffer[32] = {}; + sprintf_s(buffer, sizeof(buffer), "0x%016llx", (unsigned long long)xuid); + return buffer; + } + + bool TryParseXuid(const std::string &text, PlayerUID *outXuid) + { + if (outXuid == nullptr) + { + return false; + } + + unsigned long long parsed = 0; + if (!StringUtils::TryParseUnsignedLongLong(text, &parsed) || parsed == 0ULL) + { + return false; + } + + *outXuid = (PlayerUID)parsed; + return true; + } + + bool Initialize(const std::string &baseDirectory, bool whitelistEnabled) + { + std::lock_guard writeLock(g_accessState.writeLock); + + // Build the replacement manager privately so readers keep using the last published snapshot during disk I/O. + std::shared_ptr banManager = std::make_shared(baseDirectory); + std::shared_ptr whitelistManager = std::make_shared(baseDirectory); + if (!banManager->EnsureBanFilesExist()) + { + LogError("access", "failed to ensure dedicated server ban files exist"); + return false; + } + if (!whitelistManager->EnsureWhitelistFileExists()) + { + LogError("access", "failed to ensure dedicated server whitelist file exists"); + return false; + } + + if (!banManager->Reload()) + { + LogError("access", "failed to load dedicated server ban files"); + return false; + } + if (!whitelistManager->Reload()) + { + LogError("access", "failed to load dedicated server whitelist file"); + return false; + } + + std::vector playerEntries; + std::vector ipEntries; + std::vector whitelistEntries; + banManager->SnapshotBannedPlayers(&playerEntries); + banManager->SnapshotBannedIps(&ipEntries); + whitelistManager->SnapshotWhitelistedPlayers(&whitelistEntries); + PublishBanManagerSnapshot(banManager); + PublishWhitelistManagerSnapshot(whitelistManager); + { + std::lock_guard stateLock(g_accessState.stateLock); + g_accessState.whitelistEnabled = whitelistEnabled; + } + + LogInfof( + "access", + "loaded %u player bans, %u ip bans, and %u whitelist entries (whitelist=%s)", + (unsigned)playerEntries.size(), + (unsigned)ipEntries.size(), + (unsigned)whitelistEntries.size(), + whitelistEnabled ? "enabled" : "disabled"); + return true; + } + + void Shutdown() + { + std::lock_guard writeLock(g_accessState.writeLock); + PublishWhitelistManagerSnapshot(std::shared_ptr()); + PublishWhitelistManagerSnapshot(std::shared_ptr()); + std::lock_guard stateLock(g_accessState.stateLock); + g_accessState.whitelistEnabled = false; + } + + bool Reload() + { + std::lock_guard writeLock(g_accessState.writeLock); + std::shared_ptr current = GetBanManagerSnapshot(); + std::shared_ptr currentWhitelist = GetWhitelistManagerSnapshot(); + if (current == nullptr || currentWhitelist == nullptr) + { + return false; + } + + std::shared_ptr banManager = std::make_shared(*current); + std::shared_ptr whitelistManager = std::make_shared(*currentWhitelist); + if (!banManager->EnsureBanFilesExist()) + { + return false; + } + if (!whitelistManager->EnsureWhitelistFileExists()) + { + return false; + } + if (!banManager->Reload()) + { + return false; + } + if (!whitelistManager->Reload()) + { + return false; + } + + PublishBanManagerSnapshot(banManager); + PublishWhitelistManagerSnapshot(whitelistManager); + return true; + } + + bool ReloadWhitelist() + { + std::lock_guard writeLock(g_accessState.writeLock); + const auto current = GetWhitelistManagerSnapshot(); + if (current == nullptr) + { + return false; + } + + auto whitelistManager = std::make_shared(*current); + if (!whitelistManager->EnsureWhitelistFileExists()) + { + return false; + } + if (!whitelistManager->Reload()) + { + return false; + } + + PublishWhitelistManagerSnapshot(whitelistManager); + return true; + } + + bool IsInitialized() + { + return GetBanManagerSnapshot() != nullptr && GetWhitelistManagerSnapshot() != nullptr; + } + + bool IsWhitelistEnabled() + { + std::lock_guard stateLock(g_accessState.stateLock); + return g_accessState.whitelistEnabled; + } + + void SetWhitelistEnabled(bool enabled) + { + std::lock_guard writeLock(g_accessState.writeLock); + std::lock_guard stateLock(g_accessState.stateLock); + g_accessState.whitelistEnabled = enabled; + } + + bool IsPlayerBanned(PlayerUID xuid) + { + const std::string formatted = FormatXuid(xuid); + if (formatted.empty()) + { + return false; + } + + std::shared_ptr banManager = GetBanManagerSnapshot(); + return (banManager != nullptr) ? banManager->IsPlayerBannedByXuid(formatted) : false; + } + + bool IsIpBanned(const std::string &ip) + { + std::shared_ptr banManager = GetBanManagerSnapshot(); + return (banManager != nullptr) ? banManager->IsIpBanned(ip) : false; + } + + bool IsPlayerWhitelisted(PlayerUID xuid) + { + const std::string formatted = FormatXuid(xuid); + if (formatted.empty()) + { + return false; + } + + std::shared_ptr whitelistManager = GetWhitelistManagerSnapshot(); + return (whitelistManager != nullptr) ? whitelistManager->IsPlayerWhitelistedByXuid(formatted) : false; + } + + bool AddPlayerBan(PlayerUID xuid, const std::string &name, const BanMetadata &metadata) + { + const std::string formatted = FormatXuid(xuid); + if (formatted.empty()) + { + return false; + } + + std::lock_guard writeLock(g_accessState.writeLock); + std::shared_ptr current = GetBanManagerSnapshot(); + if (current == nullptr) + { + return false; + } + + std::shared_ptr banManager = std::make_shared(*current); + BannedPlayerEntry entry; + entry.xuid = formatted; + entry.name = name; + entry.metadata = metadata; + if (!banManager->AddPlayerBan(entry)) + { + return false; + } + + PublishBanManagerSnapshot(banManager); + return true; + } + + bool AddIpBan(const std::string &ip, const BanMetadata &metadata) + { + std::lock_guard writeLock(g_accessState.writeLock); + std::shared_ptr current = GetBanManagerSnapshot(); + if (current == nullptr) + { + return false; + } + + std::shared_ptr banManager = std::make_shared(*current); + BannedIpEntry entry; + entry.ip = ip; + entry.metadata = metadata; + if (!banManager->AddIpBan(entry)) + { + return false; + } + + PublishBanManagerSnapshot(banManager); + return true; + } + + bool RemovePlayerBan(PlayerUID xuid) + { + const std::string formatted = FormatXuid(xuid); + if (formatted.empty()) + { + return false; + } + + std::lock_guard writeLock(g_accessState.writeLock); + std::shared_ptr current = GetBanManagerSnapshot(); + if (current == nullptr) + { + return false; + } + + std::shared_ptr banManager = std::make_shared(*current); + if (!banManager->RemovePlayerBanByXuid(formatted)) + { + return false; + } + + PublishBanManagerSnapshot(banManager); + return true; + } + + bool RemoveIpBan(const std::string &ip) + { + std::lock_guard writeLock(g_accessState.writeLock); + std::shared_ptr current = GetBanManagerSnapshot(); + if (current == nullptr) + { + return false; + } + + std::shared_ptr banManager = std::make_shared(*current); + if (!banManager->RemoveIpBan(ip)) + { + return false; + } + + PublishBanManagerSnapshot(banManager); + return true; + } + + bool AddWhitelistedPlayer(PlayerUID xuid, const std::string &name, const WhitelistMetadata &metadata) + { + const auto formatted = FormatXuid(xuid); + if (formatted.empty()) + { + return false; + } + + std::lock_guard writeLock(g_accessState.writeLock); + const auto current = GetWhitelistManagerSnapshot(); + if (current == nullptr) + { + return false; + } + + auto whitelistManager = std::make_shared(*current); + const WhitelistedPlayerEntry entry = { formatted, name, metadata }; + if (!whitelistManager->AddPlayer(entry)) + { + return false; + } + + PublishWhitelistManagerSnapshot(whitelistManager); + return true; + } + + bool RemoveWhitelistedPlayer(PlayerUID xuid) + { + const auto formatted = FormatXuid(xuid); + if (formatted.empty()) + { + return false; + } + + std::lock_guard writeLock(g_accessState.writeLock); + const auto current = GetWhitelistManagerSnapshot(); + if (current == nullptr) + { + return false; + } + + auto whitelistManager = std::make_shared(*current); + if (!whitelistManager->RemovePlayerByXuid(formatted)) + { + return false; + } + + PublishWhitelistManagerSnapshot(whitelistManager); + return true; + } + + bool SnapshotBannedPlayers(std::vector *outEntries) + { + if (outEntries == nullptr) + { + return false; + } + + std::shared_ptr banManager = GetBanManagerSnapshot(); + if (banManager == nullptr) + { + outEntries->clear(); + return false; + } + + return banManager->SnapshotBannedPlayers(outEntries); + } + + bool SnapshotBannedIps(std::vector *outEntries) + { + if (outEntries == nullptr) + { + return false; + } + + std::shared_ptr banManager = GetBanManagerSnapshot(); + if (banManager == nullptr) + { + outEntries->clear(); + return false; + } + + return banManager->SnapshotBannedIps(outEntries); + } + + bool SnapshotWhitelistedPlayers(std::vector *outEntries) + { + if (outEntries == nullptr) + { + return false; + } + + const auto whitelistManager = GetWhitelistManagerSnapshot(); + if (whitelistManager == nullptr) + { + outEntries->clear(); + return false; + } + + return whitelistManager->SnapshotWhitelistedPlayers(outEntries); + } + } +} diff --git a/Minecraft.Server/Access/Access.h b/Minecraft.Server/Access/Access.h new file mode 100644 index 00000000..80e61e55 --- /dev/null +++ b/Minecraft.Server/Access/Access.h @@ -0,0 +1,47 @@ +#pragma once + +#include "BanManager.h" +#include "WhitelistManager.h" + +namespace ServerRuntime +{ + /** + * A frontend that will be general-purpose, assuming the implementation of whitelists and ops in the future. + */ + namespace Access + { + bool Initialize(const std::string &baseDirectory = ".", bool whitelistEnabled = false); + void Shutdown(); + bool Reload(); + bool ReloadWhitelist(); + bool IsInitialized(); + bool IsWhitelistEnabled(); + void SetWhitelistEnabled(bool enabled); + + bool IsPlayerBanned(PlayerUID xuid); + bool IsIpBanned(const std::string &ip); + bool IsPlayerWhitelisted(PlayerUID xuid); + + bool AddPlayerBan(PlayerUID xuid, const std::string &name, const BanMetadata &metadata); + bool AddIpBan(const std::string &ip, const BanMetadata &metadata); + bool RemovePlayerBan(PlayerUID xuid); + bool RemoveIpBan(const std::string &ip); + bool AddWhitelistedPlayer(PlayerUID xuid, const std::string &name, const WhitelistMetadata &metadata); + bool RemoveWhitelistedPlayer(PlayerUID xuid); + + /** + * Copies the current cached player bans for inspection or command output + * 現在のプレイヤーBAN一覧を複製取得 + */ + bool SnapshotBannedPlayers(std::vector *outEntries); + /** + * Copies the current cached IP bans for inspection or command output + * 現在のIP BAN一覧を複製取得 + */ + bool SnapshotBannedIps(std::vector *outEntries); + bool SnapshotWhitelistedPlayers(std::vector *outEntries); + + std::string FormatXuid(PlayerUID xuid); + bool TryParseXuid(const std::string &text, PlayerUID *outXuid); + } +} diff --git a/Minecraft.Server/Access/BanManager.cpp b/Minecraft.Server/Access/BanManager.cpp new file mode 100644 index 00000000..0b97496b --- /dev/null +++ b/Minecraft.Server/Access/BanManager.cpp @@ -0,0 +1,682 @@ +#include "stdafx.h" + +#include "BanManager.h" + +#include "../Common/AccessStorageUtils.h" +#include "../Common/FileUtils.h" +#include "../Common/NetworkUtils.h" +#include "../Common/StringUtils.h" +#include "../ServerLogger.h" +#include "../vendor/rapidjson/document.h" +#include "../vendor/rapidjson/error/en.h" +#include "../vendor/rapidjson/prettywriter.h" +#include "../vendor/rapidjson/stringbuffer.h" + +#include +#include + +namespace ServerRuntime +{ + namespace Access + { + namespace + { + static const char *kBannedPlayersFileName = "banned-players.json"; + static const char *kBannedIpsFileName = "banned-ips.json"; + static bool TryParseUtcTimestamp(const std::string &text, unsigned long long *outFileTime) + { + if (outFileTime == nullptr) + { + return false; + } + + std::string trimmed = StringUtils::TrimAscii(text); + if (trimmed.empty()) + { + return false; + } + + unsigned year = 0; + unsigned month = 0; + unsigned day = 0; + unsigned hour = 0; + unsigned minute = 0; + unsigned second = 0; + if (sscanf_s(trimmed.c_str(), "%4u-%2u-%2uT%2u:%2u:%2uZ", &year, &month, &day, &hour, &minute, &second) != 6) + { + return false; + } + + SYSTEMTIME utc = {}; + utc.wYear = (WORD)year; + utc.wMonth = (WORD)month; + utc.wDay = (WORD)day; + utc.wHour = (WORD)hour; + utc.wMinute = (WORD)minute; + utc.wSecond = (WORD)second; + + FILETIME fileTime = {}; + if (!SystemTimeToFileTime(&utc, &fileTime)) + { + return false; + } + + ULARGE_INTEGER value = {}; + value.LowPart = fileTime.dwLowDateTime; + value.HighPart = fileTime.dwHighDateTime; + *outFileTime = value.QuadPart; + return true; + } + + static bool IsMetadataExpired(const BanMetadata &metadata, unsigned long long nowFileTime) + { + if (metadata.expires.empty()) + { + return false; + } + + unsigned long long expiresFileTime = 0; + if (!TryParseUtcTimestamp(metadata.expires, &expiresFileTime)) + { + // Keep malformed metadata active instead of silently unbanning a player or address. + return false; + } + + return expiresFileTime <= nowFileTime; + } + } + BanManager::BanManager(const std::string &baseDirectory) + : m_baseDirectory(baseDirectory.empty() ? "." : baseDirectory) + { + } + + bool BanManager::EnsureBanFilesExist() const + { + const std::string playersPath = GetBannedPlayersFilePath(); + const std::string ipsPath = GetBannedIpsFilePath(); + + const bool playersOk = AccessStorageUtils::EnsureJsonListFileExists(playersPath); + const bool ipsOk = AccessStorageUtils::EnsureJsonListFileExists(ipsPath); + if (!playersOk) + { + LogErrorf("access", "failed to create %s", playersPath.c_str()); + } + if (!ipsOk) + { + LogErrorf("access", "failed to create %s", ipsPath.c_str()); + } + return playersOk && ipsOk; + } + + bool BanManager::Reload() + { + std::vector players; + std::vector ips; + + if (!LoadPlayers(&players)) + { + return false; + } + if (!LoadIps(&ips)) + { + return false; + } + + m_bannedPlayers.swap(players); + m_bannedIps.swap(ips); + return true; + } + + bool BanManager::Save() const + { + std::vector players; + std::vector ips; + return SnapshotBannedPlayers(&players) && + SnapshotBannedIps(&ips) && + SavePlayers(players) && + SaveIps(ips); + } + bool BanManager::LoadPlayers(std::vector *outEntries) const + { + if (outEntries == nullptr) + { + return false; + } + outEntries->clear(); + + std::string text; + const std::string path = GetBannedPlayersFilePath(); + if (!FileUtils::ReadTextFile(path, &text)) + { + LogErrorf("access", "failed to read %s", path.c_str()); + return false; + } + if (text.empty()) + { + text = "[]"; + } + + // Strip an optional UTF-8 BOM because some editors prepend it when rewriting JSON files. + const std::string stripped = StringUtils::StripUtf8Bom(text); + + rapidjson::Document root; + const rapidjson::ParseResult parseResult = root.Parse(stripped.c_str()); + if (!parseResult) + { + LogErrorf( + "access", + "failed to parse %s: %s (offset %zu)", + path.c_str(), + rapidjson::GetParseError_En(parseResult.Code()), + parseResult.Offset()); + return false; + } + + if (!root.IsArray()) + { + LogErrorf("access", "failed to parse %s: root json value is not an array", path.c_str()); + return false; + } + + const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime(); + for (const auto &object : root.GetArray()) + { + if (!object.IsObject()) + { + LogWarnf("access", "skipping banned player entry that is not an object in %s", path.c_str()); + continue; + } + + std::string rawXuid; + if (!AccessStorageUtils::TryGetStringField(object, "xuid", &rawXuid)) + { + LogWarnf("access", "skipping banned player entry without xuid in %s", path.c_str()); + continue; + } + + BannedPlayerEntry entry; + entry.xuid = AccessStorageUtils::NormalizeXuid(rawXuid); + if (entry.xuid.empty()) + { + LogWarnf("access", "skipping banned player entry with empty xuid in %s", path.c_str()); + continue; + } + + AccessStorageUtils::TryGetStringField(object, "name", &entry.name); + AccessStorageUtils::TryGetStringField(object, "created", &entry.metadata.created); + AccessStorageUtils::TryGetStringField(object, "source", &entry.metadata.source); + AccessStorageUtils::TryGetStringField(object, "expires", &entry.metadata.expires); + AccessStorageUtils::TryGetStringField(object, "reason", &entry.metadata.reason); + NormalizeMetadata(&entry.metadata); + + // Ignore entries that already expired before reload so the in-memory cache starts from the active set. + if (IsMetadataExpired(entry.metadata, nowFileTime)) + { + continue; + } + + outEntries->push_back(entry); + } + + return true; + } + bool BanManager::LoadIps(std::vector *outEntries) const + { + if (outEntries == nullptr) + { + return false; + } + outEntries->clear(); + + std::string text; + const std::string path = GetBannedIpsFilePath(); + if (!FileUtils::ReadTextFile(path, &text)) + { + LogErrorf("access", "failed to read %s", path.c_str()); + return false; + } + if (text.empty()) + { + text = "[]"; + } + + // Strip an optional UTF-8 BOM because some editors prepend it when rewriting JSON files. + const std::string stripped = StringUtils::StripUtf8Bom(text); + + rapidjson::Document root; + const rapidjson::ParseResult parseResult = root.Parse(stripped.c_str()); + if (!parseResult) + { + LogErrorf( + "access", + "failed to parse %s: %s (offset %zu)", + path.c_str(), + rapidjson::GetParseError_En(parseResult.Code()), + parseResult.Offset()); + return false; + } + + if (!root.IsArray()) + { + LogErrorf("access", "failed to parse %s: root json value is not an array", path.c_str()); + return false; + } + + const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime(); + for (const auto &object : root.GetArray()) + { + if (!object.IsObject()) + { + LogWarnf("access", "skipping banned ip entry that is not an object in %s", path.c_str()); + continue; + } + + std::string rawIp; + if (!AccessStorageUtils::TryGetStringField(object, "ip", &rawIp)) + { + LogWarnf("access", "skipping banned ip entry without ip in %s", path.c_str()); + continue; + } + + BannedIpEntry entry; + entry.ip = NetworkUtils::NormalizeIpToken(rawIp); + if (entry.ip.empty()) + { + LogWarnf("access", "skipping banned ip entry with empty ip in %s", path.c_str()); + continue; + } + + AccessStorageUtils::TryGetStringField(object, "created", &entry.metadata.created); + AccessStorageUtils::TryGetStringField(object, "source", &entry.metadata.source); + AccessStorageUtils::TryGetStringField(object, "expires", &entry.metadata.expires); + AccessStorageUtils::TryGetStringField(object, "reason", &entry.metadata.reason); + NormalizeMetadata(&entry.metadata); + + // Ignore entries that already expired before reload so the in-memory cache starts from the active set. + if (IsMetadataExpired(entry.metadata, nowFileTime)) + { + continue; + } + + outEntries->push_back(entry); + } + + return true; + } + bool BanManager::SavePlayers(const std::vector &entries) const + { + rapidjson::Document root(rapidjson::kArrayType); + auto &allocator = root.GetAllocator(); + for (const auto &entry : entries) + { + const std::string normalizedXuid = AccessStorageUtils::NormalizeXuid(entry.xuid); + + rapidjson::Value object(rapidjson::kObjectType); + object.AddMember( + "xuid", rapidjson::Value(normalizedXuid.c_str(), static_cast(normalizedXuid.size()), allocator), allocator); + object.AddMember( + "name", rapidjson::Value(entry.name.c_str(), static_cast(entry.name.size()), allocator), allocator); + object.AddMember( + "created", + rapidjson::Value(entry.metadata.created.c_str(), static_cast(entry.metadata.created.size()), allocator), + allocator); + object.AddMember( + "source", + rapidjson::Value(entry.metadata.source.c_str(), static_cast(entry.metadata.source.size()), allocator), + allocator); + object.AddMember( + "expires", + rapidjson::Value(entry.metadata.expires.c_str(), static_cast(entry.metadata.expires.size()), allocator), + allocator); + object.AddMember( + "reason", + rapidjson::Value(entry.metadata.reason.c_str(), static_cast(entry.metadata.reason.size()), allocator), + allocator); + root.PushBack(object, allocator); + } + + rapidjson::StringBuffer buffer; + rapidjson::PrettyWriter writer(buffer); + writer.SetIndent(' ', 2); + root.Accept(writer); + + const std::string path = GetBannedPlayersFilePath(); + const std::string json = root.Empty() ? std::string("[]\n") : (std::string(buffer.GetString(), buffer.GetSize()) + "\n"); + if (!FileUtils::WriteTextFileAtomic(path, json)) + { + LogErrorf("access", "failed to write %s", path.c_str()); + return false; + } + return true; + } + + bool BanManager::SaveIps(const std::vector &entries) const + { + rapidjson::Document root(rapidjson::kArrayType); + auto &allocator = root.GetAllocator(); + for (const auto &entry : entries) + { + const std::string normalizedIp = NetworkUtils::NormalizeIpToken(entry.ip); + + rapidjson::Value object(rapidjson::kObjectType); + object.AddMember( + "ip", rapidjson::Value(normalizedIp.c_str(), static_cast(normalizedIp.size()), allocator), allocator); + object.AddMember( + "created", + rapidjson::Value(entry.metadata.created.c_str(), static_cast(entry.metadata.created.size()), allocator), + allocator); + object.AddMember( + "source", + rapidjson::Value(entry.metadata.source.c_str(), static_cast(entry.metadata.source.size()), allocator), + allocator); + object.AddMember( + "expires", + rapidjson::Value(entry.metadata.expires.c_str(), static_cast(entry.metadata.expires.size()), allocator), + allocator); + object.AddMember( + "reason", + rapidjson::Value(entry.metadata.reason.c_str(), static_cast(entry.metadata.reason.size()), allocator), + allocator); + root.PushBack(object, allocator); + } + + rapidjson::StringBuffer buffer; + rapidjson::PrettyWriter writer(buffer); + writer.SetIndent(' ', 2); + root.Accept(writer); + + const std::string path = GetBannedIpsFilePath(); + const std::string json = root.Empty() ? std::string("[]\n") : (std::string(buffer.GetString(), buffer.GetSize()) + "\n"); + if (!FileUtils::WriteTextFileAtomic(path, json)) + { + LogErrorf("access", "failed to write %s", path.c_str()); + return false; + } + return true; + } + + const std::vector &BanManager::GetBannedPlayers() const + { + return m_bannedPlayers; + } + + const std::vector &BanManager::GetBannedIps() const + { + return m_bannedIps; + } + + bool BanManager::SnapshotBannedPlayers(std::vector *outEntries) const + { + if (outEntries == nullptr) + { + return false; + } + + outEntries->clear(); + outEntries->reserve(m_bannedPlayers.size()); + + const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime(); + for (const auto &entry : m_bannedPlayers) + { + if (!IsMetadataExpired(entry.metadata, nowFileTime)) + { + outEntries->push_back(entry); + } + } + return true; + } + + bool BanManager::SnapshotBannedIps(std::vector *outEntries) const + { + if (outEntries == nullptr) + { + return false; + } + + outEntries->clear(); + outEntries->reserve(m_bannedIps.size()); + + const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime(); + for (const auto &entry : m_bannedIps) + { + if (!IsMetadataExpired(entry.metadata, nowFileTime)) + { + outEntries->push_back(entry); + } + } + return true; + } + bool BanManager::IsPlayerBannedByXuid(const std::string &xuid) const + { + const std::string normalized = AccessStorageUtils::NormalizeXuid(xuid); + if (normalized.empty()) + { + return false; + } + + const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime(); + return std::any_of( + m_bannedPlayers.begin(), + m_bannedPlayers.end(), + [&normalized, nowFileTime](const BannedPlayerEntry &entry) + { + return entry.xuid == normalized && !IsMetadataExpired(entry.metadata, nowFileTime); + }); + } + + bool BanManager::IsIpBanned(const std::string &ip) const + { + const std::string normalized = NetworkUtils::NormalizeIpToken(ip); + if (normalized.empty()) + { + return false; + } + + const unsigned long long nowFileTime = FileUtils::GetCurrentUtcFileTime(); + return std::any_of( + m_bannedIps.begin(), + m_bannedIps.end(), + [&normalized, nowFileTime](const BannedIpEntry &entry) + { + return entry.ip == normalized && !IsMetadataExpired(entry.metadata, nowFileTime); + }); + } + + bool BanManager::AddPlayerBan(const BannedPlayerEntry &entry) + { + std::vector updatedEntries; + if (!SnapshotBannedPlayers(&updatedEntries)) + { + return false; + } + + BannedPlayerEntry normalized = entry; + normalized.xuid = AccessStorageUtils::NormalizeXuid(normalized.xuid); + NormalizeMetadata(&normalized.metadata); + if (normalized.xuid.empty()) + { + return false; + } + + const auto existing = std::find_if( + updatedEntries.begin(), + updatedEntries.end(), + [&normalized](const BannedPlayerEntry &candidate) + { + return candidate.xuid == normalized.xuid; + }); + if (existing != updatedEntries.end()) + { + // Update the existing entry in place so the stored list remains unique by canonical XUID. + *existing = normalized; + if (!SavePlayers(updatedEntries)) + { + return false; + } + m_bannedPlayers.swap(updatedEntries); + return true; + } + + updatedEntries.push_back(normalized); + if (!SavePlayers(updatedEntries)) + { + return false; + } + m_bannedPlayers.swap(updatedEntries); + return true; + } + + bool BanManager::AddIpBan(const BannedIpEntry &entry) + { + std::vector updatedEntries; + if (!SnapshotBannedIps(&updatedEntries)) + { + return false; + } + + BannedIpEntry normalized = entry; + normalized.ip = NetworkUtils::NormalizeIpToken(normalized.ip); + NormalizeMetadata(&normalized.metadata); + if (normalized.ip.empty()) + { + return false; + } + + const auto existing = std::find_if( + updatedEntries.begin(), + updatedEntries.end(), + [&normalized](const BannedIpEntry &candidate) + { + return candidate.ip == normalized.ip; + }); + if (existing != updatedEntries.end()) + { + // Update the existing entry in place so the stored list remains unique by normalized IP. + *existing = normalized; + if (!SaveIps(updatedEntries)) + { + return false; + } + m_bannedIps.swap(updatedEntries); + return true; + } + + updatedEntries.push_back(normalized); + if (!SaveIps(updatedEntries)) + { + return false; + } + m_bannedIps.swap(updatedEntries); + return true; + } + + bool BanManager::RemovePlayerBanByXuid(const std::string &xuid) + { + const std::string normalized = AccessStorageUtils::NormalizeXuid(xuid); + if (normalized.empty()) + { + return false; + } + + std::vector updatedEntries; + if (!SnapshotBannedPlayers(&updatedEntries)) + { + return false; + } + + size_t oldSize = updatedEntries.size(); + updatedEntries.erase( + std::remove_if( + updatedEntries.begin(), + updatedEntries.end(), + [&normalized](const BannedPlayerEntry &entry) { return entry.xuid == normalized; }), + updatedEntries.end()); + + if (updatedEntries.size() == oldSize) + { + return false; + } + if (!SavePlayers(updatedEntries)) + { + return false; + } + m_bannedPlayers.swap(updatedEntries); + return true; + } + + + bool BanManager::RemoveIpBan(const std::string &ip) + { + const std::string normalized = NetworkUtils::NormalizeIpToken(ip); + if (normalized.empty()) + { + return false; + } + + std::vector updatedEntries; + if (!SnapshotBannedIps(&updatedEntries)) + { + return false; + } + + size_t oldSize = updatedEntries.size(); + updatedEntries.erase( + std::remove_if( + updatedEntries.begin(), + updatedEntries.end(), + [&normalized](const BannedIpEntry &entry) { return entry.ip == normalized; }), + updatedEntries.end()); + + if (updatedEntries.size() == oldSize) + { + return false; + } + if (!SaveIps(updatedEntries)) + { + return false; + } + m_bannedIps.swap(updatedEntries); + return true; + } + std::string BanManager::GetBannedPlayersFilePath() const + { + return BuildPath(kBannedPlayersFileName); + } + + std::string BanManager::GetBannedIpsFilePath() const + { + return BuildPath(kBannedIpsFileName); + } + + + BanMetadata BanManager::BuildDefaultMetadata(const char *source) + { + BanMetadata metadata; + metadata.created = StringUtils::GetCurrentUtcTimestampIso8601(); + metadata.source = (source != nullptr) ? source : "Server"; + metadata.expires = ""; + metadata.reason = ""; + return metadata; + } + + + void BanManager::NormalizeMetadata(BanMetadata *metadata) + { + if (metadata == nullptr) + { + return; + } + + metadata->created = StringUtils::TrimAscii(metadata->created); + metadata->source = StringUtils::TrimAscii(metadata->source); + metadata->expires = StringUtils::TrimAscii(metadata->expires); + metadata->reason = StringUtils::TrimAscii(metadata->reason); + } + + + std::string BanManager::BuildPath(const char *fileName) const + { + return AccessStorageUtils::BuildPathFromBaseDirectory(m_baseDirectory, fileName); + } + } +} diff --git a/Minecraft.Server/Access/BanManager.h b/Minecraft.Server/Access/BanManager.h new file mode 100644 index 00000000..59103bec --- /dev/null +++ b/Minecraft.Server/Access/BanManager.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include + +namespace ServerRuntime +{ + namespace Access + { + /** + * Information shared with player bans and IP bans + * プレイヤーBANとIP BANで共有する情報 + */ + struct BanMetadata + { + std::string created; + std::string source; + std::string expires; + std::string reason; + }; + + struct BannedPlayerEntry + { + std::string xuid; + std::string name; + BanMetadata metadata; + }; + + struct BannedIpEntry + { + std::string ip; + BanMetadata metadata; + }; + + /** + * Dedicated server BAN file manager. + * + * Files: + * - banned-players.json + * - banned-ips.json + * + * This class only handles storage/caching. + * Connection-time hooks are wired separately. + */ + class BanManager + { + public: + /** + * **Create Ban Manager** + * + * Binds the manager to the directory that stores the dedicated-server access files + * Dedicated Server のアクセスファイル配置先を設定する + */ + explicit BanManager(const std::string &baseDirectory = "."); + + /** + * Creates empty JSON array files when the dedicated server starts without persisted access data + * BANファイルが無い初回起動時に空JSONを用意する + */ + bool EnsureBanFilesExist() const; + bool Reload(); + bool Save() const; + + bool LoadPlayers(std::vector *outEntries) const; + bool LoadIps(std::vector *outEntries) const; + bool SavePlayers(const std::vector &entries) const; + bool SaveIps(const std::vector &entries) const; + + const std::vector &GetBannedPlayers() const; + const std::vector &GetBannedIps() const; + /** + * Copies only currently active player BAN entries so expired metadata does not leak into command output + * 期限切れを除いた有効なプレイヤーBAN一覧を複製取得する + */ + bool SnapshotBannedPlayers(std::vector *outEntries) const; + /** + * Copies only currently active IP BAN entries so expired metadata does not leak into command output + * 期限切れを除いた有効なIP BAN一覧を複製取得する + */ + bool SnapshotBannedIps(std::vector *outEntries) const; + + bool IsPlayerBannedByXuid(const std::string &xuid) const; + bool IsIpBanned(const std::string &ip) const; + + bool AddPlayerBan(const BannedPlayerEntry &entry); + bool AddIpBan(const BannedIpEntry &entry); + bool RemovePlayerBanByXuid(const std::string &xuid); + bool RemoveIpBan(const std::string &ip); + + std::string GetBannedPlayersFilePath() const; + std::string GetBannedIpsFilePath() const; + + static BanMetadata BuildDefaultMetadata(const char *source = "Server"); + + private: + static void NormalizeMetadata(BanMetadata *metadata); + + std::string BuildPath(const char *fileName) const; + + private: + std::string m_baseDirectory; + std::vector m_bannedPlayers; + std::vector m_bannedIps; + }; + } +} diff --git a/Minecraft.Server/Access/WhitelistManager.cpp b/Minecraft.Server/Access/WhitelistManager.cpp new file mode 100644 index 00000000..6a12f0ff --- /dev/null +++ b/Minecraft.Server/Access/WhitelistManager.cpp @@ -0,0 +1,318 @@ +#include "stdafx.h" + +#include "WhitelistManager.h" + +#include "../Common/AccessStorageUtils.h" +#include "../Common/FileUtils.h" +#include "../Common/StringUtils.h" +#include "../ServerLogger.h" +#include "../vendor/rapidjson/document.h" +#include "../vendor/rapidjson/error/en.h" +#include "../vendor/rapidjson/prettywriter.h" +#include "../vendor/rapidjson/stringbuffer.h" + +#include + +namespace ServerRuntime +{ + namespace Access + { + namespace + { + static const char *kWhitelistFileName = "whitelist.json"; + } + + WhitelistManager::WhitelistManager(const std::string &baseDirectory) + : m_baseDirectory(baseDirectory.empty() ? "." : baseDirectory) + { + } + + bool WhitelistManager::EnsureWhitelistFileExists() const + { + const std::string path = GetWhitelistFilePath(); + if (!AccessStorageUtils::EnsureJsonListFileExists(path)) + { + LogErrorf("access", "failed to create %s", path.c_str()); + return false; + } + return true; + } + + bool WhitelistManager::Reload() + { + std::vector players; + if (!LoadPlayers(&players)) + { + return false; + } + + m_whitelistedPlayers.swap(players); + return true; + } + + bool WhitelistManager::Save() const + { + std::vector players; + return SnapshotWhitelistedPlayers(&players) && SavePlayers(players); + } + + bool WhitelistManager::LoadPlayers(std::vector *outEntries) const + { + if (outEntries == nullptr) + { + return false; + } + outEntries->clear(); + + std::string text; + const std::string path = GetWhitelistFilePath(); + if (!FileUtils::ReadTextFile(path, &text)) + { + LogErrorf("access", "failed to read %s", path.c_str()); + return false; + } + + if (text.empty()) + { + text = "[]"; + } + + const std::string stripped = StringUtils::StripUtf8Bom(text); + + rapidjson::Document root; + const rapidjson::ParseResult parseResult = root.Parse(stripped.c_str()); + if (!parseResult) + { + LogErrorf( + "access", + "failed to parse %s: %s (offset %zu)", + path.c_str(), + rapidjson::GetParseError_En(parseResult.Code()), + parseResult.Offset()); + return false; + } + + if (!root.IsArray()) + { + LogErrorf("access", "failed to parse %s: root json value is not an array", path.c_str()); + return false; + } + + for (const auto &object : root.GetArray()) + { + if (!object.IsObject()) + { + LogWarnf("access", "skipping whitelist entry that is not an object in %s", path.c_str()); + continue; + } + + std::string rawXuid; + if (!AccessStorageUtils::TryGetStringField(object, "xuid", &rawXuid)) + { + LogWarnf("access", "skipping whitelist entry without xuid in %s", path.c_str()); + continue; + } + + WhitelistedPlayerEntry entry; + entry.xuid = AccessStorageUtils::NormalizeXuid(rawXuid); + if (entry.xuid.empty()) + { + LogWarnf("access", "skipping whitelist entry with empty xuid in %s", path.c_str()); + continue; + } + + AccessStorageUtils::TryGetStringField(object, "name", &entry.name); + AccessStorageUtils::TryGetStringField(object, "created", &entry.metadata.created); + AccessStorageUtils::TryGetStringField(object, "source", &entry.metadata.source); + NormalizeMetadata(&entry.metadata); + + outEntries->push_back(entry); + } + + return true; + } + + bool WhitelistManager::SavePlayers(const std::vector &entries) const + { + rapidjson::Document root(rapidjson::kArrayType); + auto &allocator = root.GetAllocator(); + for (const auto &entry : entries) + { + const std::string normalizedXuid = AccessStorageUtils::NormalizeXuid(entry.xuid); + + rapidjson::Value object(rapidjson::kObjectType); + object.AddMember( + "xuid", rapidjson::Value(normalizedXuid.c_str(), static_cast(normalizedXuid.size()), allocator), allocator); + object.AddMember( + "name", rapidjson::Value(entry.name.c_str(), static_cast(entry.name.size()), allocator), allocator); + object.AddMember( + "created", + rapidjson::Value(entry.metadata.created.c_str(), static_cast(entry.metadata.created.size()), allocator), + allocator); + object.AddMember( + "source", + rapidjson::Value(entry.metadata.source.c_str(), static_cast(entry.metadata.source.size()), allocator), + allocator); + root.PushBack(object, allocator); + } + + rapidjson::StringBuffer buffer; + rapidjson::PrettyWriter writer(buffer); + writer.SetIndent(' ', 2); + root.Accept(writer); + + const std::string path = GetWhitelistFilePath(); + const std::string json = root.Empty() ? std::string("[]\n") : (std::string(buffer.GetString(), buffer.GetSize()) + "\n"); + if (!FileUtils::WriteTextFileAtomic(path, json)) + { + LogErrorf("access", "failed to write %s", path.c_str()); + return false; + } + return true; + } + + const std::vector &WhitelistManager::GetWhitelistedPlayers() const + { + return m_whitelistedPlayers; + } + + bool WhitelistManager::SnapshotWhitelistedPlayers(std::vector *outEntries) const + { + if (outEntries == nullptr) + { + return false; + } + + *outEntries = m_whitelistedPlayers; + return true; + } + + bool WhitelistManager::IsPlayerWhitelistedByXuid(const std::string &xuid) const + { + const auto normalized = AccessStorageUtils::NormalizeXuid(xuid); + if (normalized.empty()) + { + return false; + } + + return std::any_of( + m_whitelistedPlayers.begin(), + m_whitelistedPlayers.end(), + [&normalized](const WhitelistedPlayerEntry &entry) + { + return entry.xuid == normalized; + }); + } + + bool WhitelistManager::AddPlayer(const WhitelistedPlayerEntry &entry) + { + std::vector updatedEntries; + if (!SnapshotWhitelistedPlayers(&updatedEntries)) + { + return false; + } + + auto normalized = entry; + normalized.xuid = AccessStorageUtils::NormalizeXuid(normalized.xuid); + NormalizeMetadata(&normalized.metadata); + if (normalized.xuid.empty()) + { + return false; + } + + const auto existing = std::find_if( + updatedEntries.begin(), + updatedEntries.end(), + [&normalized](const WhitelistedPlayerEntry &candidate) + { + return candidate.xuid == normalized.xuid; + }); + + if (existing != updatedEntries.end()) + { + *existing = normalized; + if (!SavePlayers(updatedEntries)) + { + return false; + } + + m_whitelistedPlayers.swap(updatedEntries); + return true; + } + + updatedEntries.push_back(normalized); + if (!SavePlayers(updatedEntries)) + { + return false; + } + + m_whitelistedPlayers.swap(updatedEntries); + return true; + } + + bool WhitelistManager::RemovePlayerByXuid(const std::string &xuid) + { + const auto normalized = AccessStorageUtils::NormalizeXuid(xuid); + if (normalized.empty()) + { + return false; + } + + std::vector updatedEntries; + if (!SnapshotWhitelistedPlayers(&updatedEntries)) + { + return false; + } + + const auto oldSize = updatedEntries.size(); + updatedEntries.erase( + std::remove_if( + updatedEntries.begin(), + updatedEntries.end(), + [&normalized](const WhitelistedPlayerEntry &entry) { return entry.xuid == normalized; }), + updatedEntries.end()); + + if (updatedEntries.size() == oldSize) + { + return false; + } + + if (!SavePlayers(updatedEntries)) + { + return false; + } + + m_whitelistedPlayers.swap(updatedEntries); + return true; + } + + std::string WhitelistManager::GetWhitelistFilePath() const + { + return BuildPath(kWhitelistFileName); + } + + WhitelistMetadata WhitelistManager::BuildDefaultMetadata(const char *source) + { + WhitelistMetadata metadata; + metadata.created = StringUtils::GetCurrentUtcTimestampIso8601(); + metadata.source = (source != nullptr) ? source : "Server"; + return metadata; + } + + void WhitelistManager::NormalizeMetadata(WhitelistMetadata *metadata) + { + if (metadata == nullptr) + { + return; + } + + metadata->created = StringUtils::TrimAscii(metadata->created); + metadata->source = StringUtils::TrimAscii(metadata->source); + } + + std::string WhitelistManager::BuildPath(const char *fileName) const + { + return AccessStorageUtils::BuildPathFromBaseDirectory(m_baseDirectory, fileName); + } + } +} diff --git a/Minecraft.Server/Access/WhitelistManager.h b/Minecraft.Server/Access/WhitelistManager.h new file mode 100644 index 00000000..1c2c5a0b --- /dev/null +++ b/Minecraft.Server/Access/WhitelistManager.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +namespace ServerRuntime +{ + namespace Access + { + struct WhitelistMetadata + { + std::string created; + std::string source; + }; + + struct WhitelistedPlayerEntry + { + std::string xuid; + std::string name; + WhitelistMetadata metadata; + }; + + /** + * whitelist manager + * + * Files: + * - whitelist.json + * + * Stores and normalizes XUID-based allow entries. + */ + class WhitelistManager + { + public: + explicit WhitelistManager(const std::string &baseDirectory = "."); + + bool EnsureWhitelistFileExists() const; + bool Reload(); + bool Save() const; + + bool LoadPlayers(std::vector *outEntries) const; + bool SavePlayers(const std::vector &entries) const; + + const std::vector &GetWhitelistedPlayers() const; + bool SnapshotWhitelistedPlayers(std::vector *outEntries) const; + + bool IsPlayerWhitelistedByXuid(const std::string &xuid) const; + bool AddPlayer(const WhitelistedPlayerEntry &entry); + bool RemovePlayerByXuid(const std::string &xuid); + + std::string GetWhitelistFilePath() const; + + static WhitelistMetadata BuildDefaultMetadata(const char *source = "Server"); + + private: + static void NormalizeMetadata(WhitelistMetadata *metadata); + + std::string BuildPath(const char *fileName) const; + + private: + std::string m_baseDirectory; + std::vector m_whitelistedPlayers; + }; + } +} diff --git a/Minecraft.Server/Common/AccessStorageUtils.h b/Minecraft.Server/Common/AccessStorageUtils.h new file mode 100644 index 00000000..6dd60c9d --- /dev/null +++ b/Minecraft.Server/Common/AccessStorageUtils.h @@ -0,0 +1,105 @@ +#pragma once + +#include "FileUtils.h" +#include "StringUtils.h" + +#include "../vendor/rapidjson/document.h" + +#include + +namespace ServerRuntime +{ + namespace AccessStorageUtils + { + inline bool IsRegularFile(const std::string &path) + { + const std::wstring widePath = StringUtils::Utf8ToWide(path); + if (widePath.empty()) + { + return false; + } + + const DWORD attributes = GetFileAttributesW(widePath.c_str()); + return (attributes != INVALID_FILE_ATTRIBUTES) && ((attributes & FILE_ATTRIBUTE_DIRECTORY) == 0); + } + + inline bool EnsureJsonListFileExists(const std::string &path) + { + return IsRegularFile(path) || FileUtils::WriteTextFileAtomic(path, "[]\n"); + } + + inline bool TryGetStringField(const rapidjson::Value &object, const char *key, std::string *outValue) + { + if (key == nullptr || outValue == nullptr || !object.IsObject()) + { + return false; + } + + const auto it = object.FindMember(key); + if (it == object.MemberEnd() || !it->value.IsString()) + { + return false; + } + + *outValue = it->value.GetString(); + return true; + } + + inline std::string NormalizeXuid(const std::string &xuid) + { + const std::string trimmed = StringUtils::TrimAscii(xuid); + if (trimmed.empty()) + { + return ""; + } + + unsigned long long numericXuid = 0; + if (StringUtils::TryParseUnsignedLongLong(trimmed, &numericXuid)) + { + if (numericXuid == 0ULL) + { + return ""; + } + + char buffer[32] = {}; + sprintf_s(buffer, sizeof(buffer), "0x%016llx", numericXuid); + return buffer; + } + + return StringUtils::ToLowerAscii(trimmed); + } + + inline std::string BuildPathFromBaseDirectory(const std::string &baseDirectory, const char *fileName) + { + if (fileName == nullptr || fileName[0] == 0) + { + return ""; + } + + const std::wstring wideFileName = StringUtils::Utf8ToWide(fileName); + if (wideFileName.empty()) + { + return ""; + } + + if (baseDirectory.empty() || baseDirectory == ".") + { + return StringUtils::WideToUtf8(wideFileName); + } + + const std::wstring wideBaseDirectory = StringUtils::Utf8ToWide(baseDirectory); + if (wideBaseDirectory.empty()) + { + return StringUtils::WideToUtf8(wideFileName); + } + + const wchar_t last = wideBaseDirectory[wideBaseDirectory.size() - 1]; + if (last == L'\\' || last == L'/') + { + return StringUtils::WideToUtf8(wideBaseDirectory + wideFileName); + } + + return StringUtils::WideToUtf8(wideBaseDirectory + L"\\" + wideFileName); + } + } +} diff --git a/Minecraft.Server/Common/FileUtils.cpp b/Minecraft.Server/Common/FileUtils.cpp new file mode 100644 index 00000000..50eeeb72 --- /dev/null +++ b/Minecraft.Server/Common/FileUtils.cpp @@ -0,0 +1,146 @@ +#include "stdafx.h" + +#include "FileUtils.h" + +#include "StringUtils.h" + +#include +#include + +namespace ServerRuntime +{ + namespace FileUtils + { + namespace + { + static std::wstring ToWidePath(const std::string &filePath) + { + return StringUtils::Utf8ToWide(filePath); + } + } + + unsigned long long GetCurrentUtcFileTime() + { + FILETIME now = {}; + GetSystemTimeAsFileTime(&now); + + ULARGE_INTEGER value = {}; + value.LowPart = now.dwLowDateTime; + value.HighPart = now.dwHighDateTime; + return value.QuadPart; + } + + bool ReadTextFile(const std::string &filePath, std::string *outText) + { + if (outText == nullptr) + { + return false; + } + + outText->clear(); + + const std::wstring widePath = ToWidePath(filePath); + if (widePath.empty()) + { + return false; + } + + FILE *inFile = nullptr; + if (_wfopen_s(&inFile, widePath.c_str(), L"rb") != 0 || inFile == nullptr) + { + return false; + } + + if (fseek(inFile, 0, SEEK_END) != 0) + { + fclose(inFile); + return false; + } + + long fileSize = ftell(inFile); + if (fileSize < 0) + { + fclose(inFile); + return false; + } + + if (fseek(inFile, 0, SEEK_SET) != 0) + { + fclose(inFile); + return false; + } + + if (fileSize == 0) + { + fclose(inFile); + return true; + } + + outText->resize((size_t)fileSize); + size_t bytesRead = fread(&(*outText)[0], 1, (size_t)fileSize, inFile); + fclose(inFile); + + if (bytesRead != (size_t)fileSize) + { + outText->clear(); + return false; + } + + return true; + } + + bool WriteTextFileAtomic(const std::string &filePath, const std::string &text) + { + const std::wstring widePath = ToWidePath(filePath); + if (widePath.empty()) + { + return false; + } + + const std::wstring tmpPath = widePath + L".tmp"; + + FILE *outFile = nullptr; + if (_wfopen_s(&outFile, tmpPath.c_str(), L"wb") != 0 || outFile == nullptr) + { + return false; + } + + if (!text.empty()) + { + size_t bytesWritten = fwrite(text.data(), 1, text.size(), outFile); + if (bytesWritten != text.size()) + { + fclose(outFile); + DeleteFileW(tmpPath.c_str()); + return false; + } + } + + if (fflush(outFile) != 0 || _commit(_fileno(outFile)) != 0) + { + fclose(outFile); + DeleteFileW(tmpPath.c_str()); + return false; + } + fclose(outFile); + + DWORD attrs = GetFileAttributesW(widePath.c_str()); + if (attrs != INVALID_FILE_ATTRIBUTES && ((attrs & FILE_ATTRIBUTE_DIRECTORY) == 0)) + { + // Replace the destination without deleting the last known-good file first. + if (ReplaceFileW(widePath.c_str(), tmpPath.c_str(), nullptr, REPLACEFILE_IGNORE_MERGE_ERRORS, nullptr, nullptr)) + { + return true; + } + } + + if (MoveFileExW(tmpPath.c_str(), widePath.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) + { + return true; + } + + // Keep the temp file on failure so the original file remains recoverable and the caller can inspect the write result. + return false; + } + } +} \ No newline at end of file diff --git a/Minecraft.Server/Common/FileUtils.h b/Minecraft.Server/Common/FileUtils.h new file mode 100644 index 00000000..96e398cc --- /dev/null +++ b/Minecraft.Server/Common/FileUtils.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace ServerRuntime +{ + namespace FileUtils + { + /** + * Reads the full UTF-8 path target into memory without interpreting JSON or line endings + * UTF-8パスのテキストファイル全体をそのまま読み込む + */ + bool ReadTextFile(const std::string &filePath, std::string *outText); + /** + * Writes text through a same-directory temporary file and publishes it with a single replacement step + * 同一ディレクトリの一時ファイル経由で安全に書き換える + */ + bool WriteTextFileAtomic(const std::string &filePath, const std::string &text); + /** + * Returns the current UTC timestamp encoded in Windows FILETIME units for expiry comparisons + * 期限判定用に現在UTC時刻をWindows FILETIME単位で返す + */ + unsigned long long GetCurrentUtcFileTime(); + } +} \ No newline at end of file diff --git a/Minecraft.Server/Common/NetworkUtils.h b/Minecraft.Server/Common/NetworkUtils.h new file mode 100644 index 00000000..c77d4506 --- /dev/null +++ b/Minecraft.Server/Common/NetworkUtils.h @@ -0,0 +1,29 @@ +#pragma once + +#include "StringUtils.h" + +#include + +namespace ServerRuntime +{ + namespace NetworkUtils + { + inline std::string NormalizeIpToken(const std::string &ip) + { + return StringUtils::ToLowerAscii(StringUtils::TrimAscii(ip)); + } + + inline bool IsIpLiteral(const std::string &text) + { + const std::string trimmed = StringUtils::TrimAscii(text); + if (trimmed.empty()) + { + return false; + } + + IN_ADDR ipv4 = {}; + IN6_ADDR ipv6 = {}; + return InetPtonA(AF_INET, trimmed.c_str(), &ipv4) == 1 || InetPtonA(AF_INET6, trimmed.c_str(), &ipv6) == 1; + } + } +} diff --git a/Minecraft.Server/Common/StringUtils.cpp b/Minecraft.Server/Common/StringUtils.cpp new file mode 100644 index 00000000..40881ae7 --- /dev/null +++ b/Minecraft.Server/Common/StringUtils.cpp @@ -0,0 +1,212 @@ +#include "stdafx.h" + +#include "StringUtils.h" + +#include +#include +#include +#include + +namespace ServerRuntime +{ + namespace StringUtils + { + std::string WideToUtf8(const std::wstring &value) + { + if (value.empty()) + { + return std::string(); + } + + int charCount = WideCharToMultiByte(CP_UTF8, 0, value.c_str(), (int)value.length(), NULL, 0, NULL, NULL); + if (charCount <= 0) + { + return std::string(); + } + + std::string utf8; + utf8.resize(charCount); + WideCharToMultiByte(CP_UTF8, 0, value.c_str(), (int)value.length(), &utf8[0], charCount, NULL, NULL); + return utf8; + } + + std::wstring Utf8ToWide(const char *value) + { + if (value == NULL || value[0] == 0) + { + return std::wstring(); + } + + int wideCount = MultiByteToWideChar(CP_UTF8, 0, value, -1, NULL, 0); + if (wideCount <= 0) + { + // Fall back to the current ANSI code page so legacy non-UTF-8 inputs remain readable. + wideCount = MultiByteToWideChar(CP_ACP, 0, value, -1, NULL, 0); + if (wideCount <= 0) + { + return std::wstring(); + } + + std::wstring wide; + wide.resize(wideCount - 1); + MultiByteToWideChar(CP_ACP, 0, value, -1, &wide[0], wideCount); + return wide; + } + + std::wstring wide; + wide.resize(wideCount - 1); + MultiByteToWideChar(CP_UTF8, 0, value, -1, &wide[0], wideCount); + return wide; + } + + std::wstring Utf8ToWide(const std::string &value) + { + return Utf8ToWide(value.c_str()); + } + + std::string StripUtf8Bom(const std::string &value) + { + if (value.size() >= 3 && + (unsigned char)value[0] == 0xEF && + (unsigned char)value[1] == 0xBB && + (unsigned char)value[2] == 0xBF) + { + return value.substr(3); + } + + return value; + } + + std::string TrimAscii(const std::string &value) + { + size_t start = 0; + while (start < value.length() && std::isspace((unsigned char)value[start])) + { + ++start; + } + + size_t end = value.length(); + while (end > start && std::isspace((unsigned char)value[end - 1])) + { + --end; + } + + return value.substr(start, end - start); + } + + std::string ToLowerAscii(const std::string &value) + { + std::string lowered = value; + for (size_t i = 0; i < lowered.length(); ++i) + { + lowered[i] = (char)std::tolower((unsigned char)lowered[i]); + } + return lowered; + } + + std::string JoinTokens(const std::vector &tokens, size_t startIndex, const char *separator) + { + if (startIndex >= tokens.size()) + { + return std::string(); + } + + const auto joinSeparator = std::string((separator != nullptr) ? separator : " "); + size_t totalLength = 0; + for (size_t i = startIndex; i < tokens.size(); ++i) + { + totalLength += tokens[i].size(); + } + + totalLength += (tokens.size() - startIndex - 1) * joinSeparator.size(); + std::string joined; + joined.reserve(totalLength); + for (size_t i = startIndex; i < tokens.size(); ++i) + { + if (!joined.empty()) + { + joined += joinSeparator; + } + + joined += tokens[i]; + } + + return joined; + } + + bool StartsWithIgnoreCase(const std::string &value, const std::string &prefix) + { + if (prefix.size() > value.size()) + { + return false; + } + + for (size_t i = 0; i < prefix.size(); ++i) + { + unsigned char a = (unsigned char)value[i]; + unsigned char b = (unsigned char)prefix[i]; + if (std::tolower(a) != std::tolower(b)) + { + return false; + } + } + + return true; + } + + bool TryParseUnsignedLongLong(const std::string &value, unsigned long long *outValue) + { + if (outValue == nullptr) + { + return false; + } + + const std::string trimmed = TrimAscii(value); + if (trimmed.empty()) + { + return false; + } + + errno = 0; + char *end = nullptr; + const unsigned long long parsed = _strtoui64(trimmed.c_str(), &end, 0); + if (end == trimmed.c_str() || errno != 0) + { + return false; + } + + while (*end == ' ' || *end == '\t' || *end == '\r' || *end == '\n') + { + ++end; + } + + if (*end != 0) + { + return false; + } + + *outValue = parsed; + return true; + } + + std::string GetCurrentUtcTimestampIso8601() + { + SYSTEMTIME utc = {}; + GetSystemTime(&utc); + + char created[64] = {}; + sprintf_s( + created, + sizeof(created), + "%04u-%02u-%02uT%02u:%02u:%02uZ", + (unsigned)utc.wYear, + (unsigned)utc.wMonth, + (unsigned)utc.wDay, + (unsigned)utc.wHour, + (unsigned)utc.wMinute, + (unsigned)utc.wSecond); + return created; + } + } +} + diff --git a/Minecraft.Server/Common/StringUtils.h b/Minecraft.Server/Common/StringUtils.h new file mode 100644 index 00000000..68f818e1 --- /dev/null +++ b/Minecraft.Server/Common/StringUtils.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +namespace ServerRuntime +{ + namespace StringUtils + { + std::string WideToUtf8(const std::wstring &value); + std::wstring Utf8ToWide(const char *value); + std::wstring Utf8ToWide(const std::string &value); + std::string StripUtf8Bom(const std::string &value); + + std::string TrimAscii(const std::string &value); + std::string ToLowerAscii(const std::string &value); + std::string JoinTokens(const std::vector &tokens, size_t startIndex = 0, const char *separator = " "); + bool StartsWithIgnoreCase(const std::string &value, const std::string &prefix); + bool TryParseUnsignedLongLong(const std::string &value, unsigned long long *outValue); + std::string GetCurrentUtcTimestampIso8601(); + } +} + diff --git a/Minecraft.Server/Console/ServerCli.cpp b/Minecraft.Server/Console/ServerCli.cpp new file mode 100644 index 00000000..b633effd --- /dev/null +++ b/Minecraft.Server/Console/ServerCli.cpp @@ -0,0 +1,44 @@ +#include "stdafx.h" + +#include "ServerCli.h" + +#include "ServerCliEngine.h" +#include "ServerCliInput.h" + +namespace ServerRuntime +{ + ServerCli::ServerCli() + : m_engine(new ServerCliEngine()) + , m_input(new ServerCliInput()) + { + } + + ServerCli::~ServerCli() + { + Stop(); + } + + void ServerCli::Start() + { + if (m_input && m_engine) + { + m_input->Start(m_engine.get()); + } + } + + void ServerCli::Stop() + { + if (m_input) + { + m_input->Stop(); + } + } + + void ServerCli::Poll() + { + if (m_engine) + { + m_engine->Poll(); + } + } +} diff --git a/Minecraft.Server/Console/ServerCli.h b/Minecraft.Server/Console/ServerCli.h new file mode 100644 index 00000000..f544450b --- /dev/null +++ b/Minecraft.Server/Console/ServerCli.h @@ -0,0 +1,50 @@ +#pragma once + +#include + +namespace ServerRuntime +{ + class ServerCliEngine; + class ServerCliInput; + + /** + * **Server CLI facade** + * + * Owns the command engine and input component, and exposes a small lifecycle API. + * CLI 全体の開始・停止・更新をまとめる窓口クラス + */ + class ServerCli + { + public: + ServerCli(); + ~ServerCli(); + + /** + * **Start console input processing** + * + * Connects input to the engine and starts background reading. + * 入力処理を開始してエンジンに接続 + */ + void Start(); + + /** + * **Stop console input processing** + * + * Stops background input safely and detaches from the engine. + * 入力処理を安全に停止 + */ + void Stop(); + + /** + * **Process queued command lines** + * + * Drains commands collected by input and executes them in the main loop. + * 入力キューのコマンドを実行 + */ + void Poll(); + + private: + std::unique_ptr m_engine; + std::unique_ptr m_input; + }; +} diff --git a/Minecraft.Server/Console/ServerCliEngine.cpp b/Minecraft.Server/Console/ServerCliEngine.cpp new file mode 100644 index 00000000..b4c791ff --- /dev/null +++ b/Minecraft.Server/Console/ServerCliEngine.cpp @@ -0,0 +1,395 @@ +#include "stdafx.h" + +#include "ServerCliEngine.h" + +#include "ServerCliParser.h" +#include "ServerCliRegistry.h" +#include "commands/IServerCliCommand.h" +#include "commands/ban/CliCommandBan.h" +#include "commands/ban-ip/CliCommandBanIp.h" +#include "commands/ban-list/CliCommandBanList.h" +#include "commands/defaultgamemode/CliCommandDefaultGamemode.h" +#include "commands/enchant/CliCommandEnchant.h" +#include "commands/experience/CliCommandExperience.h" +#include "commands/gamemode/CliCommandGamemode.h" +#include "commands/give/CliCommandGive.h" +#include "commands/help/CliCommandHelp.h" +#include "commands/kill/CliCommandKill.h" +#include "commands/list/CliCommandList.h" +#include "commands/pardon/CliCommandPardon.h" +#include "commands/pardon-ip/CliCommandPardonIp.h" +#include "commands/stop/CliCommandStop.h" +#include "commands/time/CliCommandTime.h" +#include "commands/tp/CliCommandTp.h" +#include "commands/weather/CliCommandWeather.h" +#include "commands/whitelist/CliCommandWhitelist.h" +#include "../Common/StringUtils.h" +#include "../ServerShutdown.h" +#include "../ServerLogger.h" +#include "../../Minecraft.Client/MinecraftServer.h" +#include "../../Minecraft.Client/PlayerList.h" +#include "../../Minecraft.Client/ServerPlayer.h" +#include "../../Minecraft.World/CommandDispatcher.h" +#include "../../Minecraft.World/CommandSender.h" +#include "../../Minecraft.World/LevelSettings.h" +#include "../../Minecraft.World/StringHelpers.h" + +#include +#include + +namespace ServerRuntime +{ + + /** + * Create an authorized Sender to make the CLI appear as a user. + * The return value can also be used to display logs. + */ + namespace + { + class ServerCliConsoleCommandSender : public CommandSender + { + public: + explicit ServerCliConsoleCommandSender(const ServerCliEngine *engine) + : m_engine(engine) + { + } + + void sendMessage(const wstring &message, ChatPacket::EChatPacketMessage type, int customData, const wstring &additionalMessage) override + { + (void)type; + (void)customData; + (void)additionalMessage; + if (m_engine == nullptr) + { + return; + } + + m_engine->LogInfo(StringUtils::WideToUtf8(message)); + } + + bool hasPermission(EGameCommand command) override + { + (void)command; + return true; + } + + private: + const ServerCliEngine *m_engine; + }; + } + + ServerCliEngine::ServerCliEngine() + : m_registry(new ServerCliRegistry()) + , m_consoleSender(std::make_shared(this)) + { + RegisterDefaultCommands(); + } + + ServerCliEngine::~ServerCliEngine() + { + } + + void ServerCliEngine::RegisterDefaultCommands() + { + m_registry->Register(std::unique_ptr(new CliCommandHelp())); + m_registry->Register(std::unique_ptr(new CliCommandStop())); + m_registry->Register(std::unique_ptr(new CliCommandList())); + m_registry->Register(std::unique_ptr(new CliCommandBan())); + m_registry->Register(std::unique_ptr(new CliCommandBanIp())); + m_registry->Register(std::unique_ptr(new CliCommandPardon())); + m_registry->Register(std::unique_ptr(new CliCommandPardonIp())); + m_registry->Register(std::unique_ptr(new CliCommandBanList())); + m_registry->Register(std::unique_ptr(new CliCommandWhitelist())); + m_registry->Register(std::unique_ptr(new CliCommandTp())); + m_registry->Register(std::unique_ptr(new CliCommandTime())); + m_registry->Register(std::unique_ptr(new CliCommandWeather())); + m_registry->Register(std::unique_ptr(new CliCommandGive())); + m_registry->Register(std::unique_ptr(new CliCommandEnchant())); + m_registry->Register(std::unique_ptr(new CliCommandKill())); + m_registry->Register(std::unique_ptr(new CliCommandGamemode())); + m_registry->Register(std::unique_ptr(new CliCommandDefaultGamemode())); + m_registry->Register(std::unique_ptr(new CliCommandExperience())); + } + + void ServerCliEngine::EnqueueCommandLine(const std::string &line) + { + std::lock_guard lock(m_queueMutex); + m_pendingLines.push(line); + } + + void ServerCliEngine::Poll() + { + for (;;) + { + std::string line; + { + // Keep the lock scope minimal: dequeue only, execute outside. + std::lock_guard lock(m_queueMutex); + if (m_pendingLines.empty()) + { + break; + } + line = m_pendingLines.front(); + m_pendingLines.pop(); + } + + ExecuteCommandLine(line); + } + } + + bool ServerCliEngine::ExecuteCommandLine(const std::string &line) + { + // Normalize user input before parsing (trim + optional leading slash). + std::wstring wide = trimString(StringUtils::Utf8ToWide(line)); + if (wide.empty()) + { + return true; + } + + std::string normalizedLine = StringUtils::WideToUtf8(wide); + if (!normalizedLine.empty() && normalizedLine[0] == '/') + { + normalizedLine = normalizedLine.substr(1); + } + + ServerCliParsedLine parsed = ServerCliParser::Parse(normalizedLine); + if (parsed.tokens.empty()) + { + return true; + } + + IServerCliCommand *command = m_registry->FindMutable(parsed.tokens[0]); + if (command == NULL) + { + LogWarn("Unknown command: " + parsed.tokens[0]); + return false; + } + + return command->Execute(parsed, this); + } + + void ServerCliEngine::BuildCompletions(const std::string &line, std::vector *out) const + { + if (out == NULL) + { + return; + } + + out->clear(); + ServerCliCompletionContext context = ServerCliParser::BuildCompletionContext(line); + bool slashPrefixedCommand = false; + std::string commandToken; + if (!context.parsed.tokens.empty()) + { + // Completion accepts both "tp" and "/tp" style command heads. + commandToken = context.parsed.tokens[0]; + if (!commandToken.empty() && commandToken[0] == '/') + { + commandToken = commandToken.substr(1); + slashPrefixedCommand = true; + } + } + + if (context.currentTokenIndex == 0) + { + std::string prefix = context.prefix; + if (!prefix.empty() && prefix[0] == '/') + { + prefix = prefix.substr(1); + slashPrefixedCommand = true; + } + + std::string linePrefix = context.linePrefix; + if (slashPrefixedCommand && linePrefix.empty()) + { + // Preserve leading slash when user started with "/". + linePrefix = "/"; + } + + m_registry->SuggestCommandNames(prefix, linePrefix, out); + } + else + { + const IServerCliCommand *command = m_registry->Find(commandToken); + if (command != NULL) + { + command->Complete(context, this, out); + } + } + + std::unordered_set seen; + std::vector unique; + for (size_t i = 0; i < out->size(); ++i) + { + // Remove duplicates while keeping first-seen ordering. + if (seen.insert((*out)[i]).second) + { + unique.push_back((*out)[i]); + } + } + out->swap(unique); + } + + void ServerCliEngine::LogInfo(const std::string &message) const + { + LogInfof("console", "%s", message.c_str()); + } + + void ServerCliEngine::LogWarn(const std::string &message) const + { + LogWarnf("console", "%s", message.c_str()); + } + + void ServerCliEngine::LogError(const std::string &message) const + { + LogErrorf("console", "%s", message.c_str()); + } + + void ServerCliEngine::RequestShutdown() const + { + RequestDedicatedServerShutdown(); + } + + std::vector ServerCliEngine::GetOnlinePlayerNamesUtf8() const + { + std::vector result; + MinecraftServer *server = MinecraftServer::getInstance(); + if (server == NULL) + { + return result; + } + + PlayerList *players = server->getPlayers(); + if (players == NULL) + { + return result; + } + + for (size_t i = 0; i < players->players.size(); ++i) + { + std::shared_ptr player = players->players[i]; + if (player != NULL) + { + result.push_back(StringUtils::WideToUtf8(player->getName())); + } + } + + return result; + } + + std::shared_ptr ServerCliEngine::FindPlayerByNameUtf8(const std::string &name) const + { + MinecraftServer *server = MinecraftServer::getInstance(); + if (server == NULL) + { + return nullptr; + } + + PlayerList *players = server->getPlayers(); + if (players == NULL) + { + return nullptr; + } + + std::wstring target = StringUtils::Utf8ToWide(name); + for (size_t i = 0; i < players->players.size(); ++i) + { + std::shared_ptr player = players->players[i]; + if (player != NULL && equalsIgnoreCase(player->getName(), target)) + { + return player; + } + } + + return nullptr; + } + + void ServerCliEngine::SuggestPlayers(const std::string &prefix, const std::string &linePrefix, std::vector *out) const + { + std::vector players = GetOnlinePlayerNamesUtf8(); + std::string loweredPrefix = StringUtils::ToLowerAscii(prefix); + for (size_t i = 0; i < players.size(); ++i) + { + std::string loweredName = StringUtils::ToLowerAscii(players[i]); + if (loweredName.compare(0, loweredPrefix.size(), loweredPrefix) == 0) + { + out->push_back(linePrefix + players[i]); + } + } + } + + void ServerCliEngine::SuggestGamemodes(const std::string &prefix, const std::string &linePrefix, std::vector *out) const + { + static const char *kModes[] = { "survival", "creative", "s", "c", "0", "1" }; + std::string loweredPrefix = StringUtils::ToLowerAscii(prefix); + for (size_t i = 0; i < sizeof(kModes) / sizeof(kModes[0]); ++i) + { + std::string candidate = kModes[i]; + std::string loweredCandidate = StringUtils::ToLowerAscii(candidate); + if (loweredCandidate.compare(0, loweredPrefix.size(), loweredPrefix) == 0) + { + out->push_back(linePrefix + candidate); + } + } + } + + GameType *ServerCliEngine::ParseGamemode(const std::string &token) const + { + std::string lowered = StringUtils::ToLowerAscii(token); + if (lowered == "survival" || lowered == "s" || lowered == "0") + { + return GameType::SURVIVAL; + } + if (lowered == "creative" || lowered == "c" || lowered == "1") + { + return GameType::CREATIVE; + } + + char *end = NULL; + long id = strtol(lowered.c_str(), &end, 10); + if (end != NULL && *end == 0) + { + // Numeric fallback supports extended ids handled by level settings. + return LevelSettings::validateGameType((int)id); + } + + return NULL; + } + + bool ServerCliEngine::DispatchWorldCommand(EGameCommand command, byteArray commandData, const std::shared_ptr &sender) const + { + MinecraftServer *server = MinecraftServer::getInstance(); + if (server == NULL) + { + LogWarn("MinecraftServer instance is not available."); + return false; + } + + CommandDispatcher *dispatcher = server->getCommandDispatcher(); + if (dispatcher == NULL) + { + LogWarn("Command dispatcher is not available."); + return false; + } + + std::shared_ptr commandSender = sender; + if (commandSender == nullptr) + { + // fall back to console sender if caller did not provide one + commandSender = m_consoleSender; + } + if (commandSender == nullptr) + { + LogWarn("No command sender is available."); + return false; + } + + dispatcher->performCommand(commandSender, command, commandData); + return true; + } + + const ServerCliRegistry &ServerCliEngine::Registry() const + { + return *m_registry; + } +} diff --git a/Minecraft.Server/Console/ServerCliEngine.h b/Minecraft.Server/Console/ServerCliEngine.h new file mode 100644 index 00000000..be5735e8 --- /dev/null +++ b/Minecraft.Server/Console/ServerCliEngine.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "../../Minecraft.World/ArrayWithLength.h" +#include "../../Minecraft.World/CommandsEnum.h" + +class GameType; +class ServerPlayer; +class CommandSender; + +namespace ServerRuntime +{ + class ServerCliRegistry; + + /** + * **CLI execution engine** + * + * Handles parsing, command dispatch, completion suggestions, and server-side helpers. + * 解析・実行・補完エンジン + */ + class ServerCliEngine + { + public: + ServerCliEngine(); + ~ServerCliEngine(); + + /** + * **Queue one raw command line** + * + * Called by input thread; execution is deferred to `Poll()`. + * 入力行を実行キューに追加 + */ + void EnqueueCommandLine(const std::string &line); + + /** + * **Execute queued commands** + * + * Drains pending lines and dispatches them in order. + * キュー済みコマンドを順番に実行 + */ + void Poll(); + + /** + * **Execute one command line immediately** + * + * Parses and dispatches a normalized line to a registered command. + * 1行を直接パースしてコマンド実行 + */ + bool ExecuteCommandLine(const std::string &line); + + /** + * **Build completion candidates for current line** + * + * Produces command or argument suggestions based on parser context. + * 現在入力に対する補完候補を作成 + */ + void BuildCompletions(const std::string &line, std::vector *out) const; + + void LogInfo(const std::string &message) const; + void LogWarn(const std::string &message) const; + void LogError(const std::string &message) const; + void RequestShutdown() const; + + /** + * **List connected players as UTF-8 names** + * + * ここら辺は分けてもいいかも + */ + std::vector GetOnlinePlayerNamesUtf8() const; + + /** + * **Find a player by UTF-8 name** + */ + std::shared_ptr FindPlayerByNameUtf8(const std::string &name) const; + + /** + * **Suggest player-name arguments** + * + * Appends matching player candidates using the given completion prefix. + * プレイヤー名の補完候補 + */ + void SuggestPlayers(const std::string &prefix, const std::string &linePrefix, std::vector *out) const; + + /** + * **Suggest gamemode arguments** + * + * Appends standard gamemode aliases (survival/creative/0/1). + * ゲームモードの補完候補 + */ + void SuggestGamemodes(const std::string &prefix, const std::string &linePrefix, std::vector *out) const; + + /** + * **Parse gamemode token** + * + * Supports names, short aliases, and numeric ids. + * 文字列からゲームモードを解決 + */ + GameType *ParseGamemode(const std::string &token) const; + + /** + * **Dispatch one Minecraft.World game command** + * + * Uses `Minecraft.World::CommandDispatcher` for actual execution. + * When `sender` is null, an internal console command sender is used. + * + * Minecraft.Worldのコマンドを実行するためのディスパッチャーのラッパー + * 内部でsenderがnullの場合はコンソールコマンド送信者を使用 + */ + bool DispatchWorldCommand(EGameCommand command, byteArray commandData, const std::shared_ptr &sender = nullptr) const; + + const ServerCliRegistry &Registry() const; + + private: + void RegisterDefaultCommands(); + + private: + mutable std::mutex m_queueMutex; + std::queue m_pendingLines; + std::unique_ptr m_registry; + std::shared_ptr m_consoleSender; + }; +} diff --git a/Minecraft.Server/Console/ServerCliInput.cpp b/Minecraft.Server/Console/ServerCliInput.cpp new file mode 100644 index 00000000..8e50c6d6 --- /dev/null +++ b/Minecraft.Server/Console/ServerCliInput.cpp @@ -0,0 +1,285 @@ +#include "stdafx.h" + +#include "ServerCliInput.h" + +#include "ServerCliEngine.h" +#include "../ServerLogger.h" +#include "../vendor/linenoise/linenoise.h" + +#include +#include + +namespace +{ + bool UseStreamInputMode() + { + const char *mode = getenv("SERVER_CLI_INPUT_MODE"); + if (mode != NULL) + { + return _stricmp(mode, "stream") == 0 + || _stricmp(mode, "stdin") == 0; + } + + return false; + } + + int WaitForStdinReadable(HANDLE stdinHandle, DWORD waitMs) + { + if (stdinHandle == NULL || stdinHandle == INVALID_HANDLE_VALUE) + { + return -1; + } + + DWORD fileType = GetFileType(stdinHandle); + if (fileType == FILE_TYPE_PIPE) + { + DWORD available = 0; + if (!PeekNamedPipe(stdinHandle, NULL, 0, NULL, &available, NULL)) + { + return -1; + } + return available > 0 ? 1 : 0; + } + + if (fileType == FILE_TYPE_CHAR) + { + // console/pty char handles are often not waitable across Wine+Docker. + return 1; + } + + DWORD waitResult = WaitForSingleObject(stdinHandle, waitMs); + if (waitResult == WAIT_OBJECT_0) + { + return 1; + } + if (waitResult == WAIT_TIMEOUT) + { + return 0; + } + + return -1; + } +} + +namespace ServerRuntime +{ + // C-style completion callback bridge requires a static instance pointer. + ServerCliInput *ServerCliInput::s_instance = NULL; + + ServerCliInput::ServerCliInput() + : m_running(false) + , m_engine(NULL) + { + } + + ServerCliInput::~ServerCliInput() + { + Stop(); + } + + void ServerCliInput::Start(ServerCliEngine *engine) + { + if (engine == NULL || m_running.exchange(true)) + { + return; + } + + m_engine = engine; + s_instance = this; + linenoiseResetStop(); + linenoiseHistorySetMaxLen(128); + linenoiseSetCompletionCallback(&ServerCliInput::CompletionThunk); + m_inputThread = std::thread(&ServerCliInput::RunInputLoop, this); + LogInfo("console", "CLI input thread started."); + } + + void ServerCliInput::Stop() + { + if (!m_running.exchange(false)) + { + return; + } + + // Ask linenoise to break out first, then join thread safely. + linenoiseRequestStop(); + if (m_inputThread.joinable()) + { + CancelSynchronousIo((HANDLE)m_inputThread.native_handle()); + m_inputThread.join(); + } + linenoiseSetCompletionCallback(NULL); + + if (s_instance == this) + { + s_instance = NULL; + } + + m_engine = NULL; + LogInfo("console", "CLI input thread stopped."); + } + + bool ServerCliInput::IsRunning() const + { + return m_running.load(); + } + + void ServerCliInput::RunInputLoop() + { + if (UseStreamInputMode()) + { + LogInfo("console", "CLI input mode: stream(file stdin)"); + RunStreamInputLoop(); + return; + } + + RunLinenoiseLoop(); + } + + /** + * use linenoise for interactive console input, with line editing and history support + */ + void ServerCliInput::RunLinenoiseLoop() + { + while (m_running) + { + char *line = linenoise("server> "); + if (line == NULL) + { + // NULL is expected on stop request (or Ctrl+C inside linenoise). + if (!m_running) + { + break; + } + Sleep(10); + continue; + } + + EnqueueLine(line); + + linenoiseFree(line); + } + } + + /** + * use file-based stdin reading instead of linenoise when requested or when stdin is not a console/pty (e.g. piped input or non-interactive docker) + */ + void ServerCliInput::RunStreamInputLoop() + { + HANDLE stdinHandle = GetStdHandle(STD_INPUT_HANDLE); + if (stdinHandle == NULL || stdinHandle == INVALID_HANDLE_VALUE) + { + LogWarn("console", "stream input mode requested but STDIN handle is unavailable; falling back to linenoise."); + RunLinenoiseLoop(); + return; + } + + std::string line; + bool skipNextLf = false; + + printf("server> "); + fflush(stdout); + + while (m_running) + { + int readable = WaitForStdinReadable(stdinHandle, 50); + if (readable <= 0) + { + Sleep(10); + continue; + } + + char ch = 0; + DWORD bytesRead = 0; + if (!ReadFile(stdinHandle, &ch, 1, &bytesRead, NULL) || bytesRead == 0) + { + Sleep(10); + continue; + } + + if (skipNextLf && ch == '\n') + { + skipNextLf = false; + continue; + } + + if (ch == '\r' || ch == '\n') + { + if (ch == '\r') + { + skipNextLf = true; + } + else + { + skipNextLf = false; + } + + if (!line.empty()) + { + EnqueueLine(line.c_str()); + line.clear(); + } + + printf("server> "); + fflush(stdout); + continue; + } + + skipNextLf = false; + + if ((unsigned char)ch == 3) + { + continue; + } + + if ((unsigned char)ch == 8 || (unsigned char)ch == 127) + { + if (!line.empty()) + { + line.resize(line.size() - 1); + } + continue; + } + + if (isprint((unsigned char)ch) && line.size() < 4096) + { + line.push_back(ch); + } + } + } + + void ServerCliInput::EnqueueLine(const char *line) + { + if (line == NULL || line[0] == 0 || m_engine == NULL) + { + return; + } + + // Keep local history and forward command for main-thread execution. + linenoiseHistoryAdd(line); + m_engine->EnqueueCommandLine(line); + } + + void ServerCliInput::CompletionThunk(const char *line, linenoiseCompletions *completions) + { + // Static thunk forwards callback into instance state. + if (s_instance != NULL) + { + s_instance->BuildCompletions(line, completions); + } + } + + void ServerCliInput::BuildCompletions(const char *line, linenoiseCompletions *completions) + { + if (line == NULL || completions == NULL || m_engine == NULL) + { + return; + } + + std::vector suggestions; + m_engine->BuildCompletions(line, &suggestions); + for (size_t i = 0; i < suggestions.size(); ++i) + { + linenoiseAddCompletion(completions, suggestions[i].c_str()); + } + } +} diff --git a/Minecraft.Server/Console/ServerCliInput.h b/Minecraft.Server/Console/ServerCliInput.h new file mode 100644 index 00000000..83221a2c --- /dev/null +++ b/Minecraft.Server/Console/ServerCliInput.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +struct linenoiseCompletions; + +namespace ServerRuntime +{ + class ServerCliEngine; + + /** + * **CLI input worker** + * + * Owns the interactive input thread and bridges linenoise callbacks to the engine. + * 入力スレッドと補完コールバックを管理するクラス + */ + class ServerCliInput + { + public: + ServerCliInput(); + ~ServerCliInput(); + + /** + * **Start input loop** + * + * Binds to an engine and starts reading user input from the console. + * エンジンに接続して入力ループを開始 + */ + void Start(ServerCliEngine *engine); + + /** + * **Stop input loop** + * + * Requests stop and joins the input thread. + * 停止要求を出して入力スレッドを終了 + */ + void Stop(); + + /** + * **Check running state** + * + * Returns true while the input thread is active. + * 入力処理が動作中かどうか + */ + bool IsRunning() const; + + private: + void RunInputLoop(); + void RunLinenoiseLoop(); + void RunStreamInputLoop(); + void EnqueueLine(const char *line); + static void CompletionThunk(const char *line, linenoiseCompletions *completions); + void BuildCompletions(const char *line, linenoiseCompletions *completions); + + private: + std::atomic m_running; + std::thread m_inputThread; + ServerCliEngine *m_engine; + + static ServerCliInput *s_instance; + }; +} diff --git a/Minecraft.Server/Console/ServerCliParser.cpp b/Minecraft.Server/Console/ServerCliParser.cpp new file mode 100644 index 00000000..5888153f --- /dev/null +++ b/Minecraft.Server/Console/ServerCliParser.cpp @@ -0,0 +1,116 @@ +#include "stdafx.h" + +#include "ServerCliParser.h" + +namespace ServerRuntime +{ + static void TokenizeLine(const std::string &line, std::vector *tokens, bool *trailingSpace) + { + std::string current; + bool inQuotes = false; + bool escaped = false; + + tokens->clear(); + *trailingSpace = false; + + for (size_t i = 0; i < line.size(); ++i) + { + char ch = line[i]; + if (escaped) + { + // Keep escaped character literally (e.g. \" or \ ). + current.push_back(ch); + escaped = false; + continue; + } + + if (ch == '\\') + { + escaped = true; + continue; + } + + if (ch == '"') + { + // Double quotes group spaces into one token. + inQuotes = !inQuotes; + continue; + } + + if (!inQuotes && (ch == ' ' || ch == '\t')) + { + if (!current.empty()) + { + tokens->push_back(current); + current.clear(); + } + continue; + } + + current.push_back(ch); + } + + if (!current.empty()) + { + tokens->push_back(current); + } + + if (!line.empty()) + { + char tail = line[line.size() - 1]; + // Trailing space means completion targets the next token slot. + *trailingSpace = (!inQuotes && (tail == ' ' || tail == '\t')); + } + } + + ServerCliParsedLine ServerCliParser::Parse(const std::string &line) + { + ServerCliParsedLine parsed; + parsed.raw = line; + TokenizeLine(line, &parsed.tokens, &parsed.trailingSpace); + return parsed; + } + + ServerCliCompletionContext ServerCliParser::BuildCompletionContext(const std::string &line) + { + ServerCliCompletionContext context; + context.parsed = Parse(line); + + if (context.parsed.tokens.empty()) + { + context.currentTokenIndex = 0; + context.prefix.clear(); + context.linePrefix.clear(); + return context; + } + + if (context.parsed.trailingSpace) + { + // Cursor is after a separator, so complete a new token. + context.currentTokenIndex = context.parsed.tokens.size(); + context.prefix.clear(); + } + else + { + // Cursor is inside current token, so complete by its prefix. + context.currentTokenIndex = context.parsed.tokens.size() - 1; + context.prefix = context.parsed.tokens.back(); + } + + for (size_t i = 0; i < context.currentTokenIndex; ++i) + { + // linePrefix is the immutable left side reused by completion output. + if (!context.linePrefix.empty()) + { + context.linePrefix.push_back(' '); + } + context.linePrefix += context.parsed.tokens[i]; + } + if (!context.linePrefix.empty()) + { + context.linePrefix.push_back(' '); + } + + return context; + } +} diff --git a/Minecraft.Server/Console/ServerCliParser.h b/Minecraft.Server/Console/ServerCliParser.h new file mode 100644 index 00000000..a84d179b --- /dev/null +++ b/Minecraft.Server/Console/ServerCliParser.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include + +namespace ServerRuntime +{ + /** + * **Parsed command line** + */ + struct ServerCliParsedLine + { + std::string raw; + std::vector tokens; + bool trailingSpace; + + ServerCliParsedLine() + : trailingSpace(false) + { + } + }; + + /** + * **Completion context for one input line** + * + * Indicates current token index, token prefix, and the fixed line prefix. + */ + struct ServerCliCompletionContext + { + ServerCliParsedLine parsed; + size_t currentTokenIndex; + std::string prefix; + std::string linePrefix; + + ServerCliCompletionContext() + : currentTokenIndex(0) + { + } + }; + + /** + * **CLI parser helpers** + * + * Converts raw input text into tokenized data used by execution and completion. + */ + class ServerCliParser + { + public: + /** + * **Tokenize one command line** + * + * Supports quoted segments and escaped characters. + */ + static ServerCliParsedLine Parse(const std::string &line); + + /** + * **Build completion metadata** + * + * Determines active token position and reusable prefix parts. + */ + static ServerCliCompletionContext BuildCompletionContext(const std::string &line); + }; +} diff --git a/Minecraft.Server/Console/ServerCliRegistry.cpp b/Minecraft.Server/Console/ServerCliRegistry.cpp new file mode 100644 index 00000000..183eb915 --- /dev/null +++ b/Minecraft.Server/Console/ServerCliRegistry.cpp @@ -0,0 +1,99 @@ +#include "stdafx.h" + +#include "ServerCliRegistry.h" + +#include "commands/IServerCliCommand.h" +#include "../Common/StringUtils.h" + +namespace ServerRuntime +{ + bool ServerCliRegistry::Register(std::unique_ptr command) + { + if (!command) + { + return false; + } + + IServerCliCommand *raw = command.get(); + std::string baseName = StringUtils::ToLowerAscii(raw->Name()); + // Reject empty/duplicate primary command names. + if (baseName.empty() || m_lookup.find(baseName) != m_lookup.end()) + { + return false; + } + std::vector aliases = raw->Aliases(); + std::vector normalizedAliases; + normalizedAliases.reserve(aliases.size()); + for (size_t i = 0; i < aliases.size(); ++i) + { + std::string alias = StringUtils::ToLowerAscii(aliases[i]); + // Alias must also be unique across all names and aliases. + if (alias.empty() || m_lookup.find(alias) != m_lookup.end()) + { + return false; + } + normalizedAliases.push_back(alias); + } + + m_lookup[baseName] = raw; + for (size_t i = 0; i < normalizedAliases.size(); ++i) + { + m_lookup[normalizedAliases[i]] = raw; + } + + // Command objects are owned here; lookup stores non-owning pointers. + m_commands.push_back(std::move(command)); + return true; + } + + const IServerCliCommand *ServerCliRegistry::Find(const std::string &name) const + { + std::string key = StringUtils::ToLowerAscii(name); + auto it = m_lookup.find(key); + if (it == m_lookup.end()) + { + return NULL; + } + return it->second; + } + + IServerCliCommand *ServerCliRegistry::FindMutable(const std::string &name) + { + std::string key = StringUtils::ToLowerAscii(name); + auto it = m_lookup.find(key); + if (it == m_lookup.end()) + { + return NULL; + } + return it->second; + } + + void ServerCliRegistry::SuggestCommandNames(const std::string &prefix, const std::string &linePrefix, std::vector *out) const + { + for (size_t i = 0; i < m_commands.size(); ++i) + { + const IServerCliCommand *command = m_commands[i].get(); + std::string name = command->Name(); + if (StringUtils::StartsWithIgnoreCase(name, prefix)) + { + out->push_back(linePrefix + name); + } + + // Include aliases so users can discover shorthand commands. + std::vector aliases = command->Aliases(); + for (size_t aliasIndex = 0; aliasIndex < aliases.size(); ++aliasIndex) + { + if (StringUtils::StartsWithIgnoreCase(aliases[aliasIndex], prefix)) + { + out->push_back(linePrefix + aliases[aliasIndex]); + } + } + } + } + + const std::vector> &ServerCliRegistry::Commands() const + { + return m_commands; + } +} + diff --git a/Minecraft.Server/Console/ServerCliRegistry.h b/Minecraft.Server/Console/ServerCliRegistry.h new file mode 100644 index 00000000..5104b534 --- /dev/null +++ b/Minecraft.Server/Console/ServerCliRegistry.h @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include + +namespace ServerRuntime +{ + class IServerCliCommand; + + /** + * **CLI command registry** + */ + class ServerCliRegistry + { + public: + /** + * **Register a command object** + * + * Validates name/aliases and adds lookup entries. + * コマンドの追加 + */ + bool Register(std::unique_ptr command); + + /** + * **Find command by name or alias (const)** + * + * Returns null when no match exists. + */ + const IServerCliCommand *Find(const std::string &name) const; + + /** + * **Find mutable command by name or alias** + * + * Used by runtime dispatch path. + */ + IServerCliCommand *FindMutable(const std::string &name); + + /** + * **Suggest top-level command names** + * + * Adds matching command names and aliases to the output list. + */ + void SuggestCommandNames(const std::string &prefix, const std::string &linePrefix, std::vector *out) const; + + /** + * **Get registered command list** + * + * Intended for help output and inspection. + */ + const std::vector> &Commands() const; + + private: + std::vector> m_commands; + std::unordered_map m_lookup; + }; +} diff --git a/Minecraft.Server/Console/commands/CommandParsing.h b/Minecraft.Server/Console/commands/CommandParsing.h new file mode 100644 index 00000000..edef68d0 --- /dev/null +++ b/Minecraft.Server/Console/commands/CommandParsing.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include + +namespace ServerRuntime +{ + namespace CommandParsing + { + inline bool TryParseInt(const std::string &text, int *outValue) + { + if (outValue == nullptr || text.empty()) + { + return false; + } + + char *end = nullptr; + errno = 0; + const long parsedValue = std::strtol(text.c_str(), &end, 10); + if (end == text.c_str() || *end != '\0') + { + return false; + } + if (errno == ERANGE) + { + return false; + } + if (parsedValue < (std::numeric_limits::min)() || parsedValue > (std::numeric_limits::max)()) + { + return false; + } + + *outValue = static_cast(parsedValue); + return true; + } + } +} diff --git a/Minecraft.Server/Console/commands/IServerCliCommand.h b/Minecraft.Server/Console/commands/IServerCliCommand.h new file mode 100644 index 00000000..e68bfd0a --- /dev/null +++ b/Minecraft.Server/Console/commands/IServerCliCommand.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +namespace ServerRuntime +{ + class ServerCliEngine; + struct ServerCliParsedLine; + struct ServerCliCompletionContext; + + /** + * **Command interface for server CLI** + * + * Implement this contract to add new commands without changing engine internals. + */ + class IServerCliCommand + { + public: + virtual ~IServerCliCommand() {} + + /** Primary command name */ + virtual const char *Name() const = 0; + /** Optional aliases */ + virtual std::vector Aliases() const { return std::vector(); } + /** Usage text for help */ + virtual const char *Usage() const = 0; + /** Short command description*/ + virtual const char *Description() const = 0; + + /** + * **Execute command logic** + * + * Called after tokenization and command lookup. + */ + virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) = 0; + + /** + * **Provide argument completion candidates** + * + * Override when command-specific completion is needed. + */ + virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + (void)engine; + (void)out; + } + }; +} diff --git a/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp b/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp new file mode 100644 index 00000000..96ac1784 --- /dev/null +++ b/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.cpp @@ -0,0 +1,171 @@ +#include "stdafx.h" + +#include "CliCommandBanIp.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../Access/Access.h" +#include "../../../Common/NetworkUtils.h" +#include "../../../Common/StringUtils.h" +#include "../../../ServerLogManager.h" +#include "../../../../Minecraft.Client/MinecraftServer.h" +#include "../../../../Minecraft.Client/PlayerConnection.h" +#include "../../../../Minecraft.Client/PlayerList.h" +#include "../../../../Minecraft.Client/ServerPlayer.h" +#include "../../../../Minecraft.World/Connection.h" +#include "../../../../Minecraft.World/DisconnectPacket.h" + +namespace ServerRuntime +{ + namespace + { + // The dedicated server keeps the accepted remote IP in ServerLogManager, keyed by connection smallId. + // It's a bit strange from a responsibility standpoint, so we'll need to implement it separately. + static bool TryGetPlayerRemoteIp(const std::shared_ptr &player, std::string *outIp) + { + if (outIp == nullptr || player == nullptr || player->connection == nullptr || player->connection->connection == nullptr || player->connection->connection->getSocket() == nullptr) + { + return false; + } + + const unsigned char smallId = player->connection->connection->getSocket()->getSmallId(); + if (smallId == 0) + { + return false; + } + + return ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, outIp); + } + + // After persisting the ban, walk a snapshot of current players so every matching session is removed. + static int DisconnectPlayersByRemoteIp(const std::string &ip) + { + auto *server = MinecraftServer::getInstance(); + if (server == nullptr || server->getPlayers() == nullptr) + { + return 0; + } + + const std::string normalizedIp = NetworkUtils::NormalizeIpToken(ip); + const std::vector> playerSnapshot = server->getPlayers()->players; + int disconnectedCount = 0; + for (const auto &player : playerSnapshot) + { + std::string playerIp; + if (!TryGetPlayerRemoteIp(player, &playerIp)) + { + continue; + } + + if (NetworkUtils::NormalizeIpToken(playerIp) == normalizedIp) + { + if (player != nullptr && player->connection != nullptr) + { + player->connection->disconnect(DisconnectPacket::eDisconnect_Banned); + ++disconnectedCount; + } + } + } + + return disconnectedCount; + } + } + + const char *CliCommandBanIp::Name() const + { + return "ban-ip"; + } + + const char *CliCommandBanIp::Usage() const + { + return "ban-ip [reason ...]"; + } + + const char *CliCommandBanIp::Description() const + { + return "Ban an IP address or a player's current IP."; + } + + /** + * Resolves either a literal IP or an online player's current IP, persists the ban, and disconnects every matching connection + * IPまたは接続中プレイヤーの現在IPをBANし一致する接続を切断する + */ + bool CliCommandBanIp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() < 2) + { + engine->LogWarn("Usage: ban-ip [reason ...]"); + return false; + } + if (!ServerRuntime::Access::IsInitialized()) + { + engine->LogWarn("Access manager is not initialized."); + return false; + } + + const std::string targetToken = line.tokens[1]; + std::string remoteIp; + // Match Java Edition behavior by accepting either a literal IP or an online player name. + const auto targetPlayer = engine->FindPlayerByNameUtf8(targetToken); + if (targetPlayer != nullptr) + { + if (!TryGetPlayerRemoteIp(targetPlayer, &remoteIp)) + { + engine->LogWarn("Cannot ban that player's IP because no current remote IP is available."); + return false; + } + } + else if (NetworkUtils::IsIpLiteral(targetToken)) + { + remoteIp = StringUtils::TrimAscii(targetToken); + } + else + { + engine->LogWarn("Unknown player or invalid IP address: " + targetToken); + return false; + } + + // Refuse duplicate bans so operators get immediate feedback instead of rewriting the same entry. + if (ServerRuntime::Access::IsIpBanned(remoteIp)) + { + engine->LogWarn("That IP address is already banned."); + return false; + } + + ServerRuntime::Access::BanMetadata metadata = ServerRuntime::Access::BanManager::BuildDefaultMetadata("Console"); + metadata.reason = StringUtils::JoinTokens(line.tokens, 2); + if (metadata.reason.empty()) + { + metadata.reason = "Banned by an operator."; + } + + // Publish the ban before disconnecting players so reconnect attempts are rejected immediately. + if (!ServerRuntime::Access::AddIpBan(remoteIp, metadata)) + { + engine->LogError("Failed to write IP ban."); + return false; + } + + const int disconnectedCount = DisconnectPlayersByRemoteIp(remoteIp); + // Report the resolved IP rather than the original token so player-name targets are explicit in the console. + engine->LogInfo("Banned IP address " + remoteIp + "."); + if (disconnectedCount > 0) + { + engine->LogInfo("Disconnected " + std::to_string(disconnectedCount) + " player(s) with that IP."); + } + return true; + } + + /** + * Suggests online player names for the player-target form of the Java Edition command + * プレイヤー名指定時の補完候補を返す + */ + void CliCommandBanIp::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + if (context.currentTokenIndex == 1) + { + engine->SuggestPlayers(context.prefix, context.linePrefix, out); + } + } +} + diff --git a/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.h b/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.h new file mode 100644 index 00000000..48199f10 --- /dev/null +++ b/Minecraft.Server/Console/commands/ban-ip/CliCommandBanIp.h @@ -0,0 +1,19 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + /** + * Applies a dedicated-server IP ban using Java Edition style syntax and Access-backed persistence + */ + class CliCommandBanIp : public IServerCliCommand + { + public: + virtual const char *Name() const; + virtual const char *Usage() const; + virtual const char *Description() const; + virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine); + virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const; + }; +} diff --git a/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp b/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp new file mode 100644 index 00000000..b347dcf9 --- /dev/null +++ b/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.cpp @@ -0,0 +1,136 @@ +#include "stdafx.h" + +#include "CliCommandBanList.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../Access/Access.h" +#include "../../../Common/StringUtils.h" + +#include + +namespace ServerRuntime +{ + namespace + { + static void AppendUniqueText(const std::string &text, std::vector *out) + { + if (out == nullptr || text.empty()) + { + return; + } + + if (std::find(out->begin(), out->end(), text) == out->end()) + { + out->push_back(text); + } + } + + static bool CompareLowerAscii(const std::string &left, const std::string &right) + { + return StringUtils::ToLowerAscii(left) < StringUtils::ToLowerAscii(right); + } + + static bool LogBannedPlayers(ServerCliEngine *engine) + { + std::vector entries; + if (!ServerRuntime::Access::SnapshotBannedPlayers(&entries)) + { + engine->LogError("Failed to read banned players."); + return false; + } + + std::vector names; + for (const auto &entry : entries) + { + AppendUniqueText(entry.name, &names); + } + std::sort(names.begin(), names.end(), CompareLowerAscii); + + engine->LogInfo("There are " + std::to_string(names.size()) + " banned player(s)."); + for (const auto &name : names) + { + engine->LogInfo(" " + name); + } + return true; + } + + static bool LogBannedIps(ServerCliEngine *engine) + { + std::vector entries; + if (!ServerRuntime::Access::SnapshotBannedIps(&entries)) + { + engine->LogError("Failed to read banned IPs."); + return false; + } + + std::vector ips; + for (const auto &entry : entries) + { + AppendUniqueText(entry.ip, &ips); + } + std::sort(ips.begin(), ips.end(), CompareLowerAscii); + + engine->LogInfo("There are " + std::to_string(ips.size()) + " banned IP(s)."); + for (const auto &ip : ips) + { + engine->LogInfo(" " + ip); + } + return true; + } + + static bool LogAllBans(ServerCliEngine *engine) + { + if (!LogBannedPlayers(engine)) + { + return false; + } + + // Always print the IP snapshot as well so ban-ip entries are visible from the same command output. + return LogBannedIps(engine); + } + } + + const char *CliCommandBanList::Name() const + { + return "banlist"; + } + + const char *CliCommandBanList::Usage() const + { + return "banlist"; + } + + const char *CliCommandBanList::Description() const + { + return "List all banned players and IPs."; + } + + /** + * Reads the current Access snapshots and always prints both banned players and banned IPs + * Access の一覧を読みプレイヤーBANとIP BANをまとめて表示する + */ + bool CliCommandBanList::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() > 1) + { + engine->LogWarn("Usage: banlist"); + return false; + } + if (!ServerRuntime::Access::IsInitialized()) + { + engine->LogWarn("Access manager is not initialized."); + return false; + } + + return LogAllBans(engine); + } + + void CliCommandBanList::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + (void)context; + (void)engine; + (void)out; + } +} + diff --git a/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.h b/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.h new file mode 100644 index 00000000..8d1a4f77 --- /dev/null +++ b/Minecraft.Server/Console/commands/ban-list/CliCommandBanList.h @@ -0,0 +1,23 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + /** + * **Ban List Command** + * + * Lists dedicated-server player bans and IP bans in a single command output + * 専用サーバーのプレイヤーBANとIP BANをまとめて表示する + */ + class CliCommandBanList : public IServerCliCommand + { + public: + virtual const char *Name() const; + virtual const char *Usage() const; + virtual const char *Description() const; + virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine); + virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const; + }; +} + diff --git a/Minecraft.Server/Console/commands/ban/CliCommandBan.cpp b/Minecraft.Server/Console/commands/ban/CliCommandBan.cpp new file mode 100644 index 00000000..28970b76 --- /dev/null +++ b/Minecraft.Server/Console/commands/ban/CliCommandBan.cpp @@ -0,0 +1,145 @@ +#include "stdafx.h" + +#include "CliCommandBan.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../Access/Access.h" +#include "../../../Common/StringUtils.h" +#include "../../../../Minecraft.Client/PlayerConnection.h" +#include "../../../../Minecraft.Client/ServerPlayer.h" +#include "../../../../Minecraft.World/DisconnectPacket.h" + +#include + +namespace ServerRuntime +{ + namespace + { + static void AppendUniqueXuid(PlayerUID xuid, std::vector *out) + { + if (out == nullptr || xuid == INVALID_XUID) + { + return; + } + + if (std::find(out->begin(), out->end(), xuid) == out->end()) + { + out->push_back(xuid); + } + } + + static void CollectPlayerBanXuids(const std::shared_ptr &player, std::vector *out) + { + if (player == nullptr || out == nullptr) + { + return; + } + + // Keep both identity variants because the dedicated server checks login and online XUIDs separately. + AppendUniqueXuid(player->getXuid(), out); + AppendUniqueXuid(player->getOnlineXuid(), out); + } + } + + const char *CliCommandBan::Name() const + { + return "ban"; + } + + const char *CliCommandBan::Usage() const + { + return "ban [reason ...]"; + } + + const char *CliCommandBan::Description() const + { + return "Ban an online player."; + } + + /** + * Resolves the live player, writes one or more Access ban entries, and disconnects the target with the banned reason + * 対象プレイヤーを解決してBANを保存し切断する + */ + bool CliCommandBan::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() < 2) + { + engine->LogWarn("Usage: ban [reason ...]"); + return false; + } + if (!ServerRuntime::Access::IsInitialized()) + { + engine->LogWarn("Access manager is not initialized."); + return false; + } + + const auto target = engine->FindPlayerByNameUtf8(line.tokens[1]); + if (target == nullptr) + { + engine->LogWarn("Unknown player: " + line.tokens[1] + " (this server build can only ban players that are currently online)."); + return false; + } + + std::vector xuids; + CollectPlayerBanXuids(target, &xuids); + if (xuids.empty()) + { + engine->LogWarn("Cannot ban that player because no valid XUID is available."); + return false; + } + + const bool hasUnbannedIdentity = std::any_of( + xuids.begin(), + xuids.end(), + [](PlayerUID xuid) { return !ServerRuntime::Access::IsPlayerBanned(xuid); }); + if (!hasUnbannedIdentity) + { + engine->LogWarn("That player is already banned."); + return false; + } + + ServerRuntime::Access::BanMetadata metadata = ServerRuntime::Access::BanManager::BuildDefaultMetadata("Console"); + metadata.reason = StringUtils::JoinTokens(line.tokens, 2); + if (metadata.reason.empty()) + { + metadata.reason = "Banned by an operator."; + } + + const std::string playerName = StringUtils::WideToUtf8(target->getName()); + for (const auto xuid : xuids) + { + if (ServerRuntime::Access::IsPlayerBanned(xuid)) + { + continue; + } + + if (!ServerRuntime::Access::AddPlayerBan(xuid, playerName, metadata)) + { + engine->LogError("Failed to write player ban."); + return false; + } + } + + if (target->connection != nullptr) + { + target->connection->disconnect(DisconnectPacket::eDisconnect_Banned); + } + + engine->LogInfo("Banned player " + playerName + "."); + return true; + } + + /** + * Suggests currently connected player names for the Java-style player argument + * プレイヤー引数の補完候補を返す + */ + void CliCommandBan::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + if (context.currentTokenIndex == 1) + { + engine->SuggestPlayers(context.prefix, context.linePrefix, out); + } + } +} + diff --git a/Minecraft.Server/Console/commands/ban/CliCommandBan.h b/Minecraft.Server/Console/commands/ban/CliCommandBan.h new file mode 100644 index 00000000..bae522ac --- /dev/null +++ b/Minecraft.Server/Console/commands/ban/CliCommandBan.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + /** + * Applies a dedicated-server player ban using Java Edition style syntax and Access-backed persistence + * Java Edition 風の ban コマンドで永続プレイヤーBANを行う + */ + class CliCommandBan : public IServerCliCommand + { + public: + virtual const char *Name() const; + virtual const char *Usage() const; + virtual const char *Description() const; + virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine); + virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const; + }; +} diff --git a/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.cpp b/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.cpp new file mode 100644 index 00000000..575085a7 --- /dev/null +++ b/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.cpp @@ -0,0 +1,117 @@ +#include "stdafx.h" + +#include "CliCommandDefaultGamemode.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../../Minecraft.Client/MinecraftServer.h" +#include "../../../../Minecraft.Client/PlayerList.h" +#include "../../../../Minecraft.Client/ServerLevel.h" +#include "../../../../Minecraft.Client/ServerPlayer.h" +#include "../../../../Minecraft.World/net.minecraft.world.level.storage.h" + +namespace ServerRuntime +{ + namespace + { + const char *kDefaultGamemodeUsage = "defaultgamemode "; + + static std::string ModeLabel(GameType *mode) + { + if (mode == GameType::SURVIVAL) + { + return "survival"; + } + if (mode == GameType::CREATIVE) + { + return "creative"; + } + if (mode == GameType::ADVENTURE) + { + return "adventure"; + } + + return std::to_string(mode != nullptr ? mode->getId() : -1); + } + } + + const char *CliCommandDefaultGamemode::Name() const + { + return "defaultgamemode"; + } + + const char *CliCommandDefaultGamemode::Usage() const + { + return kDefaultGamemodeUsage; + } + + const char *CliCommandDefaultGamemode::Description() const + { + return "Set the default game mode (server-side implementation)."; + } + + bool CliCommandDefaultGamemode::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() != 2) + { + engine->LogWarn(std::string("Usage: ") + kDefaultGamemodeUsage); + return false; + } + + GameType *mode = engine->ParseGamemode(line.tokens[1]); + if (mode == nullptr) + { + engine->LogWarn("Unknown gamemode: " + line.tokens[1]); + return false; + } + + MinecraftServer *server = MinecraftServer::getInstance(); + if (server == nullptr) + { + engine->LogWarn("MinecraftServer instance is not available."); + return false; + } + + PlayerList *players = server->getPlayers(); + if (players == nullptr) + { + engine->LogWarn("Player list is not available."); + return false; + } + + players->setOverrideGameMode(mode); + + for (unsigned int i = 0; i < server->levels.length; ++i) + { + ServerLevel *level = server->levels[i]; + if (level != nullptr && level->getLevelData() != nullptr) + { + level->getLevelData()->setGameType(mode); + } + } + + if (server->getForceGameType()) + { + for (size_t i = 0; i < players->players.size(); ++i) + { + std::shared_ptr player = players->players[i]; + if (player != nullptr) + { + player->setGameMode(mode); + player->fallDistance = 0.0f; + } + } + } + + engine->LogInfo("Default gamemode set to " + ModeLabel(mode) + "."); + return true; + } + + void CliCommandDefaultGamemode::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + if (context.currentTokenIndex == 1) + { + engine->SuggestGamemodes(context.prefix, context.linePrefix, out); + } + } +} diff --git a/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.h b/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.h new file mode 100644 index 00000000..e52cf531 --- /dev/null +++ b/Minecraft.Server/Console/commands/defaultgamemode/CliCommandDefaultGamemode.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandDefaultGamemode : public IServerCliCommand + { + public: + const char *Name() const override; + const char *Usage() const override; + const char *Description() const override; + bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override; + void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const override; + }; +} diff --git a/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.cpp b/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.cpp new file mode 100644 index 00000000..50a50bd0 --- /dev/null +++ b/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" + +#include "CliCommandEnchant.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../CommandParsing.h" +#include "../../../../Minecraft.World/GameCommandPacket.h" +#include "../../../../Minecraft.World/EnchantItemCommand.h" +#include "../../../../Minecraft.World/net.minecraft.world.entity.player.h" +#include "../../../../Minecraft.Client/ServerPlayer.h" + +namespace ServerRuntime +{ + namespace + { + const char *kEnchantUsage = "enchant [level]"; + } + + const char *CliCommandEnchant::Name() const + { + return "enchant"; + } + + const char *CliCommandEnchant::Usage() const + { + return kEnchantUsage; + } + + const char *CliCommandEnchant::Description() const + { + return "Enchant held item via Minecraft.World command dispatcher."; + } + + bool CliCommandEnchant::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() < 3 || line.tokens.size() > 4) + { + engine->LogWarn(std::string("Usage: ") + kEnchantUsage); + return false; + } + + std::shared_ptr target = engine->FindPlayerByNameUtf8(line.tokens[1]); + if (target == nullptr) + { + engine->LogWarn("Unknown player: " + line.tokens[1]); + return false; + } + + int enchantmentId = 0; + int enchantmentLevel = 1; + if (!CommandParsing::TryParseInt(line.tokens[2], &enchantmentId)) + { + engine->LogWarn("Invalid enchantment id: " + line.tokens[2]); + return false; + } + if (line.tokens.size() >= 4 && !CommandParsing::TryParseInt(line.tokens[3], &enchantmentLevel)) + { + engine->LogWarn("Invalid enchantment level: " + line.tokens[3]); + return false; + } + + std::shared_ptr player = std::dynamic_pointer_cast(target); + if (player == nullptr) + { + engine->LogWarn("Cannot resolve target player entity."); + return false; + } + + std::shared_ptr packet = EnchantItemCommand::preparePacket(player, enchantmentId, enchantmentLevel); + if (packet == nullptr) + { + engine->LogError("Failed to build enchant command packet."); + return false; + } + + return engine->DispatchWorldCommand(packet->command, packet->data); + } + + void CliCommandEnchant::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + if (context.currentTokenIndex == 1) + { + engine->SuggestPlayers(context.prefix, context.linePrefix, out); + } + } +} diff --git a/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.h b/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.h new file mode 100644 index 00000000..32ccb7a4 --- /dev/null +++ b/Minecraft.Server/Console/commands/enchant/CliCommandEnchant.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandEnchant : public IServerCliCommand + { + public: + const char *Name() const override; + const char *Usage() const override; + const char *Description() const override; + bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override; + void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const override; + }; +} diff --git a/Minecraft.Server/Console/commands/experience/CliCommandExperience.cpp b/Minecraft.Server/Console/commands/experience/CliCommandExperience.cpp new file mode 100644 index 00000000..d0c89f2e --- /dev/null +++ b/Minecraft.Server/Console/commands/experience/CliCommandExperience.cpp @@ -0,0 +1,192 @@ +#include "stdafx.h" + +#include "CliCommandExperience.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../CommandParsing.h" +#include "../../../Common/StringUtils.h" +#include "../../../../Minecraft.Client/MinecraftServer.h" +#include "../../../../Minecraft.Client/PlayerList.h" +#include "../../../../Minecraft.Client/ServerPlayer.h" + +#include + +namespace ServerRuntime +{ + namespace + { + const char *kExperienceUsage = "xp [L] [player]"; + const char *kExperienceUsageWithPlayer = "xp [L] "; + + struct ExperienceAmount + { + int amount; + bool levels; + bool take; + }; + + static bool TryParseExperienceAmount(const std::string &token, ExperienceAmount *outValue) + { + if (outValue == nullptr || token.empty()) + { + return false; + } + + ExperienceAmount parsed; + parsed.amount = 0; + parsed.levels = false; + parsed.take = false; + std::string numericToken = token; + const char suffix = token[token.size() - 1]; + if (suffix == 'l' || suffix == 'L') + { + parsed.levels = true; + numericToken = token.substr(0, token.size() - 1); + if (numericToken.empty()) + { + return false; + } + } + + int signedAmount = 0; + if (!CommandParsing::TryParseInt(numericToken, &signedAmount)) + { + return false; + } + if (signedAmount == (std::numeric_limits::min)()) + { + return false; + } + + parsed.take = signedAmount < 0; + parsed.amount = parsed.take ? -signedAmount : signedAmount; + *outValue = parsed; + return true; + } + + static std::shared_ptr ResolveTargetPlayer(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() >= 3) + { + return engine->FindPlayerByNameUtf8(line.tokens[2]); + } + + MinecraftServer *server = MinecraftServer::getInstance(); + if (server == nullptr || server->getPlayers() == nullptr) + { + return nullptr; + } + + PlayerList *players = server->getPlayers(); + if (players->players.size() == 1 && players->players[0] != nullptr) + { + return players->players[0]; + } + + return nullptr; + } + } + + const char *CliCommandExperience::Name() const + { + return "xp"; + } + + std::vector CliCommandExperience::Aliases() const + { + std::vector aliases; + aliases.push_back("experience"); + return aliases; + } + + const char *CliCommandExperience::Usage() const + { + return kExperienceUsage; + } + + const char *CliCommandExperience::Description() const + { + return "Grant or remove experience (server-side implementation)."; + } + + bool CliCommandExperience::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() < 2 || line.tokens.size() > 3) + { + engine->LogWarn(std::string("Usage: ") + kExperienceUsage); + return false; + } + + ExperienceAmount amount; + amount.amount = 0; + amount.levels = false; + amount.take = false; + if (!TryParseExperienceAmount(line.tokens[1], &amount)) + { + engine->LogWarn(std::string("Usage: ") + kExperienceUsage); + return false; + } + + std::shared_ptr target = ResolveTargetPlayer(line, engine); + if (target == nullptr) + { + if (line.tokens.size() >= 3) + { + engine->LogWarn("Unknown player: " + line.tokens[2]); + } + else + { + engine->LogWarn(std::string("Usage: ") + kExperienceUsageWithPlayer); + } + return false; + } + + if (amount.levels) + { + target->giveExperienceLevels(amount.take ? -amount.amount : amount.amount); + if (amount.take) + { + engine->LogInfo("Removed " + std::to_string(amount.amount) + " level(s) from " + StringUtils::WideToUtf8(target->getName()) + "."); + } + else + { + engine->LogInfo("Added " + std::to_string(amount.amount) + " level(s) to " + StringUtils::WideToUtf8(target->getName()) + "."); + } + return true; + } + + if (amount.take) + { + engine->LogWarn("Removing raw experience points is not supported. Use negative levels (example: xp -5L )."); + return false; + } + + target->increaseXp(amount.amount); + engine->LogInfo("Added " + std::to_string(amount.amount) + " experience points to " + StringUtils::WideToUtf8(target->getName()) + "."); + return true; + } + + void CliCommandExperience::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + if (context.currentTokenIndex == 1) + { + if (StringUtils::StartsWithIgnoreCase("10", context.prefix)) + { + out->push_back(context.linePrefix + "10"); + } + if (StringUtils::StartsWithIgnoreCase("10L", context.prefix)) + { + out->push_back(context.linePrefix + "10L"); + } + if (StringUtils::StartsWithIgnoreCase("-5L", context.prefix)) + { + out->push_back(context.linePrefix + "-5L"); + } + } + else if (context.currentTokenIndex == 2) + { + engine->SuggestPlayers(context.prefix, context.linePrefix, out); + } + } +} diff --git a/Minecraft.Server/Console/commands/experience/CliCommandExperience.h b/Minecraft.Server/Console/commands/experience/CliCommandExperience.h new file mode 100644 index 00000000..65d26061 --- /dev/null +++ b/Minecraft.Server/Console/commands/experience/CliCommandExperience.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandExperience : public IServerCliCommand + { + public: + const char *Name() const override; + std::vector Aliases() const override; + const char *Usage() const override; + const char *Description() const override; + bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override; + void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const override; + }; +} diff --git a/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.cpp b/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.cpp new file mode 100644 index 00000000..c4ec798d --- /dev/null +++ b/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.cpp @@ -0,0 +1,111 @@ +#include "stdafx.h" + +#include "CliCommandGamemode.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../../Minecraft.Client/MinecraftServer.h" +#include "../../../../Minecraft.Client/PlayerList.h" +#include "../../../../Minecraft.Client/ServerPlayer.h" + +namespace ServerRuntime +{ + namespace + { + const char *kGamemodeUsage = "gamemode [player]"; + const char *kGamemodeUsageWithPlayer = "gamemode "; + } + + const char *CliCommandGamemode::Name() const + { + return "gamemode"; + } + + std::vector CliCommandGamemode::Aliases() const + { + std::vector aliases; + aliases.push_back("gm"); + return aliases; + } + + const char *CliCommandGamemode::Usage() const + { + return kGamemodeUsage; + } + + const char *CliCommandGamemode::Description() const + { + return "Set a player's game mode."; + } + + bool CliCommandGamemode::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() < 2 || line.tokens.size() > 3) + { + engine->LogWarn(std::string("Usage: ") + kGamemodeUsage); + return false; + } + + GameType *mode = engine->ParseGamemode(line.tokens[1]); + if (mode == nullptr) + { + engine->LogWarn("Unknown gamemode: " + line.tokens[1]); + return false; + } + + std::shared_ptr target = nullptr; + if (line.tokens.size() >= 3) + { + target = engine->FindPlayerByNameUtf8(line.tokens[2]); + if (target == nullptr) + { + engine->LogWarn("Unknown player: " + line.tokens[2]); + return false; + } + } + else + { + MinecraftServer *server = MinecraftServer::getInstance(); + if (server == nullptr || server->getPlayers() == nullptr) + { + engine->LogWarn("Player list is not available."); + return false; + } + + PlayerList *players = server->getPlayers(); + if (players->players.size() != 1 || players->players[0] == nullptr) + { + engine->LogWarn(std::string("Usage: ") + kGamemodeUsageWithPlayer); + return false; + } + target = players->players[0]; + } + + target->setGameMode(mode); + target->fallDistance = 0.0f; + + if (line.tokens.size() >= 3) + { + engine->LogInfo("Set " + line.tokens[2] + " gamemode to " + line.tokens[1] + "."); + } + else + { + engine->LogInfo("Set gamemode to " + line.tokens[1] + "."); + } + return true; + } + + void CliCommandGamemode::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + if (context.currentTokenIndex == 1) + { + engine->SuggestGamemodes(context.prefix, context.linePrefix, out); + } + else if (context.currentTokenIndex == 2) + { + engine->SuggestPlayers(context.prefix, context.linePrefix, out); + } + } +} + + diff --git a/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.h b/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.h new file mode 100644 index 00000000..ab0ff5bc --- /dev/null +++ b/Minecraft.Server/Console/commands/gamemode/CliCommandGamemode.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandGamemode : public IServerCliCommand + { + public: + const char *Name() const override; + std::vector Aliases() const override; + const char *Usage() const override; + const char *Description() const override; + bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override; + void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const override; + }; +} + diff --git a/Minecraft.Server/Console/commands/give/CliCommandGive.cpp b/Minecraft.Server/Console/commands/give/CliCommandGive.cpp new file mode 100644 index 00000000..254c6c32 --- /dev/null +++ b/Minecraft.Server/Console/commands/give/CliCommandGive.cpp @@ -0,0 +1,103 @@ +#include "stdafx.h" + +#include "CliCommandGive.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../CommandParsing.h" +#include "../../../../Minecraft.World/GameCommandPacket.h" +#include "../../../../Minecraft.World/GiveItemCommand.h" +#include "../../../../Minecraft.World/net.minecraft.world.entity.player.h" +#include "../../../../Minecraft.Client/ServerPlayer.h" + +namespace ServerRuntime +{ + namespace + { + const char *kGiveUsage = "give [amount] [aux]"; + } + + const char *CliCommandGive::Name() const + { + return "give"; + } + + const char *CliCommandGive::Usage() const + { + return kGiveUsage; + } + + const char *CliCommandGive::Description() const + { + return "Give an item via Minecraft.World command dispatcher."; + } + + bool CliCommandGive::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() < 3 || line.tokens.size() > 5) + { + engine->LogWarn(std::string("Usage: ") + kGiveUsage); + return false; + } + + std::shared_ptr target = engine->FindPlayerByNameUtf8(line.tokens[1]); + if (target == nullptr) + { + engine->LogWarn("Unknown player: " + line.tokens[1]); + return false; + } + + int itemId = 0; + int amount = 1; + int aux = 0; + if (!CommandParsing::TryParseInt(line.tokens[2], &itemId)) + { + engine->LogWarn("Invalid item id: " + line.tokens[2]); + return false; + } + if (itemId <= 0) + { + engine->LogWarn("Item id must be greater than 0."); + return false; + } + if (line.tokens.size() >= 4 && !CommandParsing::TryParseInt(line.tokens[3], &amount)) + { + engine->LogWarn("Invalid amount: " + line.tokens[3]); + return false; + } + if (line.tokens.size() >= 5 && !CommandParsing::TryParseInt(line.tokens[4], &aux)) + { + engine->LogWarn("Invalid aux value: " + line.tokens[4]); + return false; + } + if (amount <= 0) + { + engine->LogWarn("Amount must be greater than 0."); + return false; + } + + std::shared_ptr player = std::dynamic_pointer_cast(target); + if (player == nullptr) + { + engine->LogWarn("Cannot resolve target player entity."); + return false; + } + + std::shared_ptr packet = GiveItemCommand::preparePacket(player, itemId, amount, aux); + if (packet == nullptr) + { + engine->LogError("Failed to build give command packet."); + return false; + } + + return engine->DispatchWorldCommand(packet->command, packet->data); + } + + void CliCommandGive::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + if (context.currentTokenIndex == 1) + { + engine->SuggestPlayers(context.prefix, context.linePrefix, out); + } + } +} diff --git a/Minecraft.Server/Console/commands/give/CliCommandGive.h b/Minecraft.Server/Console/commands/give/CliCommandGive.h new file mode 100644 index 00000000..45aaf53a --- /dev/null +++ b/Minecraft.Server/Console/commands/give/CliCommandGive.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandGive : public IServerCliCommand + { + public: + const char *Name() const override; + const char *Usage() const override; + const char *Description() const override; + bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override; + void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const override; + }; +} diff --git a/Minecraft.Server/Console/commands/help/CliCommandHelp.cpp b/Minecraft.Server/Console/commands/help/CliCommandHelp.cpp new file mode 100644 index 00000000..f7d1a6bd --- /dev/null +++ b/Minecraft.Server/Console/commands/help/CliCommandHelp.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" + +#include "CliCommandHelp.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliRegistry.h" +#include "../../ServerCliParser.h" + +namespace ServerRuntime +{ + const char *CliCommandHelp::Name() const + { + return "help"; + } + + std::vector CliCommandHelp::Aliases() const + { + std::vector aliases; + aliases.push_back("?"); + return aliases; + } + + const char *CliCommandHelp::Usage() const + { + return "help"; + } + + const char *CliCommandHelp::Description() const + { + return "Show available server console commands."; + } + + bool CliCommandHelp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + (void)line; + const std::vector> &commands = engine->Registry().Commands(); + engine->LogInfo("Available commands:"); + for (size_t i = 0; i < commands.size(); ++i) + { + std::string row = " "; + row += commands[i]->Usage(); + row += " - "; + row += commands[i]->Description(); + engine->LogInfo(row); + } + return true; + } +} + diff --git a/Minecraft.Server/Console/commands/help/CliCommandHelp.h b/Minecraft.Server/Console/commands/help/CliCommandHelp.h new file mode 100644 index 00000000..8308e3f8 --- /dev/null +++ b/Minecraft.Server/Console/commands/help/CliCommandHelp.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandHelp : public IServerCliCommand + { + public: + virtual const char *Name() const; + virtual std::vector Aliases() const; + virtual const char *Usage() const; + virtual const char *Description() const; + virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine); + }; +} + diff --git a/Minecraft.Server/Console/commands/kill/CliCommandKill.cpp b/Minecraft.Server/Console/commands/kill/CliCommandKill.cpp new file mode 100644 index 00000000..95d93cd4 --- /dev/null +++ b/Minecraft.Server/Console/commands/kill/CliCommandKill.cpp @@ -0,0 +1,64 @@ +#include "stdafx.h" + +#include "CliCommandKill.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../../Minecraft.World/CommandSender.h" +#include "../../../../Minecraft.Client/ServerPlayer.h" + +namespace ServerRuntime +{ + namespace + { + const char *kKillUsage = "kill "; + } + + const char *CliCommandKill::Name() const + { + return "kill"; + } + + const char *CliCommandKill::Usage() const + { + return kKillUsage; + } + + const char *CliCommandKill::Description() const + { + return "Kill a player via Minecraft.World command dispatcher."; + } + + bool CliCommandKill::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() != 2) + { + engine->LogWarn(std::string("Usage: ") + kKillUsage); + return false; + } + + std::shared_ptr target = engine->FindPlayerByNameUtf8(line.tokens[1]); + if (target == nullptr) + { + engine->LogWarn("Unknown player: " + line.tokens[1]); + return false; + } + + std::shared_ptr sender = std::dynamic_pointer_cast(target); + if (sender == nullptr) + { + engine->LogWarn("Cannot resolve target command sender."); + return false; + } + + return engine->DispatchWorldCommand(eGameCommand_Kill, byteArray(), sender); + } + + void CliCommandKill::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + if (context.currentTokenIndex == 1) + { + engine->SuggestPlayers(context.prefix, context.linePrefix, out); + } + } +} diff --git a/Minecraft.Server/Console/commands/kill/CliCommandKill.h b/Minecraft.Server/Console/commands/kill/CliCommandKill.h new file mode 100644 index 00000000..e071edd4 --- /dev/null +++ b/Minecraft.Server/Console/commands/kill/CliCommandKill.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandKill : public IServerCliCommand + { + public: + const char *Name() const override; + const char *Usage() const override; + const char *Description() const override; + bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override; + void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const override; + }; +} diff --git a/Minecraft.Server/Console/commands/list/CliCommandList.cpp b/Minecraft.Server/Console/commands/list/CliCommandList.cpp new file mode 100644 index 00000000..d4e83e52 --- /dev/null +++ b/Minecraft.Server/Console/commands/list/CliCommandList.cpp @@ -0,0 +1,50 @@ +#include "stdafx.h" + +#include "CliCommandList.h" + +#include "../../ServerCliEngine.h" +#include "../../../Common/StringUtils.h" +#include "../../../../Minecraft.Client/MinecraftServer.h" +#include "../../../../Minecraft.Client/PlayerList.h" +#include "../../ServerCliParser.h" + +namespace ServerRuntime +{ + const char *CliCommandList::Name() const + { + return "list"; + } + + const char *CliCommandList::Usage() const + { + return "list"; + } + + const char *CliCommandList::Description() const + { + return "List connected players."; + } + + bool CliCommandList::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + (void)line; + MinecraftServer *server = MinecraftServer::getInstance(); + if (server == NULL || server->getPlayers() == NULL) + { + engine->LogWarn("Player list is not available."); + return false; + } + + PlayerList *players = server->getPlayers(); + std::string names = StringUtils::WideToUtf8(players->getPlayerNames()); + if (names.empty()) + { + names = "(none)"; + } + + engine->LogInfo("Players (" + std::to_string(players->getPlayerCount()) + "): " + names); + return true; + } +} + + diff --git a/Minecraft.Server/Console/commands/list/CliCommandList.h b/Minecraft.Server/Console/commands/list/CliCommandList.h new file mode 100644 index 00000000..e29d5125 --- /dev/null +++ b/Minecraft.Server/Console/commands/list/CliCommandList.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandList : public IServerCliCommand + { + public: + virtual const char *Name() const; + virtual const char *Usage() const; + virtual const char *Description() const; + virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine); + }; +} + diff --git a/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.cpp b/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.cpp new file mode 100644 index 00000000..7e90702a --- /dev/null +++ b/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.cpp @@ -0,0 +1,98 @@ +#include "stdafx.h" + +#include "CliCommandPardonIp.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../Access/Access.h" +#include "../../../Common/NetworkUtils.h" +#include "../../../Common/StringUtils.h" + +namespace ServerRuntime +{ + const char *CliCommandPardonIp::Name() const + { + return "pardon-ip"; + } + + const char *CliCommandPardonIp::Usage() const + { + return "pardon-ip
"; + } + + const char *CliCommandPardonIp::Description() const + { + return "Remove an IP ban."; + } + + /** + * Validates the literal IP argument and removes the matching Access IP ban entry + * リテラルIPを検証して一致するIP BANを解除する + */ + bool CliCommandPardonIp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() != 2) + { + engine->LogWarn("Usage: pardon-ip
"); + return false; + } + if (!ServerRuntime::Access::IsInitialized()) + { + engine->LogWarn("Access manager is not initialized."); + return false; + } + + // Java Edition pardon-ip only operates on a literal address, so do not resolve player names here. + const std::string ip = StringUtils::TrimAscii(line.tokens[1]); + if (!NetworkUtils::IsIpLiteral(ip)) + { + engine->LogWarn("Invalid IP address: " + line.tokens[1]); + return false; + } + // Distinguish invalid input from a valid but currently unbanned address for clearer operator feedback. + if (!ServerRuntime::Access::IsIpBanned(ip)) + { + engine->LogWarn("That IP address is not banned."); + return false; + } + if (!ServerRuntime::Access::RemoveIpBan(ip)) + { + engine->LogError("Failed to remove IP ban."); + return false; + } + + engine->LogInfo("Unbanned IP address " + ip + "."); + return true; + } + + /** + * Suggests currently banned IP addresses for the Java Edition literal-IP argument + * BAN済みIPの補完候補を返す + */ + void CliCommandPardonIp::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + (void)engine; + // Complete from the persisted IP-ban snapshot because this command only accepts already-banned literals. + if (context.currentTokenIndex != 1 || out == nullptr) + { + return; + } + + std::vector entries; + if (!ServerRuntime::Access::SnapshotBannedIps(&entries)) + { + return; + } + + // Reuse the normalized prefix match used by other commands so completion stays case-insensitive. + for (const auto &entry : entries) + { + const std::string &candidate = entry.ip; + if (StringUtils::StartsWithIgnoreCase(candidate, context.prefix)) + { + out->push_back(context.linePrefix + candidate); + } + } + } +} + diff --git a/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.h b/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.h new file mode 100644 index 00000000..f99a7f62 --- /dev/null +++ b/Minecraft.Server/Console/commands/pardon-ip/CliCommandPardonIp.h @@ -0,0 +1,19 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + /** + * Removes a dedicated-server IP ban using Java Edition style syntax and Access-backed persistence + */ + class CliCommandPardonIp : public IServerCliCommand + { + public: + virtual const char *Name() const; + virtual const char *Usage() const; + virtual const char *Description() const; + virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine); + virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const; + }; +} diff --git a/Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp b/Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp new file mode 100644 index 00000000..a8a647fa --- /dev/null +++ b/Minecraft.Server/Console/commands/pardon/CliCommandPardon.cpp @@ -0,0 +1,173 @@ +#include "stdafx.h" + +#include "CliCommandPardon.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../Access/Access.h" +#include "../../../Common/StringUtils.h" +#include "../../../../Minecraft.Client/ServerPlayer.h" + +#include + +namespace ServerRuntime +{ + namespace + { + static void AppendUniqueText(const std::string &text, std::vector *out) + { + if (out == nullptr || text.empty()) + { + return; + } + + if (std::find(out->begin(), out->end(), text) == out->end()) + { + out->push_back(text); + } + } + + static void AppendUniqueXuid(PlayerUID xuid, std::vector *out) + { + if (out == nullptr || xuid == INVALID_XUID) + { + return; + } + + if (std::find(out->begin(), out->end(), xuid) == out->end()) + { + out->push_back(xuid); + } + } + } + + const char *CliCommandPardon::Name() const + { + return "pardon"; + } + + const char *CliCommandPardon::Usage() const + { + return "pardon "; + } + + const char *CliCommandPardon::Description() const + { + return "Remove a player ban."; + } + + /** + * Removes every Access ban entry that matches the requested player name so dual-XUID entries are cleared together + * 名前に一致するBANをまとめて解除する + */ + bool CliCommandPardon::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() != 2) + { + engine->LogWarn("Usage: pardon "); + return false; + } + if (!ServerRuntime::Access::IsInitialized()) + { + engine->LogWarn("Access manager is not initialized."); + return false; + } + + std::vector xuidsToRemove; + std::vector matchedNames; + std::shared_ptr onlineTarget = engine->FindPlayerByNameUtf8(line.tokens[1]); + if (onlineTarget != nullptr) + { + if (ServerRuntime::Access::IsPlayerBanned(onlineTarget->getXuid())) + { + AppendUniqueXuid(onlineTarget->getXuid(), &xuidsToRemove); + } + if (ServerRuntime::Access::IsPlayerBanned(onlineTarget->getOnlineXuid())) + { + AppendUniqueXuid(onlineTarget->getOnlineXuid(), &xuidsToRemove); + } + } + + std::vector entries; + if (!ServerRuntime::Access::SnapshotBannedPlayers(&entries)) + { + engine->LogError("Failed to read banned players."); + return false; + } + + const std::string loweredTarget = StringUtils::ToLowerAscii(line.tokens[1]); + for (const auto &entry : entries) + { + if (StringUtils::ToLowerAscii(entry.name) == loweredTarget) + { + PlayerUID parsedXuid = INVALID_XUID; + if (ServerRuntime::Access::TryParseXuid(entry.xuid, &parsedXuid)) + { + AppendUniqueXuid(parsedXuid, &xuidsToRemove); + } + AppendUniqueText(entry.name, &matchedNames); + } + } + + if (xuidsToRemove.empty()) + { + engine->LogWarn("That player is not banned."); + return false; + } + + for (const auto xuid : xuidsToRemove) + { + if (!ServerRuntime::Access::RemovePlayerBan(xuid)) + { + engine->LogError("Failed to remove player ban."); + return false; + } + } + + std::string playerName = line.tokens[1]; + if (!matchedNames.empty()) + { + playerName = matchedNames[0]; + } + else if (onlineTarget != nullptr) + { + playerName = StringUtils::WideToUtf8(onlineTarget->getName()); + } + + engine->LogInfo("Unbanned player " + playerName + "."); + return true; + } + + /** + * Suggests currently banned player names first and then online names for convenience + * BAN済み名とオンライン名を補完候補に出す + */ + void CliCommandPardon::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + if (context.currentTokenIndex != 1 || out == nullptr) + { + return; + } + + std::vector entries; + if (ServerRuntime::Access::SnapshotBannedPlayers(&entries)) + { + std::vector names; + for (const auto &entry : entries) + { + AppendUniqueText(entry.name, &names); + } + + for (const auto &name : names) + { + if (StringUtils::StartsWithIgnoreCase(name, context.prefix)) + { + out->push_back(context.linePrefix + name); + } + } + } + + engine->SuggestPlayers(context.prefix, context.linePrefix, out); + } +} + diff --git a/Minecraft.Server/Console/commands/pardon/CliCommandPardon.h b/Minecraft.Server/Console/commands/pardon/CliCommandPardon.h new file mode 100644 index 00000000..4aac0350 --- /dev/null +++ b/Minecraft.Server/Console/commands/pardon/CliCommandPardon.h @@ -0,0 +1,19 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + /** + * Removes dedicated-server player bans using Java Edition style syntax and Access-backed persistence + */ + class CliCommandPardon : public IServerCliCommand + { + public: + virtual const char *Name() const; + virtual const char *Usage() const; + virtual const char *Description() const; + virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine); + virtual void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const; + }; +} diff --git a/Minecraft.Server/Console/commands/stop/CliCommandStop.cpp b/Minecraft.Server/Console/commands/stop/CliCommandStop.cpp new file mode 100644 index 00000000..0405d074 --- /dev/null +++ b/Minecraft.Server/Console/commands/stop/CliCommandStop.cpp @@ -0,0 +1,33 @@ +#include "stdafx.h" + +#include "CliCommandStop.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" + +namespace ServerRuntime +{ + const char *CliCommandStop::Name() const + { + return "stop"; + } + + const char *CliCommandStop::Usage() const + { + return "stop"; + } + + const char *CliCommandStop::Description() const + { + return "Stop the dedicated server."; + } + + bool CliCommandStop::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + (void)line; + engine->LogInfo("Stopping server..."); + engine->RequestShutdown(); + return true; + } +} + diff --git a/Minecraft.Server/Console/commands/stop/CliCommandStop.h b/Minecraft.Server/Console/commands/stop/CliCommandStop.h new file mode 100644 index 00000000..5f3288fa --- /dev/null +++ b/Minecraft.Server/Console/commands/stop/CliCommandStop.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandStop : public IServerCliCommand + { + public: + virtual const char *Name() const; + virtual const char *Usage() const; + virtual const char *Description() const; + virtual bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine); + }; +} + diff --git a/Minecraft.Server/Console/commands/time/CliCommandTime.cpp b/Minecraft.Server/Console/commands/time/CliCommandTime.cpp new file mode 100644 index 00000000..fd3fcef9 --- /dev/null +++ b/Minecraft.Server/Console/commands/time/CliCommandTime.cpp @@ -0,0 +1,118 @@ +#include "stdafx.h" + +#include "CliCommandTime.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../Common/StringUtils.h" +#include "../../../../Minecraft.World/GameCommandPacket.h" +#include "../../../../Minecraft.World/TimeCommand.h" + +namespace ServerRuntime +{ + namespace + { + const char *kTimeUsage = "time "; + + static bool TryResolveNightFlag(const std::vector &tokens, bool *outNight) + { + if (outNight == nullptr) + { + return false; + } + + std::string value; + if (tokens.size() == 2) + { + value = StringUtils::ToLowerAscii(tokens[1]); + } + else if (tokens.size() == 3 && StringUtils::ToLowerAscii(tokens[1]) == "set") + { + value = StringUtils::ToLowerAscii(tokens[2]); + } + else + { + return false; + } + + if (value == "day") + { + *outNight = false; + return true; + } + if (value == "night") + { + *outNight = true; + return true; + } + + return false; + } + + static void SuggestLiteral(const char *candidate, const ServerCliCompletionContext &context, std::vector *out) + { + if (candidate == nullptr || out == nullptr) + { + return; + } + + const std::string text(candidate); + if (StringUtils::StartsWithIgnoreCase(text, context.prefix)) + { + out->push_back(context.linePrefix + text); + } + } + } + + const char *CliCommandTime::Name() const + { + return "time"; + } + + const char *CliCommandTime::Usage() const + { + return kTimeUsage; + } + + const char *CliCommandTime::Description() const + { + return "Set day or night via Minecraft.World command dispatcher."; + } + + bool CliCommandTime::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + bool night = false; + if (!TryResolveNightFlag(line.tokens, &night)) + { + engine->LogWarn(std::string("Usage: ") + kTimeUsage); + return false; + } + + std::shared_ptr packet = TimeCommand::preparePacket(night); + if (packet == nullptr) + { + engine->LogError("Failed to build time command packet."); + return false; + } + + return engine->DispatchWorldCommand(packet->command, packet->data); + } + + void CliCommandTime::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + (void)engine; + if (context.currentTokenIndex == 1) + { + SuggestLiteral("day", context, out); + SuggestLiteral("night", context, out); + SuggestLiteral("set", context, out); + } + else if (context.currentTokenIndex == 2 && + context.parsed.tokens.size() >= 2 && + StringUtils::ToLowerAscii(context.parsed.tokens[1]) == "set") + { + SuggestLiteral("day", context, out); + SuggestLiteral("night", context, out); + } + } +} diff --git a/Minecraft.Server/Console/commands/time/CliCommandTime.h b/Minecraft.Server/Console/commands/time/CliCommandTime.h new file mode 100644 index 00000000..300bba33 --- /dev/null +++ b/Minecraft.Server/Console/commands/time/CliCommandTime.h @@ -0,0 +1,16 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandTime : public IServerCliCommand + { + public: + const char *Name() const override; + const char *Usage() const override; + const char *Description() const override; + bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override; + void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const override; + }; +} diff --git a/Minecraft.Server/Console/commands/tp/CliCommandTp.cpp b/Minecraft.Server/Console/commands/tp/CliCommandTp.cpp new file mode 100644 index 00000000..1b0923e3 --- /dev/null +++ b/Minecraft.Server/Console/commands/tp/CliCommandTp.cpp @@ -0,0 +1,84 @@ +#include "stdafx.h" + +#include "CliCommandTp.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../../Minecraft.Client/PlayerConnection.h" +#include "../../../../Minecraft.Client/TeleportCommand.h" +#include "../../../../Minecraft.Client/ServerPlayer.h" +#include "../../../../Minecraft.World/GameCommandPacket.h" + +namespace ServerRuntime +{ + namespace + { + const char *kTpUsage = "tp "; + } + + const char *CliCommandTp::Name() const + { + return "tp"; + } + + std::vector CliCommandTp::Aliases() const + { + std::vector aliases; + aliases.push_back("teleport"); + return aliases; + } + + const char *CliCommandTp::Usage() const + { + return kTpUsage; + } + + const char *CliCommandTp::Description() const + { + return "Teleport one player to another via Minecraft.World command dispatcher."; + } + + bool CliCommandTp::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() != 3) + { + engine->LogWarn(std::string("Usage: ") + kTpUsage); + return false; + } + + std::shared_ptr subject = engine->FindPlayerByNameUtf8(line.tokens[1]); + std::shared_ptr destination = engine->FindPlayerByNameUtf8(line.tokens[2]); + if (subject == nullptr) + { + engine->LogWarn("Unknown player: " + line.tokens[1]); + return false; + } + if (destination == nullptr) + { + engine->LogWarn("Unknown player: " + line.tokens[2]); + return false; + } + if (subject->connection == nullptr) + { + engine->LogWarn("Cannot teleport because source player connection is inactive."); + return false; + } + std::shared_ptr packet = TeleportCommand::preparePacket(subject->getXuid(), destination->getXuid()); + if (packet == nullptr) + { + engine->LogError("Failed to build teleport command packet."); + return false; + } + + return engine->DispatchWorldCommand(packet->command, packet->data); + } + + void CliCommandTp::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + if (context.currentTokenIndex == 1 || context.currentTokenIndex == 2) + { + engine->SuggestPlayers(context.prefix, context.linePrefix, out); + } + } +} + diff --git a/Minecraft.Server/Console/commands/tp/CliCommandTp.h b/Minecraft.Server/Console/commands/tp/CliCommandTp.h new file mode 100644 index 00000000..39ede0a9 --- /dev/null +++ b/Minecraft.Server/Console/commands/tp/CliCommandTp.h @@ -0,0 +1,18 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandTp : public IServerCliCommand + { + public: + const char *Name() const override; + std::vector Aliases() const override; + const char *Usage() const override; + const char *Description() const override; + bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override; + void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const override; + }; +} + diff --git a/Minecraft.Server/Console/commands/weather/CliCommandWeather.cpp b/Minecraft.Server/Console/commands/weather/CliCommandWeather.cpp new file mode 100644 index 00000000..8a9f164a --- /dev/null +++ b/Minecraft.Server/Console/commands/weather/CliCommandWeather.cpp @@ -0,0 +1,49 @@ +#include "stdafx.h" + +#include "CliCommandWeather.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../../Minecraft.World/GameCommandPacket.h" +#include "../../../../Minecraft.World/ToggleDownfallCommand.h" + +namespace ServerRuntime +{ + namespace + { + const char *kWeatherUsage = "weather"; + } + + const char *CliCommandWeather::Name() const + { + return "weather"; + } + + const char *CliCommandWeather::Usage() const + { + return kWeatherUsage; + } + + const char *CliCommandWeather::Description() const + { + return "Toggle weather via Minecraft.World command dispatcher."; + } + + bool CliCommandWeather::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() != 1) + { + engine->LogWarn(std::string("Usage: ") + kWeatherUsage); + return false; + } + + std::shared_ptr packet = ToggleDownfallCommand::preparePacket(); + if (packet == nullptr) + { + engine->LogError("Failed to build weather command packet."); + return false; + } + + return engine->DispatchWorldCommand(packet->command, packet->data); + } +} diff --git a/Minecraft.Server/Console/commands/weather/CliCommandWeather.h b/Minecraft.Server/Console/commands/weather/CliCommandWeather.h new file mode 100644 index 00000000..cea4f66a --- /dev/null +++ b/Minecraft.Server/Console/commands/weather/CliCommandWeather.h @@ -0,0 +1,15 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandWeather : public IServerCliCommand + { + public: + const char *Name() const override; + const char *Usage() const override; + const char *Description() const override; + bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override; + }; +} diff --git a/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.cpp b/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.cpp new file mode 100644 index 00000000..33f7b357 --- /dev/null +++ b/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.cpp @@ -0,0 +1,285 @@ +#include "stdafx.h" + +#include "CliCommandWhitelist.h" + +#include "../../ServerCliEngine.h" +#include "../../ServerCliParser.h" +#include "../../../Access/Access.h" +#include "../../../Common/StringUtils.h" +#include "../../../ServerProperties.h" + +#include +#include + +namespace ServerRuntime +{ + namespace + { + static const char *kWhitelistUsage = "whitelist [...]"; + + static bool CompareWhitelistEntries(const ServerRuntime::Access::WhitelistedPlayerEntry &left, const ServerRuntime::Access::WhitelistedPlayerEntry &right) + { + const auto leftName = StringUtils::ToLowerAscii(left.name); + const auto rightName = StringUtils::ToLowerAscii(right.name); + if (leftName != rightName) + { + return leftName < rightName; + } + + return StringUtils::ToLowerAscii(left.xuid) < StringUtils::ToLowerAscii(right.xuid); + } + + static bool PersistWhitelistToggle(bool enabled) + { + auto config = LoadServerPropertiesConfig(); + config.whiteListEnabled = enabled; + return SaveServerPropertiesConfig(config); + } + + static std::string BuildWhitelistEntryRow(const ServerRuntime::Access::WhitelistedPlayerEntry &entry) + { + std::string row = " "; + row += entry.xuid; + if (!entry.name.empty()) + { + row += " - "; + row += entry.name; + } + return row; + } + + static void LogWhitelistMode(ServerCliEngine *engine) + { + engine->LogInfo(std::string("Whitelist is ") + (ServerRuntime::Access::IsWhitelistEnabled() ? "enabled." : "disabled.")); + } + + static bool LogWhitelistEntries(ServerCliEngine *engine) + { + std::vector entries; + if (!ServerRuntime::Access::SnapshotWhitelistedPlayers(&entries)) + { + engine->LogError("Failed to read whitelist entries."); + return false; + } + + std::sort(entries.begin(), entries.end(), CompareWhitelistEntries); + LogWhitelistMode(engine); + engine->LogInfo("There are " + std::to_string(entries.size()) + " whitelisted player(s)."); + for (const auto &entry : entries) + { + engine->LogInfo(BuildWhitelistEntryRow(entry)); + } + return true; + } + + static bool TryParseWhitelistXuid(const std::string &text, ServerCliEngine *engine, PlayerUID *outXuid) + { + if (ServerRuntime::Access::TryParseXuid(text, outXuid)) + { + return true; + } + + engine->LogWarn("Invalid XUID: " + text); + return false; + } + + static void SuggestLiteral(const std::string &candidate, const ServerCliCompletionContext &context, std::vector *out) + { + if (out == nullptr) + { + return; + } + + if (StringUtils::StartsWithIgnoreCase(candidate, context.prefix)) + { + out->push_back(context.linePrefix + candidate); + } + } + } + + const char *CliCommandWhitelist::Name() const + { + return "whitelist"; + } + + const char *CliCommandWhitelist::Usage() const + { + return kWhitelistUsage; + } + + const char *CliCommandWhitelist::Description() const + { + return "Manage the dedicated-server XUID whitelist."; + } + + bool CliCommandWhitelist::Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) + { + if (line.tokens.size() < 2) + { + engine->LogWarn(std::string("Usage: ") + kWhitelistUsage); + return false; + } + if (!ServerRuntime::Access::IsInitialized()) + { + engine->LogWarn("Access manager is not initialized."); + return false; + } + + const auto subcommand = StringUtils::ToLowerAscii(line.tokens[1]); + if (subcommand == "on" || subcommand == "off") + { + if (line.tokens.size() != 2) + { + engine->LogWarn("Usage: whitelist "); + return false; + } + + const bool enabled = (subcommand == "on"); + if (!PersistWhitelistToggle(enabled)) + { + engine->LogError("Failed to persist whitelist mode to server.properties."); + return false; + } + + ServerRuntime::Access::SetWhitelistEnabled(enabled); + engine->LogInfo(std::string("Whitelist ") + (enabled ? "enabled." : "disabled.")); + return true; + } + + if (subcommand == "list") + { + if (line.tokens.size() != 2) + { + engine->LogWarn("Usage: whitelist list"); + return false; + } + + return LogWhitelistEntries(engine); + } + + if (subcommand == "reload") + { + if (line.tokens.size() != 2) + { + engine->LogWarn("Usage: whitelist reload"); + return false; + } + if (!ServerRuntime::Access::ReloadWhitelist()) + { + engine->LogError("Failed to reload whitelist."); + return false; + } + + const auto config = LoadServerPropertiesConfig(); + ServerRuntime::Access::SetWhitelistEnabled(config.whiteListEnabled); + engine->LogInfo("Reloaded whitelist from disk."); + LogWhitelistMode(engine); + return true; + } + + if (subcommand == "add") + { + if (line.tokens.size() < 3) + { + engine->LogWarn("Usage: whitelist add [name ...]"); + return false; + } + + PlayerUID xuid = INVALID_XUID; + if (!TryParseWhitelistXuid(line.tokens[2], engine, &xuid)) + { + return false; + } + + if (ServerRuntime::Access::IsPlayerWhitelisted(xuid)) + { + engine->LogWarn("That XUID is already whitelisted."); + return false; + } + + const auto metadata = ServerRuntime::Access::WhitelistManager::BuildDefaultMetadata("Console"); + const auto name = StringUtils::JoinTokens(line.tokens, 3); + if (!ServerRuntime::Access::AddWhitelistedPlayer(xuid, name, metadata)) + { + engine->LogError("Failed to write whitelist entry."); + return false; + } + + std::string message = "Whitelisted XUID " + ServerRuntime::Access::FormatXuid(xuid) + "."; + if (!name.empty()) + { + message += " Name: " + name; + } + engine->LogInfo(message); + return true; + } + + if (subcommand == "remove") + { + if (line.tokens.size() != 3) + { + engine->LogWarn("Usage: whitelist remove "); + return false; + } + + PlayerUID xuid = INVALID_XUID; + if (!TryParseWhitelistXuid(line.tokens[2], engine, &xuid)) + { + return false; + } + + if (!ServerRuntime::Access::IsPlayerWhitelisted(xuid)) + { + engine->LogWarn("That XUID is not whitelisted."); + return false; + } + + if (!ServerRuntime::Access::RemoveWhitelistedPlayer(xuid)) + { + engine->LogError("Failed to remove whitelist entry."); + return false; + } + + engine->LogInfo("Removed XUID " + ServerRuntime::Access::FormatXuid(xuid) + " from the whitelist."); + return true; + } + + engine->LogWarn(std::string("Usage: ") + kWhitelistUsage); + return false; + } + + void CliCommandWhitelist::Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const + { + (void)engine; + if (out == nullptr) + { + return; + } + + if (context.currentTokenIndex == 1) + { + SuggestLiteral("on", context, out); + SuggestLiteral("off", context, out); + SuggestLiteral("list", context, out); + SuggestLiteral("add", context, out); + SuggestLiteral("remove", context, out); + SuggestLiteral("reload", context, out); + return; + } + + if (context.currentTokenIndex == 2 && context.parsed.tokens.size() >= 2 && StringUtils::ToLowerAscii(context.parsed.tokens[1]) == "remove") + { + std::vector entries; + if (!ServerRuntime::Access::SnapshotWhitelistedPlayers(&entries)) + { + return; + } + + for (const auto &entry : entries) + { + SuggestLiteral(entry.xuid, context, out); + } + } + } +} + diff --git a/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.h b/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.h new file mode 100644 index 00000000..700005d8 --- /dev/null +++ b/Minecraft.Server/Console/commands/whitelist/CliCommandWhitelist.h @@ -0,0 +1,17 @@ +#pragma once + +#include "../IServerCliCommand.h" + +namespace ServerRuntime +{ + class CliCommandWhitelist : public IServerCliCommand + { + public: + const char *Name() const override; + const char *Usage() const override; + const char *Description() const override; + bool Execute(const ServerCliParsedLine &line, ServerCliEngine *engine) override; + void Complete(const ServerCliCompletionContext &context, const ServerCliEngine *engine, std::vector *out) const override; + }; +} + diff --git a/Minecraft.Server/Minecraft.Server.vcxproj b/Minecraft.Server/Minecraft.Server.vcxproj new file mode 100644 index 00000000..330f822a --- /dev/null +++ b/Minecraft.Server/Minecraft.Server.vcxproj @@ -0,0 +1,308 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {FF2DDBF5-8A27-417A-A01C-B98A25C59760} + Win32Proj + MinecraftServer + 10.0 + + + + Application + true + v110 + MultiByte + + + Application + false + v110 + true + MultiByte + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)\Minecraft.Server\$(Configuration)\ + $(SolutionDir)$(Platform)\Minecraft.Server\$(Configuration)\obj\MinecraftServer\ + Minecraft.Server + $(OutDir) + -port 25565 -bind 0.0.0.0 -name DedicatedServer + WindowsLocalDebugger + + + $(SolutionDir)$(Platform)\Server\ + $(Platform)\Server\ + Minecraft.Server + $(OutDir) + -port 25565 -bind 0.0.0.0 -name DedicatedServer + WindowsLocalDebugger + ..\Minecraft.World\x64headers;..\Minecraft.Client\Windows64\Sentient\Include;$(IncludePath) + + + + Level3 + Disabled + Use + Server\$(ProjectName).pch + MultiThreadedDebug + ProgramDatabase + Sync + true + true + _LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_DEBUG;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD;%(PreprocessorDefinitions) + ..\Minecraft.Client;..\Minecraft.Client\Windows64\Iggy\include;..\Minecraft.Client\Xbox\Sentient\Include;..\Minecraft.World\x64headers;..\include;$(ProjectDir)Windows64;%(AdditionalIncludeDirectories) + + + false + + + _WINDOWS64;%(PreprocessorDefinitions) + ..\Minecraft.Client;..\Minecraft.Client\Xbox;%(AdditionalIncludeDirectories) + + + Console + mainCRTStartup + true + d3d11.lib;..\Minecraft.Client\x64_Server\Minecraft.Client.lib;..\Minecraft.World\x64_$(Configuration)\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;;XInput9_1_0.lib;Windows64\Miles\Lib\mss64.lib;wsock32.lib;%(AdditionalDependencies) + + + powershell -ExecutionPolicy Bypass -File "$(ProjectDir)Windows64\postbuild_server.ps1" -OutDir "$(OutDir)." -ProjectRoot "$(ProjectDir).." -Configuration "$(Configuration)" + + + + + Level3 + MaxSpeed + true + true + Use + Server\$(ProjectName).pch + MultiThreaded + Sync + true + true + MINECRAFT_SERVER_BUILD;_LARGE_WORLDS;_DEBUG_MENUS_ENABLED;_CRT_NON_CONFORMING_SWPRINTFS;_CRT_SECURE_NO_WARNINGS;_WINDOWS64;MINECRAFT_SERVER_BUILD;%(PreprocessorDefinitions) + ..\Minecraft.Client;..\Minecraft.Client\Windows64\Iggy\include;..\Minecraft.Client\Xbox\Sentient\Include;..\Minecraft.World\x64headers;..\include;..\Minecraft.Client\Windows64;%(AdditionalIncludeDirectories) + + + false + + + _WINDOWS64;%(PreprocessorDefinitions) + ..\Minecraft.Client;..\Minecraft.Client\Xbox;%(AdditionalIncludeDirectories) + + + Console + mainCRTStartup + true + true + true + d3d11.lib;..\Minecraft.Client\x64_Server\Minecraft.Client.lib;..\Minecraft.World\x64_$(Configuration)\Minecraft.World.lib;XInput9_1_0.lib;Windows64\Iggy\lib\iggy_w64.lib;;XInput9_1_0.lib;Windows64\Miles\Lib\mss64.lib;wsock32.lib;%(AdditionalDependencies) + + + powershell -ExecutionPolicy Bypass -File "../Minecraft.Client/Common/Postbuilds/Server-postbuild.ps1" -OutDir "$(OutDir)/" -ProjectDir "$(ProjectDir)/" + Run Server postbuild script + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + NotUsing + false + NotUsing + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + true + + + true + + + + + + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8} + false + + + + + + + + + + true + + + true + + + + true + + + true + + + true + + + true + + + + + + + + + + + \ No newline at end of file diff --git a/Minecraft.Server/Minecraft.Server.vcxproj.filters b/Minecraft.Server/Minecraft.Server.vcxproj.filters new file mode 100644 index 00000000..ce065193 --- /dev/null +++ b/Minecraft.Server/Minecraft.Server.vcxproj.filters @@ -0,0 +1,529 @@ + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {51610b47-4ac2-4dde-87e2-aceebf26989c} + + + {c148b1e2-1f21-4aa0-b174-cc8c24c8951a} + + + {24c18f03-9685-42cc-8e58-e4b12e4c174c} + + + {029a5d10-ef33-4a25-9bcd-9f39944dd5f1} + + + {5a30b59a-62d3-4f1e-b967-b77db2cec47b} + + + {01b98446-8ff2-495d-bc87-32b2681a7df3} + + + {89569093-5317-40c0-be12-a19e4a3249a8} + + + {e313f52b-43b9-4dda-9559-af5a245d73b2} + + + {a6863c9c-ed24-4571-8004-e8ed4cd900a7} + + + {af0f8212-558d-4182-821e-dc5c6af8765d} + + + {ea6bea52-2793-4618-86b5-c737d1c0b873} + + + {a6301a38-475d-47f2-9450-37a3c135b826} + + + {bb281a8e-a612-459b-91c3-6f3bf8ede342} + + + {efa1f6f1-ab08-4256-a8ab-1ad7eea0d10a} + + + {83df2dae-2a78-476c-b708-05659e444219} + + + {ae702713-2488-411d-8141-fd5d5a348ffa} + + + {5f7f200c-d8a9-4b30-98a7-4f71e4d000f9} + + + {81a36a77-c03d-459f-a951-dc00a7d760d7} + + + {eb779138-4cc1-4ee4-b212-337d56e109ee} + + + {7bdf9604-e397-4e03-bbf1-2fda3cd851f9} + + + {dbda2ebe-0841-4b02-b6a0-9f77f0dd88ea} + + + {ec72e562-e436-4e4d-afd1-96662ff66149} + + + {6b18e686-df1f-467d-a533-75dca2ac6f68} + + + {59579052-3978-492b-acb4-c31a0bd63bcd} + + + {4d7261d2-3fae-4386-bbdd-8d4dad156f59} + + + {08e4175f-35b8-4f37-9335-5f460a62ba2c} + + + {227f9fab-7527-4e66-bb0a-77634e3c1c63} + + + {fafde418-a3b2-4986-82a8-46fb199d0bd5} + + + {3d43b9b8-1c52-4d65-b93d-d16b8d610800} + + + {a7c5b6eb-46cc-4cc0-ac7b-88a2def415fb} + + + {4a22e8a3-ce95-4389-9199-0c728fe25f8e} + + + {bd464b7d-b9ee-471a-ba0a-66c8cd67daa8} + + + {99a0ecc1-cdc6-4077-9988-88a17add709b} + + + {4ca625a8-aae3-4121-a9e3-61a71490928f} + + + {b4403f41-77f4-49c6-8948-8ec7afd744c7} + + + {ad11c0f5-63b0-49cb-a426-dd5c2e93b0e3} + + + {a0a5cd4d-bc9b-450f-a2c1-c67e7739e52c} + + + {fd6ceca9-2e6d-4b2d-b6aa-8c103049a8e7} + + + {005fdd55-6b11-40bc-bc7a-59afa39d5782} + + + {6976df84-e36b-4d48-b2f8-35d2ac4a9495} + + + {19759ef7-84d7-4625-8cf5-65ce2b44c8e1} + + + + + Access + + + Access + + + Access + + + Common + + + Common + + + Console + + + Console + + + Console + + + Console + + + Console + + + Console\commands\ban + + + Console\commands\ban-ip + + + Console\commands\ban-list + + + Console\commands\defaultgamemode + + + Console\commands\enchant + + + Console\commands\experience + + + Console\commands\gamemode + + + Console\commands\give + + + Console\commands\help + + + Console\commands\kill + + + Console\commands\list + + + Console\commands\whitelist + + + Console\commands\weather + + + Console\commands\tp + + + Console\commands\time + + + Console\commands\stop + + + Console\commands\pardon-ip + + + Console\commands\pardon + + + vendor\linenoise + + + Windows64 + + + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Access + + + Access + + + Access + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson + + + vendor\rapidjson\error + + + vendor\rapidjson\error + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\internal + + + vendor\rapidjson\msinttypes + + + vendor\rapidjson\msinttypes + + + Windows64\4JLibs\inc + + + Windows64\4JLibs\inc + + + Windows64\4JLibs\inc + + + Windows64\4JLibs\inc + + + + + docs + + + docs + + + vendor\linenoise + + + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\4JLibs\libs + + + Windows64\Iggy\lib + + + Windows64\Iggy\lib + + + Windows64\Iggy\lib + + + Windows64\Miles Sound System\lib + + + \ No newline at end of file diff --git a/Minecraft.Server/ServerLogManager.cpp b/Minecraft.Server/ServerLogManager.cpp new file mode 100644 index 00000000..bfa8d620 --- /dev/null +++ b/Minecraft.Server/ServerLogManager.cpp @@ -0,0 +1,401 @@ +#include "stdafx.h" + +#include "ServerLogManager.h" + +#include "Common/StringUtils.h" +#include "ServerLogger.h" + +#include +#include + +namespace ServerRuntime +{ + namespace ServerLogManager + { + namespace + { + /** + * **!! This information is managed solely for logging purposes, but it is questionable from a liability perspective, so it will eventually need to be separated !!** + * + * Tracks the remote IP and accepted player name associated with one `smallId` + * 1つのsmallIdに紐づく接続IPとプレイヤー名を保持する + */ + struct ConnectionLogEntry + { + std::string remoteIp; + std::string playerName; + }; + + /** + * Owns the shared connection cache used by hook points running on different threads + * 複数スレッドのhookから共有される接続キャッシュを保持する + */ + struct ServerLogState + { + std::mutex stateLock; + std::array entries; + }; + + ServerLogState g_serverLogState; + + static bool IsDedicatedServerLoggingEnabled() + { + // PIEBOT: Minecraft.Server only compiles if the build config is set to Server + return true; + } + + static void ResetConnectionLogEntry(ConnectionLogEntry *entry) + { + if (entry == NULL) + { + return; + } + + entry->remoteIp.clear(); + entry->playerName.clear(); + } + + static std::string NormalizeRemoteIp(const char *ip) + { + if (ip == NULL || ip[0] == 0) + { + return std::string("unknown"); + } + + return std::string(ip); + } + + static std::string NormalizePlayerName(const std::wstring &playerName) + { + std::string playerNameUtf8 = StringUtils::WideToUtf8(playerName); + if (playerNameUtf8.empty()) + { + playerNameUtf8 = ""; + } + + return playerNameUtf8; + } + + // Default to the main app channel when the caller does not provide a source tag. + static const char *NormalizeClientLogSource(const char *source) + { + if (source == NULL || source[0] == 0) + { + return "app"; + } + + return source; + } + + static void EmitClientDebugLogLine(const char *source, const std::string &line) + { + if (line.empty()) + { + return; + } + + LogDebugf("client", "[%s] %s", NormalizeClientLogSource(source), line.c_str()); + } + + // Split one debug payload into individual lines so each line becomes a prompt-safe server log entry. + static void ForwardClientDebugMessage(const char *source, const char *message) + { + if (message == NULL || message[0] == 0) + { + return; + } + + const char *cursor = message; + while (*cursor != 0) + { + const char *lineStart = cursor; + while (*cursor != 0 && *cursor != '\r' && *cursor != '\n') + { + ++cursor; + } + + // Split multi-line client debug output into prompt-safe server log entries. + if (cursor > lineStart) + { + EmitClientDebugLogLine(source, std::string(lineStart, (size_t)(cursor - lineStart))); + } + + while (*cursor == '\r' || *cursor == '\n') + { + ++cursor; + } + } + } + + // Share the same formatting path for app, user, and legacy debug-spew forwards. + static void ForwardFormattedClientDebugLogV(const char *source, const char *format, va_list args) + { + if (!IsDedicatedServerLoggingEnabled() || format == NULL || format[0] == 0) + { + return; + } + + char messageBuffer[2048] = {}; + vsnprintf_s(messageBuffer, sizeof(messageBuffer), _TRUNCATE, format, args); + ForwardClientDebugMessage(source, messageBuffer); + } + + static const char *TcpRejectReasonToString(ETcpRejectReason reason) + { + switch (reason) + { + case eTcpRejectReason_BannedIp: return "banned-ip"; + case eTcpRejectReason_GameNotReady: return "game-not-ready"; + case eTcpRejectReason_ServerFull: return "server-full"; + default: return "unknown"; + } + } + + static const char *LoginRejectReasonToString(ELoginRejectReason reason) + { + switch (reason) + { + case eLoginRejectReason_BannedXuid: return "banned-xuid"; + case eLoginRejectReason_NotWhitelisted: return "not-whitelisted"; + case eLoginRejectReason_DuplicateXuid: return "duplicate-xuid"; + case eLoginRejectReason_DuplicateName: return "duplicate-name"; + default: return "unknown"; + } + } + + static const char *DisconnectReasonToString(DisconnectPacket::eDisconnectReason reason) + { + switch (reason) + { + case DisconnectPacket::eDisconnect_None: return "none"; + case DisconnectPacket::eDisconnect_Quitting: return "quitting"; + case DisconnectPacket::eDisconnect_Closed: return "closed"; + case DisconnectPacket::eDisconnect_LoginTooLong: return "login-too-long"; + case DisconnectPacket::eDisconnect_IllegalStance: return "illegal-stance"; + case DisconnectPacket::eDisconnect_IllegalPosition: return "illegal-position"; + case DisconnectPacket::eDisconnect_MovedTooQuickly: return "moved-too-quickly"; + case DisconnectPacket::eDisconnect_NoFlying: return "no-flying"; + case DisconnectPacket::eDisconnect_Kicked: return "kicked"; + case DisconnectPacket::eDisconnect_TimeOut: return "timeout"; + case DisconnectPacket::eDisconnect_Overflow: return "overflow"; + case DisconnectPacket::eDisconnect_EndOfStream: return "end-of-stream"; + case DisconnectPacket::eDisconnect_ServerFull: return "server-full"; + case DisconnectPacket::eDisconnect_OutdatedServer: return "outdated-server"; + case DisconnectPacket::eDisconnect_OutdatedClient: return "outdated-client"; + case DisconnectPacket::eDisconnect_UnexpectedPacket: return "unexpected-packet"; + case DisconnectPacket::eDisconnect_ConnectionCreationFailed: return "connection-creation-failed"; + case DisconnectPacket::eDisconnect_NoMultiplayerPrivilegesHost: return "no-multiplayer-privileges-host"; + case DisconnectPacket::eDisconnect_NoMultiplayerPrivilegesJoin: return "no-multiplayer-privileges-join"; + case DisconnectPacket::eDisconnect_NoUGC_AllLocal: return "no-ugc-all-local"; + case DisconnectPacket::eDisconnect_NoUGC_Single_Local: return "no-ugc-single-local"; + case DisconnectPacket::eDisconnect_ContentRestricted_AllLocal: return "content-restricted-all-local"; + case DisconnectPacket::eDisconnect_ContentRestricted_Single_Local: return "content-restricted-single-local"; + case DisconnectPacket::eDisconnect_NoUGC_Remote: return "no-ugc-remote"; + case DisconnectPacket::eDisconnect_NoFriendsInGame: return "no-friends-in-game"; + case DisconnectPacket::eDisconnect_Banned: return "banned"; + case DisconnectPacket::eDisconnect_NotFriendsWithHost: return "not-friends-with-host"; + case DisconnectPacket::eDisconnect_NATMismatch: return "nat-mismatch"; + default: return "unknown"; + } + } + } + + // Only forward client-side debug output while the process is running as the dedicated server. + bool ShouldForwardClientDebugLogs() + { + return IsDedicatedServerLoggingEnabled(); + } + + void ForwardClientAppDebugLogV(const char *format, va_list args) + { + ForwardFormattedClientDebugLogV("app", format, args); + } + + void ForwardClientUserDebugLogV(int user, const char *format, va_list args) + { + char source[32] = {}; + _snprintf_s(source, sizeof(source), _TRUNCATE, "app:user=%d", user); + ForwardFormattedClientDebugLogV(source, format, args); + } + + void ForwardClientDebugSpewLogV(const char *format, va_list args) + { + ForwardFormattedClientDebugLogV("debug-spew", format, args); + } + + // Clear every cached connection slot during startup so stale metadata never leaks into future logs. + void Initialize() + { + std::lock_guard stateLock(g_serverLogState.stateLock); + for (size_t index = 0; index < g_serverLogState.entries.size(); ++index) + { + ResetConnectionLogEntry(&g_serverLogState.entries[index]); + } + } + + // Reuse Initialize as the shutdown cleanup path because both operations wipe the cache. + void Shutdown() + { + Initialize(); + } + + // Log the raw socket arrival before a smallId is assigned so early rejects still have an IP in the logs. + void OnIncomingTcpConnection(const char *ip) + { + if (!IsDedicatedServerLoggingEnabled()) + { + return; + } + + const std::string remoteIp = NormalizeRemoteIp(ip); + LogInfof("network", "incoming tcp connection from %s", remoteIp.c_str()); + } + + // TCP rejects happen before connection state is cached, so log directly from the supplied remote IP. + void OnRejectedTcpConnection(const char *ip, ETcpRejectReason reason) + { + if (!IsDedicatedServerLoggingEnabled()) + { + return; + } + + const std::string remoteIp = NormalizeRemoteIp(ip); + LogWarnf("network", "rejected tcp connection from %s: reason=%s", remoteIp.c_str(), TcpRejectReasonToString(reason)); + } + + // Cache the accepted remote IP immediately so later login and disconnect logs can reuse it. + void OnAcceptedTcpConnection(unsigned char smallId, const char *ip) + { + if (!IsDedicatedServerLoggingEnabled()) + { + return; + } + + const std::string remoteIp = NormalizeRemoteIp(ip); + { + std::lock_guard stateLock(g_serverLogState.stateLock); + ConnectionLogEntry &entry = g_serverLogState.entries[smallId]; + ResetConnectionLogEntry(&entry); + entry.remoteIp = remoteIp; + } + + LogInfof("network", "accepted tcp connection from %s as smallId=%u", remoteIp.c_str(), (unsigned)smallId); + } + + // Once login succeeds, bind the resolved player name onto the cached transport entry. + void OnAcceptedPlayerLogin(unsigned char smallId, const std::wstring &playerName) + { + if (!IsDedicatedServerLoggingEnabled()) + { + return; + } + + const std::string playerNameUtf8 = NormalizePlayerName(playerName); + std::string remoteIp("unknown"); + { + std::lock_guard stateLock(g_serverLogState.stateLock); + ConnectionLogEntry &entry = g_serverLogState.entries[smallId]; + entry.playerName = playerNameUtf8; + if (!entry.remoteIp.empty()) + { + remoteIp = entry.remoteIp; + } + } + + LogInfof("network", "accepted player login: name=\"%s\" ip=%s smallId=%u", playerNameUtf8.c_str(), remoteIp.c_str(), (unsigned)smallId); + } + + // Read the cached IP for the rejection log, then clear the slot because the player never fully joined. + void OnRejectedPlayerLogin(unsigned char smallId, const std::wstring &playerName, ELoginRejectReason reason) + { + if (!IsDedicatedServerLoggingEnabled()) + { + return; + } + + const std::string playerNameUtf8 = NormalizePlayerName(playerName); + std::string remoteIp("unknown"); + { + std::lock_guard stateLock(g_serverLogState.stateLock); + ConnectionLogEntry &entry = g_serverLogState.entries[smallId]; + if (!entry.remoteIp.empty()) + { + remoteIp = entry.remoteIp; + } + ResetConnectionLogEntry(&entry); + } + + LogWarnf("network", "rejected login from %s: name=\"%s\" reason=%s", remoteIp.c_str(), playerNameUtf8.c_str(), LoginRejectReasonToString(reason)); + } + + // Disconnect logging is the final consumer of cached metadata, so it also clears the slot afterward. + void OnPlayerDisconnected( + unsigned char smallId, + const std::wstring &playerName, + DisconnectPacket::eDisconnectReason reason, + bool initiatedByServer) + { + if (!IsDedicatedServerLoggingEnabled()) + { + return; + } + + std::string playerNameUtf8 = NormalizePlayerName(playerName); + std::string remoteIp("unknown"); + { + // Copy state under lock and emit the log after unlocking so CLI output never blocks connection bookkeeping. + std::lock_guard stateLock(g_serverLogState.stateLock); + ConnectionLogEntry &entry = g_serverLogState.entries[smallId]; + if (!entry.remoteIp.empty()) + { + remoteIp = entry.remoteIp; + } + if (playerNameUtf8 == "" && !entry.playerName.empty()) + { + playerNameUtf8 = entry.playerName; + } + ResetConnectionLogEntry(&entry); + } + + LogInfof( + "network", + "%s: name=\"%s\" ip=%s smallId=%u reason=%s", + initiatedByServer ? "disconnecting player" : "player disconnected", + playerNameUtf8.c_str(), + remoteIp.c_str(), + (unsigned)smallId, + DisconnectReasonToString(reason)); + } + + /** + * For logging purposes, the responsibility is technically misplaced, but the IP is cached in `LogManager`. + * Those cached values are then used to retrieve the player's IP. + * + * Eventually, this should be implemented in a separate class or on the `Minecraft.Client` side instead. + */ + bool TryGetConnectionRemoteIp(unsigned char smallId, std::string *outIp) + { + if (!IsDedicatedServerLoggingEnabled() || outIp == NULL) + { + return false; + } + + std::lock_guard stateLock(g_serverLogState.stateLock); + const ConnectionLogEntry &entry = g_serverLogState.entries[smallId]; + if (entry.remoteIp.empty() || entry.remoteIp == "unknown") + { + return false; + } + + *outIp = entry.remoteIp; + return true; + } + + // Provide explicit cache cleanup for paths that terminate without going through disconnect logging. + void ClearConnection(unsigned char smallId) + { + std::lock_guard stateLock(g_serverLogState.stateLock); + ResetConnectionLogEntry(&g_serverLogState.entries[smallId]); + } + } +} diff --git a/Minecraft.Server/ServerLogManager.h b/Minecraft.Server/ServerLogManager.h new file mode 100644 index 00000000..fe150b21 --- /dev/null +++ b/Minecraft.Server/ServerLogManager.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include + +#include "../Minecraft.World/DisconnectPacket.h" + +namespace ServerRuntime +{ + namespace ServerLogManager + { + /** + * Identifies why the dedicated server rejected a TCP connection before login completed + * ログイン完了前にTCP接続を拒否した理由 + */ + enum ETcpRejectReason + { + eTcpRejectReason_BannedIp = 0, + eTcpRejectReason_GameNotReady, + eTcpRejectReason_ServerFull + }; + + /** + * Identifies why the dedicated server rejected a player during login validation + * ログイン検証中にプレイヤーを拒否した理由 + */ + enum ELoginRejectReason + { + eLoginRejectReason_BannedXuid = 0, + eLoginRejectReason_NotWhitelisted, + eLoginRejectReason_DuplicateXuid, + eLoginRejectReason_DuplicateName + }; + + /** + * Returns `true` when client-side debug logs should be redirected into the dedicated server logger + * dedicated server時にclient側デバッグログを転送すかどうか + */ + bool ShouldForwardClientDebugLogs(); + + /** + * Formats and forwards `CMinecraftApp::DebugPrintf` output through the dedicated server logger + * CMinecraftApp::DebugPrintf の出力を専用サーバーロガーへ転送 + */ + void ForwardClientAppDebugLogV(const char *format, va_list args); + + /** + * Formats and forwards `CMinecraftApp::DebugPrintf(int user, ...)` output through the dedicated server logger + * CMinecraftApp::DebugPrintf(int user, ...) の出力を専用サーバーロガーへ転送 + */ + void ForwardClientUserDebugLogV(int user, const char *format, va_list args); + + /** + * Formats and forwards legacy `DebugSpew` output through the dedicated server logger + * 従来の DebugSpew 出力を専用サーバーロガーへ転送 + */ + void ForwardClientDebugSpewLogV(const char *format, va_list args); + + /** + * Clears cached connection metadata before the dedicated server starts accepting players + * 接続ログ管理用のキャッシュを初期化 + */ + void Initialize(); + + /** + * Releases cached connection metadata after the dedicated server stops + * 接続ログ管理用のキャッシュを停止時に破棄 + */ + void Shutdown(); + + /** + * **Log Incoming TCP Connection** + * + * Emits a named log for a raw TCP accept before smallId assignment finishes + * smallId割り当て前のTCP接続を記録 + */ + void OnIncomingTcpConnection(const char *ip); + + /** + * Emits a named log for a TCP connection rejected before login starts + * ログイン開始前に拒否したTCP接続を記録 + */ + void OnRejectedTcpConnection(const char *ip, ETcpRejectReason reason); + + /** + * Stores the remote IP for the assigned smallId and logs the accepted transport connection + * 割り当て済みsmallIdに対接続IPを保存して記録 + */ + void OnAcceptedTcpConnection(unsigned char smallId, const char *ip); + + /** + * Associates a player name with the connection and emits the accepted login log + * 接続にプレイヤー名を関連付けてログイン成功を記録 + */ + void OnAcceptedPlayerLogin(unsigned char smallId, const std::wstring &playerName); + + /** + * Emits a named login rejection log and clears cached metadata for that smallId + * ログイン拒否を記録し対象smallIdのキャッシュを破棄 + */ + void OnRejectedPlayerLogin(unsigned char smallId, const std::wstring &playerName, ELoginRejectReason reason); + + /** + * Emits a named disconnect log using cached connection metadata and then clears that entry + * 接続キャッシュを使って切断ログを出しその後で破棄 + */ + void OnPlayerDisconnected( + unsigned char smallId, + const std::wstring &playerName, + DisconnectPacket::eDisconnectReason reason, + bool initiatedByServer); + + /** + * Reads the cached remote IP for a live smallId without consuming the entry + * Eventually, this should be implemented in a separate class or on the `Minecraft.Client` side instead. + * + * 指定smallIdの接続IPをキャッシュから参照する + */ + bool TryGetConnectionRemoteIp(unsigned char smallId, std::string *outIp); + + /** + * Removes any remembered IP or player name for the specified smallId + * 指定smallIdに紐づく接続キャッシュを消去 + */ + void ClearConnection(unsigned char smallId); + } +} diff --git a/Minecraft.Server/ServerLogger.cpp b/Minecraft.Server/ServerLogger.cpp new file mode 100644 index 00000000..1dcf7c47 --- /dev/null +++ b/Minecraft.Server/ServerLogger.cpp @@ -0,0 +1,258 @@ +#include "stdafx.h" + +#include "ServerLogger.h" +#include "Common//StringUtils.h" +#include "vendor//linenoise//linenoise.h" + +#include +#include +#include + +namespace ServerRuntime +{ +static volatile LONG g_minLogLevel = (LONG)eServerLogLevel_Info; + +static const char *NormalizeCategory(const char *category) +{ + if (category == NULL || category[0] == 0) + { + return "server"; + } + return category; +} + +static const char *LogLevelToString(EServerLogLevel level) +{ + switch (level) + { + case eServerLogLevel_Debug: + return "DEBUG"; + case eServerLogLevel_Info: + return "INFO"; + case eServerLogLevel_Warn: + return "WARN"; + case eServerLogLevel_Error: + return "ERROR"; + default: + return "INFO"; + } +} + +static WORD LogLevelToColor(EServerLogLevel level) +{ + switch (level) + { + case eServerLogLevel_Debug: + return FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; + case eServerLogLevel_Warn: + return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; + case eServerLogLevel_Error: + return FOREGROUND_RED | FOREGROUND_INTENSITY; + case eServerLogLevel_Info: + default: + return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; + } +} + +static void BuildTimestamp(char *buffer, size_t bufferSize) +{ + if (buffer == NULL || bufferSize == 0) + { + return; + } + + SYSTEMTIME localTime; + GetLocalTime(&localTime); + sprintf_s( + buffer, + bufferSize, + "%04u-%02u-%02u %02u:%02u:%02u.%03u", + (unsigned)localTime.wYear, + (unsigned)localTime.wMonth, + (unsigned)localTime.wDay, + (unsigned)localTime.wHour, + (unsigned)localTime.wMinute, + (unsigned)localTime.wSecond, + (unsigned)localTime.wMilliseconds); +} + +static bool ShouldLog(EServerLogLevel level) +{ + return ((LONG)level >= g_minLogLevel); +} + +static void WriteLogLine(EServerLogLevel level, const char *category, const char *message) +{ + if (!ShouldLog(level)) + { + return; + } + + linenoiseExternalWriteBegin(); + + const char *safeCategory = NormalizeCategory(category); + const char *safeMessage = (message != NULL) ? message : ""; + + char timestamp[32] = {}; + BuildTimestamp(timestamp, sizeof(timestamp)); + + HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO originalInfo; + bool hasColorConsole = false; + if (stdoutHandle != INVALID_HANDLE_VALUE && stdoutHandle != NULL) + { + if (GetConsoleScreenBufferInfo(stdoutHandle, &originalInfo)) + { + hasColorConsole = true; + SetConsoleTextAttribute(stdoutHandle, LogLevelToColor(level)); + } + } + + printf( + "[%s][%s][%s] %s\n", + timestamp, + LogLevelToString(level), + safeCategory, + safeMessage); + fflush(stdout); + + if (hasColorConsole) + { + SetConsoleTextAttribute(stdoutHandle, originalInfo.wAttributes); + } + + linenoiseExternalWriteEnd(); +} + +static void WriteLogLineV(EServerLogLevel level, const char *category, const char *format, va_list args) +{ + char messageBuffer[2048] = {}; + if (format == NULL) + { + WriteLogLine(level, category, ""); + return; + } + + vsnprintf_s(messageBuffer, sizeof(messageBuffer), _TRUNCATE, format, args); + WriteLogLine(level, category, messageBuffer); +} + +bool TryParseServerLogLevel(const char *value, EServerLogLevel *outLevel) +{ + if (value == NULL || outLevel == NULL) + { + return false; + } + + if (_stricmp(value, "debug") == 0) + { + *outLevel = eServerLogLevel_Debug; + return true; + } + if (_stricmp(value, "info") == 0) + { + *outLevel = eServerLogLevel_Info; + return true; + } + if (_stricmp(value, "warn") == 0 || _stricmp(value, "warning") == 0) + { + *outLevel = eServerLogLevel_Warn; + return true; + } + if (_stricmp(value, "error") == 0) + { + *outLevel = eServerLogLevel_Error; + return true; + } + + return false; +} + +void SetServerLogLevel(EServerLogLevel level) +{ + if (level < eServerLogLevel_Debug) + { + level = eServerLogLevel_Debug; + } + else if (level > eServerLogLevel_Error) + { + level = eServerLogLevel_Error; + } + + g_minLogLevel = (LONG)level; +} + +EServerLogLevel GetServerLogLevel() +{ + return (EServerLogLevel)g_minLogLevel; +} + +void LogDebug(const char *category, const char *message) +{ + WriteLogLine(eServerLogLevel_Debug, category, message); +} + +void LogInfo(const char *category, const char *message) +{ + WriteLogLine(eServerLogLevel_Info, category, message); +} + +void LogWarn(const char *category, const char *message) +{ + WriteLogLine(eServerLogLevel_Warn, category, message); +} + +void LogError(const char *category, const char *message) +{ + WriteLogLine(eServerLogLevel_Error, category, message); +} + +void LogDebugf(const char *category, const char *format, ...) +{ + va_list args; + va_start(args, format); + WriteLogLineV(eServerLogLevel_Debug, category, format, args); + va_end(args); +} + +void LogInfof(const char *category, const char *format, ...) +{ + va_list args; + va_start(args, format); + WriteLogLineV(eServerLogLevel_Info, category, format, args); + va_end(args); +} + +void LogWarnf(const char *category, const char *format, ...) +{ + va_list args; + va_start(args, format); + WriteLogLineV(eServerLogLevel_Warn, category, format, args); + va_end(args); +} + +void LogErrorf(const char *category, const char *format, ...) +{ + va_list args; + va_start(args, format); + WriteLogLineV(eServerLogLevel_Error, category, format, args); + va_end(args); +} + +void LogStartupStep(const char *message) +{ + LogInfo("startup", message); +} + +void LogWorldIO(const char *message) +{ + LogInfo("world-io", message); +} + +void LogWorldName(const char *prefix, const std::wstring &name) +{ + std::string utf8 = StringUtils::WideToUtf8(name); + LogInfof("world-io", "%s: %s", (prefix != NULL) ? prefix : "name", utf8.c_str()); +} +} + diff --git a/Minecraft.Server/ServerLogger.h b/Minecraft.Server/ServerLogger.h new file mode 100644 index 00000000..89b820e6 --- /dev/null +++ b/Minecraft.Server/ServerLogger.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +namespace ServerRuntime +{ + enum EServerLogLevel + { + eServerLogLevel_Debug = 0, + eServerLogLevel_Info = 1, + eServerLogLevel_Warn = 2, + eServerLogLevel_Error = 3 + }; + + /** + * **Parse Log Level String** + * + * Converts a string value into log level (`debug`/`info`/`warn`/`error`) + * ログレベル文字列の変換処理 + * + * @param value Source string + * @param outLevel Output location for parsed level + * @return `true` when conversion succeeds + */ + bool TryParseServerLogLevel(const char *value, EServerLogLevel *outLevel); + + void SetServerLogLevel(EServerLogLevel level); + EServerLogLevel GetServerLogLevel(); + + void LogDebug(const char *category, const char *message); + void LogInfo(const char *category, const char *message); + void LogWarn(const char *category, const char *message); + void LogError(const char *category, const char *message); + + /** Emit formatted log output with the specified level and category */ + void LogDebugf(const char *category, const char *format, ...); + void LogInfof(const char *category, const char *format, ...); + void LogWarnf(const char *category, const char *format, ...); + void LogErrorf(const char *category, const char *format, ...); + + void LogStartupStep(const char *message); + void LogWorldIO(const char *message); + void LogWorldName(const char *prefix, const std::wstring &name); +} diff --git a/Minecraft.Server/ServerProperties.cpp b/Minecraft.Server/ServerProperties.cpp new file mode 100644 index 00000000..ddc75a4a --- /dev/null +++ b/Minecraft.Server/ServerProperties.cpp @@ -0,0 +1,930 @@ +#include "stdafx.h" + +#include "ServerProperties.h" + +#include "ServerLogger.h" +#include "Common//StringUtils.h" +#include "Common//FileUtils.h" +#include "..//Minecraft.World//ChunkSource.h" + +#include +#include +#include +#include +#include + +namespace ServerRuntime +{ +using StringUtils::ToLowerAscii; +using StringUtils::TrimAscii; +using StringUtils::StripUtf8Bom; +using StringUtils::Utf8ToWide; +using StringUtils::WideToUtf8; + +struct ServerPropertyDefault +{ + const char *key; + const char *value; +}; + +static const char *kServerPropertiesPath = "server.properties"; +static const size_t kMaxSaveIdLength = 31; + +static const int kDefaultServerPort = 25565; +static const int kDefaultMaxPlayers = 16; +static const int kMaxDedicatedPlayers = 256; +static const int kDefaultAutosaveIntervalSeconds = 60; +static const char *kLanAdvertisePropertyKey = "lan-advertise"; + +static const ServerPropertyDefault kServerPropertyDefaults[] = +{ + { "allow-flight", "true" }, + { "allow-nether", "true" }, + { "autosave-interval", "60" }, + { "bedrock-fog", "true" }, + { "bonus-chest", "false" }, + { "difficulty", "1" }, + { "disable-saving", "false" }, + { "do-daylight-cycle", "true" }, + { "do-mob-loot", "true" }, + { "do-mob-spawning", "true" }, + { "do-tile-drops", "true" }, + { "fire-spreads", "true" }, + { "friends-of-friends", "false" }, + { "gamemode", "0" }, + { "gamertags", "true" }, + { "generate-structures", "true" }, + { "host-can-be-invisible", "true" }, + { "host-can-change-hunger", "true" }, + { "host-can-fly", "true" }, + { "keep-inventory", "false" }, + { "level-id", "world" }, + { "level-name", "world" }, + { "level-seed", "" }, + { "level-type", "default" }, + { "world-size", "classic" }, + { "spawn-protection", "0" }, + { "log-level", "info" }, + { "max-build-height", "256" }, + { "max-players", "16" }, + { "mob-griefing", "true" }, + { "motd", "A Minecraft Server" }, + { "natural-regeneration", "true" }, + { "pvp", "true" }, + { "server-ip", "0.0.0.0" }, + { "server-name", "DedicatedServer" }, + { "server-port", "25565" }, + { "white-list", "false" }, + { "lan-advertise", "false" }, + { "spawn-animals", "true" }, + { "spawn-monsters", "true" }, + { "spawn-npcs", "true" }, + { "tnt", "true" }, + { "trust-players", "true" } +}; + +static std::string BoolToString(bool value) +{ + return value ? "true" : "false"; +} + +static std::string IntToString(int value) +{ + char buffer[32] = {}; + sprintf_s(buffer, sizeof(buffer), "%d", value); + return std::string(buffer); +} + +static std::string Int64ToString(__int64 value) +{ + char buffer[64] = {}; + _i64toa_s(value, buffer, sizeof(buffer), 10); + return std::string(buffer); +} + +static int ClampInt(int value, int minValue, int maxValue) +{ + if (value < minValue) + { + return minValue; + } + if (value > maxValue) + { + return maxValue; + } + return value; +} + +static bool TryParseBool(const std::string &value, bool *outValue) +{ + if (outValue == NULL) + { + return false; + } + + std::string lowered = ToLowerAscii(TrimAscii(value)); + if (lowered == "true" || lowered == "1" || lowered == "yes" || lowered == "on") + { + *outValue = true; + return true; + } + if (lowered == "false" || lowered == "0" || lowered == "no" || lowered == "off") + { + *outValue = false; + return true; + } + return false; +} + +static bool TryParseInt(const std::string &value, int *outValue) +{ + if (outValue == NULL) + { + return false; + } + + std::string trimmed = TrimAscii(value); + if (trimmed.empty()) + { + return false; + } + + char *end = NULL; + long parsed = strtol(trimmed.c_str(), &end, 10); + if (end == trimmed.c_str() || *end != 0) + { + return false; + } + + *outValue = (int)parsed; + return true; +} + +static bool TryParseInt64(const std::string &value, __int64 *outValue) +{ + if (outValue == NULL) + { + return false; + } + + std::string trimmed = TrimAscii(value); + if (trimmed.empty()) + { + return false; + } + + char *end = NULL; + __int64 parsed = _strtoi64(trimmed.c_str(), &end, 10); + if (end == trimmed.c_str() || *end != 0) + { + return false; + } + + *outValue = parsed; + return true; +} + +static std::string LogLevelToPropertyValue(EServerLogLevel level) +{ + switch (level) + { + case eServerLogLevel_Debug: + return "debug"; + case eServerLogLevel_Warn: + return "warn"; + case eServerLogLevel_Error: + return "error"; + case eServerLogLevel_Info: + default: + return "info"; + } +} + +/** + * **Normalize Save ID** + * + * Normalizes an arbitrary string into a safe save destination ID + * Conversion rules: + * - Lowercase alphabetic characters + * - Keep only `[a-z0-9_.-]` + * - Replace spaces and unsupported characters with `_` + * - Fallback to `world` when empty + * - Enforce max length to match storage constraints + * 保存先IDの正規化処理 + */ +static std::string NormalizeSaveId(const std::string &source) +{ + std::string out; + out.reserve(source.length()); + + // Normalize into a character set that is safe for storage save IDs + // Replace invalid characters with '_' and fold letter case to reduce collisions + for (size_t i = 0; i < source.length(); ++i) + { + unsigned char ch = (unsigned char)source[i]; + if (ch >= 'A' && ch <= 'Z') + { + ch = (unsigned char)(ch - 'A' + 'a'); + } + + const bool alnum = (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'); + const bool passthrough = (ch == '_') || (ch == '-') || (ch == '.'); + if (alnum || passthrough) + { + out.push_back((char)ch); + } + else if (std::isspace(ch)) + { + out.push_back('_'); + } + else if (ch < 0x80) + { + out.push_back('_'); + } + } + + if (out.empty()) + { + out = "world"; + } + + // Add a prefix when needed to avoid awkward leading characters + if (!((out[0] >= 'a' && out[0] <= 'z') || (out[0] >= '0' && out[0] <= '9'))) + { + out = std::string("w_") + out; + } + + // Clamp length to the 4J-side filename buffer constraint + if (out.length() > kMaxSaveIdLength) + { + out.resize(kMaxSaveIdLength); + } + + return out; +} + +static void ApplyDefaultServerProperties(std::unordered_map *properties) +{ + if (properties == NULL) + { + return; + } + + const size_t defaultCount = sizeof(kServerPropertyDefaults) / sizeof(kServerPropertyDefaults[0]); + for (size_t i = 0; i < defaultCount; ++i) + { + (*properties)[kServerPropertyDefaults[i].key] = kServerPropertyDefaults[i].value; + } +} + +/** + * **Parse server.properties Text** + * + * Extracts key/value pairs from `server.properties` format text + * - Ignores lines starting with `#` or `!` as comments + * - Accepts `=` or `:` as separators + * - Skips invalid lines and continues + * server.propertiesのパース処理 + */ +static bool ReadServerPropertiesFile(const char *filePath, std::unordered_map *properties, int *outParsedCount) +{ + if (properties == NULL) + { + return false; + } + + std::string text; + if (filePath == NULL || !FileUtils::ReadTextFile(filePath, &text)) + { + return false; + } + + text = StripUtf8Bom(text); + + int parsedCount = 0; + for (size_t start = 0; start <= text.length();) + { + size_t end = text.find_first_of("\r\n", start); + size_t nextStart = text.length() + 1; + if (end != std::string::npos) + { + nextStart = end + 1; + if (text[end] == '\r' && nextStart < text.length() && text[nextStart] == '\n') + { + ++nextStart; + } + } + + std::string line; + if (end == std::string::npos) + { + line = text.substr(start); + } + else + { + line = text.substr(start, end - start); + } + + std::string trimmedLine = TrimAscii(line); + if (trimmedLine.empty()) + { + start = nextStart; + continue; + } + + if (trimmedLine[0] == '#' || trimmedLine[0] == '!') + { + start = nextStart; + continue; + } + + size_t eqPos = trimmedLine.find('='); + size_t colonPos = trimmedLine.find(':'); + size_t sepPos = std::string::npos; + if (eqPos == std::string::npos) + { + sepPos = colonPos; + } + else if (colonPos == std::string::npos) + { + sepPos = eqPos; + } + else + { + sepPos = (eqPos < colonPos) ? eqPos : colonPos; + } + + if (sepPos == std::string::npos) + { + start = nextStart; + continue; + } + + std::string key = TrimAscii(trimmedLine.substr(0, sepPos)); + if (key.empty()) + { + start = nextStart; + continue; + } + + std::string value = TrimAscii(trimmedLine.substr(sepPos + 1)); + (*properties)[key] = value; + ++parsedCount; + start = nextStart; + } + + if (outParsedCount != NULL) + { + *outParsedCount = parsedCount; + } + + return true; +} + +/** + * **Write server.properties Text** + * + * Writes key/value data back as `server.properties` + * Sorts keys before writing to keep output order stable + * server.propertiesの書き戻し処理 + */ +static bool WriteServerPropertiesFile(const char *filePath, const std::unordered_map &properties) +{ + if (filePath == NULL) + { + return false; + } + + std::string text; + text += "# Minecraft server properties\n"; + text += "# Auto-generated and normalized when missing\n"; + + std::map sortedProperties(properties.begin(), properties.end()); + for (std::map::const_iterator it = sortedProperties.begin(); it != sortedProperties.end(); ++it) + { + text += it->first; + text += "="; + text += it->second; + text += "\n"; + } + + return FileUtils::WriteTextFileAtomic(filePath, text); +} + +static bool ReadNormalizedBoolProperty( + std::unordered_map *properties, + const char *key, + bool defaultValue, + bool *shouldWrite) +{ + std::string raw = TrimAscii((*properties)[key]); + bool value = defaultValue; + if (!TryParseBool(raw, &value)) + { + value = defaultValue; + } + + std::string normalized = BoolToString(value); + if (raw != normalized) + { + (*properties)[key] = normalized; + if (shouldWrite != NULL) + { + *shouldWrite = true; + } + } + + return value; +} + +static int ReadNormalizedIntProperty( + std::unordered_map *properties, + const char *key, + int defaultValue, + int minValue, + int maxValue, + bool *shouldWrite) +{ + std::string raw = TrimAscii((*properties)[key]); + int value = defaultValue; + if (!TryParseInt(raw, &value)) + { + value = defaultValue; + } + value = ClampInt(value, minValue, maxValue); + + std::string normalized = IntToString(value); + if (raw != normalized) + { + (*properties)[key] = normalized; + if (shouldWrite != NULL) + { + *shouldWrite = true; + } + } + + return value; +} + +static std::string ReadNormalizedStringProperty( + std::unordered_map *properties, + const char *key, + const std::string &defaultValue, + size_t maxLength, + bool *shouldWrite) +{ + std::string value = TrimAscii((*properties)[key]); + if (value.empty()) + { + value = defaultValue; + } + if (maxLength > 0 && value.length() > maxLength) + { + value.resize(maxLength); + } + + if (value != (*properties)[key]) + { + (*properties)[key] = value; + if (shouldWrite != NULL) + { + *shouldWrite = true; + } + } + + return value; +} + +static bool ReadNormalizedOptionalInt64Property( + std::unordered_map *properties, + const char *key, + __int64 *outValue, + bool *shouldWrite) +{ + std::string raw = TrimAscii((*properties)[key]); + if (raw.empty()) + { + if ((*properties)[key] != "") + { + (*properties)[key] = ""; + if (shouldWrite != NULL) + { + *shouldWrite = true; + } + } + return false; + } + + __int64 parsed = 0; + if (!TryParseInt64(raw, &parsed)) + { + (*properties)[key] = ""; + if (shouldWrite != NULL) + { + *shouldWrite = true; + } + return false; + } + + std::string normalized = Int64ToString(parsed); + if (raw != normalized) + { + (*properties)[key] = normalized; + if (shouldWrite != NULL) + { + *shouldWrite = true; + } + } + + if (outValue != NULL) + { + *outValue = parsed; + } + return true; +} + +static EServerLogLevel ReadNormalizedLogLevelProperty( + std::unordered_map *properties, + const char *key, + EServerLogLevel defaultValue, + bool *shouldWrite) +{ + std::string raw = TrimAscii((*properties)[key]); + EServerLogLevel value = defaultValue; + if (!TryParseServerLogLevel(raw.c_str(), &value)) + { + value = defaultValue; + } + + std::string normalized = LogLevelToPropertyValue(value); + if (raw != normalized) + { + (*properties)[key] = normalized; + if (shouldWrite != NULL) + { + *shouldWrite = true; + } + } + + return value; +} + +static std::string ReadNormalizedLevelTypeProperty( + std::unordered_map *properties, + const char *key, + bool *outIsFlat, + bool *shouldWrite) +{ + std::string raw = TrimAscii((*properties)[key]); + std::string lowered = ToLowerAscii(raw); + + bool isFlat = false; + std::string normalized = "default"; + if (lowered == "flat" || lowered == "superflat" || lowered == "1") + { + isFlat = true; + normalized = "flat"; + } + else if (lowered == "default" || lowered == "normal" || lowered == "0") + { + isFlat = false; + normalized = "default"; + } + + if (raw != normalized) + { + (*properties)[key] = normalized; + if (shouldWrite != NULL) + { + *shouldWrite = true; + } + } + + if (outIsFlat != NULL) + { + *outIsFlat = isFlat; + } + + return normalized; +} + +static std::string WorldSizeToPropertyValue(int worldSize) +{ + switch (worldSize) + { + case e_worldSize_Small: + return "small"; + case e_worldSize_Medium: + return "medium"; + case e_worldSize_Large: + return "large"; + case e_worldSize_Classic: + default: + return "classic"; + } +} + +static int WorldSizeToXzChunks(int worldSize) +{ + switch (worldSize) + { + case e_worldSize_Small: + return LEVEL_WIDTH_SMALL; + case e_worldSize_Medium: + return LEVEL_WIDTH_MEDIUM; + case e_worldSize_Large: + return LEVEL_WIDTH_LARGE; + case e_worldSize_Classic: + default: + return LEVEL_WIDTH_CLASSIC; + } +} + +static int WorldSizeToHellScale(int worldSize) +{ + switch (worldSize) + { + case e_worldSize_Small: + return HELL_LEVEL_SCALE_SMALL; + case e_worldSize_Medium: + return HELL_LEVEL_SCALE_MEDIUM; + case e_worldSize_Large: + return HELL_LEVEL_SCALE_LARGE; + case e_worldSize_Classic: + default: + return HELL_LEVEL_SCALE_CLASSIC; + } +} + +static bool TryParseWorldSize(const std::string &lowered, int *outWorldSize) +{ + if (outWorldSize == NULL) + { + return false; + } + + if (lowered == "classic" || lowered == "54" || lowered == "1") + { + *outWorldSize = e_worldSize_Classic; + return true; + } + if (lowered == "small" || lowered == "64" || lowered == "2") + { + *outWorldSize = e_worldSize_Small; + return true; + } + if (lowered == "medium" || lowered == "192" || lowered == "3") + { + *outWorldSize = e_worldSize_Medium; + return true; + } + if (lowered == "large" || lowered == "320" || lowered == "4") + { + *outWorldSize = e_worldSize_Large; + return true; + } + + return false; +} + +static int ReadNormalizedWorldSizeProperty( + std::unordered_map *properties, + const char *key, + int defaultWorldSize, + int *outXzChunks, + int *outHellScale, + bool *shouldWrite) +{ + std::string raw = TrimAscii((*properties)[key]); + std::string lowered = ToLowerAscii(raw); + + int worldSize = defaultWorldSize; + if (!raw.empty()) + { + int parsedWorldSize = defaultWorldSize; + if (TryParseWorldSize(lowered, &parsedWorldSize)) + { + worldSize = parsedWorldSize; + } + } + + std::string normalized = WorldSizeToPropertyValue(worldSize); + if (raw != normalized) + { + (*properties)[key] = normalized; + if (shouldWrite != NULL) + { + *shouldWrite = true; + } + } + + if (outXzChunks != NULL) + { + *outXzChunks = WorldSizeToXzChunks(worldSize); + } + if (outHellScale != NULL) + { + *outHellScale = WorldSizeToHellScale(worldSize); + } + + return worldSize; +} + +/** + * **Load Effective Server Properties Config** + * + * Loads effective world settings, repairs missing or invalid values, and returns normalized config + * - Creates defaults when file is missing + * - Fills required keys when absent + * - Normalizes `level-id` to a safe format + * - Auto-saves when any fix is applied + * 実効設定の読み込みと補正処理 + */ +ServerPropertiesConfig LoadServerPropertiesConfig() +{ + ServerPropertiesConfig config; + + std::unordered_map defaults; + std::unordered_map loaded; + ApplyDefaultServerProperties(&defaults); + + int parsedCount = 0; + bool readSuccess = ReadServerPropertiesFile(kServerPropertiesPath, &loaded, &parsedCount); + std::unordered_map merged = defaults; + bool shouldWrite = false; + + if (!readSuccess) + { + LogWorldIO("server.properties not found or unreadable; creating defaults"); + shouldWrite = true; + } + else + { + if (parsedCount == 0) + { + LogWorldIO("server.properties has no properties; applying defaults"); + shouldWrite = true; + } + + const size_t defaultCount = sizeof(kServerPropertyDefaults) / sizeof(kServerPropertyDefaults[0]); + for (size_t i = 0; i < defaultCount; ++i) + { + if (loaded.find(kServerPropertyDefaults[i].key) == loaded.end()) + { + shouldWrite = true; + break; + } + } + } + + for (std::unordered_map::const_iterator it = loaded.begin(); it != loaded.end(); ++it) + { + // Merge loaded values over defaults and keep unknown keys whenever possible + merged[it->first] = it->second; + } + + std::string worldName = TrimAscii(merged["level-name"]); + if (worldName.empty()) + { + worldName = "world"; + shouldWrite = true; + } + + std::string worldSaveId = TrimAscii(merged["level-id"]); + if (worldSaveId.empty()) + { + // If level-id is missing, derive it from level-name to lock save destination + worldSaveId = NormalizeSaveId(worldName); + shouldWrite = true; + } + else + { + // Normalize existing level-id as well to avoid future inconsistencies + std::string normalized = NormalizeSaveId(worldSaveId); + if (normalized != worldSaveId) + { + worldSaveId = normalized; + shouldWrite = true; + } + } + + merged["level-name"] = worldName; + merged["level-id"] = worldSaveId; + + config.worldName = Utf8ToWide(worldName.c_str()); + config.worldSaveId = worldSaveId; + + config.serverPort = ReadNormalizedIntProperty(&merged, "server-port", kDefaultServerPort, 1, 65535, &shouldWrite); + config.serverIp = ReadNormalizedStringProperty(&merged, "server-ip", "0.0.0.0", 255, &shouldWrite); + config.lanAdvertise = ReadNormalizedBoolProperty(&merged, kLanAdvertisePropertyKey, false, &shouldWrite); + config.whiteListEnabled = ReadNormalizedBoolProperty(&merged, "white-list", false, &shouldWrite); + config.serverName = ReadNormalizedStringProperty(&merged, "server-name", "DedicatedServer", 16, &shouldWrite); + config.maxPlayers = ReadNormalizedIntProperty(&merged, "max-players", kDefaultMaxPlayers, 1, kMaxDedicatedPlayers, &shouldWrite); + config.seed = 0; + config.hasSeed = ReadNormalizedOptionalInt64Property(&merged, "level-seed", &config.seed, &shouldWrite); + config.logLevel = ReadNormalizedLogLevelProperty(&merged, "log-level", eServerLogLevel_Info, &shouldWrite); + config.autosaveIntervalSeconds = ReadNormalizedIntProperty(&merged, "autosave-interval", kDefaultAutosaveIntervalSeconds, 5, 3600, &shouldWrite); + + config.difficulty = ReadNormalizedIntProperty(&merged, "difficulty", 1, 0, 3, &shouldWrite); + config.gameMode = ReadNormalizedIntProperty(&merged, "gamemode", 0, 0, 1, &shouldWrite); + config.worldSize = ReadNormalizedWorldSizeProperty( + &merged, + "world-size", + e_worldSize_Classic, + &config.worldSizeChunks, + &config.worldHellScale, + &shouldWrite); + config.levelType = ReadNormalizedLevelTypeProperty(&merged, "level-type", &config.levelTypeFlat, &shouldWrite); + config.spawnProtectionRadius = ReadNormalizedIntProperty(&merged, "spawn-protection", 0, 0, 256, &shouldWrite); + config.generateStructures = ReadNormalizedBoolProperty(&merged, "generate-structures", true, &shouldWrite); + config.bonusChest = ReadNormalizedBoolProperty(&merged, "bonus-chest", false, &shouldWrite); + config.pvp = ReadNormalizedBoolProperty(&merged, "pvp", true, &shouldWrite); + config.trustPlayers = ReadNormalizedBoolProperty(&merged, "trust-players", true, &shouldWrite); + config.fireSpreads = ReadNormalizedBoolProperty(&merged, "fire-spreads", true, &shouldWrite); + config.tnt = ReadNormalizedBoolProperty(&merged, "tnt", true, &shouldWrite); + config.spawnAnimals = ReadNormalizedBoolProperty(&merged, "spawn-animals", true, &shouldWrite); + config.spawnNpcs = ReadNormalizedBoolProperty(&merged, "spawn-npcs", true, &shouldWrite); + config.spawnMonsters = ReadNormalizedBoolProperty(&merged, "spawn-monsters", true, &shouldWrite); + config.allowFlight = ReadNormalizedBoolProperty(&merged, "allow-flight", true, &shouldWrite); + config.allowNether = ReadNormalizedBoolProperty(&merged, "allow-nether", true, &shouldWrite); + config.friendsOfFriends = ReadNormalizedBoolProperty(&merged, "friends-of-friends", false, &shouldWrite); + config.gamertags = ReadNormalizedBoolProperty(&merged, "gamertags", true, &shouldWrite); + config.bedrockFog = ReadNormalizedBoolProperty(&merged, "bedrock-fog", true, &shouldWrite); + config.hostCanFly = ReadNormalizedBoolProperty(&merged, "host-can-fly", true, &shouldWrite); + config.hostCanChangeHunger = ReadNormalizedBoolProperty(&merged, "host-can-change-hunger", true, &shouldWrite); + config.hostCanBeInvisible = ReadNormalizedBoolProperty(&merged, "host-can-be-invisible", true, &shouldWrite); + config.disableSaving = ReadNormalizedBoolProperty(&merged, "disable-saving", false, &shouldWrite); + config.mobGriefing = ReadNormalizedBoolProperty(&merged, "mob-griefing", true, &shouldWrite); + config.keepInventory = ReadNormalizedBoolProperty(&merged, "keep-inventory", false, &shouldWrite); + config.doMobSpawning = ReadNormalizedBoolProperty(&merged, "do-mob-spawning", true, &shouldWrite); + config.doMobLoot = ReadNormalizedBoolProperty(&merged, "do-mob-loot", true, &shouldWrite); + config.doTileDrops = ReadNormalizedBoolProperty(&merged, "do-tile-drops", true, &shouldWrite); + config.naturalRegeneration = ReadNormalizedBoolProperty(&merged, "natural-regeneration", true, &shouldWrite); + config.doDaylightCycle = ReadNormalizedBoolProperty(&merged, "do-daylight-cycle", true, &shouldWrite); + + config.maxBuildHeight = ReadNormalizedIntProperty(&merged, "max-build-height", 256, 64, 256, &shouldWrite); + config.motd = ReadNormalizedStringProperty(&merged, "motd", "A Minecraft Server", 255, &shouldWrite); + + if (shouldWrite) + { + if (WriteServerPropertiesFile(kServerPropertiesPath, merged)) + { + LogWorldIO("wrote server.properties"); + } + else + { + LogWorldIO("failed to write server.properties"); + } + } + + return config; +} + +/** + * **Save World Identity While Preserving Other Keys** + * + * Saves world identity fields while preserving as many other settings as possible + * - Reads existing file and merges including unknown keys + * - Updates `level-name`, `level-id`, and `white-list` before writing back + * ワールド識別情報の保存処理 + */ +bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config) +{ + std::unordered_map merged; + ApplyDefaultServerProperties(&merged); + + std::unordered_map loaded; + int parsedCount = 0; + if (ReadServerPropertiesFile(kServerPropertiesPath, &loaded, &parsedCount)) + { + for (std::unordered_map::const_iterator it = loaded.begin(); it != loaded.end(); ++it) + { + // Keep existing content so keys untouched by caller are not dropped + merged[it->first] = it->second; + } + } + + std::string worldName = TrimAscii(WideToUtf8(config.worldName)); + if (worldName.empty()) + { + worldName = "world"; // Default world name + } + + std::string worldSaveId = TrimAscii(config.worldSaveId); + if (worldSaveId.empty()) + { + worldSaveId = NormalizeSaveId(worldName); + } + else + { + worldSaveId = NormalizeSaveId(worldSaveId); + } + + merged["level-name"] = worldName; + merged["level-id"] = worldSaveId; + merged["white-list"] = BoolToString(config.whiteListEnabled); + + return WriteServerPropertiesFile(kServerPropertiesPath, merged); +} +} + diff --git a/Minecraft.Server/ServerProperties.h b/Minecraft.Server/ServerProperties.h new file mode 100644 index 00000000..3bb5aca8 --- /dev/null +++ b/Minecraft.Server/ServerProperties.h @@ -0,0 +1,105 @@ +#pragma once + +#include +#include "ServerLogger.h" + +namespace ServerRuntime +{ + /** + * `server.properties` + */ + struct ServerPropertiesConfig + { + /** world name `level-name` */ + std::wstring worldName; + /** world save id `level-id` */ + std::string worldSaveId; + + /** `server-port` */ + int serverPort; + /** `server-ip` */ + std::string serverIp; + /** `lan-advertise` */ + bool lanAdvertise; + /** `white-list` */ + bool whiteListEnabled; + /** `server-name` (max 16 chars at runtime) */ + std::string serverName; + /** `max-players` */ + int maxPlayers; + /** `level-seed` is explicitly set */ + bool hasSeed; + /** `level-seed` */ + __int64 seed; + /** `log-level` */ + EServerLogLevel logLevel; + /** `autosave-interval` (seconds) */ + int autosaveIntervalSeconds; + + /** host options / game settings */ + int difficulty; + int gameMode; + /** `world-size` preset (`classic` / `small` / `medium` / `large`) */ + int worldSize; + /** Overworld chunk width derived from `world-size` */ + int worldSizeChunks; + /** Nether scale derived from `world-size` */ + int worldHellScale; + bool levelTypeFlat; + /** `spawn-protection` radius in blocks (0 disables protection) */ + int spawnProtectionRadius; + bool generateStructures; + bool bonusChest; + bool pvp; + bool trustPlayers; + bool fireSpreads; + bool tnt; + bool spawnAnimals; + bool spawnNpcs; + bool spawnMonsters; + bool allowFlight; + bool allowNether; + bool friendsOfFriends; + bool gamertags; + bool bedrockFog; + bool hostCanFly; + bool hostCanChangeHunger; + bool hostCanBeInvisible; + bool disableSaving; + bool mobGriefing; + bool keepInventory; + bool doMobSpawning; + bool doMobLoot; + bool doTileDrops; + bool naturalRegeneration; + bool doDaylightCycle; + + /** other MinecraftServer runtime settings */ + int maxBuildHeight; + std::string levelType; + std::string motd; + }; + + /** + * server.properties loader + * + * - ファイル欠損時はデフォルト値で新規作成 + * - 必須キー不足時は補完して再保存 + * - `level-id` は保存先として安全な形式へ正規化 + * + * @return `WorldManager` が利用するワールド設定 + */ + ServerPropertiesConfig LoadServerPropertiesConfig(); + + /** + * server.properties saver + * + * - `level-name` と `level-id` を更新 + * - `white-list` を更新 + * - それ以外の既存キーは極力保持 + * + * @param config 保存するワールド識別情報と永続化対象設定 + * @return 書き込み成功時 `true` + */ + bool SaveServerPropertiesConfig(const ServerPropertiesConfig &config); +} diff --git a/Minecraft.Server/ServerShutdown.h b/Minecraft.Server/ServerShutdown.h new file mode 100644 index 00000000..302b1a55 --- /dev/null +++ b/Minecraft.Server/ServerShutdown.h @@ -0,0 +1,6 @@ +#pragma once + +namespace ServerRuntime +{ + void RequestDedicatedServerShutdown(); +} diff --git a/Minecraft.Server/Windows64/Iggy/lib/iggy_w32.lib b/Minecraft.Server/Windows64/Iggy/lib/iggy_w32.lib new file mode 100644 index 0000000000000000000000000000000000000000..3c6ad976fbecdb6d732a80868d7ef6766af3282b GIT binary patch literal 49766 zcmeHQTaaB9cFCvB`55erb{Xw`}>U-j&Oit?F4W-!IJ`mn~nqY<2qk zw-yo|)h3!ahiGz&sD6-W`acvkUngRk`*lX+_zBwmUy3drBw}hnm+8khXLJ&Nf*wAs z=%JqzF+K3#iXOa(i0K(%G2QePMK=P6>HdX^?u99uVKA;DGtLQPLlj$Dho9Vt&6dhO$JD|INq3Ei&h?pJ)pXuAEN2VwKpy<*2 z@g8*NUPbq=K>33npHXzjHAGCe->T@2g?I)%F`?+-FNv7;O)9$UZ$wP{ey8Yeq@U>p zU^Cryg`x+JL%U!CF4HY2FQ&T|D0=))zz02cf};EOA%CDd@yv8pOVQ1*5;5J3OexA{}_zC*aK}F58(LX@< ztW$K;n`pN}UnfclX?qj&L*$$32Y*)d zqcf2|(CharYP^a54SMkfMK7UEFdYV;>E(|oI)t<{efzSEkdJSJF8_q0%g!KTy5b~7 zm+!-S&}9b{U5a!uy>d)ObNC6m;(ru<_b^dPtML=`64J!<R+P0fc8R{={neC+6z6V>#kRHZ4VLCHLokW7PgqK8Bnwv<;OGwKGRSBoKXiqnNX%o zQ@>X<{WXl&ps6XQ(b0{Ywr&|38SP&`+&8pw>&TX&(fW=Zdq#IJ@1fDr&F2pex)O9{ zE}{fR&OLwgd87SnN7mBlMkx0+8x!>%v#n%ZvpKb9HSE-jaC3Zp5YS){MWPDvRS=&`_wgScHl^a^yKRi6T zZOg_+yMr?5Z`F4thJhekG5Uw2dLA~XeCt$gPtqDr8sh`4+H^9~Tt7E6)ocM*R9#f8 zurrj@#`lagN9uOfD}9Sy=xwddwi8)YN<%XYS)|yPI9A~IEQ!Y0-<;h(mGn&^DI@i1 ztCIcv0;8`vv*-Mgfz?*qQ)v=RM`u}YOey0QTIbf<1I;nyeti9Sy;DPyN0PbDL_L`r z_hoV(@xtzgq?6iZi(9r|l#F%SYkHQN9Z6*-B*&K0PQy)OTdmcoH+GoXE{`?(>+P{x zYaFe+lgzD~otQ{kdMFU%$UK5<3w5-1cD&vk#30ZZv#Q*~G<7!BGIVd%r)C`y)=xRy zIIOPc=jpdu$J?}Zd4Dq298X%@iRxqZj;zAedQ>*cL;v7lf4zk|Yqs2c+f0wnHi^p_ zGAyRtaN5_1R^m@H%diF2hSG6;U9B@VIgD&~q=BG_Pk&=$KJ{F}r#;-MwL0lAHct|S zoX^sx<}Pc*u(Od%__X^bo6UBzzTxe`MPUgswlmOdbWm3t8{=u^rvp%r7=}X1WY)ryaW>PIRWaZf(D>=Zr!*r82uS6nkR@QG zHJfzy%p|HDMXTnNg`~n(xUfo?$;2!>7o^=;O)9kKb`BUc#=Qf9u>E^C1Sz_(URb??9 z9R?07Wn9)$BMLgy+D4~do2u8^m|}fvAdiU9S0%)0>#L1TvJu%zHIQGhu*FmLz-(iz zgB9%1u-A%5MCxx!I+M+D))oVoN&{;P0Yf+6b8P6-OJ`Y*nTdd0VTor5F3(GnwT*FJ zi6Swcv;@QoJ6l`L8LVUKN!zC`xrG)gE1iLcuxbfZrr;Nv8)~iXwH--cb7~4hg-_R& zFtn4-1<15_%E1~>^9l`aR=mEtpgA>*^|4r<=_X@I6x!)PzIJNLS2iY(H3sY3Tea36 z^L4SG^Dqk(q!BB{8LaGwQJ+@F(uuT1g#On0TrxF0S(^!}TM|MWONt#y3|k5a-GSQd z-17cZz}p+RsaPU~KJvs%cT>RAi2YK971aDXY{Pf7Fq4m?W!d{tRfK-2!U_^P)NFP} zTD3-dqS>0ZW*=P-u25kkUG|(mGmdUy>;kvdJCg&oop{y2s@d0;%IaB2>lp0AtKDp+y>nZAyfZ12 z8xb;65OV@#NpM99{jIfjhZh3Qq^@)=wyWoNAmnO_TWIyodJ8FgUC8lx$GffdyArHN zhP}h0Po}WIt#7hE!OaxYRA9MaNeJ!Kw-A7aag$x>ZD@9yYVz}^+2$9T{YksiYFc~Y zto{lX%V*PxSHa~K2z{*tMKRE7P7k21W97iseH}L_=(Dv6D@knOed9-A#}G1dVDqz9 zDAxU1hQgu|d)6QxFqKLbIm1kGemiMl6tBtNsOrLE7kX$CTF<}GFxjk!XYGj;8Wy9# z`08xZ+OwgLCu{GBZ}VAmL()iE^)aNI2ON03;l6Ipw#E{yQfqE5ONkdLg4s+XP*?_+ zwX!-0sd?E(F0v63x!`7;=frA!8DvD(u$XO;b5qDe>g+}gZMQz~=Oi0%+#a6IIm`zx16BcBg`JrTD0Oeqn zK~yYcr-Y4E#L-{dYSs22Au$6&Mp!^A-&$*7l9U6v&Vu0EnyFd%;*z7mmkT}`pHIyyl)nC>-zX2^{THw!}NsV0}{tA$Mt%+8o%2}?X(nJfM&ZrbbcDu13K#HDnzKUuT{jN5reI$~SI6%a|5q8N2D0d{dKG5dBz~ zp!(cChcc3Ut%;5$ITohFXU%jDT8Aa$&Ml3#!^_vtjU_YbPEX`GCdF2esBGCWJiEQ! zsdr}CMm|Eftue2RbP20^t|{_4k7cdF);$SM{W`MDV^T3IjzcMAC=<-`J6LP%n1!Q8 zF0+s=lfklT{sd#=ruFPF)mbYFBPbHk4{QwuhT-)EFc0NeZLXigcTMN3te?-=;P7@? z^*?KXba|P^wt8cE57N@toStd6;TUZ;)Sgdt5va!mOE7~=aV-K1!Ro!7-DCs9C@?S&>WUVA0J7klIf&_{g}wS&B>*w2kW*AmqqpK zGz}_JV{6iyn1-K6c<8bB&#Ua18!Ah?$?7#94tl(SJ-M z7QRXJ>p7xv(0}bF8oU%T4aEBYnCK+X;fINS{t(fBKY;Tlh#f!p4AEC^g3gUZ3-1U2 zUZTx=h*l$BeCR2npMn0i6K6$0Z#+%31oD5o2xld3!?}%HiH^OU=wtYO5q@6)yc>4` z>sg|ni;0dwy!h#e1wVwdJEzX#91MP6g5M9f@Dp?hGABX)0C-CuQRhkicsSGvgHhrf&Tg5HFUe}58r0Ifm&-vizmusQb~qQAoCg6B|v z&!f)qb3cAQh%#La?32+34m|=}*P%X8r@utG?B5GnJfDo8*W+i;wZOdwa`-ubpO@{% z^9=Ix6SOxFA0YVs6w%kF)wzN8QK&Qe2l@mG>ihHty@wXk^;D<#(nslgbQ&$CFVPbE zBAr2>r!(mb)I-bYv$U9Aq4(4K=otD4jnNL8pbya9bO-ID+vzU4lOCb{bRm6{zD{4J zguX*B(|745I!rInbM!pDNYBzCT1maspjC7toj^NjioQYrN+a|cT12PPr|8pkE^VX@ z^e@y;=hIhd6AjX4+Ct~hMf6X!o<22wP{MvtOG zpPBVy@ghc>Clo!; zI9xM=TT5YqTSSKIw>&2nbCh_U#_{T@(qvhaojyRq{!O0!SysRFo)%$=`8C}oDjeT}eJC&V+`5vLZ4NL@#)*pE2Iify&mh}eo5%0|dA z(!gFNOWhDy?c?hRUVj!eTP9t{l(H7S7UrP(J;aUIs4^o)oD+%_XRafeNTlZBlc;B* z%X;aqO`LgRe~CJW#j9B@<2#zAnc}2K`nJs1bwe=r6}jwpYkgLW?fM*n**n9Hnn}i8 zjo@-CH=VYbUg|jo+wT@Ng-|s#-Gz6J-pyUQtB!4M^_E(M9)n>iO*3(Cme5h&op*}feNpuq> zYN?h8cr`WJd61j&(i;aX_)aXK!Hoqs@))0wIaz7pjSeI=tYojtM0gfj_Hd#g!jU)g zM7Mk5m)G+@fRjTzq# zQh>$C=Rfn?R76a!`{E|m9AD-Goe3WO(8Vi}G6rpEUSs>>{1Ve28zv&Y*vrQP!-5 zS0Yd%e<$J|@_@WRG!{?CZVVg0)v=0fHn-x5UW2R-H1o#lX>J}ZXc{!aD9zWNn73^D zFXG0ud>#oKjxObmO%^Asr-w94xoXDLLA1g)8c4b5QCq8sXw@9p3e*HRo|@pdX-(u? zlr-6R>!}HTn}=O`Bla{mt%+>UlN@HUClN!c8muxVCm7 zmV0x<61h(|ETKKS5fO3YlUL61@_MCsROZ{{31k#c;% zj*RQga6~eHu)OXdeDWG=|`80@bMdKV}N9q@~M|KuU2CsY8?<^&$*kNu&i&l5;g8m4DaTA9(R#N zy&sJF-Mdk--3v%LgM@tR)`DvuY0n~0K`S~cL0wVny364;Fn3mb;OL6mKCQL7K$$emNt0N zItD*j$IR2;3S7IvI)eIV&>ylFN8{%jTKeNmt~Yy)h<^M7UM|OvYiQAr7vSfKIC;STjx{rN8l8Cj`|y$t z@;?^&z}yL&>SL{DyE)NWbo$We7Y%OQd|t}6{yP@WtjGok=OOsF5<$I%-v1@`@cCL>+nBL-vY{Wy@n`F@nrrc%^xEjoxfSZGI)X00-4sz;tjw5H2;_V zW9x*I4aL6&I6pyHqaNA617YU4Ol#>`xh(w)L#nkbNLvdZ#8K(>x0dlD6X|b8YgwMN zxV7Nml=#Q3<(+8-Woq`5NK5b!Yb@-#+*ap*rK#v;zX3w5+#Az^3*y+ucjRhmSh+ImidGsvgESm1(trt6f1j?1+3Uw#49xckOw8ED zcW(h|K9m`Adh@Ph<@0o4zSqGNpVLh!QR8KBS4Q%BjE!4-rLTs$!e73pBCqeuG3}54 zl@NPZ`4PVa#P>UhzL)|5-*r#w4YcAf0{;U!zWR{hja8_K9vMRx3UutBoEV6T*pZU| zpuk4|^~P3I#LpLgR=-+`l0R1CM`Jcb0?dap{j0af-}24?{@>;Jp->PLtm~c~8&Qta z$leH(O1P^6Ip%5lLmGG9z?Eugh|FBamq|nF(Gf}LzRw3nxie7?e;-VPc7zFV^PyC$ zi}d&tV%SF_-+>ur3G#iS2ipk?GBewKtB;JdC&kgzXd?lBMC<&+9&{a`#2+JiMP|iA z)jI!34EqSw`O3g3|5?=eM@?)UR;9kaR4eV++T~+jRAjYB6s&kCRmy?UjmPtko0yo+ z^3MUPPa{T5WY>Hsg&Y{uJ`dzigpt$GGZU-(eL!yn`5y{oZ%A7;NttuRP1Y!#E8k{5grut;q;>t%kcQFgCMu{4@hM8bm9SU_O*-TfKpH%zL}j?G&Ih zdG0GPA-nF&D*|iHj|2bn8efI{3ZSlgY^+V6Q6O2N#MLlkK8r8pn1SG9XEED#j$^Is z%z4m{%pNLM=3lf@P~1ULt;}O9+9ejI5o#zv&4)6*Xl0-meGaYoOBN=^uOej71nRo) z#88*!sJ^)JYN&xV2zwrvIjC8!d)3@Q)fB2*-49PhygDae*^kQ2T>p3sDv5Y5B^h*sp6I0b0O7`Bkk#I z7uh^+QVln-g5vKY|Lo#Ar&t7d*CS-?tI#OQZree?MN-2CKgHRPCY?E;A$a&vV(Q^x!u+bm4^ z7gww0HCD=Bleo?a;EKqRdFo#*WZ%i+is+G9`s)t5@5FL7{Mhd58xDTJt=WAm3#|Cr zoBFQ~sy>)mF*YF9pT6m!<_EVcmTzR#y3oRu2g0l2#=awuO5FHCbCC@5q0E>a+i!eJ zrow-I-307TH|;>Xn%> z@lYz4fgKgEIL0kJbvb~5>pp*hagt-F5)03~bf6+?r1wrpRO>Q>inx*Safif}mm~;S z^PyCo24+G&MKEch7I!b0C~sqR74b;aC8qyQ2LW$Bl*w_-mwb_pZeJB)f_2@0V(Xoq zF0#0RLI8K&lVZqIeq?>KMMdO5t7bpnw1e!uF+(K4e5hL64F})4h{FWyzP1CSGkbxW z4zhK>MKI}WnEl*Pn)8>4r`b3W@JJD0r(aPy&Ru6H@u@(vjj>uuyZ(#m$b z$kyF76|p0J%{f1Ie&>w!={^GmC$<+3+2GwwCe^3yNYWmp2-d??k517iori(DtL{da7ccyD9G z2&{t^0sVRd-MG6;0GbbFavM8Id4q`;zY@%Z6)n6}8Uj7(Eadsda%u2~<96N3WAoQd zrBi}6X~lDPu@f9O8|esNcIG7Eh?d1IrPAQ96tk(6Y8`?8$aDFvWz*qSN!R5O8QiHEwaaVKuZ^OvwHmEplmMMfAYf!+yv6=0jJPF^UA34`o_c?8NN- zIX@?v62A+9!qyX%?=f`B17nBYozf94)_5Qrv=Q zB6Z(qV&9J+wsH0CitileBMxSMGv_;pdBDchw?S6JjKys|>R{$KNalYhtRi+`$HLzE z$AZ||Um+9F=0mBL9Pl>r`H64a$i>B{CQSFELtr(^U-=%lG4(~90%0H8B6g%zJ?X_Z?v)kL=0llQ6^pufDu$e2S8D=x-M(UF{GecYgvvGc*cjF-L5M#+&yZfbwBGv)wlM(bGU~DE=u`bng_pQ`CXWk zzUyEfxj8Ae8#tWf%FC?H;_P}nj(xd%nejq*WWQsmuN0{6)!WrjBkQT}F=}ePy{9oY mIyRY%?HpAXZ&$*M`3_$7VH&r33*aL<@4V(i)^~sTbw0*6#)LQoJH*D=mNCW{V=T0cgzd=E%xGl6Xz|Q|9EU_a&D_yEH1po@ z-WwsYuL8ur?;tj@@2dd|kOd|Vaq)vnrTl{*RLVuUs1!d^u2g=Ie5bqby{G$j_q$I| zO65nWIy~Jz=k(Y6a{6@7H-B7dj7}`t{|B?||BjtEd+wa$7R;O1V~5W+pU2Lfy>O2D z_bK}med<=C@f}1H9}%5;I8m)v)AUV5OlO{9=m7kJ&e*2u><@^TYR_o;&HY4*X5$xh z_s=!m)gofL<2g-t9!A9U$e%P_aSo9p#C-+mws$q%vXzMGso!ck^JCzHPCH1`Lr)Pg zJ&d@RCO2u?@Sj9Xk3de*Mfe46I8W0x4-qk4{i>#GXAv=7Ge^_aixCdmjPxp6gkR8O zCuwTzN5u5lyP7ubB2sibenB&z7y{-DsP$ilYWM|h{MZoEyAjkxIVeIoG(ipME8>5H zPXE233H*ZYeoxaq3yGMv9&PAe{DLmOM$_#hL`-*HrRhGD2h+{BX}a|zBBpISHQn?A z5z|FOn(jgVneK0C+P<2I>E5F>UB88h>6QaE-Tz0(fv(%C>DoI`uAu7<*7U%?5-~l9 zG&9|JvZfn8CStnr9Zff(?3tckr0IrD;Dc^|SJNH6Xh)!{-qUmgfe(6Mou+%YA$_3R z5I@uPz-PMf15H=HPsDT;cuW_LX}SPuXSx+wOxsbfOfT-E=`LU}odq1GTldv;5oDOo zMi|psf6(;ksYFa~?bK9%h=}RtBQ#y{?`ZF!E5|h5^&#R1U5>Oez52GM9Y+!|{puM* zKg2KSwJS93xEFlTYlmohrA5T_%k74G@C$kwaWcK~Z<=2I1m8?sG`)m$Dmos&pbI86 z{rV0fMMvWowCey(Zz69@Z>`j{>pu|Abe5)_FA*`l`Fl;Tjvzmv*HMN{Z>-bw+{;8v z)3Y?afqG`z*{kXN4~dvw|AV1v{DSJoXnF=^&-CnorWf`nQgji1L60LZO!pzLinibv zbS}z_=^S7)op*yFl+k&hbC1$=&LH@p7lEnhef)yXM;l>!9%aJxT)(Dg-$6P+4P^fcs{o>`>n(T~v1 zK-&g2U530WLY^)IU5vPyF8QOTOHUzUx}>G)()aKU+H#$tL-7l`WM54eKL&l!87L2? z)2`955y z!#SisSvRwOIB5+gl?_8lb7r#DSDBn#R~g+P)FMJ0$FlxL<@6O$tB)?J)#|NED;X2o zUbds>7gu>v>I01=ab;APiYI9;uZ`Cw--IwXD~pqoMzdP4C1btGL}g>O-Vh2Ap`j)H z!^3OWtXf`cwvfmEMs;J7s``04w0}6P;qo}0t0ya)lg4mT8yjd;rjn8R(oNHo^+tvj zm&v(9No8#FNPVPgRiTbu@^X#Um6@h2T`j9ag%r{!O3VmxAj_Z$_19y3fQN^?S}dYG)E+CDdlHI+uK zT3atP3M`iIuQo?3jWM+JRlzvm6eWUS{y08qGAw> zm9KPcK5MFAZX2>oc^Fxc=};?H&+Sh}>tjiSyGV7k+7jiWA`J_g6!#Af_E#IIzk0*T zpT+S=uMt?Bb`jAU)%N@e5Ly8N#?oz34Y$3O*674AvL`d==le95*P@Dw?b91>RT?cd z0ErYAU~?f~94qP@W$$;j72BuRH&L%Qlclv{@61aim*E%&>a`XsVR>y#Ri-nH__%&8 zYd|TgnNdX`u?!k?Uwx(~>n9|kP$y@a6Q*G%V;q4TI|e*l)>1L5iAU>%Kz$-2uLEXM zXIOtJ6(qvizS45kO^Gp_$?`~JCTVS+PCV#7i4?uaE1-Kt0{Uq(`!IS;*Q?&baN_f; zbP#{L1p5Gr<#&=Atiq)y_)gj~PADN#z|rlkwx%l6VN}5nX@*cU)nZ;XYZca6jp|sW z#dzersSq*6Q)3IJaSbI?7|}e1AQ%PN0#GJw@~ekM^zS9LR<$x&tu*Zk-p^;8jHC0< zovg1iIuTGW3gvO|;$UE=Hrm3vcxbpV;yY|xZ$;9YsE-A-GN#Z;_aw{uJPUB0r)(K6 zWbznz(&CajBUw@#HY@wI5PD192lOGn8nvu(LtKk*eN%hGYYo(nv4av^+)GV`PQf>*g zM#f;-!Roq3rLj2+%lD5iCyyB^!s2rp+ks)!Pa)S1+tOQI-IPoYPgJJ8YJzKNVWF}< z31Q_-OJ|@mvuSR>V){}j*|{FF#mjxk%nL-9o<}6tNA>k$JH5VvwZa%$g|+Y0`W}&7 zA4NA*ueU}Tm0EMW-k9>G+!n}n)w=1#=`l24vGrS1ZA}bRHXx{mHFP1qQc4cS(wZux z>SPO>Y=4Zlh@+28I@<oUO0kI&E>jnl66P(MjQDdY!S!|RU z!Q5;LMC^eh)xFEA#rs_Lus$;F-R#Qj_)Zj+m zREopTHCI=fEuJW?$xkZ@R#CpV`A@|ciW2ObDHZ{Tonz{YkUcsLMTS+A(xEOPW!+DC#is zSo<0YvN_PGPYs}TVK(JR3k}#W<%?wnR!G)HS|>7(zzB%cx;(eln6&%FBJqe^1fme_ z1q1Ji&7^_xu_E+z;w3N7GpaSHA$hC;L5~ejhUW49Y$j=JUe?DGT5iz;6D4XA=MP>jj|l5yT|ASpf^JLWB7X&zCBFj8A1;ugWPAXb_vyJJ zA^kSGYP87`!;vm&G%A|`98%2q79yJA>PiFCnolifx;iQ{*vFDh0X-q$>N+hZ%LLFA zE0gDO2&Nrt7F5zXL&^^H%=3!sa7%LT9KWOrmy*K)NgXaFPYg)va49+5YG6;z4cnUt zi|y4rwkNKr_)?RP>ynDS6;BH?=SdA!eS=ADeQUytW%F5cc~ixR1~`lkpieQD=rE`2 zwUf-!!9%34PP1@J(pUa0Y1b)2ymgv|%Q(Gt3Q4C<5t8P;SXeew#%@(~zV}m6adKoc z@#^{7fmF2H0XTVG7vXZA#NtIsKbA4xd7dQ@O(fR2Vp^U$TuM4e zwy9utYAecnusUU(RuotqZ#p}!oS91F5<{AgYp13@n=p)7@wbtIWgrjnMmE4G4Dxv9abWpQ93 z9j5TK*U9RnF+OG4xy;5UWw0zt*Z2vf{Z5pMhiPuW4uC zr~w9+!dfLeeF)>X6E>-di@9lQD{W%!n`yS{Q{2ka1lmRF;j^Z5`ktM(4^Par#_Fd_ zdu0}65C`j(u@>BCl)YDPNz)S=)Qy{uw$klmwKEinQ0-(;dl8Xh>uD!!(l>;2^wODb zC)_qpJImqd(j12+iWzPrYtojDI_0Xly?D8JrJa>~Xgc=!Q=x6IkW;=!cA@DYM$M)o zb>!5aY^+*F5HFkccq^>zv@7A0CnC4QElC&%?8Bu@)^IH8B{;e?kky&;1;zUpBGyx^LVj?duLiLYfSgDi&$$Wl0+Wi*=m|=X{niPux{BoGNqhZ z)LB-k!e7!?IZDg0Exi*GX-LjcIM|Zwo2ba6r%X_Vv!pU;>BH;cDOi)7^KCC%>A{}E z4IB1AOfgI017~~bvArk6uL5^%b8~HU?I`ba*0$?|})+@Lx@=Kx~(7`Vg{TseN!S5Cj-+?$DG=X&7@oSVB=zv|ody{A-@csk; zJ`2QmFMf}(5#Z44NY@*6x5bfq&{JR(Y56?$h!J7shgLLkChG^hfqWxb${eg~u z9Ju!Z7j*Br&_4%7bRN3Fs2>>bAdaKap0=ahZ$q0p2)u3J zEqns$LYzDByYoq;0W|Ow!cZ5Bfc+85Y;YTJF9Q~6;l(KLONdUn6lH?n_wak&7NSEz z`{MgC@Q*qJ?d3F5mj|}KP4s2ZFQ$-3(1&Qtw|&_B~>Xn(qu>U2GQ zmcC4H(~&fv=Fwc5LqDKn==*du9ZR!m0rk-L=qNgZzD>WO{pfSFFa0%5&?If3&(n6g zmmZ*d=zhA7o}!28Cc2nLX&tSlema$Q&|9>Neob%E8}u5AekZ+3zoH+~kElgIrZ3V# zbQ(426spkQ(&6+?`WDTi!)OJqq(S-zT28;9lW2&3M#D5heKbvr>3I4ps?r!Op}(gu z(cjP-`Z@gt9Y~94J$-?i^yjpYUZGw(j+W9->16r|ZKJzsfKH&lqgAww5?W19(Bt$K zI)t92uhG}(FKLQirti>q(R`n!XXrV4fu5%q>0mmMUZP*pZFD<5Mvu~U^awpochH?Q zP7l&UbP-)h=g`@7E?q$9(^+<}?BziB6}DbOxPCo9P<5g>I&6(UoqbE9ny2LYLAN zbU9r{SJ76g(E;>l^eNis|Lf%cSe^V6{TBCc^#4Dd&!1+;oO_Q)SeavaHZx)wZ2Z)- zAfAxTlxT%1Ut~Q=%Wyf_W{z%b%~k2hLWxWE{N!@NI^pHh`sTvoteW)eUQs}+{UB} z=}CpXDH6lX45K%jm%bjRTDthouZv}B^0tc#Z0qVEq(Z0tWqb`W9KvyJ_};b;tj@9 zV@5ThZEccq+b7@WP%+sCK^5dZ(MwMs!V>BnAv^hqdCLTNd1E&MsS6TgnxM>!RD_}I zq%TRf_w(<{vmIe37upX3}8$ z?y;+(W+8cQBP3Z=QPK%da^F#fYVXT$KCaX&f`xs~Y z<3k0DFzvr*iDGDjC%p&~j+c38xl5mIt`wlZ7?;;PVxRY__dcyq$*(5lfd$7uoo(UE zE%NB`tn%F^&SS?yz`A>4bNGINvE~*T=Hh|H&?^C6Z=|EpX(0SJn2MFntG>13e=Xf<^Pd z4Cpvrnsnu73s6W`ovfH$el&vCKEq{N$LWYDauSthBqEY8b>dNF+YX3@kAhObu!Myd z70ara*PQZpxhbSP*m3qp3Yn)4zx@Y5c`IupXr%$}$bKIXk)V)_EIy>kI+qryaX1-# z>KUUQiVU86Ca!|hgINCH%sC0tejza?JAE)89XWIIHsATte;SaV=c^QA+A8|xr5ulc z7tf5gcN~W(k2^+4HH*HEnQNNuVFvAt!@i9&6644zgB&k9X!Gc0yv;H}-bk=t6njir zfxNg@+4XXa7j98Sy60N)q1~$JFfzca)n(b9shlXt8_(HH7AYw@tZ8?u+$Ab)OmR1> zY@3^ka~WitDj6s@WGWm9^HerMoyz7AyRs4DR5pjCeu#EuOO939U^q3)A)cB=aGDR( zSfppW|FskU%P70|)=lm|r)IID9j~20X3cX1kum?<6DWn$vgfEUN7(m@+|tB&CZGa9J1>vy-3ZaNSFqiUM1qI*p#)06*ErjI+rGo>n z7))EyOH)0kMj}$PkC&fX@KURqd_Ms5QpY-hbfz$olnRkRB$?STl(0;|jUclm7Bn-C ztAdu3mPx+;wu_@E;)x_(=J|4t2n5?!_E;cu^xO{Vgc)n3H#E0NI&5i~6s)%P$%3rU zN?8y>rp>xUPpbNUmuP9|@y`7rX}8Sdv#pm_IHFJ%vt%q=-w6OHLV!6-eGZ{vd!~gE zv}y_|`>h&NJgH}+kc-+m+sUaZOij$WWWcT|3LzljHdDv`SwP5aCj}0QIJIl!yv!b% zSwua&kOz&lj(XH`8>xpG@ZOR3QjeCfnufSh|EQotQ^+-};0UHS^^Q$7PfO0uEUP}I zV_z+Zc&w~Nnbg)A;KkDUO{wbr@E8E92aS z&Uly%d@rpCE3u0evlY9tK8vv{lbVRFrIt=#$|?>OftMP6m-oecHZHVfRd$)uwp`$a zF7^8C%&s!Jo@H(J@rBJf#121sQ?x&O;*eHo4_n%zeQaZq_OUX%w1=77pgq;i%iGL) zU95Akg{9gPxoxlZ2negSk89bkLjs|fc7mO(@XQL=e>{(>Ya#t(&iYddCAMm)(&o1 zI!j2@kjK()r?Oygx7ew1&~uH~wY>|y)B>Leap^kQ({}jwB5aM%r7$REHhC*Fx6IqY z?j1apBP=0oD}An$@^kZTk*AHgo^WN#Erxq%O?L# za^5_r7Jerk%(CqK0e))jkF!Hha-gXS%fBZM2q^o%pXGl)BRdOllsH^%z4VDdh#SA6 z9lQh|y^Wrp6gZg3U9iD{=WTEt?rU%e_ck~VPa9ObXA3+H4(i@^#o_tMLNQlVeR=0Qj?7)Q}-`$-oCKtlQ5q`m6Dd6@0 zW5y;Y`L0d7zgjczxP|8L*POEd*p^Sz_Pg#M{sQg_?1!ra{NFKS>{@mBu>Qhb1^fe7 zIIRB`(}BIm@Qv!q;d4^Pm++6@fj%$+pT_TEn*F+w>oLLb+sA*vOXc`;G0l2?AN+n1 zcMSNy17xPYLti}ba|nD_u}F!0e$zoKs-um1vp(LMb>z@@XALf2d4l0vKL_B8Q^G{J z1Hu0~5dZ(I`j>=3Dn@bJ9LfFsrt9gy|6A%nm^gj=P@3z-xLd*@6KRObpGGn=^H>HV zxy<}Lgu}nd|1^V6c@ zUdp{p{Uh4tq`!}nJ|M-8 zc!fwj>b$%o_59}s-S2JEC2nN2`a+89@nY!?H8uiT>k)d zeP9Xvg;qcAytZSr|6hjiwV6+5)k;N~e8)zUgCe-y=66A$b^i$E{ly|~>hY8M**LyO zRuOdE(-s6)K)i4HQfFy#ibbZYOl`zwmA?w7WDm51p1|DwU6jGWQDi5}9hZKr<^OdN zIdj~?DUvK+>!VCr$6EfEBe>n>cYb7D_7#DfxtqBe?YM+vE9^sjh)xz|F_$UXSS$Xj zz{8ZC_UGJhPJ(q@r?FOiXcXDWa_40n7(W)O0c##$AaW0;`dtt%Kk5cU?&|3~iE`R7n zRqwk>7^}aQ!VH|k@mb%GTui-{TUR3=aum#%jxIaHq zn~S`t>Z*hVDnClqW+bOS@nY(0I1*pflKMJ!57Ts(7as7u61yX`fI z+K=D4j%2~gk5aW6X+eDj)a;g%gt5A78O)e1tG|HhT%PI0Pfb5hH7pWj;j5h$Yd>)N9P<@>3hnx_f3dAnT(HYOFOakDy}l*JXZV zJE`M+$j(JNS-zc*h_M~>2?2C-LC*q~A7#orW-}bLadGcY#=aYQ4D=c9uPZF9{H|br zpRhaZSZiJxz*hGZEm-+crgUSds{*KdU``M0AKCw5bpTc0s+9TLjrtAPOWE7xXCd7D zwx$IxKgyJJY}^>T#zHm-A_tUKI! zfi>)*Xk~wER0GwLE{SB)jgZj8B+UNh!m!t9QB0xUrJ z(QO_R8&5nxAW=HcW9+nf+`^1KYtUU9dLk`(y_E*va|zvH$6E4)g`K{&;5r&f{8F_X z$Sqq9s$N8AQ`>QGjrnPu>LAWEon>j~Xn}E zle(MCEBO~JJ1yu7$O?QZq5ph!iF?;)_JIu%~e6x$0KbhlB z-JOq$ksfJ}Snn(Pz1+_RB&Ru>9yYw`aS!;=v+` zvuAUAP65?=2+0DLAEj~|={@Iqan<`u5~ky^IOhLxo{PDs_EG_BC3_5k~#1%dq|U(~*^Y%01PI|3!ayN!!P8swu`-Q_yAn!4CQ zv>)5rd$?P2T+bt3UE)Sc_>vsgdK|1v#K;_fsULBTHAq+=-HuY13B;J`S?0M6W=~*_ zXAj7&IcD^UG>O^yoG>piHXni%T%M;uKfPu_$&XTb4fw*aujLgvQhF!GdeKc{b)LtV z{qjnQ?0YHBf|eg;@*J~DT$Lkd575c@cO##Hl^eg`aJ4|ISH@a6n)CdX6RXo$A7yeI zn~|<5h3|1~-J>(owPjP$<%|^btiCQ!L--{=tL!^(39(uE`ZD;QijZxyRBH(IcfR9u zL&-Ea)zEQUh@DyAn5Q9s$Z%k?(wwjhzteojZXVv`!xWDvO03bwU8Rv7Ff zbGt}|^QPrqwIbXR#&xP))=Ge=F|*V9jQN)W5-mqHAzpV%|~t{l;Q`Lip` z*qZI`6f=ELv@4|8%4wTH3cgd?9dh72fbV+r0)IMuY5G)$Y*d zN9+do#|+q23ilFq;Gd{ z^G8Ix#0;!=_$JPS4yJybbnoFk98~j&v4pU|OW{RY>*Efde#6#+>v%+st;C;j@$$EEEl~MU zDvyEnFrS=1DKX_!&Jv{aQjN6Pr(8tqg=h=5^U{s9*{6ND>C4j=to$fbzOmK8GZ9qv zptS_+xSV6>HqW}qYy@vKDw|Wwt6((f|DO*`f04yzFa^v zZ?Q`dtJ9Q1jMV5W9>nyCcMG)hb`)9ly(&>LBK&Wx`hMkL?(J3I4i|4Pulind@b>nq z?{ydNe`(eCMhLh2RbOPJ-RYq2?N!*XUA#TD3X9BOyBy5aFw^a7I<|j*(_qTKr?B_N zowtDI|2qwP5A!z}ru+9Fy2K1vH2IwUZD6X{o11H+Yey%N(G6>}zXM_3Am0u9L1bO~ dj~+z(uS}${GS{VMuSkpitp`>98H^P3e*k14Ld*aF literal 0 HcmV?d00001 diff --git a/Minecraft.Server/Windows64/Iggy/lib/iggyexpruntime_w32.lib b/Minecraft.Server/Windows64/Iggy/lib/iggyexpruntime_w32.lib new file mode 100644 index 0000000000000000000000000000000000000000..06905e7cf6e1675ef2a86f6cb50e38b2e9b1ee3b GIT binary patch literal 2430 zcmcgu&2HL25FXn=!R-OHRnJAoE$;AMLj`~sfPymI0Ek=yL@&lqEQCY&L~$&tnr7wT=&)8d%X`PATIHyI zSTpTT=iC|%owHA_-M7roS{6*RdSVzsTlf+*9-G$Ro>ceE@@{<>%nB~JHyV~4tLa*L zrhv=Y{UN#z;2bGir>-+N*Krl6xy#HNoPB zY6lpXZ1;MgrUbRQ>1}J~HSfF(NrCG>_iN83!L! zVxwIaLM;-1f{HXHI@Q>@YiO04=W6fTKX>t@R`=e~jQEWGqr zu1hT~Qr=KSUH(Q7V;0QM;6Lgz&y&TNPIWomrs^^$aN_hYp>VmbOfQq&EsS9uOf3|> zI~7<>8xq}tDYoa(!Y|T-8)D^io)k)g{y^wX8T;Qx{}P^7@XPlHqg&wl~1 C`T!dM literal 0 HcmV?d00001 diff --git a/Minecraft.Server/Windows64/Iggy/lib/iggyexpruntime_w64.lib b/Minecraft.Server/Windows64/Iggy/lib/iggyexpruntime_w64.lib new file mode 100644 index 0000000000000000000000000000000000000000..693fa9a25552b898013ab0e860b395524a193402 GIT binary patch literal 2386 zcmcImPj3=I6#ualT8Iat#&~IhrfQ7_C@d5cW41yQNhxiBdfifXfsG)9ZX@l*8z*nx zJ?Y(#(1RbqqlX?$Pp0uZ)Hk~`z?Q{A8YY>Yd2im#`!m1yW*@w)bgPY5!D!go*5p_s z{$gF0lTLn&lksGl&-Q}=0)TW5_|ky?1X!x^enunp#S7L|HMhOHQ!K05%~GbA+b!=D zRij=%)_a}q(RFS4+3{@@c<@4jR_&6?XiW*kd1M=v#uo}a>ZE&*vDa6&|F2{@yE zv1o-Aek{NT4OX>KoiC6L#mFd@*nA2i00 z6w#VZ1fv47T=cp#lZoNoXnilfefe5?=ph`_J+T(i^n;`NO39{xJWW0cf{zM9uOo(5 zF)J%^v2>Rvzo`G^3@?oeVy9d##o4$|lOX96a*VGad`b|(GD|dS_uQR*f;%BX4Srfe zHhcoT+uKI9+dgd9%Wrw0r`87bQtL|=s(x4&GrQeUrP)h^?+B_Ag2_L|)2*&*Xj!f{#pYo{^p=%By6{JP@0bhqV<} zZfkv33!huQFSx%#6UxNQHF(`!_aoBnq1u#lyz&Wy0ZFHM;J)39o0d;FNRN<*#U IL7SlVA8;=JI{*Lx literal 0 HcmV?d00001 diff --git a/Minecraft.Server/Windows64/Iggy/lib/iggyperfmon_w32.lib b/Minecraft.Server/Windows64/Iggy/lib/iggyperfmon_w32.lib new file mode 100644 index 0000000000000000000000000000000000000000..25ba046eebb48a9d8615fd06207b62e00cadf590 GIT binary patch literal 2436 zcmcIl&2AD=6#j<47IZtQ zj_Z>`R9|JpXg>lV4EWc8U=Ijg0n3-7MyaP74sbP$;&x@HS~Cjob~mfVN^PfVnCIsT}bjSu;gX2GhT>8h6_m%+RUr;{UuLj>`fALs>vD-tf3 zfhg4g;Z7fd2p|LnJ~8r-q2V3B1EXoX`iCQ%isS^!j(8^KTu{#BLc#_1Og2ke$Mz%U zI3wfD<6|U-^8?b)T2E88VRn=hKmPo>SADEdF3?#~QR2OM?mz^JH25q9(d&r5 zjhoGSzrL!(v*rQn9>c&$^oXB=e8F08!H(QSK8ZS?A#mdZXLvQS9OK zFSxmpZuw6x9IOu#0v)i!Kav`Iv+U0=H5s-c1%@`-NW=PdZbaeLfxM8o^WlKn#a>d8 zd6}eY8xAJX{lv~e3gRenMn&}$X(9+2?cZOBQQ65j#oMBFc0Z&b`h}o;7i6DN0JCq-Id8hGD(Fc!C+s9H uDSX?#w09f0LUvJ*bd-I#$d-LRFU2h(~iX>u_Wj&Kj zr?xgS8EI0UwRn`1%X|G&Wl{b~`a=ujE zE32J$`@G-M54yc$^)#D-suuSP1-l|XOlg{W)wlb_9aYIybI8+4CG#Yubu!R<=X6-7 z+gBYHETV0_RUfqMs$6c-tah63a>tsY*H7*0)mYDN zJ%J2AmJomkUOw_!?``*4ATjeA|M4O=bZ0>+oUm=Mf;RVk6H~CxWvHNPtQP4^vMjD0 z9U(DVH;Fv&JSinYp%&4GK79TBrSix_yrx%SC9bs^@7pUCa|YyT@KH>7FC+Ro-qGrV z`btVHJ)pr)s(-k^N4<>XCD+SxF_>PE>fqN`Yr5`pl8ZJ@$f z6h6DZ+iB{(litA~wpM-~E98qi#_CjakLJuFRzcKbqbRk+P8vB_gWT+O`{aB0>=SNp ztvhK8(Uw&KfeaY#9}0nL`2*W6-ldB0odoch+jo~kCEI$A2^UpuucVATfT)bq_x#y_w zZ3hXl&!Iwm_zodH@<}27;YlI)8P&A<4k3xQEYvi7w~!$5)DxP1Gb#kp?`~BjH;oG- z@v})qa_DiP=?(b%2FM(%>9=yT3qD}WH5Yf-Vf+S7ov7)$c|sDk-=}Hk79ok+7i!vhFMflj-ll2CjqsP~U`^A| zC7N8KX#(+AG=#sPYu}{lx>o}SbnVfa#^4{(bqFU&Ja)ULyOF+%w%{-56X$8V?*So* zo_JW(lSn6`r`}xA0{jL2`U*`?-V8p`W14>bQMiMC^F>X+daDpbPwdq6tKZ@`=wFfU zAh`kYAli6(MQ;(}EAJ5Eais4L-l^%)dxaqSU=Kh>%1t8m;Jl`~{uG;Qm1c{GrRwU=|6hz`O`1-BJ;Dt-80DGhNR%a_Ls6EAjzl>nI{gSu=WP>`Xz^2;&i$c~ zMEkDRv~6D@iAIjqwEa#YiLUvsrdMtdl4#3YHEmxGKIr90L!zrL(bPith?<|()VNJZ zqUIHvwgQ7_Qo`IZf-=!X0$!qna*4x+$8*U(h8dX`1&0>H}yk!b5aC(t+qwga;(Pe1#%8 z4`qz#`1y*&$8SV@KtDnqB>Dl$2hk4?(Ddjw)JM=)Kcwjs??Kz~9wA=<45AC+57B;4 zYTEzRh!bc(U=Tgy9h&yL6~948Bdv)pSgGl`pA?elu;Vlxg0ce=zd*YLl4rkD)8RXX zBsyewMQ9%nnTPiL0!NRfB|c?FUuen63U0CfW-UkGfX=dG*ggZK+N=5S3f zxCik99e1IoqrNO8(epoC5%iu9I`%P5hu@EIftJ2q)3QT^RD|?c272)VO&8vVbOJr+ zC{1VGh4ujSoHuD&1U#abpv-{87m(f{xq6YNmplP?^nbs&N|8ME^Jqsvs}L@tm)xZ3 zsBH)@XvqgP9rG081$r^kk?7D1G%ZHiAUY3mBRc=?iZSIF>V%?q;4kQFw`uzPb|Hwqx=xWi z8Q}zpe@356^ragWNqK=F68FDb)7OrJKIn@}6iM+)^h2Nn?$dN2@|5V}+cd3v5a|F~ zbC9OB$SQ#bbl*Hha@j2?*PtW72Z_($uj$9=ABi5^qDY>$4E+n}phcR7uf`Y- zbn*d;#P?AjLGp@^C=z$Q3;oi&gnZeV72S@%per_M+KPNr^f3N{PS~tS{0{9X(HGyN z>C+t{i0-{l(>*r{LG-D^HQkGGBhjbcr0Fw=8_}m9)%3}ug&?|TUrnFc06yrm7izla zNw|YP`vFa#ydCnOuPoN|%`qW}zV$v$4}Ve!qHjK^>7mDkP;?Ofg1(J(Cwh3ZriV~o zh`t3pqVG>Ck|!N26pf$`5^d{fdL{CeXcT3G=o+MxBGj2{KqsLr5MBKiP1{h{h+gqN zMdBwYe?&hOaxu4_34sf0v`LLYNdCfWOGYXP&4?JaRL}U7(-dTG1i+OZ1c? zdD%Ll=`Q?5I)4uNK=fm@&qR+Q?nIw^qau0G9Vjn%pzdtZ^lhXq(IaU4h`w`#A}JvU z5??~Ted*CgWBta>mkn)ctX?y`YH0oDEtd^7Cc52aOGKlwY3qg!FoT#SJ!4WPa3nNJgid2-XQ$O1ZR~7Lj*c7Q9GBcF zVWvB+u~w%wIl>hJhLP1MJKH;Uwz@No(bjm=guEm{yxB>Vs_PmPvoo#TEJZwLl1oZ4 z$D#Er8?8=fs#AI5e$y8!$*%S&5~91OJA*Ph+MH=J!6cjXx+)!tx>*qq7nP!uMRm*w z)3J2*E(j27d zyG2FD^|Xpgj!8N*d!}2B?hG2g9V|B7m84pev#2P|nU+=NNtWXJT4-8Zow25^EIBT@ z&rWt)JK89S9WDqMCZa$(svoiSX}rC? z)3mKvo=+TAb_<$me(a~$$Yt5Y(4Cisj1<#+8`ka&>X<9{jX1I@$T=};)l{8A1wkRG zawBx5-!EHnMPs7bxen!J`<}*R)9VQn%vc9i+G}2YCCv@ARA0yf#Z)wlzQ=GAXyp&@D|#4Gry*%OugeS=DXXzf%it_rE;?TR_i(z3dJWU!&gI~2!O^# zU0-G-@I@((_%eJbMM%~7%3x8C8~CD}4FCF$>$rV4^J`>$svAaEyIBeNh$d^SGljI> zjV8r4azzoJ8H92~d1~W|;pJo?p`6GMYF0-krWxC~RNTp!9Xajkq8x3XHLWvghQFzV zPR^(xsqQ2w2hNR@)vqA+of}C|Zt2QRBM27yfI+ewH(uFj?rOHjo7=silkZNFXh=q= zCXBXsv}c;*jppoV+YG1keBw}ajCyM{>p@YB{F$1anVy}Y1fafUwA0>&KL}PhK`6_q z5H%;J(XXTJ+Mz-g`p`}CNz-+eDVcVQ*S@)B$;T=>iFkw@AK#B<6`IPITsY>^DIxgj z-5Wxz<0Dit>@DzF;iR8)q=cWg6dUqIH9u93uzKS;lN_}Wu^4wvg(+P&bVxM5sasw? zv4Eucgf%;jL4OOKG{#+%s21m}ZlJGe(Enr!jf`UKa6z?FierdJG{gpWBg zHR(mb!YKxmof@f{a2{PP?O06SHsrCqV=?)u?U>WdjsydRDvF$`biBBQafq3Ljtj8M z6jtLLJJ53hPwF{#pvQxs)Z_xBi#aY>Z2_&onzTmyL}f&$tRA6S0-vnn{%+3Tfq@O>Si;$Bo=qO<@Ibw}-|(HEgA~1%j(* zC#EwIOR<%n3I2xW_SX1@_C(tb`m$`r7{V?_v)fWpU)h{A6YVt5c%=fjhF0=dVQ{y- zY3Jt!0mVYTB#}IK?a9vBnbE21Co}0VoM3A`%H7#q@~OP{xk_slR>;O%GcAR7IaPqo zdZECr)yyrYBvxY8SO>b`OZI7D*PO*J zGxqX!wJ}K58NjkC#U(AfS5jr!h~KgmMO0{YrUV73@=UohsZ?Rm`j(@lT@Awv#N>s@({;ljrFDb4M{@-C}2N>lS$ zD42gyoEVQZrkf}@aS`yGwa@4@VmYLPbJk(c6sn-KvCvjyP;cKV%O-|iri+TL=S*^H zoh}mNt_lX#z#F2g-^8KTPPL%n9!J0+*`yqEv@*0%6tLazNfk7vf-kp#}71D&Y{Wpp62jnjN_8d zrjW-c%__Z9?WWEN%TdxU3#_|CQEgI6OS_Grc2Bm(<3=#WW?|dWX->|LH?haaEpe)V zb0*W-K05}BpRkTqI-E&5dT(2YLi1P?onQtjQF#~E3YHq41PdkkjlO5x+m);W$#_h| z^NFQA4}6utu~usD&rcF3LE01T$@au7_N6`Fsoi1eQxGCQOq(*%R7N757WVPBGxaoI zg&k!r&1Lb<%4D&T+F0u2-F6bhh!sO(gn9^&5lw`vTKQJt%BPGcl?jz6Qw)?RseIFU z%ti)GKjL{B*JA*Ioq`c;*9IFHMlXYnXg1V zrUC2JJv4R4yv8>Lth54tN>kZl#p|*4h&6==+5$sqc#@eVzJC&u;iUrk;@q z&`UnJ)Q_Z+VMta3r)q0L%HKdYClZ9dihNd?@ktZow-I#x*zCsUhZ#Ra4R}dP;ZL`k zWO3%_Dogn!--bNNW!e<=qzw6B_b)qWKGYz((;8_hYnEoGm$^hCbhfD9!bQk(N7v}2?YTrB zk`@ba(?UVSbr92{EL_?0T_r%k4?+@flR*e%0v~Ko6!mm2F$0;L54RU~s-oHT*h?P*%iXW(EA9 zXT_91IwxVH7~xkL>R1LhV)%Fw+vuqs9#c4J^^fNu%0T7?;D#XJ>$ogZNG#Bq*F#?_%f&S#Kpo3;bT z$5lwo5dyE2Wdul^@C8WM6iymOPmb^a9iL?^HmkB1FU^)5NQxavq zqRgpZbR-N`E`BYd@*8rM-_{*_i|r||BM(`=Jnf3`I9h4NAX&Sf9|k>OD1XOWEpO9V z{Wgf*pgR_L9%oxMIEaPb~Al2MF09bnqzm7)ZbbX0Cog#o|o zooER;B%Sp=V5qPoUY!=%M56h`q6aLtdFMmXj@YDU4sfo)Ru)d;;WtEC`lk6C*I(J7 z#0Mzo=BDtwAVKCizldzL+7 zF+WUk)*(qSn3Gr9V|VAW7DL5hZqATo`Hri|y4lQ8lPuqH$eoTzFiie5$)eJ3eufzo za!G3vrhpAhmnUfe;k9%jhop6Psl-{C;;tlxc|>Dk3OnArnw_-YISHdp#d~~gDzBUn zFuJ@lIZj1U;~#pF8JM~^R0Pmir%Ek_EH7t# zLd4L{W0aotBPC(A^)y|jb>4%tkny9n8#g6r<*XNJ8RG-2G@4i@nlm->a>l3L{K-j? zFZ!ggjt_4bFx1ENOhr(Q5n9^J*+d)}5L#8{BnZXlp>b(bZ_N*VkT(;(3Cg0+KYH)U1X&_R^i#bP zEK7hDO#iwei{xh~br-Cey6UqnqXolfMPYvHtBBtW_t z)J%O7G+e8|k}4+=YtsaZ`p&Gf6^M0< zdWuOpCd>KwEn6-ULfj8Z?SzO+a|4zUB)@c~7JbQzkiM!Znc^MutVR(~=_Mpwcry`l zF?VG(;}0*62@&Vh9D)=kDqp(eQX>)n(0+nRI9 zi{!PHI#p7tZmgaJ43gC|SX(!laL9Q&x5%HTFPPtU&>N)?cYGLkaDD`LlK+Dcqx(p4 z$7(4SZjs{dVJV)#J(Q!r5#rY02{FD&ipihh-q#^1-T*olZg>6`_l~w8KPJU};J@o9 zxaSlyXI?JFhF{_i%NFCxv)3 z!n^=<#jl098NVL`eH7ncL_FU5Du$@t7~gUHJPT{w~Jdpzl2m;X7E0P0)Qk z{C*hUKRps@eJIive~*CMw(}6C#Zvt6Tq#x~Ui)rCK8{Fn=XNQ+dkymJl~TMFdA}UL z?|-=zms|z^kiVa8N^x65iYxGU->p*I0-b9|;s2FV><1c$>_;HG>=jZhfZR=RTX3}$ z58*EAU7*L1j@KfOu6!x{xkQQ?;2(@Iz5NKpYXk6>!>@lrdD&lz53PrvmrC(D$gair zqxk;rMTk4-B`~qd+Y$4cu?v4cfWMF9@3I$4vF>?Nd=PZ_F;d(EtP78m;>$-#@!{u#2YL); z=YIJ0_N7uBvJBw>EqF1~17SMqImkU)SL5>kDx{05aRY{LLc#20L&*hg1-;yD&qWl z==}}CH;!_8JLL95Ss7o4^0`KeH$(oRa}b^f5m&g~0Jl?+4tFA43-3pLKLKq7((6v7 z*Omn+BM8SdRpM|&) zb>;$zvJQWb1AoaEg}Tc5z5@_f_;uUGQap$-9E333jXb{-<>ZtVD5IdAka=_^+>rKf z0nR-L%R1EQlTf$!h5JJ2-i5L;59RI_r1e6Swflkp$dA!(Aa2V}LmC|<#nr=79DwjV z{(ZcqaRu5i)TJ|DhBgMY3BRYeqAr0pBi=tnoA(}svGZxPbN32y(>+2Q{wcJP_Jh6ykl~LL2rl>My<@e@KXfKqq}$ zh|O@j5#JYrKZ!6MdlK?+M2ZgT>YcAddZ6A-UxV@pdMkKuK|Ol}y!X8V?FHK3eG$J6 zC?_A>jCuvS5%uZ-(1|DuH>2&k^`}A{0(uIx?lPni=o=w5xB#_Z>)=Ey$nS z5uPWYa|GJAJCIM0BEJsZ2jLRaq9fMGb>dEOyI3U75@(AKi7VvQ@^U#WUnV!ptK=rR zRSwC^@lsHmcESAa(#YN&J;vBI=zEms`=ZW*hx#9w` zOe_@#hy%ro#b?DK;yL0Vu|b|Jw}=zuAItZPOU2)ceZ^DaHDbN^jrgee2k|lSf5b<` z9pWr`iacDNDwoS!=B<3&yfEr zJ}n*;UlLyyFP85Ve=cqiyTw0?`^5v|YvL~PZ21-OTk%t|L3~~OZ*iaas`#DwAL8%C zgqRYq5|_$n$3DUXw{5Tjy5Y!^Qghsbl}OJ!I5p4cTCVw-4-adEY{ z2DR`yajj^I?}+b-XUc=*J>p3DbMgD)P`RIcuKbC3QhY-^gu44p@eXmLc(wQ!@k{Xw z@p17S`LOtz_^|kvctreI{6PG*c&~W7_^;w!;!OD>xlk^Xmxx7jmwZe-DxM)umj}vy z<@>}L@?5z<9w7ITr^)w-|04cI{AclQ@!!N>iN6rb#R~Cj@l0`;xJmwj+%2z{M~Ih+ zmUy;!mRKS87yF6ViGL^7%S+@sxmvE37t8-3-YVWK-Xi``yk7i?xJ`8BpNdz>DLE-$ zD{c|v@;{0{7B`9OWJkVHyg}S5ZWeza-XyM-|6WYUKN8Oshl(@BN#cd#C~>?vLChD& zi8I7$;sxS#@gnhjag10XP8Fw!qs6h}L~*h}P27w1VU_rQTO(Hg|2pw+v`(!2H(e|C zQOoO@JC4XXzKsa3iF<>h%~P5l#aCP=&hOfr4M>B2xSMK|_sW_ZYy)w3r)=(EEN$_` zGkra+RePA^quK|`?2lM}dXLYtSi-BaW9-1~01okdy;40ctG zr(i`JVwR^Cx}r@*%Pwzp&9VK&P56|L#-TFNsLT=-E80%1mZKaqv)bYd@TCP+ZFFIZ zofB6)kWU30zKod&ImhuCDICus9KH0Aurdqx+x2g@>7*UBRi5+7EpIS4bxlLYen*Lg zTX?>KPV!C??ug5v$r}@k(0tLjV>yZ)s^=P!0WxPs7KKTsvj#M9#+s9h`)6#k9CO+- zA;8b3d9l`ZM!E!)t}i7&dBXt{B<_L>!vRsseaOh*i{zoQKhQ1 zhOc-C25t6heH8p)FV4F2#M09Aln%2FB+1MoJ2^AMY$M5c35^>%J!36uoNUXkQr+(0 zkf)ACR-dz7+HZ>0JQcEvwKCc^*MHE3^usd<1vCA?;?*9g!~m4vaZ0{&p4VJ(hF7^Q z19sBM6QasndJUzj46GR>GurCG8ItnNVpR(GPOfB6@}fr9Aq0i&j#9dN?7W)Vo2KYn1V6mei{se)iZ$brAQ1Q}O>=D4rI zzF(xmf^KDx5iCt2mrs-gPrhBxY`E$AvD+Q|)W{C3lgDX2l$ z%|LWt6HZ444{K3S(gy?6mtiXTVH zU61E=cww2x)OPgRNe#C6Ap-7P)j>9>&mM0`8!Bth+!lV^qpR%aTcJ_23?@6kTKVW) z*fGM~HMkfUdACKddAF{ARXh_doa1=yn{)PBx60XFDxglbdKivpFC5S|NxGq9PgYP& zD-SE4n``^-?nHp`%7Ziqp(Ay}h(j4%JgdFU+&*MZXlkMGoNwjo?*3uXVz+99u2EQV z^}2ua3e)h;1yo6crVZZH4TM~fUAf4d!^?6jFM?ZpbxaZm^EJbs&ejLYnfI0x-df@@ z%>B|_(Y$lO-R#Lq=IGz`z!>)6u^l8-ahvmezBdTfO|DVZrt=JiPU`4K(wHNkU3tYZ zxb?yYQB54G60bAO-blepvSVv0duVm z-0V;-)hq^lzi|wheiNBtnoI9ZKrMP{phzjF=C!7wr0Ci73P#=}oIHc0820XU6M(9; zBl52MfDg_&xB%Vb+4d9VQr#YJ1LgGReoP`9Zc{*=ew5}h3**S@t&ccXy*|+qYexC$ zfDJ3mO(x+#kP-OqdPm+TxoA7S?L8e6YseTYxI$fl&UMBxvAS;f`kgYStlNLssV>}tN}*{DvEEq<8`|HTHzp&QNQZ6g1*%; zY@f)w(FE6Zr5R?}Ayi48t2A+=UMVEHR)<*VMV(O4J30n$l4Ev7ciFPul02ROV zehBzvbn4I#-{WfD( zx`ZSGa@Uop$hwP4JQzrEPqDUug$w`gXRWR{uGP`mr%DMc3)(d*2in;$1KO>sJ8a%q z%|~g3VVYDe8Wff4UBW7G`pH4{Bfc+)oGFsk`_blS_-Hb@8*}B%^wY2l+fiI3nxPtI zf)T7)&Gk%K`AH=2$!hZh&t7|He}gB#gZIGcK+6jMi41j20&c@pT~?Cqt#+ZascRu$ z9ewp3hiDzxcN*4t>l}ye*N0bpx`HyVT;_NMA^9x8s?tLZ%GWjQxekrPU8)B> zBFR92}W?MiIM7=yj4;4@VknnOh03Q8TmNu5AZx@+yD2I}X<;RzSxx`w=|PTY8I z`lS;}{gwuV7kGkYE&owHj*>lDG{oO*P`(d^Z-7*;YD-5Q{jQ=nlsX2bgpvPLoZN{U zEq|jE!+ASDRxnmQ|~jAs@1A4f>RH|dkoj1kh5J;^;6WW zR?gvy9HDo*$s^SHJh;d71E4mz(EHK(M)D{5NO+^Me&goLhPE_TuNhu7w0`rJ%Z6|P zMi)(cqp@l023(&Ive}V`#43n8WI{}T9mL}WDV#$}t+b#KWq4ko_rJ51gFhv59B~u`gwr2{2^nhE5Rx4xv^%#^edD|xDnK5Xh zsei)RAKEg3oV8OZk{M*C#JVavmDN-zkYgsSpqy&P7K#bXb*oM;{w3=u=e@Rc@)uND zDr>gNzsMES7yk@Dx0(tORN`#+&yi{Q=yen`n@-+?gz1sJ5W!)Dubev@D=vdm?v!=i z#)`tjP_VaRI@JT@-0CXkngHh>G;zD7(G`p);Uhb&7{!WcYphtn*kr{VZ=pL8J&$h| z!n5A+Rg#ovb6lM2?IbgO-jrgU!NG^Og!&v^`~&f-PK5WP`sP%XAp`f0n@7!c0mB?O zQ8){)96(fm{NUMXd`{old}Sr54fc|TN6^`S<@C+svFAm}7L0pjJ?a}1v;6|J-nsPX_4)i9`~Soz}2T-6(A|aDKM66 zKEw8|&VDT?piP!awap}PPMwBt*XA(kz=HRj9XEI5VyGmg%WIkGo71v8 zk-BIvG%G7{{5`JkB{%`qK{N2(3XU?l z^HqaZhNLjL*XDP=Bso_`cs?U+O$r8iOtG@W<8gMzd7hjx(Q}TWp`PO?BRew$tjzTS zZq!yO9c{W7h}uSgg;GgJBW%A@bHh_@%;&uIO!}B}WZ1`ev>n2>23S}!4_Y(bq^Td{ zM+Se6XP1>cv%e&t8UH2to(W)rpEm?d2*VD_*&rtP)h3KF|6qw_g$d$oneJhoOL&aH~9u?tBt zoMlLg%dI+6{6r7pFcbYp#dM|-mFYy}x0487B+@oSkpW`lfoCa^;|42zRp|WHl*-YV zQiSVx0Xr8TXKN9Kg_|lyO#cB19eHL?5y`X12@^cDK+$uxm7mE+a1AdoXN-KdMvXDY znl`3b`QpRPF(OT8kP)+HXXwKETs8z28qZo;%6)c=6X4a&%3-kpLI^tZics%Sb6WZ38H}UB@aGwsWh2s2veQw37W*Czsa|- zX~njUV%~ZZYumFjiJ}~P$1@@DG#t$m zP_^i+^W2$Z`Vp&yj8Ue~RI^XhH+$I*@411duV$wY+35)dk4vRmh(ILO&16K3sb3~k zb5Ck0c(Dg6YALQ)O0#$wXrI zWO=?$_Dp6(MyN@DxP@9$ATt7TF-eYs9NP|MdU-T39PT8%A(6z8he;-eJ&c>_7^@PT z9sW2U<{aZV(d2l=v3Yly$;9-yg}g`_wgx$vw9X7Hw;MSba~PFx zDRX6};TbJ^?rP0CSxATI0LpBuGGO+UXdyVCfGCN+F*DNzPbGtI((DT;pSfXkf~|_~ zWn*KQ*z)27@598>PF`Y}3X!R`%5N`v9)zwmS>5Pm2^RX4Av9xe<>t_VyEcl}j$|CG zcPg!ouh-NQ-`R3F!(FHCxW)%F5LLR$SlUTj188(mBi6LHN)%FX0>--(lbKHkey6lN zi|HtOsv$ONShCsPY1&uhC`1LbYKN_a=JKSQF|l^uSRc5db%GrZ4Je#;Hz}1%vFWw* zHR4qU*iHgf!{(LQ`BOIF8Ie@*xJkBCOB!asMpOeEr+}zo`0Ha^BMFYuiDix9%eve{{$2{t`D2RY^Bw78#<-@oUcF{*Gxn-3zZr>HS zXV=M*`*Jb=qx10h5ZuyB{|>Z~I#nEY$o_D_ba*kn93HeL&qxwrFg!~$*_WqIFfYY{@bUg-v$P~r8sk-!> zDpBWeL$g6ubM#hfuTD_$+*h4bO>7WZnEI{bQj?7V;(Q?DMForK?>IR?`_*H)DdEj? zc)<>qMG^L^SG=>+DSABOw@-x^9k{U|_0!WfB|&-~3%q?RJpUAr1G8T}#%+o1@%++4 z75y0%R&X-NLOhL_WyRm?9XuADJ6YpvV*i=J*H!+)*X;U zREColrhZy3EtJXwV?@1R+7DhKO%JN@QYW|^r2XnKO_!aU&c(Bki_bKObDavap!HLD zS*Be(9ry<`{M=D92W`K4jd><-PXqF^ICA> zKz)vfnmcIc!0lJBXq8o9RW+&M!c$pUaVxkJmnZ+@)ry z8sMG++(Q{|>X6~nM=j;?VGK2YaPjG5m->Ul8FqLWvJb>UJ$fEPqzQ3wz|w)%o~M>_ z_6Q#}I<@Iw?N_hmxX@Z1$x+o#Rdlnxg{$9MEh``&#gX+Ul)`W6qcDEet)0@P()@ge zoIkVbqHVu=Oz$O`vPF}KqYXAc2WtW9r*Nr1I3~pMx2O6+&5TMYhoh!F^ zt^oIlywohm1|ygkBBm!BM=&9)p;2QS-ZtwvMfCtIU1O!c$lh(adC2AJ;tuo zzMtwLRp)*7QmhtuSo#!^gR);e#%gJ1v`Y)Sr+G-KrBSEWL?39SInLPzEpbDIgS210 zre~%_JPGkz93c9)URWd-;a)K_X-Xr*a{^3vRIxgL;9zU#<1tGF`GmDF^{Tcw~ivvvWsIiNk z`|1(1w5xGpg~`|RV47>A^)%{ep|-rlqWO=CIXL^(Yuqwp-{+%lUc}J!D8H|GWmZ>c z-|M9jQe{zVqw_SK-SpEBW}z0YOk-Bp2RTst)nlnC ztqiV8V!A7dES4)wy`mO!bafK7{~1kbw!bEcX>D{}L~FO{rSx5!M0FQ3IavGEYx-tJ z&a{?taT=9YLR~((uU-)gdk5>1i00YHk7b!QWf9Wj@+ce?GE89o`_e3G?Q|&h7(+3>T6wSTU~{1L zOI;?`v=gQ|DP^f@0ahfFkaQJNlhby^cmT+W5dko$;vX>`7| z3IiRDwU^=W%2qp_mt@8uG+w@fqv>k_`iWPjH>P#0mvJOI2*nc6LiSi+O6hlHh@-Ct zvk>)1<)ziRmvc0A-IfEgUjvBMRUyt^jnykSn!0_^#>M4YkFhGPdS4ylR5uzveY8?p zzlNjfd(9kB?OIie*DD>8clDx!vtPZYVxg^UShVzYjuxwS-j#NIwsF*G(qb`tjbEna zpmBVYBL&w^TAbQ(D~)-!bHvQHuSM=PhJ`hf5rY>z;A}DK$F8ueJzAmBwUpWx#9q1S z0BhHWQk&dji0mHEKF~_FZ;YYw%RwES{pvCAN^#r4(AX_67NUNCQyPu!WN3Dm(A{F% z4{D)>X(v(jb&_;7s)My(J*I5QYWUhDYQI*)OENWsEU>R*h;(L~MXYvxD6NN#2RPxy zur?K40D4T%C7IFf^ANjUS*JQBW-IEFVqGXWI0&-)oZF|W(2g(+)1DgWPPxJ*-c=Y z3zu9KsGpA|`}vs=Gk4LKP2BqUrPgiM;Kx@Y_kma{xw|Z)y=S=}q|B-n?d@M*AyE&( zE^qDwt+cnlJBb!fkSutQb)+=w+rtp`Ez{O_S6q9=$Mlf`0?DrXtySLjSxMb}Wt1ybVkzvt= z($4exdA208Dt!dv^cseP7Mj7>c=Z~aLK(b?qv%V^t@lqOJ_V#d2$9BElLGPNQJWST80!XdJPok7KWoPg6}7f3h{Xz$H8@7edSPQPm8ShZ)G^R0zqF{-%p$} zZ6Ebke;8m@_u%(~R%r2F&(Oj}C=1zRy)B*o_#=aZf%wEMo(IqukX83R21}I#EsJk3 z$mSIQ8?C{%7k?b!;O?lsg!D#-WZo<21F5uj@$V|6@DT(DXTN$ZBc+kkn<_N*>_OI0 zye?vCcKGi(V&AKVrP1!2IZ|l4UBA2++K>Ohk?f*OUu7|~KT4MNZ}HGV)9t!>RodbI z6N99$Pp`JTolU8J{=y=<2=`brOIH53RoLE>8V;&<{*_jH|D(Z*-sy0F_N&LZl`O5_ zYVg9hKP*W7cCxfi@}~x`)5db>o&lZ+DOpp#&7!Ky&n;?mcC ze`YYFmrq>M+OJ;mGIfz`yl=1Y?0r$*YbqA0ejE#!|0%{~FSP6(%(sV_-m5Q9AG2in z@}EP@{7W(}0QK92LQ20Q41xDNjRUn`y_Tpn|)?^f*sHh=sg-SAbZJhN~Ow9H{;3H7_%>Oj=3(%K%f~0vkaNw9MXNF;hTub^OfHi4tJjj0 znKRN3_+sCPL)TV_2lxX?xT3rhV_YpJFCppVBN&FcfP)Gz)o9}%zAI3WAgYyB(CWIyENum@}`ibEa%-v18p zuqoXd#Ys=ztXbp%;C(p6v#$_Zl!3SvLAjbEYF{z!-yyq0LO>c_HRNB9pu zs(pd6541vU|7d__Uv+eF^;@(;5Am@GZ*Pxl?(z}&>r4H_v5=-84-iL2#%H_jDP9fh zr)gnCb9aE}y_DHc91A_uCw$D{O)3X!zk02Yg?N54gQ?!QvOx8Fr_xFGPZcoDqgf8x ze)SsLLOXCzfNUP&vKaMSokE%Ybbw|b^>T0nEwlIfc>XJ4dkOP1KBoWjY9E-TTNpkY zU{-Hc`Omrbfn8d?|HlBEpEcu0N-cbkrM9%9^0^3;-kr4w?yE64Rfm+UWM0riFI>iv}YyL>Z7Hcx`@ zCB%mr;^0n3mn^Zq#Sr=9D_3@`8yfpfy}K7xZlm- z4r1qES)pzC-W<5*fp43;wcCc$4Fiv)(D%m4lS~Sb)ynr-7~*&9pMJbc`+Scw%mJN} zEX?+Qz|cA^+T(-yQKvOBGll2qTm);6+e)*SA9BnA?3$L^-yboo0q&d@)-xYVA`W8r zv{YAroJ6(T3RpzJCGTVWn1IdPUfbl%Sdre{_~#@#UUHwt-6w;{7e!DD;cJj*%-1jed6*OQWf$ zQmFl~xe~pg1JP9GG1|=@yyi#<<;q@6#93`p_7EFnigBdURNf$#+$F zhfu$T%+w=Vw>&(H>rZCtmgQ1g^*oMQW$d6{aw*{+k-*Ir=(_POjiZj_xax3mV|;Iq zX^zTZ_C2O4^tR8BF$dMhXVyUI9M91)YGPPXJ3kBU!!a@9VA_X5ERSWF`pg?{+`)0Q z$q~NzUOSeB*~SYZM4U};PjTby+AUFO{(W48M~TWh@cL*OyH~oE@OXo^b9QEQ>iWKH zK})M-^9>H}buqRHFakG53*o`-`f<$6PRWkv1cN=^>gz@6lFiqN2E*N79$pewJ5Hsx z^o0g*!2L!E^P~WCP<=+}9;%ZqCS4EJ&u&a9_f9b=>Lp{k?@_faeT_CsH&C4F;pw?< zKQ+9xV!9xK6&>%aT{cUxJIzN7uMw$_l^M6ujU}ghSmD9)y8S?@B%Z;MX8U=WzO+Vo zrbWSGPqHA^j#DA^Uc}L&VptccbPvkH7%3`*b@58)@6O_QVX>%P0!yvpB8!AQy$QN5 zc^n-Z#t0+WR%DgBcI*oIcD9ch9AvDERLZx-K9U-*_dRybj7R9S?>Ro+pvKLa(E+Vp zFYyug@|bz4k5(74V^p~R-F_} zz`AIqvC*XrZJ5?>RyHTE>jSH_%CNy=b@xn;tVJAGH77?};|#8Tj%D^z=xo`>2yduQ ztHV<4HbrQ|;Xt!?>`LY2G7oRKHM6;i(I#J(tzAwEX}Q@)8^CI5sZ?I>qtU8VUn>r! z(aVs*TG`sso>VpN^4ZpGYqQ=~?ejIlQi&Nh$g6NuU&kT$Qy)rq&Tg@IQxntUt(lgC zOh&eL`N`Pw(Hj9<4fdKz_12?;W)ZE6RT=?bk-}QJXH(OAbf`9BW^_+?SG_ESxGH*9 zyMF!8)JQs8bfv?r9tqy8-UZ&=>U1$;>TiBgzw==lDW9{tZ2>tB~xirRjg+X46*PX(GTR%?=r^>H3NEf$es3T5At#-LBto>ZW z@KnS$v?h1V?Chsr7HY{WLo_|Ip>C2Jf!0pZ(#X2OFt2EK*0QJ#6la@{v%Vjk(%i3U za8z$UxSCh$jkg=jA@q=VE~_(yCwo~0YnRAE4H$7)>iXJ2j7dwo&7%%a^-C*gXxdjx zQd%o-IkYYs?QHf9GDXJ>CWeRuYf}o=A3GSHYE#y9I#Zoh*vr}89N|-o+V!tgFLp9i zm8=_@+gsxs+7sN`PFPIJFRv1^Y^z-dGiwKAn>6Vm&Q7NzIY`7Qhd4FuMUgjdwGj)o zVcH;WY3-g_Jv)(!WbK?R^-Qlam|MH8Ev@m^L~F)7gj_d%rBPsqV@+&m_cvxK^%7km z(YhHOz#SoDpmBs;u%6~av%2Y7LY$2dO-%bKp{4tb zb{RzKkou|Vh2HP_3S$gY1@&|=_OVB!3WM4#XOv)DGQTA=)>4Qk_WsI2PuMueFGnzTqm86ky#} zsB~WMmK+}To% +#include +#include + +#include + +extern ATOM MyRegisterClass(HINSTANCE hInstance); +extern BOOL InitInstance(HINSTANCE hInstance, int nCmdShow); +extern HRESULT InitDevice(); +extern void CleanupDevice(); +extern void DefineActions(void); + +extern HWND g_hWnd; +extern int g_iScreenWidth; +extern int g_iScreenHeight; +extern char g_Win64Username[17]; +extern wchar_t g_Win64UsernameW[17]; +extern ID3D11Device* g_pd3dDevice; +extern ID3D11DeviceContext* g_pImmediateContext; +extern IDXGISwapChain* g_pSwapChain; +extern ID3D11RenderTargetView* g_pRenderTargetView; +extern ID3D11DepthStencilView* g_pDepthStencilView; +extern DWORD dwProfileSettingsA[]; + +static const int kProfileValueCount = 5; +static const int kProfileSettingCount = 4; + +struct DedicatedServerConfig +{ + int port; + char bindIP[256]; + char name[17]; + int maxPlayers; + int worldSize; + int worldSizeChunks; + int worldHellScale; + __int64 seed; + ServerRuntime::EServerLogLevel logLevel; + bool hasSeed; + bool showHelp; +}; + +static std::atomic g_shutdownRequested(false); +static const DWORD kDefaultAutosaveIntervalMs = 60 * 1000; +static const int kServerActionPad = 0; + +static bool IsShutdownRequested() +{ + return g_shutdownRequested.load(); +} + +namespace ServerRuntime +{ + void RequestDedicatedServerShutdown() + { + g_shutdownRequested.store(true); + } +} + +/** + * Calls Access::Shutdown automatically once dedicated access control was initialized successfully + * アクセス制御初期化後のShutdownを自動化する + */ +class AccessShutdownGuard +{ +public: + AccessShutdownGuard() + : m_active(false) + { + } + + void Activate() + { + m_active = true; + } + + ~AccessShutdownGuard() + { + if (m_active) + { + ServerRuntime::Access::Shutdown(); + } + } + +private: + bool m_active; +}; +static BOOL WINAPI ConsoleCtrlHandlerProc(DWORD ctrlType) +{ + switch (ctrlType) + { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + case CTRL_CLOSE_EVENT: + case CTRL_SHUTDOWN_EVENT: + ServerRuntime::RequestDedicatedServerShutdown(); + return TRUE; + default: + return FALSE; + } +} + +/** + * **Wait For Server Stopped Signal** + * + * Thread entry used during shutdown to wait until the network layer reports server stop completion + * 停止通知待機用の終了スレッド処理 + */ +static int WaitForServerStoppedThreadProc(void *) +{ + if (g_NetworkManager.ServerStoppedValid()) + { + g_NetworkManager.ServerStoppedWait(); + } + return 0; +} + +static void PrintUsage() +{ + ServerRuntime::LogInfo("usage", "Minecraft.Server.exe [options]"); + ServerRuntime::LogInfo("usage", " -port <1-65535> Listen TCP port (default: server.properties:server-port)"); + ServerRuntime::LogInfo("usage", " -ip Bind address (default: server.properties:server-ip)"); + ServerRuntime::LogInfo("usage", " -bind Alias of -ip"); + ServerRuntime::LogInfo("usage", " -name Host display name (max 16 chars, default: server.properties:server-name)"); + ServerRuntime::LogInfo("usage", " -maxplayers <1-8> Public slots (default: server.properties:max-players)"); + ServerRuntime::LogInfo("usage", " -seed World seed (overrides server.properties:level-seed)"); + ServerRuntime::LogInfo("usage", " -loglevel debug|info|warn|error (default: server.properties:log-level)"); + ServerRuntime::LogInfo("usage", " -help Show this help"); +} + +using ServerRuntime::LoadServerPropertiesConfig; +using ServerRuntime::LogError; +using ServerRuntime::LogErrorf; +using ServerRuntime::LogInfof; +using ServerRuntime::LogDebugf; +using ServerRuntime::LogStartupStep; +using ServerRuntime::LogWarn; +using ServerRuntime::LogWorldIO; +using ServerRuntime::SaveServerPropertiesConfig; +using ServerRuntime::SetServerLogLevel; +using ServerRuntime::ServerPropertiesConfig; +using ServerRuntime::TryParseServerLogLevel; +using ServerRuntime::StringUtils::WideToUtf8; +using ServerRuntime::BootstrapWorldForServer; +using ServerRuntime::eWorldBootstrap_CreatedNew; +using ServerRuntime::eWorldBootstrap_Failed; +using ServerRuntime::eWorldBootstrap_Loaded; +using ServerRuntime::WaitForWorldActionIdle; +using ServerRuntime::WorldBootstrapResult; + +static bool ParseIntArg(const char *value, int *outValue) +{ + if (value == NULL || *value == 0) + return false; + + char *end = NULL; + long parsed = strtol(value, &end, 10); + if (end == value || *end != 0) + return false; + + *outValue = (int)parsed; + return true; +} + +static bool ParseInt64Arg(const char *value, __int64 *outValue) +{ + if (value == NULL || *value == 0) + return false; + + char *end = NULL; + __int64 parsed = _strtoi64(value, &end, 10); + if (end == value || *end != 0) + return false; + + *outValue = parsed; + return true; +} + +static bool ParseCommandLine(int argc, char **argv, DedicatedServerConfig *config) +{ + for (int i = 1; i < argc; ++i) + { + const char *arg = argv[i]; + if (_stricmp(arg, "-help") == 0 || _stricmp(arg, "--help") == 0 || _stricmp(arg, "-h") == 0) + { + config->showHelp = true; + return true; + } + else if ((_stricmp(arg, "-port") == 0) && (i + 1 < argc)) + { + int port = 0; + if (!ParseIntArg(argv[++i], &port) || port <= 0 || port > 65535) + { + LogError("startup", "Invalid -port value."); + return false; + } + config->port = port; + } + else if ((_stricmp(arg, "-ip") == 0 || _stricmp(arg, "-bind") == 0) && (i + 1 < argc)) + { + strncpy_s(config->bindIP, sizeof(config->bindIP), argv[++i], _TRUNCATE); + } + else if ((_stricmp(arg, "-name") == 0) && (i + 1 < argc)) + { + strncpy_s(config->name, sizeof(config->name), argv[++i], _TRUNCATE); + } + else if ((_stricmp(arg, "-maxplayers") == 0) && (i + 1 < argc)) + { + int maxPlayers = 0; + if (!ParseIntArg(argv[++i], &maxPlayers) || maxPlayers <= 0 || maxPlayers > MINECRAFT_NET_MAX_PLAYERS) + { + LogError("startup", "Invalid -maxplayers value."); + return false; + } + config->maxPlayers = maxPlayers; + } + else if ((_stricmp(arg, "-seed") == 0) && (i + 1 < argc)) + { + if (!ParseInt64Arg(argv[++i], &config->seed)) + { + LogError("startup", "Invalid -seed value."); + return false; + } + config->hasSeed = true; + } + else if ((_stricmp(arg, "-loglevel") == 0) && (i + 1 < argc)) + { + if (!TryParseServerLogLevel(argv[++i], &config->logLevel)) + { + LogError("startup", "Invalid -loglevel value. Use debug/info/warn/error."); + return false; + } + } + else + { + LogErrorf("startup", "Unknown or incomplete argument: %s", arg); + return false; + } + } + + return true; +} + +static void SetExeWorkingDirectory() +{ + char exePath[MAX_PATH] = {}; + GetModuleFileNameA(NULL, exePath, MAX_PATH); + char *slash = strrchr(exePath, '\\'); + if (slash != NULL) + { + *(slash + 1) = 0; + SetCurrentDirectoryA(exePath); + } +} + +static void ApplyServerPropertiesToDedicatedConfig(const ServerPropertiesConfig &serverProperties, DedicatedServerConfig *config) +{ + if (config == NULL) + { + return; + } + + config->port = serverProperties.serverPort; + strncpy_s( + config->bindIP, + sizeof(config->bindIP), + serverProperties.serverIp.empty() ? "0.0.0.0" : serverProperties.serverIp.c_str(), + _TRUNCATE); + strncpy_s( + config->name, + sizeof(config->name), + serverProperties.serverName.empty() ? "DedicatedServer" : serverProperties.serverName.c_str(), + _TRUNCATE); + config->maxPlayers = serverProperties.maxPlayers; + config->worldSize = serverProperties.worldSize; + config->worldSizeChunks = serverProperties.worldSizeChunks; + config->worldHellScale = serverProperties.worldHellScale; + config->logLevel = serverProperties.logLevel; + config->hasSeed = serverProperties.hasSeed; + config->seed = serverProperties.seed; +} + +/** + * **Tick Core Async Subsystems** + * + * Advances core subsystems for one frame to keep async processing alive + * Call continuously even inside wait loops to avoid stalling storage/profile/network work + * 非同期進行維持のためのティック処理 + */ +static void TickCoreSystems() +{ + g_NetworkManager.DoWork(); + ProfileManager.Tick(); + StorageManager.Tick(); + //ConsoleSaveFileOriginal::flushPendingBackgroundSave(); +} + +/** + * **Handle Queued XUI Server Action Once** + * + * Processes queued XUI/server action once + * XUIアクションの単発処理 + */ +static void HandleXuiActions() +{ + app.HandleXuiActions(); +} + +/** + * Dedicated Server Entory Point + * + * 主な責務: + * - プロセス/描画/ネットワークの初期化 + * - `WorldManager` によるワールドロードまたは新規作成 + * - メインループと定期オートセーブ実行 + * - 終了時の最終保存と各サブシステムの安全停止 + */ +int main(int argc, char **argv) +{ + DedicatedServerConfig config; + config.port = NETWORK_LAN_DEFAULT_PORT; + strncpy_s(config.bindIP, sizeof(config.bindIP), "0.0.0.0", _TRUNCATE); + strncpy_s(config.name, sizeof(config.name), "DedicatedServer", _TRUNCATE); + config.maxPlayers = MINECRAFT_NET_MAX_PLAYERS; + config.worldSize = e_worldSize_Classic; + config.worldSizeChunks = LEVEL_WIDTH_CLASSIC; + config.worldHellScale = HELL_LEVEL_SCALE_CLASSIC; + config.seed = 0; + config.logLevel = ServerRuntime::eServerLogLevel_Info; + config.hasSeed = false; + config.showHelp = false; + + SetConsoleCtrlHandler(ConsoleCtrlHandlerProc, TRUE); + SetExeWorkingDirectory(); + + // Load base settings from server.properties, then override with CLI values when provided + ServerPropertiesConfig serverProperties = LoadServerPropertiesConfig(); + ApplyServerPropertiesToDedicatedConfig(serverProperties, &config); + + if (!ParseCommandLine(argc, argv, &config)) + { + PrintUsage(); + return 1; + } + if (config.showHelp) + { + PrintUsage(); + return 0; + } + + SetServerLogLevel(config.logLevel); + LogStartupStep("initializing process state"); + AccessShutdownGuard accessShutdownGuard; + + g_iScreenWidth = 1280; + g_iScreenHeight = 720; + + strncpy_s(g_Win64Username, sizeof(g_Win64Username), config.name, _TRUNCATE); + MultiByteToWideChar(CP_ACP, 0, g_Win64Username, -1, g_Win64UsernameW, 17); + + g_MultiplayerHost = true; + g_MultiplayerJoin = false; + g_MultiplayerPort = config.port; + strncpy_s(g_MultiplayerIP, sizeof(g_MultiplayerIP), config.bindIP, _TRUNCATE); + g_DedicatedServerPort = config.port; + strncpy_s(g_DedicatedServerBindIP, sizeof(g_DedicatedServerBindIP), config.bindIP, _TRUNCATE); + g_DedicatedServerLanAdvertise = serverProperties.lanAdvertise; + LogStartupStep("initializing server log manager"); + ServerRuntime::ServerLogManager::Initialize(); + LogStartupStep("initializing dedicated access control"); + if (!ServerRuntime::Access::Initialize(".", serverProperties.whiteListEnabled)) + { + LogError("startup", "Failed to initialize dedicated server access control."); + return 2; + } + accessShutdownGuard.Activate(); + LogInfof("startup", "LAN advertise: %s", serverProperties.lanAdvertise ? "enabled" : "disabled"); + LogInfof("startup", "Whitelist: %s", serverProperties.whiteListEnabled ? "enabled" : "disabled"); + LogInfof("startup", "Spawn protection radius: %d", serverProperties.spawnProtectionRadius); +#ifdef _LARGE_WORLDS + LogInfof( + "startup", + "World size preset: %d (xz=%d, hell-scale=%d)", + config.worldSize, + config.worldSizeChunks, + config.worldHellScale); +#endif + + LogStartupStep("registering hidden window class"); + HINSTANCE hInstance = GetModuleHandle(NULL); + MyRegisterClass(hInstance); + + LogStartupStep("creating hidden window"); + if (!InitInstance(hInstance, SW_HIDE)) + { + LogError("startup", "Failed to create window instance."); + + return 2; + } + ShowWindow(g_hWnd, SW_HIDE); + + LogStartupStep("initializing graphics device wrappers"); + if (FAILED(InitDevice())) + { + LogError("startup", "Failed to initialize D3D device."); + CleanupDevice(); + + return 2; + } + + LogStartupStep("loading media/string tables"); + app.loadMediaArchive(); + RenderManager.Initialise(g_pd3dDevice, g_pSwapChain); + app.loadStringTable(); + ui.init(g_pd3dDevice, g_pImmediateContext, g_pRenderTargetView, g_pDepthStencilView, g_iScreenWidth, g_iScreenHeight); + + InputManager.Initialise(1, 3, MINECRAFT_ACTION_MAX, ACTION_MAX_MENU); + DefineActions(); + InputManager.SetJoypadMapVal(0, 0); + InputManager.SetKeyRepeatRate(0.3f, 0.2f); + + ProfileManager.Initialise( + TITLEID_MINECRAFT, + app.m_dwOfferID, + PROFILE_VERSION_10, + kProfileValueCount, + kProfileSettingCount, + dwProfileSettingsA, + app.GAME_DEFINED_PROFILE_DATA_BYTES * XUSER_MAX_COUNT, + &app.uiGameDefinedDataChangedBitmask); + ProfileManager.SetDefaultOptionsCallback(&CConsoleMinecraftApp::DefaultOptionsCallback, (LPVOID)&app); + ProfileManager.SetDebugFullOverride(true); + + LogStartupStep("initializing network manager"); + g_NetworkManager.Initialise(); + + for (int i = 0; i < MINECRAFT_NET_MAX_PLAYERS; ++i) + { + IQNet::m_player[i].m_smallId = (BYTE)i; + IQNet::m_player[i].m_isRemote = false; + IQNet::m_player[i].m_isHostPlayer = (i == 0); + swprintf_s(IQNet::m_player[i].m_gamertag, 32, L"Player%d", i); + } + wcscpy_s(IQNet::m_player[0].m_gamertag, 32, g_Win64UsernameW); + NetworkSocketLayer::Initialize(); + + Tesselator::CreateNewThreadStorage(1024 * 1024); + AABB::CreateNewThreadStorage(); + Vec3::CreateNewThreadStorage(); + IntCache::CreateNewThreadStorage(); + Compression::CreateNewThreadStorage(); + OldChunkStorage::CreateNewThreadStorage(); + Level::enableLightingCache(); + Tile::CreateNewThreadStorage(); + + LogStartupStep("creating Minecraft singleton"); + Minecraft::main(); + Minecraft *minecraft = Minecraft::GetInstance(); + if (minecraft == NULL) + { + LogError("startup", "Minecraft initialization failed."); + CleanupDevice(); + + return 3; + } + + app.InitGameSettings(); + + MinecraftServer::resetFlags(); + app.SetTutorialMode(false); + app.SetCorruptSaveDeleted(false); + app.SetGameHostOption(eGameHostOption_Difficulty, serverProperties.difficulty); + app.SetGameHostOption(eGameHostOption_FriendsOfFriends, serverProperties.friendsOfFriends ? 1 : 0); + app.SetGameHostOption(eGameHostOption_Gamertags, serverProperties.gamertags ? 1 : 0); + app.SetGameHostOption(eGameHostOption_BedrockFog, serverProperties.bedrockFog ? 1 : 0); + app.SetGameHostOption(eGameHostOption_GameType, serverProperties.gameMode); + app.SetGameHostOption(eGameHostOption_LevelType, serverProperties.levelTypeFlat ? 1 : 0); + app.SetGameHostOption(eGameHostOption_Structures, serverProperties.generateStructures ? 1 : 0); + app.SetGameHostOption(eGameHostOption_BonusChest, serverProperties.bonusChest ? 1 : 0); + app.SetGameHostOption(eGameHostOption_PvP, serverProperties.pvp ? 1 : 0); + app.SetGameHostOption(eGameHostOption_TrustPlayers, serverProperties.trustPlayers ? 1 : 0); + app.SetGameHostOption(eGameHostOption_FireSpreads, serverProperties.fireSpreads ? 1 : 0); + app.SetGameHostOption(eGameHostOption_TNT, serverProperties.tnt ? 1 : 0); + app.SetGameHostOption( + eGameHostOption_CheatsEnabled, + (serverProperties.hostCanFly || serverProperties.hostCanChangeHunger || serverProperties.hostCanBeInvisible) ? 1 : 0); + app.SetGameHostOption(eGameHostOption_HostCanFly, serverProperties.hostCanFly ? 1 : 0); + app.SetGameHostOption(eGameHostOption_HostCanChangeHunger, serverProperties.hostCanChangeHunger ? 1 : 0); + app.SetGameHostOption(eGameHostOption_HostCanBeInvisible, serverProperties.hostCanBeInvisible ? 1 : 0); + app.SetGameHostOption(eGameHostOption_DisableSaving, serverProperties.disableSaving ? 1 : 0); + app.SetGameHostOption(eGameHostOption_MobGriefing, serverProperties.mobGriefing ? 1 : 0); + app.SetGameHostOption(eGameHostOption_KeepInventory, serverProperties.keepInventory ? 1 : 0); + app.SetGameHostOption(eGameHostOption_DoMobSpawning, serverProperties.doMobSpawning ? 1 : 0); + app.SetGameHostOption(eGameHostOption_DoMobLoot, serverProperties.doMobLoot ? 1 : 0); + app.SetGameHostOption(eGameHostOption_DoTileDrops, serverProperties.doTileDrops ? 1 : 0); + app.SetGameHostOption(eGameHostOption_NaturalRegeneration, serverProperties.naturalRegeneration ? 1 : 0); + app.SetGameHostOption(eGameHostOption_DoDaylightCycle, serverProperties.doDaylightCycle ? 1 : 0); +#ifdef _LARGE_WORLDS + app.SetGameHostOption(eGameHostOption_WorldSize, serverProperties.worldSize); + // Apply desired target size for loading existing worlds. + // Expansion happens only when target size is larger than current level size. + app.SetGameNewWorldSize(serverProperties.worldSizeChunks, true); + app.SetGameNewHellScale(serverProperties.worldHellScale); +#endif + + StorageManager.SetSaveDisabled(serverProperties.disableSaving); + // Read world name and fixed save-id from server.properties + // Delegate load-vs-create decision to WorldManager + std::wstring targetWorldName = serverProperties.worldName; + if (targetWorldName.empty()) + { + targetWorldName = L"world"; // Default world name + } + WorldBootstrapResult worldBootstrap = BootstrapWorldForServer(serverProperties, kServerActionPad, &TickCoreSystems); + if (worldBootstrap.status == eWorldBootstrap_Loaded) + { + const std::string &loadedSaveFilename = worldBootstrap.resolvedSaveId; + if (!loadedSaveFilename.empty() && _stricmp(loadedSaveFilename.c_str(), serverProperties.worldSaveId.c_str()) != 0) + { + // Persist the actually loaded save-id back to config + // Keep lookup keys aligned for next startup + LogWorldIO("updating level-id to loaded save filename"); + serverProperties.worldSaveId = loadedSaveFilename; + if (!SaveServerPropertiesConfig(serverProperties)) + { + LogWorldIO("failed to persist updated level-id"); + } + } + } + else if (worldBootstrap.status == eWorldBootstrap_Failed) + { + LogErrorf("world-io", "Failed to load configured world \"%s\".", WideToUtf8(targetWorldName).c_str()); + NetworkSocketLayer::Shutdown(); + g_NetworkManager.Terminate(); + CleanupDevice(); + + return 4; + } + + NetworkGameInitData *param = new NetworkGameInitData(); + if (config.hasSeed) + { + param->seed = config.seed; + } + else + { + param->seed = (new Random())->nextLong(); + } +#ifdef _LARGE_WORLDS + param->xzSize = (unsigned int)config.worldSizeChunks; + param->hellScale = (unsigned char)config.worldHellScale; +#endif + param->saveData = worldBootstrap.saveData; + param->settings = app.GetGameHostOption(eGameHostOption_All); + param->dedicatedNoLocalHostPlayer = true; + + LogStartupStep("starting hosted network game thread"); + g_NetworkManager.HostGame(0, true, false, (unsigned char)config.maxPlayers, 0); + g_NetworkManager.FakeLocalPlayerJoined(); + + C4JThread *startThread = new C4JThread(&CGameNetworkManager::RunNetworkGameThreadProc, (LPVOID)param, "RunNetworkGame"); + startThread->Run(); + + while (startThread->isRunning() && !IsShutdownRequested()) + { + TickCoreSystems(); + Sleep(10); + } + + startThread->WaitForCompletion(INFINITE); + int startupResult = startThread->GetExitCode(); + delete startThread; + + if (startupResult != 0) + { + LogErrorf("startup", "Failed to start dedicated server (code %d).", startupResult); + NetworkSocketLayer::Shutdown(); + g_NetworkManager.Terminate(); + CleanupDevice(); + + return 4; + } + + LogStartupStep("server startup complete"); + LogInfof("startup", "Dedicated server listening on %s:%d", g_MultiplayerIP, g_MultiplayerPort); + if (worldBootstrap.status == eWorldBootstrap_CreatedNew && !IsShutdownRequested()/* && !app.m_bShutdown*/) + { + // Windows64 suppresses saveToDisc right after new world creation + // Dedicated Server explicitly runs the initial save here + LogWorldIO("requesting initial save for newly created world"); + WaitForWorldActionIdle(kServerActionPad, 5000, &TickCoreSystems, &HandleXuiActions); + app.SetXuiServerAction(kServerActionPad, eXuiServerAction_AutoSaveGame); + if (!WaitForWorldActionIdle(kServerActionPad, 30000, &TickCoreSystems, &HandleXuiActions)) + { + LogWorldIO("initial save timed out"); + LogWarn("world-io", "Timed out waiting for initial save action to finish."); + } + else + { + LogWorldIO("initial save completed"); + } + } + DWORD autosaveIntervalMs = kDefaultAutosaveIntervalMs; + if (serverProperties.autosaveIntervalSeconds > 0) + { + autosaveIntervalMs = (DWORD)(serverProperties.autosaveIntervalSeconds * 1000); + } + DWORD nextAutosaveTick = GetTickCount() + autosaveIntervalMs; + bool autosaveRequested = false; + ServerRuntime::ServerCli serverCli; + serverCli.Start(); + + while (!IsShutdownRequested() /*&& !app.m_bShutdown*/) + { + TickCoreSystems(); + HandleXuiActions(); + serverCli.Poll(); + + if (IsShutdownRequested()/* || app.m_bShutdown*/) + { + break; + } + + if (autosaveRequested && app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle/* && !ConsoleSaveFileOriginal::hasPendingBackgroundSave()*/) + { + LogWorldIO("autosave completed"); + autosaveRequested = false; + } + + if (MinecraftServer::serverHalted()) + { + break; + } + + DWORD now = GetTickCount(); + if ((LONG)(now - nextAutosaveTick) >= 0) + { + if (app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle/* && !ConsoleSaveFileOriginal::hasPendingBackgroundSave()*/) + { + LogWorldIO("requesting autosave"); + app.SetXuiServerAction(kServerActionPad, eXuiServerAction_AutoSaveGame); + autosaveRequested = true; + } + nextAutosaveTick = now + autosaveIntervalMs; + } + + Sleep(10); + } + serverCli.Stop(); + /*app.m_bShutdown = true;*/ + + LogInfof("shutdown", "Dedicated server stopped"); + MinecraftServer *server = MinecraftServer::getInstance(); + if (server != NULL/* && !ConsoleSaveFileOriginal::hasPendingBackgroundSave()*/) + { + server->setSaveOnExit(true); + LogWorldIO("requesting save before shutdown"); + LogWorldIO("using saveOnExit for shutdown"); + } + + /*if (ConsoleSaveFileOriginal::hasPendingBackgroundSave()) + { + LogWorldIO("Waiting for autosave to complete..."); + }*/ + + MinecraftServer::HaltServer(); + + if (g_NetworkManager.ServerStoppedValid()) + { + C4JThread waitThread(&WaitForServerStoppedThreadProc, NULL, "WaitServerStopped"); + waitThread.Run(); + while (waitThread.isRunning()) + { + TickCoreSystems(); + Sleep(10); + } + waitThread.WaitForCompletion(INFINITE); + } + + /*while (ConsoleSaveFileOriginal::hasPendingBackgroundSave()) + { + TickCoreSystems(); + Sleep(10); + }*/ + + LogInfof("shutdown", "Cleaning up and exiting."); + NetworkSocketLayer::Shutdown(); + LogDebugf("shutdown", "Network layer shutdown complete."); + g_NetworkManager.Terminate(); + LogDebugf("shutdown", "Network manager terminated."); + ServerRuntime::ServerLogManager::Shutdown(); + CleanupDevice(); + + + return 0; +} + diff --git a/Minecraft.Server/WorldManager.cpp b/Minecraft.Server/WorldManager.cpp new file mode 100644 index 00000000..9f378d12 --- /dev/null +++ b/Minecraft.Server/WorldManager.cpp @@ -0,0 +1,641 @@ +#include "stdafx.h" + +#include "WorldManager.h" + +#include "Minecraft.h" +#include "MinecraftServer.h" +#include "ServerLogger.h" +#include "Common//StringUtils.h" + +#include +#include + +namespace ServerRuntime +{ +using StringUtils::Utf8ToWide; +using StringUtils::WideToUtf8; + +enum EWorldSaveLoadResult +{ + eWorldSaveLoad_Loaded, + eWorldSaveLoad_NotFound, + eWorldSaveLoad_Failed +}; + +struct SaveInfoQueryContext +{ + bool done; + bool success; + SAVE_DETAILS *details; + + SaveInfoQueryContext() + : done(false) + , success(false) + , details(NULL) + { + } +}; + +struct SaveDataLoadContext +{ + bool done; + bool isCorrupt; + bool isOwner; + + SaveDataLoadContext() + : done(false) + , isCorrupt(true) + , isOwner(false) + { + } +}; + +/** + * **Apply Save ID To StorageManager** + * + * Applies the configured save destination ID (`level-id`) to `StorageManager` + * - Re-applies the same ID at startup and before save to avoid destination drift + * - Ignores empty values as invalid + * - For some reason, a date-based world file occasionally appears after a debug build, but the cause is unknown. + * 保存先IDの適用処理 + * + * @param saveFilename Normalized save destination ID + */ +static void SetStorageSaveUniqueFilename(const std::string &saveFilename) +{ + if (saveFilename.empty()) + { + return; + } + + char filenameBuffer[64] = {}; + strncpy_s(filenameBuffer, sizeof(filenameBuffer), saveFilename.c_str(), _TRUNCATE); + StorageManager.SetSaveUniqueFilename(filenameBuffer); +} + +static void LogSaveFilename(const char *prefix, const std::string &saveFilename) +{ + LogInfof("world-io", "%s: %s", (prefix != NULL) ? prefix : "save-filename", saveFilename.c_str()); +} + +/** + * Verifies a directory exists and creates it when missing + * - Treats an existing non-directory path as failure + * - Returns whether the directory had to be created + * ディレクトリ存在保証の補助処理 + */ +static bool EnsureDirectoryExists(const std::wstring &directoryPath, bool *outCreated) +{ + if (outCreated != NULL) + { + *outCreated = false; + } + if (directoryPath.empty()) + { + return false; + } + + DWORD attrs = GetFileAttributesW(directoryPath.c_str()); + if (attrs != INVALID_FILE_ATTRIBUTES) + { + if ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0) + { + return true; + } + + LogErrorf("world-io", "path exists but is not a directory: %s", WideToUtf8(directoryPath).c_str()); + return false; + } + + if (CreateDirectoryW(directoryPath.c_str(), NULL)) + { + if (outCreated != NULL) + { + *outCreated = true; + } + return true; + } + + DWORD error = GetLastError(); + if (error == ERROR_ALREADY_EXISTS) + { + attrs = GetFileAttributesW(directoryPath.c_str()); + if (attrs != INVALID_FILE_ATTRIBUTES && ((attrs & FILE_ATTRIBUTE_DIRECTORY) != 0)) + { + return true; + } + } + + LogErrorf( + "world-io", + "failed to create directory %s (error=%lu)", + WideToUtf8(directoryPath).c_str(), + (unsigned long)error); + return false; +} + +/** + * Prepares the save root used by the Windows64 storage layout + * - Creates `Windows64` first because `CreateDirectoryW` is not recursive + * - Creates `Windows64\\GameHDD` when missing before world bootstrap starts + * Windows64用保存先ディレクトリの存在保証 + */ +static bool EnsureGameHddRootExists() +{ + bool windows64Created = false; + if (!EnsureDirectoryExists(L"Windows64", &windows64Created)) + { + return false; + } + + bool gameHddCreated = false; + if (!EnsureDirectoryExists(L"Windows64\\GameHDD", &gameHddCreated)) + { + return false; + } + + if (windows64Created || gameHddCreated) + { + LogWorldIO("created missing Windows64\\GameHDD storage directories"); + } + + return true; +} + +static void LogEnumeratedSaveInfo(int index, const SAVE_INFO &saveInfo) +{ + std::wstring title = Utf8ToWide(saveInfo.UTF8SaveTitle); + std::wstring filename = Utf8ToWide(saveInfo.UTF8SaveFilename); + std::string titleUtf8 = WideToUtf8(title); + std::string filenameUtf8 = WideToUtf8(filename); + + char logLine[512] = {}; + sprintf_s( + logLine, + sizeof(logLine), + "save[%d] title=\"%s\" filename=\"%s\"", + index, + titleUtf8.c_str(), + filenameUtf8.c_str()); + LogDebug("world-io", logLine); +} + +/** + * **Save List Callback** + * + * Captures async save-list results into `SaveInfoQueryContext` and marks completion for the waiter + * セーブ一覧取得の完了通知 + */ +static int GetSavesInfoCallbackProc(LPVOID lpParam, SAVE_DETAILS *pSaveDetails, const bool bRes) +{ + SaveInfoQueryContext *context = (SaveInfoQueryContext *)lpParam; + if (context != NULL) + { + context->details = pSaveDetails; + context->success = bRes; + context->done = true; + } + return 0; +} + +/** + * **Save Data Load Callback** + * + * Writes load results such as corruption status into `SaveDataLoadContext` + * セーブ読み込み結果の反映 + */ +static int LoadSaveDataCallbackProc(LPVOID lpParam, const bool bIsCorrupt, const bool bIsOwner) +{ + SaveDataLoadContext *context = (SaveDataLoadContext *)lpParam; + if (context != NULL) + { + context->isCorrupt = bIsCorrupt; + context->isOwner = bIsOwner; + context->done = true; + } + return 0; +} + +/** + * **Wait For Save List Completion** + * + * Waits until save-list retrieval completes + * - Prefers callback completion as the primary signal + * - Also falls back to polling because some environments populate `ReturnSavesInfo()` before callback + * セーブ一覧待機の補助処理 + * + * @return `true` when completion is detected + */ +static bool WaitForSaveInfoResult(SaveInfoQueryContext *context, DWORD timeoutMs, WorldManagerTickProc tickProc) +{ + DWORD start = GetTickCount(); + while ((GetTickCount() - start) < timeoutMs) + { + if (context->done) + { + return true; + } + + if (context->details == NULL) + { + // Some implementations fill ReturnSavesInfo before the callback + // Keep polling as a fallback instead of relying only on callback completion + SAVE_DETAILS *details = StorageManager.ReturnSavesInfo(); + if (details != NULL) + { + context->details = details; + context->success = true; + context->done = true; + return true; + } + } + + if (tickProc != NULL) + { + tickProc(); + } + Sleep(10); + } + + return context->done; +} + +/** + * **Wait For Save Data Load Completion** + * + * Waits for the save-data load callback to complete + * セーブ本体の読み込み待機 + * + * @return `true` when callback is reached, `false` on timeout + */ +static bool WaitForSaveLoadResult(SaveDataLoadContext *context, DWORD timeoutMs, WorldManagerTickProc tickProc) +{ + DWORD start = GetTickCount(); + while ((GetTickCount() - start) < timeoutMs) + { + if (context->done) + { + return true; + } + + if (tickProc != NULL) + { + tickProc(); + } + Sleep(10); + } + + return context->done; +} + +/** + * **Match SAVE_INFO By World Name** + * + * Compares both save title and save filename against the target world name + * ワールド名一致判定 + */ +static bool SaveInfoMatchesWorldName(const SAVE_INFO &saveInfo, const std::wstring &targetWorldName) +{ + if (targetWorldName.empty()) + { + return false; + } + + std::wstring saveTitle = Utf8ToWide(saveInfo.UTF8SaveTitle); + std::wstring saveFilename = Utf8ToWide(saveInfo.UTF8SaveFilename); + + if (!saveTitle.empty() && (_wcsicmp(saveTitle.c_str(), targetWorldName.c_str()) == 0)) + { + return true; + } + if (!saveFilename.empty() && (_wcsicmp(saveFilename.c_str(), targetWorldName.c_str()) == 0)) + { + return true; + } + + return false; +} + +/** + * **Match SAVE_INFO By Save Filename** + * + * Checks whether `SAVE_INFO` matches by save destination ID (`UTF8SaveFilename`) + * 保存先ID一致判定 + */ +static bool SaveInfoMatchesSaveFilename(const SAVE_INFO &saveInfo, const std::string &targetSaveFilename) +{ + if (targetSaveFilename.empty() || saveInfo.UTF8SaveFilename[0] == 0) + { + return false; + } + + return (_stricmp(saveInfo.UTF8SaveFilename, targetSaveFilename.c_str()) == 0); +} + +/** + * **Apply World Identity To Storage** + * + * Applies world identity (`level-name` + `level-id`) to storage + * - Always sets both display name and ID to avoid partial configuration + * - Helps prevent unintended new save destinations across environment differences + * 保存先と表示名の同期処理 + */ +static void ApplyWorldStorageTarget(const std::wstring &worldName, const std::string &saveId) +{ + // Set both title (display name) and save ID (actual folder name) explicitly + // Setting only one side can create unexpected new save targets in some environments + StorageManager.SetSaveTitle(worldName.c_str()); + SetStorageSaveUniqueFilename(saveId); +} + +/** + * **Prepare World Save Data For Startup** + * + * Searches for a save matching the target world and extracts startup payload when found + * Match priority: + * 1. Exact match by `level-id` (`UTF8SaveFilename`) + * 2. Fallback match by `level-name` against title or filename + * ワールド一致セーブの探索処理 + * + * @return + * - `eWorldSaveLoad_Loaded`: Existing save loaded successfully + * - `eWorldSaveLoad_NotFound`: No matching save found + * - `eWorldSaveLoad_Failed`: API failure, corruption, or invalid data + */ +static EWorldSaveLoadResult PrepareWorldSaveData( + const std::wstring &targetWorldName, + const std::string &targetSaveFilename, + int actionPad, + WorldManagerTickProc tickProc, + LoadSaveDataThreadParam **outSaveData, + std::string *outResolvedSaveFilename) +{ + if (outSaveData == NULL) + { + return eWorldSaveLoad_Failed; + } + *outSaveData = NULL; + if (outResolvedSaveFilename != NULL) + { + outResolvedSaveFilename->clear(); + } + + LogWorldIO("enumerating saves for configured world"); + StorageManager.ClearSavesInfo(); + + SaveInfoQueryContext infoContext; + int infoState = StorageManager.GetSavesInfo(actionPad, &GetSavesInfoCallbackProc, &infoContext, "save"); + if (infoState == C4JStorage::ESaveGame_Idle) + { + infoContext.done = true; + infoContext.success = true; + infoContext.details = StorageManager.ReturnSavesInfo(); + } + else if (infoState != C4JStorage::ESaveGame_GetSavesInfo) + { + LogWorldIO("GetSavesInfo failed to start"); + return eWorldSaveLoad_Failed; + } + + if (!WaitForSaveInfoResult(&infoContext, 10000, tickProc)) + { + LogWorldIO("timed out waiting for save list"); + return eWorldSaveLoad_Failed; + } + + if (infoContext.details == NULL) + { + infoContext.details = StorageManager.ReturnSavesInfo(); + } + if (infoContext.details == NULL) + { + LogWorldIO("failed to retrieve save list"); + return eWorldSaveLoad_Failed; + } + + int matchedIndex = -1; + if (!targetSaveFilename.empty()) + { + // 1) If save ID is provided, search by it first + // This is the most stable way to reuse the same world target + for (int i = 0; i < infoContext.details->iSaveC; ++i) + { + LogEnumeratedSaveInfo(i, infoContext.details->SaveInfoA[i]); + if (SaveInfoMatchesSaveFilename(infoContext.details->SaveInfoA[i], targetSaveFilename)) + { + matchedIndex = i; + break; + } + } + } + + if (matchedIndex < 0 && targetSaveFilename.empty()) + { + for (int i = 0; i < infoContext.details->iSaveC; ++i) + { + LogEnumeratedSaveInfo(i, infoContext.details->SaveInfoA[i]); + } + } + + for (int i = 0; i < infoContext.details->iSaveC; ++i) + { + // 2) If no save matched by ID, try compatibility fallback + // Match worldName against save title or save filename + if (matchedIndex >= 0) + { + break; + } + if (SaveInfoMatchesWorldName(infoContext.details->SaveInfoA[i], targetWorldName)) + { + matchedIndex = i; + break; + } + } + + if (matchedIndex < 0) + { + LogWorldIO("no save matched configured world name"); + return eWorldSaveLoad_NotFound; + } + + std::wstring matchedTitle = Utf8ToWide(infoContext.details->SaveInfoA[matchedIndex].UTF8SaveTitle); + if (matchedTitle.empty()) + { + matchedTitle = targetWorldName; + } + LogWorldName("matched save title", matchedTitle); + SAVE_INFO *matchedSaveInfo = &infoContext.details->SaveInfoA[matchedIndex]; + std::wstring matchedFilename = Utf8ToWide(matchedSaveInfo->UTF8SaveFilename); + if (!matchedFilename.empty()) + { + LogWorldName("matched save filename", matchedFilename); + } + + ApplyWorldStorageTarget(targetWorldName, targetSaveFilename); + + std::string resolvedSaveFilename; + if (matchedSaveInfo->UTF8SaveFilename[0] != 0) + { + // Prefer the save ID that was actually matched, then keep using it for future saves + resolvedSaveFilename = matchedSaveInfo->UTF8SaveFilename; + SetStorageSaveUniqueFilename(resolvedSaveFilename); + } + else if (!targetSaveFilename.empty()) + { + resolvedSaveFilename = targetSaveFilename; + } + + if (outResolvedSaveFilename != NULL) + { + *outResolvedSaveFilename = resolvedSaveFilename; + } + + SaveDataLoadContext loadContext; + int loadState = StorageManager.LoadSaveData(matchedSaveInfo, &LoadSaveDataCallbackProc, &loadContext); + if (loadState != C4JStorage::ESaveGame_Load && loadState != C4JStorage::ESaveGame_Idle) + { + LogWorldIO("LoadSaveData failed to start"); + return eWorldSaveLoad_Failed; + } + + if (loadState == C4JStorage::ESaveGame_Load) + { + if (!WaitForSaveLoadResult(&loadContext, 15000, tickProc)) + { + LogWorldIO("timed out waiting for save data load"); + return eWorldSaveLoad_Failed; + } + if (loadContext.isCorrupt) + { + LogWorldIO("target save is corrupt; aborting load"); + return eWorldSaveLoad_Failed; + } + } + + unsigned int saveSize = StorageManager.GetSaveSize(); + if (saveSize == 0) + { + // Treat zero-byte payload as failure even when load API reports success + LogWorldIO("loaded save has zero size"); + return eWorldSaveLoad_Failed; + } + + byteArray loadedSaveData(saveSize, false); + unsigned int loadedSize = saveSize; + StorageManager.GetSaveData(loadedSaveData.data, &loadedSize); + if (loadedSize == 0) + { + LogWorldIO("failed to copy loaded save data from storage manager"); + return eWorldSaveLoad_Failed; + } + + *outSaveData = new LoadSaveDataThreadParam(loadedSaveData.data, loadedSize, matchedTitle); + LogWorldIO("prepared save data payload for server startup"); + return eWorldSaveLoad_Loaded; +} + +/** + * **Bootstrap World State For Server Startup** + * + * Determines final world startup state + * - Returns loaded save data when an existing save is found + * - Prepares a new world context when not found + * - Returns `Failed` when startup should be aborted + * サーバー起動時のワールド確定処理 + */ +WorldBootstrapResult BootstrapWorldForServer( + const ServerPropertiesConfig &config, + int actionPad, + WorldManagerTickProc tickProc) +{ + WorldBootstrapResult result; + if (!EnsureGameHddRootExists()) + { + LogWorldIO("failed to prepare Windows64\\GameHDD storage root"); + return result; + } + + std::wstring targetWorldName = config.worldName; + std::string targetSaveFilename = config.worldSaveId; + if (targetWorldName.empty()) + { + targetWorldName = L"world"; + } + + LogWorldName("configured level-name", targetWorldName); + if (!targetSaveFilename.empty()) + { + LogSaveFilename("configured level-id", targetSaveFilename); + } + + ApplyWorldStorageTarget(targetWorldName, targetSaveFilename); + + std::string loadedSaveFilename; + EWorldSaveLoadResult worldLoadResult = PrepareWorldSaveData( + targetWorldName, + targetSaveFilename, + actionPad, + tickProc, + &result.saveData, + &loadedSaveFilename); + if (worldLoadResult == eWorldSaveLoad_Loaded) + { + result.status = eWorldBootstrap_Loaded; + result.resolvedSaveId = loadedSaveFilename; + LogStartupStep("loading configured world from save data"); + } + else if (worldLoadResult == eWorldSaveLoad_NotFound) + { + // Create a new context only when no matching save exists + // Fix saveId here so the next startup writes to the same location + result.status = eWorldBootstrap_CreatedNew; + result.resolvedSaveId = targetSaveFilename; + LogStartupStep("configured world not found; creating new world"); + LogWorldIO("creating new world save context"); + StorageManager.ResetSaveData(); + ApplyWorldStorageTarget(targetWorldName, targetSaveFilename); + } + else + { + result.status = eWorldBootstrap_Failed; + } + + return result; +} + +/** + * **Wait Until Server XUI Action Is Idle** + * + * Keeps tick/handle running during save action so async processing does not stall + * XUIアクション待機中の進行維持処理 + */ +bool WaitForWorldActionIdle( + int actionPad, + DWORD timeoutMs, + WorldManagerTickProc tickProc, + WorldManagerHandleActionsProc handleActionsProc) +{ + DWORD start = GetTickCount(); + while (app.GetXuiServerAction(actionPad) != eXuiServerAction_Idle && !MinecraftServer::serverHalted()) + { + // Keep network and storage progressing while waiting + // If this stops, save action itself may stall and time out + if (tickProc != NULL) + { + tickProc(); + } + if (handleActionsProc != NULL) + { + handleActionsProc(); + } + if ((GetTickCount() - start) >= timeoutMs) + { + return false; + } + Sleep(10); + } + + return (app.GetXuiServerAction(actionPad) == eXuiServerAction_Idle); +} +} + diff --git a/Minecraft.Server/WorldManager.h b/Minecraft.Server/WorldManager.h new file mode 100644 index 00000000..a4a8e77b --- /dev/null +++ b/Minecraft.Server/WorldManager.h @@ -0,0 +1,92 @@ +#pragma once + +#include +#include + +#include "ServerProperties.h" + +struct _LoadSaveDataThreadParam; +typedef struct _LoadSaveDataThreadParam LoadSaveDataThreadParam; + +namespace ServerRuntime +{ + /** Tick callback used while waiting on async storage/network work */ + typedef void (*WorldManagerTickProc)(); + /** Optional action handler used while waiting for server actions */ + typedef void (*WorldManagerHandleActionsProc)(); + + /** + * **World Bootstrap Status** + * + * Result type for world startup preparation, either loading an existing world or creating a new one + * ワールド起動準備の結果種別 + */ + enum EWorldBootstrapStatus + { + /** Found and loaded an existing world */ + eWorldBootstrap_Loaded, + /** No matching save was found, created a new world context */ + eWorldBootstrap_CreatedNew, + /** Bootstrap failed and server startup should be aborted */ + eWorldBootstrap_Failed + }; + + /** + * **World Bootstrap Result** + * + * Output payload returned by world startup preparation + * ワールド起動準備の出力データ + */ + struct WorldBootstrapResult + { + /** Bootstrap status */ + EWorldBootstrapStatus status; + /** Save data used for server initialization, `NULL` when creating a new world */ + LoadSaveDataThreadParam *saveData; + /** Save ID that was actually selected */ + std::string resolvedSaveId; + + WorldBootstrapResult() + : status(eWorldBootstrap_Failed) + , saveData(NULL) + { + } + }; + + /** + * **Bootstrap Target World For Server Startup** + * + * Resolves whether the target world should be loaded from an existing save or created as new + * - Applies `level-name` and `level-id` from `server.properties` + * - Loads when a matching save exists + * - Creates a new world context only when no save matches + * サーバー起動時のロードか新規作成かを確定する + * + * @param config Normalized `server.properties` values + * @param actionPad padId used by async storage APIs + * @param tickProc Tick callback run while waiting for async completion + * @return Bootstrap result including whether save data was loaded + */ + WorldBootstrapResult BootstrapWorldForServer( + const ServerPropertiesConfig &config, + int actionPad, + WorldManagerTickProc tickProc); + + /** + * **Wait Until Server Action Returns To Idle** + * + * Waits until server action state reaches `Idle` + * サーバーアクションの待機処理 + * + * @param actionPad padId to monitor + * @param timeoutMs Timeout in milliseconds + * @param tickProc Tick callback run inside the wait loop + * @param handleActionsProc Optional action handler callback + * @return `true` when `Idle` is reached before timeout + */ + bool WaitForWorldActionIdle( + int actionPad, + DWORD timeoutMs, + WorldManagerTickProc tickProc, + WorldManagerHandleActionsProc handleActionsProc); +} diff --git a/Minecraft.Server/docs/DEVELOPMENT.en.md b/Minecraft.Server/docs/DEVELOPMENT.en.md new file mode 100644 index 00000000..5348f020 --- /dev/null +++ b/Minecraft.Server/docs/DEVELOPMENT.en.md @@ -0,0 +1,284 @@ +# Minecraft.Server Developer Guide (English) + +This document is for contributors who are new to `Minecraft.Server` and need a practical map for adding or modifying features safely. + +## 1. What This Server Does + +`Minecraft.Server` is the dedicated-server executable entry for this codebase. + +Core responsibilities: +- Switch the process working directory to the executable folder before relative file I/O +- Load, normalize, and repair `server.properties` +- Initialize dedicated runtime systems, connection logging, and access control +- Load or create the target world and keep `level-id` aligned with the actual save destination +- Run the dedicated main loop (network tick, XUI actions, autosave, CLI input) +- Maintain operator-facing access files such as `banned-players.json` and `banned-ips.json` +- Perform an initial save for newly created worlds and then shut down safely + +## 2. Important Files + +### Startup and Runtime +- `Windows64/ServerMain.cpp` + - `PrintUsage()` and `ParseCommandLine()` + - `SetExeWorkingDirectory()` + - Runtime setup and shutdown flow + - Initial save path for newly created worlds + - Main loop, autosave scheduler, and CLI polling + +### World Selection and Save Load +- `WorldManager.h` +- `WorldManager.cpp` + - Finds matching save by `level-id` first, then world-name fallback + - Applies storage title + save ID consistently + - Wait helpers for async storage/server action completion + +### Server Properties +- `ServerProperties.h` +- `ServerProperties.cpp` + - Default values and normalization ranges + - Parse/repair/write `server.properties` + - Exposes `ServerPropertiesConfig` + - `SaveServerPropertiesConfig()` rewrites `level-name`, `level-id`, and `white-list` + +### Access Control, Ban, and Whitelist Storage +- `Access/Access.h` +- `Access/Access.cpp` + - Process-wide access-control facade + - Published snapshot model used by console commands and login checks +- `Access/BanManager.h` +- `Access/BanManager.cpp` + - Reads/writes `banned-players.json` and `banned-ips.json` + - Normalizes identifiers and filters expired entries from snapshots +- `Access/WhitelistManager.h` +- `Access/WhitelistManager.cpp` + - Reads/writes `whitelist.json` + - Normalizes XUID-based whitelist entries used by login validation and CLI commands + +### Logging and Connection Audit +- `ServerLogger.h` +- `ServerLogger.cpp` + - Log level parsing + - Colored/timestamped console logs + - General categories such as `startup`, `world-io`, `console`, `access`, `network`, and `shutdown` +- `ServerLogManager.h` +- `ServerLogManager.cpp` + - Accepted/rejected TCP connection logs + - Login/disconnect audit logs + - Remote-IP cache used by `ban-ip ` + +### Console Command System +- `Console/ServerCli.cpp` (facade) +- `Console/ServerCliInput.cpp` (linenoise input thread + completion bridge) +- `Console/ServerCliParser.cpp` (tokenization, quoted args, completion context) +- `Console/ServerCliEngine.cpp` (dispatch, completion, helpers) +- `Console/ServerCliRegistry.cpp` (command registration + lookup) +- `Console/commands/*` (individual commands) + +## 3. End-to-End Startup Flow + +Main flow in `Windows64/ServerMain.cpp`: +1. `SetExeWorkingDirectory()` switches the current directory to the executable folder. +2. Load and normalize `server.properties` via `LoadServerPropertiesConfig()`. +3. Copy config into `DedicatedServerConfig`, then apply CLI overrides (`-port`, `-ip`/`-bind`, `-name`, `-maxplayers`, `-seed`, `-loglevel`, `-help`/`--help`/`-h`). +4. Initialize process state, `ServerLogManager`, and `Access::Initialize(".")`. +5. Initialize window/device/profile/network/thread-local systems. +6. Set host/game options from `ServerPropertiesConfig`. +7. Bootstrap world with `BootstrapWorldForServer(...)`. +8. If world bootstrap resolves a different normalized save ID, persist it with `SaveServerPropertiesConfig()`. +9. Start hosted game thread (`RunNetworkGameThreadProc`). +10. If a brand-new world was created, explicitly request one initial save. +11. Enter the main loop: + - `TickCoreSystems()` + - `HandleXuiActions()` + - `serverCli.Poll()` + - autosave scheduling +12. On shutdown: + - stop CLI input + - request save-on-exit / halt server + - wait for network shutdown completion + - terminate log, access, network, and device systems + +## 4. Current Operator Surface + +### 4.1 Launch Arguments +- `-port <1-65535>` +- `-ip ` or `-bind ` +- `-name ` (runtime max 16 chars) +- `-maxplayers <1-8>` +- `-seed ` +- `-loglevel ` +- `-help`, `--help`, `-h` + +Notes: +- CLI overrides affect only the current process. +- The only values currently written back by the server are `level-name` and `level-id`, and that happens when world bootstrap resolves identity changes. + +### 4.2 Built-in Console Commands +- `help` / `?` +- `stop` +- `list` +- `ban [reason ...]` + - currently requires the target player to be online +- `ban-ip [reason ...]` + - accepts a literal IPv4/IPv6 address or an online player's current remote IP +- `pardon ` +- `pardon-ip
` + - only accepts a literal address +- `banlist` +- `tp ` / `teleport` +- `gamemode [player]` / `gm` + +CLI behavior notes: +- Command parsing accepts both `cmd` and `/cmd`. +- Quoted arguments are supported by `ServerCliParser`. +- Completion is implemented per command via `Complete(...)`. + +### 4.3 Files Written Next to the Executable +- `server.properties` +- `banned-players.json` +- `banned-ips.json` + +This follows from `SetExeWorkingDirectory()`, so these files are resolved relative to `Minecraft.Server.exe`, not the shell directory you launched from. + +## 5. Common Development Tasks + +### 5.1 Add a New CLI Command + +Use this pattern when adding commands like `/kick`, `/time`, etc. + +1. Add files under `Console/commands/` + - `CliCommandYourCommand.h` + - `CliCommandYourCommand.cpp` +2. Implement `IServerCliCommand` + - `Name()`, `Usage()`, `Description()`, `Execute(...)` + - optional: `Aliases()` and `Complete(...)` +3. Register the command in `ServerCliEngine::RegisterDefaultCommands()`. +4. Add source/header to build definitions: + - `CMakeLists.txt` (`MINECRAFT_SERVER_SOURCES`) + - `Minecraft.Server/Minecraft.Server.vcxproj` (`` / ``) +5. Manual verify: + - command appears in `help` + - command executes correctly + - completion works for both `cmd` and `/cmd` + - quoted arguments behave as expected + +Implementation references: +- `CliCommandHelp.cpp` for a simple no-arg command +- `CliCommandTp.cpp` for multi-arg + completion + runtime checks +- `CliCommandGamemode.cpp` for argument parsing and aliases +- `CliCommandBanIp.cpp` for access-backed behavior with connection metadata + +### 5.2 Add or Change a `server.properties` Key + +1. Add/update the field in `ServerPropertiesConfig` (`ServerProperties.h`). +2. Add a default entry to `kServerPropertyDefaults` (`ServerProperties.cpp`). +3. Load and normalize the value in `LoadServerPropertiesConfig()`. + - Use existing helpers for bool/int/string/int64/log level/level type. +4. If this value should be written back, update `SaveServerPropertiesConfig()`. + - Note: today that function intentionally only persists world identity. +5. Apply it to runtime where needed: + - `ApplyServerPropertiesToDedicatedConfig(...)` + - host options in `ServerMain.cpp` (`app.SetGameHostOption(...)`) + - `PrintUsage()` / `ParseCommandLine()` if the key also gets a CLI override +6. Manual verify: + - missing key regeneration + - invalid value normalization + - clamped ranges still make sense + - runtime behavior reflects the new value + +Normalization details worth remembering: +- `level-id` is normalized to a safe save ID and length-limited. +- `server-name` is capped to 16 runtime chars. +- `max-players` is clamped to `1..8`. +- `autosave-interval` is clamped to `5..3600`. +- `level-type` normalizes to `default` or `flat`. + +### 5.3 Change Ban / Access Behavior + +Primary code lives in `Access/Access.cpp`, `Access/BanManager.cpp`, and `ServerLogManager.cpp`. + +When changing this area: +- Keep `BanManager` responsible for storage/caching, not live-network policy. +- Keep the clone-and-publish snapshot pattern in `Access.cpp` so readers never block on disk I/O. +- Remember that `ban-ip ` depends on `ServerLogManager::TryGetConnectionRemoteIp(...)`. +- Keep expired entries out of `SnapshotBannedPlayers()` / `SnapshotBannedIps()` output. +- Verify: + - clean boot creates empty ban files when missing + - `ban`, `ban-ip`, `pardon`, `pardon-ip`, and `banlist` still work + - online bans disconnect live targets immediately + - manual edits still reload safely if you later add or extend reload paths + +### 5.4 Change World Load/Create Behavior + +Primary code is in `WorldManager.cpp`. + +Current matching policy: +1. Match by `level-id` (`UTF8SaveFilename`) first. +2. Fall back to world-name match on title/file name. + +When changing this logic: +- Keep `ApplyWorldStorageTarget(...)` usage consistent (title + save ID together). +- Preserve periodic ticking in wait loops (`tickProc`) to avoid async deadlocks. +- Keep timeout/error logs specific enough for diagnosis. +- Verify: + - existing world is reused correctly + - no accidental new save directory creation + - shutdown save still succeeds + - newly created worlds still get the explicit initial save from `ServerMain.cpp` + +### 5.5 Add Logging for New Feature Work + +Use `ServerLogger` helpers: +- `LogDebug`, `LogInfo`, `LogWarn`, `LogError` +- formatted variants `LogDebugf`, `LogInfof`, etc. + +Use `ServerLogManager` when the event is specifically part of the transport/login/disconnect lifecycle. + +Recommended categories: +- `startup` for init/shutdown lifecycle +- `world-io` for save/world operations +- `console` for CLI command handling +- `access` for ban/access control state +- `network` for connection/login audit + +## 6. Build and Run + +From repository root: + +```powershell +cmake -S . -B build -G "Visual Studio 17 2022" -A x64 +cmake --build build --config Debug --target MinecraftServer +cd .\build\Debug +.\Minecraft.Server.exe -port 25565 -bind 0.0.0.0 -maxplayers 8 -name DedicatedServer +``` + +Notes: +- The process switches its working directory to the executable directory at startup. +- `server.properties`, `banned-players.json`, and `banned-ips.json` are therefore read/written next to the executable. +- For Visual Studio workflow, see root `COMPILE.md`. + +## 7. Safety Checklist Before Commit + +- the server starts without crash when `server.properties` is missing or sparse +- missing access files are recreated on a clean boot +- existing world loads by expected `level-id` +- new world creation still performs the explicit initial save +- CLI input and completion remain responsive +- `banlist` output stays sane after adding/removing bans +- no busy-wait path removed from async wait loops +- both CMake and `.vcxproj` include newly added source files + +## 8. Quick Troubleshooting + +- Unknown command: + - check `RegisterDefaultCommands()` and build-file entries +- `server.properties` or ban files seem to load from the wrong folder: + - remember `SetExeWorkingDirectory()` moves the working directory to the executable folder +- Autosave or shutdown save timing out: + - confirm wait loops still call `TickCoreSystems()` and `HandleXuiActions()` where required +- World not reused on restart: + - inspect `level-id` normalization and matching logic in `WorldManager.cpp` +- `ban-ip ` cannot resolve an address: + - confirm the player is currently online and `ServerLogManager` has a cached remote IP for that connection +- Settings not applied: + - confirm the value is loaded into `ServerPropertiesConfig`, optionally copied into `DedicatedServerConfig`, and then applied in `ServerMain.cpp` diff --git a/Minecraft.Server/docs/DEVELOPMENT.ja.md b/Minecraft.Server/docs/DEVELOPMENT.ja.md new file mode 100644 index 00000000..70d05aaa --- /dev/null +++ b/Minecraft.Server/docs/DEVELOPMENT.ja.md @@ -0,0 +1,286 @@ +# Minecraft.Server 開発ガイド (日本語) + +この文書は、`Minecraft.Server` に新しく入る開発者が、安全に機能追加や改修を行うための実践的な地図として使うことを想定しています + +## 1. このサーバーが担うこと + +`Minecraft.Server` は、このコードベースにおける専用サーバー実行ファイルのエントリーポイントです + +主な責務: +- 相対パスのファイル I/O を行う前に、カレントディレクトリを実行ファイルのあるフォルダへ切り替える +- `server.properties` を読み込み、正規化し、不足や不正値を補完する +- 専用サーバー向けランタイム、接続ログ、アクセス制御を初期化する +- 対象ワールドをロードまたは新規作成し、実際のセーブ先と `level-id` を整合させる +- 専用サーバーのメインループを回す (network tick, XUI actions, autosave, CLI input) +- `banned-players.json` や `banned-ips.json` など運用向けファイルを維持する +- 新規ワールドの初回保存を実行し、その後安全にシャットダウンする + +## 2. 重要ファイル + +### 起動とランタイム +- `Windows64/ServerMain.cpp` + - `PrintUsage()` と `ParseCommandLine()` + - `SetExeWorkingDirectory()` + - 起動/終了フロー + - 新規ワールド初回保存の経路 + - メインループ、オートセーブ、CLI ポーリング + +### ワールド選択とセーブ読込 +- `WorldManager.h` +- `WorldManager.cpp` + - `level-id` 優先、その後 world 名フォールバックでセーブ探索 + - storage title と save ID を常にセットで適用 + - 非同期 storage/server action 完了待ちの helper を提供 + +### サーバー設定 +- `ServerProperties.h` +- `ServerProperties.cpp` + - 既定値と正規化レンジ + - `server.properties` の読込/補修/書込 + - `ServerPropertiesConfig` の提供 + - `SaveServerPropertiesConfig()` は `level-name` / `level-id` / `white-list` を書き換える + +### アクセス制御と BAN / Whitelist 永続化 +- `Access/Access.h` +- `Access/Access.cpp` + - プロセス全体で使うアクセス制御 facade + - コンソールコマンドとログイン判定から参照される公開スナップショット管理 +- `Access/BanManager.h` +- `Access/BanManager.cpp` + - `banned-players.json` と `banned-ips.json` の読込/書込 + - 識別子の正規化と、期限切れエントリを除いた snapshot 出力 +- `Access/WhitelistManager.h` +- `Access/WhitelistManager.cpp` + - `whitelist.json` の読込/書込 + - ログイン判定と CLI で使う XUID whitelist の正規化管理 + +### ログと接続監査 +- `ServerLogger.h` +- `ServerLogger.cpp` + - ログレベル解釈 + - 色付き/タイムスタンプ付きコンソールログ + - `startup`, `world-io`, `console`, `access`, `network`, `shutdown` などのカテゴリ +- `ServerLogManager.h` +- `ServerLogManager.cpp` + - TCP 接続 accept/reject ログ + - ログイン/切断の監査ログ + - `ban-ip ` が使う remote IP キャッシュ + +### コンソールコマンドシステム +- `Console/ServerCli.cpp` (facade) +- `Console/ServerCliInput.cpp` (linenoise 入力スレッド + completion bridge) +- `Console/ServerCliParser.cpp` (トークン分解、クォート、補完コンテキスト) +- `Console/ServerCliEngine.cpp` (実行ディスパッチ、補完、共通ヘルパー) +- `Console/ServerCliRegistry.cpp` (登録と名前解決) +- `Console/commands/*` (各コマンド実装) + +## 3. 起動フロー全体 + +`Windows64/ServerMain.cpp` の主な流れ: +1. `SetExeWorkingDirectory()` でカレントディレクトリを実行ファイルのフォルダへ切り替える +2. `LoadServerPropertiesConfig()` で `server.properties` を読み込み、正規化する +3. `DedicatedServerConfig` へ反映したあと、CLI 引数で上書きする (`-port`, `-ip`/`-bind`, `-name`, `-maxplayers`, `-seed`, `-loglevel`, `-help`/`--help`/`-h`) +4. プロセス状態、`ServerLogManager`、`Access::Initialize(".")` を初期化する +5. window/device/profile/network/thread-local 系を初期化する +6. `ServerPropertiesConfig` をゲームホスト設定へ反映する +7. `BootstrapWorldForServer(...)` でワールドを決定する +8. 読み込まれたセーブ ID が正規化後に変わった場合は、`SaveServerPropertiesConfig()` で書き戻す +9. `RunNetworkGameThreadProc` でホストゲームスレッドを起動する +10. 新規ワールドが作成された場合は、専用サーバー側で明示的に初回保存を要求する +11. メインループに入る: + - `TickCoreSystems()` + - `HandleXuiActions()` + - `serverCli.Poll()` + - オートセーブスケジュール +12. 終了時: + - CLI 入力を停止 + - save-on-exit を要求してサーバー停止 + - ネットワーク停止完了を待機 + - ログ/アクセス制御/ネットワーク/デバイスを終了 + +## 4. 現在の運用インターフェース + +### 4.1 起動引数 +- `-port <1-65535>` +- `-ip ` または `-bind ` +- `-name ` (実行時上限 16 文字) +- `-maxplayers <1-8>` +- `-seed ` +- `-loglevel ` +- `-help`, `--help`, `-h` + +補足: +- CLI による上書きは、その起動中のプロセスにだけ効きます +- 現在サーバーが書き戻す値は `level-name` と `level-id` だけで、ワールド解決時に識別情報が変わった場合に限られます + +### 4.2 組み込みコンソールコマンド +- `help` / `?` +- `stop` +- `list` +- `ban [reason ...]` + - 現状では対象プレイヤーがオンラインである必要があります +- `ban-ip [reason ...]` + - リテラル IPv4/IPv6 か、オンラインプレイヤーの現在 IP を対象にできます +- `pardon ` +- `pardon-ip
` + - リテラルアドレスのみ受け付けます +- `banlist` +- `tp ` / `teleport` +- `gamemode [player]` / `gm` + +CLI 挙動の補足: +- `cmd` と `/cmd` の両方を受け付けます +- `ServerCliParser` により引用符付き引数を扱えます +- 補完は各コマンドの `Complete(...)` で実装します + +### 4.3 実行ファイル横に書かれるファイル +- `server.properties` +- `banned-players.json` +- `banned-ips.json` + +これは `SetExeWorkingDirectory()` による挙動ですつまり、これらのファイルはシェル上の起動場所ではなく `Minecraft.Server.exe` 基準で解決されます + +## 5. よくある開発作業 + +### 5.1 CLI コマンドを追加する + +`/kick` や `/time` のようなコマンド追加時の基本手順: + +1. `Console/commands/` にファイルを追加 + - `CliCommandYourCommand.h` + - `CliCommandYourCommand.cpp` +2. `IServerCliCommand` を実装 + - `Name()`, `Usage()`, `Description()`, `Execute(...)` + - 必要なら `Aliases()` と `Complete(...)` +3. `ServerCliEngine::RegisterDefaultCommands()` に登録する +4. ビルド定義に追加する + - `CMakeLists.txt` (`MINECRAFT_SERVER_SOURCES`) + - `Minecraft.Server/Minecraft.Server.vcxproj` (`` / ``) +5. 手動確認 + - `help` に表示される + - 実行結果が期待通り + - 補完が `cmd` と `/cmd` の両方で動く + - 引用符付き引数が期待通り処理される + +参考実装: +- `CliCommandHelp.cpp` (単純コマンド) +- `CliCommandTp.cpp` (複数引数 + 補完 + 実行時チェック) +- `CliCommandGamemode.cpp` (引数解釈 + エイリアス) +- `CliCommandBanIp.cpp` (接続メタデータを使うアクセス制御系コマンド) + +### 5.2 `server.properties` キーを追加/変更する + +1. `ServerProperties.h` の `ServerPropertiesConfig` にフィールドを追加/更新する +2. `ServerProperties.cpp` の `kServerPropertyDefaults` に既定値を追加する +3. `LoadServerPropertiesConfig()` で読み込みと正規化を実装する + - bool/int/string/int64/log level/level type 用の既存 helper を使う +4. 書き戻し対象にしたいなら `SaveServerPropertiesConfig()` を更新する + - ただし現状この関数は、意図的にワールド識別情報だけを永続化します +5. 実行時反映箇所を更新する: + - `ApplyServerPropertiesToDedicatedConfig(...)` + - `ServerMain.cpp` の `app.SetGameHostOption(...)` + - CLI 上書きも持たせるなら `PrintUsage()` / `ParseCommandLine()` +6. 手動確認: + - 欠損キーの自動補完 + - 不正値の正規化 + - clamp 範囲が妥当か + - 実行時挙動に反映されるか + +> 覚えておくと良い正規化ポイント +- `level-id` は安全な save ID に正規化され、長さ制限も掛かる +- `server-name` は実行時 16 文字まで +- `max-players` は `1..8` に clamp される(あとで増やす必要あり) +- `autosave-interval` は `5..3600` に clamp される +- `level-type` は `default` または `flat` に正規化される + +### 5.3 BAN / アクセス制御挙動を変更する + +主な実装は `Access/Access.cpp`, `Access/BanManager.cpp`, `ServerLogManager.cpp` にあります + +変更時の注意: +- `BanManager` は storage/caching に責務を寄せ、 live-network policy を持ち込みすぎない +- `Access.cpp` の clone-and-publish スナップショット方式を保ち、読取側がディスク I/O で止まらないようにする +- `ban-ip ` は `ServerLogManager::TryGetConnectionRemoteIp(...)` に依存することを忘れない +- `SnapshotBannedPlayers()` / `SnapshotBannedIps()` には期限切れエントリを混ぜない +- 確認項目: + - 欠損時に空の BAN ファイルが初回起動で生成される + - `ban`, `ban-ip`, `pardon`, `pardon-ip`, `banlist` が動く + - オンライン対象の BAN が即時切断まで到達する + - 将来 reload 経路を増やしても手動編集が安全に再読込できる + +### 5.4 ワールドロード/新規作成ロジックを変更する + +主な実装は `WorldManager.cpp` にあります + +現在の探索ポリシー: +1. `level-id` (`UTF8SaveFilename`) 完全一致を優先 +2. 失敗時に world 名一致へフォールバック + +変更時の注意: +- `ApplyWorldStorageTarget(...)` で title と save ID を常にセットで扱う +- 待機ループで `tickProc` を回し続ける + - これを止めると非同期進行が止まり、タイムアウトしやすくなる +- タイムアウトや失敗ログは具体的に残す +- 確認項目 + - 既存ワールドを正しく再利用できるか + - 意図しない新規セーブ先が増えていないか + - 終了時保存が成功するか + - 新規ワールド時の明示的初回保存が `ServerMain.cpp` から維持されているか + +### 5.5 ログを追加する + +`ServerLogger` の API を利用: +- `LogDebug`, `LogInfo`, `LogWarn`, `LogError` +- フォーマット付きは `LogDebugf`, `LogInfof` など + +transport/login/disconnect ライフサイクルに属するイベントなら `ServerLogManager` 側を使います + +推奨カテゴリ: +- `startup`: 起動ライフサイクル +- `shutdown`: 停止ライフサイクル +- `world-io`: ワールド/保存処理 +- `console`: CLI コマンド処理 +- `access`: BAN/アクセス制御状態 +- `network`: 接続/ログイン監査 + +## 6. ビルドと実行 + +リポジトリルートで実行: + +```powershell +cmake -S . -B build -G "Visual Studio 17 2022" -A x64 +cmake --build build --config Debug --target MinecraftServer +cd .\build\Debug +.\Minecraft.Server.exe -port 25565 -bind 0.0.0.0 -name DedicatedServer +``` + +補足: +- プロセスは起動時にカレントディレクトリを実行ファイルの場所へ切り替えます +- `server.properties`, `banned-players.json`, `banned-ips.json` はそのため実行ファイル横に読み書きされます +- Visual Studio ワークフローはルートの `COMPILE.md` を参照してください + +## 7. 変更前チェックリスト + +- `server.properties` が欠損または疎でもクラッシュせず起動できる +- 欠損したアクセス制御ファイルがクリーンブート時に再生成される +- 既存ワールドが期待した `level-id` でロードされる +- 新規ワールド作成時の明示的初回保存が維持される +- CLI 入力と補完が引き続き応答する +- `banlist` 出力が BAN 追加/解除後も破綻しない +- 非同期待機ループから `TickCoreSystems()` など busy-wait 防止用ティックを消していない +- 新規追加したソースが CMake と `.vcxproj` の両方に入っている + +## 8. クイックトラブルシュート + +- コマンドが認識されない: + - `RegisterDefaultCommands()` とビルド定義を確認する +- `server.properties` や BAN ファイルの読込先が想定と違う: + - `SetExeWorkingDirectory()` により実行ファイルのフォルダへ移動していることを確認する +- オートセーブ/終了時保存がタイムアウトする: + - 待機ループ内で `TickCoreSystems()` と `HandleXuiActions()` を回しているか確認する +- 再起動時に同じワールドを使わない: + - `level-id` の正規化と `WorldManager.cpp` の一致判定を確認する +- `ban-ip ` で IP を解決できない: + - 対象プレイヤーがオンラインで、`ServerLogManager` に接続 IP がキャッシュされているか確認する +- 設定変更が効かない: + - 値が `ServerPropertiesConfig` にロードされ、必要なら `DedicatedServerConfig` にコピーされ、その後 `ServerMain.cpp` で反映されているか確認する diff --git a/Minecraft.Server/stdafx.cpp b/Minecraft.Server/stdafx.cpp new file mode 100644 index 00000000..4eed7b3d --- /dev/null +++ b/Minecraft.Server/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// Minecraft.Server.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/Minecraft.Server/vendor/linenoise/LICENSE b/Minecraft.Server/vendor/linenoise/LICENSE new file mode 100644 index 00000000..d7391566 --- /dev/null +++ b/Minecraft.Server/vendor/linenoise/LICENSE @@ -0,0 +1,25 @@ +This vendored component is based on the linenoise project idea/API. + +Copyright (c) 2010-2014, Salvatore Sanfilippo +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Minecraft.Server/vendor/linenoise/linenoise.c b/Minecraft.Server/vendor/linenoise/linenoise.c new file mode 100644 index 00000000..0f583755 --- /dev/null +++ b/Minecraft.Server/vendor/linenoise/linenoise.c @@ -0,0 +1,652 @@ +#include "linenoise.h" + +#include +#include +#include +#include +#include +#include + +#define LINENOISE_MAX_LINE 4096 +#define LINENOISE_MAX_PROMPT 128 + +typedef struct linenoiseHistory { + char **items; + int len; + int cap; + int maxLen; +} linenoiseHistory; + +static linenoiseCompletionCallback *g_completionCallback = NULL; +static volatile LONG g_stopRequested = 0; +static linenoiseHistory g_history = { NULL, 0, 0, 128 }; +/* Guards redraw/log interleaving so prompt and log lines do not overlap. */ +static CRITICAL_SECTION g_ioLock; +static volatile LONG g_ioLockState = 0; /* 0=not init, 1=init in progress, 2=ready */ +/* Snapshot of current editor line used to restore prompt after external output. */ +static volatile LONG g_editorActive = 0; +static char g_editorPrompt[LINENOISE_MAX_PROMPT] = { 0 }; +static char g_editorBuf[LINENOISE_MAX_LINE] = { 0 }; +static int g_editorLen = 0; +static int g_editorPos = 0; +static int g_editorPrevLen = 0; + +/** + * Lazily initialize the console I/O critical section. + * This avoids static init order issues and keeps startup cost minimal. + */ +static void linenoiseEnsureIoLockInit(void) +{ + LONG state = InterlockedCompareExchange(&g_ioLockState, 0, 0); + if (state == 2) + return; + + if (state == 0 && InterlockedCompareExchange(&g_ioLockState, 1, 0) == 0) + { + InitializeCriticalSection(&g_ioLock); + InterlockedExchange(&g_ioLockState, 2); + return; + } + + while (InterlockedCompareExchange(&g_ioLockState, 0, 0) != 2) + { + Sleep(0); + } +} + +static void linenoiseLockIo(void) +{ + linenoiseEnsureIoLockInit(); + EnterCriticalSection(&g_ioLock); +} + +static void linenoiseUnlockIo(void) +{ + LeaveCriticalSection(&g_ioLock); +} + +/** + * Save current prompt/buffer/cursor state for later redraw. + * Called after each redraw while editor is active. + */ +static void linenoiseUpdateEditorState(const char *prompt, const char *buf, int len, int pos, int prevLen) +{ + if (prompt == NULL) + prompt = ""; + if (buf == NULL) + buf = ""; + + strncpy_s(g_editorPrompt, sizeof(g_editorPrompt), prompt, _TRUNCATE); + strncpy_s(g_editorBuf, sizeof(g_editorBuf), buf, _TRUNCATE); + g_editorLen = len; + g_editorPos = pos; + g_editorPrevLen = prevLen; + InterlockedExchange(&g_editorActive, 1); +} + +static void linenoiseDeactivateEditorState(void) +{ + InterlockedExchange(&g_editorActive, 0); + g_editorPrompt[0] = 0; + g_editorBuf[0] = 0; + g_editorLen = 0; + g_editorPos = 0; + g_editorPrevLen = 0; +} + +static char *linenoiseStrdup(const char *src) +{ + size_t n = strlen(src) + 1; + char *out = (char *)malloc(n); + if (out == NULL) + return NULL; + memcpy(out, src, n); + return out; +} + +static void linenoiseEnsureHistoryCapacity(int wanted) +{ + int newCap; + char **newItems; + + if (wanted <= g_history.cap) + return; + + newCap = g_history.cap == 0 ? 32 : g_history.cap; + while (newCap < wanted) + newCap *= 2; + + newItems = (char **)realloc(g_history.items, sizeof(char *) * (size_t)newCap); + if (newItems == NULL) + return; + + g_history.items = newItems; + g_history.cap = newCap; +} + +static void linenoiseClearCompletions(linenoiseCompletions *lc) +{ + size_t i = 0; + for (i = 0; i < lc->len; ++i) + { + free(lc->cvec[i]); + } + free(lc->cvec); + lc->cvec = NULL; + lc->len = 0; +} + +void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) +{ + char **newVec = (char **)realloc(lc->cvec, sizeof(char *) * (lc->len + 1)); + if (newVec == NULL) + return; + + lc->cvec = newVec; + lc->cvec[lc->len] = linenoiseStrdup(str); + if (lc->cvec[lc->len] == NULL) + return; + + lc->len += 1; +} + +void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) +{ + g_completionCallback = fn; +} + +void linenoiseFree(void *ptr) +{ + free(ptr); +} + +int linenoiseHistorySetMaxLen(int len) +{ + if (len <= 0) + return 0; + + g_history.maxLen = len; + while (g_history.len > g_history.maxLen) + { + free(g_history.items[0]); + memmove(g_history.items, g_history.items + 1, sizeof(char *) * (size_t)(g_history.len - 1)); + g_history.len -= 1; + } + + return 1; +} + +int linenoiseHistoryAdd(const char *line) +{ + if (line == NULL || line[0] == 0) + return 0; + + if (g_history.len > 0) + { + const char *last = g_history.items[g_history.len - 1]; + if (last != NULL && strcmp(last, line) == 0) + return 1; + } + + linenoiseEnsureHistoryCapacity(g_history.len + 1); + if (g_history.cap <= g_history.len) + return 0; + + g_history.items[g_history.len] = linenoiseStrdup(line); + if (g_history.items[g_history.len] == NULL) + return 0; + + g_history.len += 1; + + while (g_history.len > g_history.maxLen) + { + free(g_history.items[0]); + memmove(g_history.items, g_history.items + 1, sizeof(char *) * (size_t)(g_history.len - 1)); + g_history.len -= 1; + } + + return 1; +} + +void linenoiseRequestStop(void) +{ + InterlockedExchange(&g_stopRequested, 1); +} + +void linenoiseResetStop(void) +{ + InterlockedExchange(&g_stopRequested, 0); +} + +static int linenoiseIsStopRequested(void) +{ + return InterlockedCompareExchange(&g_stopRequested, 0, 0) != 0; +} + +static void linenoiseWriteHint(const char *hint, size_t hintLen) +{ + HANDLE stdoutHandle; + CONSOLE_SCREEN_BUFFER_INFO originalInfo; + int hasColorConsole = 0; + + if (hint == NULL || hintLen == 0) + { + return; + } + + stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); + if (stdoutHandle != INVALID_HANDLE_VALUE && stdoutHandle != NULL) + { + if (GetConsoleScreenBufferInfo(stdoutHandle, &originalInfo)) + { + hasColorConsole = 1; + /* Draw predictive tail in dim gray, then restore original console colors. */ + SetConsoleTextAttribute(stdoutHandle, FOREGROUND_INTENSITY); + } + } + + fwrite(hint, 1, hintLen, stdout); + + if (hasColorConsole) + { + SetConsoleTextAttribute(stdoutHandle, originalInfo.wAttributes); + } +} + +static int linenoiseStartsWithIgnoreCase(const char *full, const char *prefix) +{ + while (*prefix != 0) + { + if (tolower((unsigned char)*full) != tolower((unsigned char)*prefix)) + { + return 0; + } + ++full; + ++prefix; + } + return 1; +} + +static size_t linenoiseBuildHint(const char *buf, char *hint, size_t hintSize) +{ + linenoiseCompletions lc; + size_t inputLen = 0; + size_t i = 0; + + if (hint == NULL || hintSize == 0) + { + return 0; + } + hint[0] = 0; + + if (buf == NULL || buf[0] == 0 || g_completionCallback == NULL) + { + return 0; + } + + lc.len = 0; + lc.cvec = NULL; + /* Reuse the completion callback and derive a "ghost text" suffix from the first extending match. */ + g_completionCallback(buf, &lc); + + inputLen = strlen(buf); + for (i = 0; i < lc.len; ++i) + { + const char *candidate = lc.cvec[i]; + if (candidate == NULL) + { + continue; + } + if (strlen(candidate) <= inputLen) + { + continue; + } + if (!linenoiseStartsWithIgnoreCase(candidate, buf)) + { + continue; + } + + /* Keep only the part not yet typed by the user (rendered as hint text). */ + strncpy_s(hint, hintSize, candidate + inputLen, _TRUNCATE); + break; + } + + linenoiseClearCompletions(&lc); + return strlen(hint); +} + +static void linenoiseRedrawUnsafe(const char *prompt, const char *buf, int len, int pos, int *prevLen) +{ + int i; + char hint[LINENOISE_MAX_LINE] = {0}; + int renderedLen = len; + /* Hint length contributes to the rendered width so stale tail characters can be cleared correctly. */ + int hintLen = (int)linenoiseBuildHint(buf, hint, sizeof(hint)); + if (hintLen > 0) + { + renderedLen += hintLen; + } + + fputc('\r', stdout); + fputs(prompt, stdout); + if (len > 0) + { + fwrite(buf, 1, (size_t)len, stdout); + } + if (hintLen > 0) + { + linenoiseWriteHint(hint, (size_t)hintLen); + } + + if (*prevLen > renderedLen) + { + for (i = renderedLen; i < *prevLen; ++i) + { + fputc(' ', stdout); + } + } + + fputc('\r', stdout); + fputs(prompt, stdout); + if (pos > 0) + { + /* Cursor positioning reflects only real user input, not the ghost hint suffix. */ + fwrite(buf, 1, (size_t)pos, stdout); + } + + fflush(stdout); + *prevLen = renderedLen; + linenoiseUpdateEditorState(prompt, buf, len, pos, *prevLen); +} + +static void linenoiseRedraw(const char *prompt, const char *buf, int len, int pos, int *prevLen) +{ + linenoiseLockIo(); + linenoiseRedrawUnsafe(prompt, buf, len, pos, prevLen); + linenoiseUnlockIo(); +} + +static int linenoiseStartsWith(const char *full, const char *prefix) +{ + while (*prefix != 0) + { + if (*full != *prefix) + return 0; + ++full; + ++prefix; + } + return 1; +} + +static int linenoiseComputeCommonPrefix(const linenoiseCompletions *lc, const char *seed, char *out, size_t outSize) +{ + size_t commonLen = 0; + size_t i; + + if (lc->len == 0 || outSize == 0) + return 0; + + strncpy_s(out, outSize, lc->cvec[0], _TRUNCATE); + commonLen = strlen(out); + + for (i = 1; i < lc->len; ++i) + { + const char *candidate = lc->cvec[i]; + size_t j = 0; + + while (j < commonLen && out[j] != 0 && candidate[j] != 0 && out[j] == candidate[j]) + ++j; + + commonLen = j; + out[commonLen] = 0; + + if (commonLen == 0) + break; + } + + if (strlen(out) <= strlen(seed)) + return 0; + + return linenoiseStartsWith(out, seed); +} + +static void linenoiseApplyCompletion(const char *prompt, char *buf, int *len, int *pos, int *prevLen) +{ + linenoiseCompletions lc; + int i; + + if (g_completionCallback == NULL) + { + Beep(750, 15); + return; + } + + lc.len = 0; + lc.cvec = NULL; + g_completionCallback(buf, &lc); + + if (lc.len == 0) + { + Beep(750, 15); + linenoiseClearCompletions(&lc); + return; + } + + if (lc.len == 1) + { + strncpy_s(buf, LINENOISE_MAX_LINE, lc.cvec[0], _TRUNCATE); + *len = (int)strlen(buf); + *pos = *len; + linenoiseRedraw(prompt, buf, *len, *pos, prevLen); + linenoiseClearCompletions(&lc); + return; + } + + { + char common[LINENOISE_MAX_LINE] = { 0 }; + if (linenoiseComputeCommonPrefix(&lc, buf, common, sizeof(common))) + { + strncpy_s(buf, LINENOISE_MAX_LINE, common, _TRUNCATE); + *len = (int)strlen(buf); + *pos = *len; + linenoiseRedraw(prompt, buf, *len, *pos, prevLen); + } + } + + linenoiseLockIo(); + fputc('\n', stdout); + for (i = 0; i < (int)lc.len; ++i) + { + fputs(lc.cvec[i], stdout); + fputs(" ", stdout); + } + fputc('\n', stdout); + linenoiseRedrawUnsafe(prompt, buf, *len, *pos, prevLen); + linenoiseUnlockIo(); + linenoiseClearCompletions(&lc); +} + +char *linenoise(const char *prompt) +{ + char buf[LINENOISE_MAX_LINE]; + int len = 0; + int pos = 0; + int prevLen = 0; + int historyIndex = g_history.len; + + if (prompt == NULL) + prompt = ""; + + buf[0] = 0; + linenoiseLockIo(); + linenoiseUpdateEditorState(prompt, buf, len, pos, prevLen); + fputs(prompt, stdout); + fflush(stdout); + linenoiseUnlockIo(); + + while (!linenoiseIsStopRequested()) + { + if (!_kbhit()) + { + Sleep(10); + continue; + } + + { + int c = _getwch(); + + if (c == 0 || c == 224) + { + int ext = _getwch(); + if (ext == 72) + { + if (g_history.len > 0 && historyIndex > 0) + { + historyIndex -= 1; + strncpy_s(buf, sizeof(buf), g_history.items[historyIndex], _TRUNCATE); + len = (int)strlen(buf); + pos = len; + linenoiseRedraw(prompt, buf, len, pos, &prevLen); + } + } + else if (ext == 80) + { + if (g_history.len > 0 && historyIndex < g_history.len) + { + historyIndex += 1; + if (historyIndex == g_history.len) + buf[0] = 0; + else + strncpy_s(buf, sizeof(buf), g_history.items[historyIndex], _TRUNCATE); + + len = (int)strlen(buf); + pos = len; + linenoiseRedraw(prompt, buf, len, pos, &prevLen); + } + } + else if (ext == 75) + { + if (pos > 0) + { + pos -= 1; + linenoiseRedraw(prompt, buf, len, pos, &prevLen); + } + } + else if (ext == 77) + { + if (pos < len) + { + pos += 1; + linenoiseRedraw(prompt, buf, len, pos, &prevLen); + } + } + continue; + } + + if (c == 3) + { + linenoiseLockIo(); + linenoiseDeactivateEditorState(); + fputc('\n', stdout); + fflush(stdout); + linenoiseUnlockIo(); + return NULL; + } + + if (c == '\r' || c == '\n') + { + char *out; + linenoiseLockIo(); + linenoiseDeactivateEditorState(); + fputc('\n', stdout); + fflush(stdout); + linenoiseUnlockIo(); + + out = linenoiseStrdup(buf); + return out; + } + + if (c == '\t') + { + linenoiseApplyCompletion(prompt, buf, &len, &pos, &prevLen); + continue; + } + + if (c == 8) + { + if (pos > 0 && len > 0) + { + memmove(buf + pos - 1, buf + pos, (size_t)(len - pos + 1)); + pos -= 1; + len -= 1; + linenoiseRedraw(prompt, buf, len, pos, &prevLen); + } + continue; + } + + if (isprint((unsigned char)c) && len < LINENOISE_MAX_LINE - 1) + { + if (pos == len) + { + buf[pos++] = (char)c; + len += 1; + buf[len] = 0; + } + else + { + memmove(buf + pos + 1, buf + pos, (size_t)(len - pos + 1)); + buf[pos] = (char)c; + pos += 1; + len += 1; + } + linenoiseRedraw(prompt, buf, len, pos, &prevLen); + } + } + } + + linenoiseLockIo(); + linenoiseDeactivateEditorState(); + fputc('\n', stdout); + fflush(stdout); + linenoiseUnlockIo(); + return NULL; +} + +void linenoiseExternalWriteBegin(void) +{ + int i; + int totalChars = 0; + + /* Lock shared console state and clear current prompt area before external output. */ + linenoiseLockIo(); + if (InterlockedCompareExchange(&g_editorActive, 0, 0) == 0) + { + return; + } + + totalChars = (int)strlen(g_editorPrompt) + g_editorPrevLen; + if (totalChars < 0) + { + totalChars = 0; + } + + fputc('\r', stdout); + for (i = 0; i < totalChars; ++i) + { + fputc(' ', stdout); + } + fputc('\r', stdout); + fflush(stdout); +} + +void linenoiseExternalWriteEnd(void) +{ + /* Restore prompt line after external output has been printed. */ + if (InterlockedCompareExchange(&g_editorActive, 0, 0) != 0) + { + int prevLen = g_editorPrevLen; + linenoiseRedrawUnsafe(g_editorPrompt, g_editorBuf, g_editorLen, g_editorPos, &prevLen); + g_editorPrevLen = prevLen; + } + linenoiseUnlockIo(); +} diff --git a/Minecraft.Server/vendor/linenoise/linenoise.h b/Minecraft.Server/vendor/linenoise/linenoise.h new file mode 100644 index 00000000..6f7a0d2b --- /dev/null +++ b/Minecraft.Server/vendor/linenoise/linenoise.h @@ -0,0 +1,37 @@ +#ifndef VENDORED_LINENOISE_H +#define VENDORED_LINENOISE_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct linenoiseCompletions { + size_t len; + char **cvec; +} linenoiseCompletions; + +typedef void(linenoiseCompletionCallback)(const char *buf, linenoiseCompletions *lc); + +char *linenoise(const char *prompt); +void linenoiseFree(void *ptr); + +void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn); +void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str); + +int linenoiseHistoryAdd(const char *line); +int linenoiseHistorySetMaxLen(int len); + +void linenoiseRequestStop(void); +void linenoiseResetStop(void); + +/* Wrap external stdout/stderr writes so active prompt can be cleared/restored safely. */ +void linenoiseExternalWriteBegin(void); +void linenoiseExternalWriteEnd(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Minecraft.Server/vendor/rapidjson/allocators.h b/Minecraft.Server/vendor/rapidjson/allocators.h new file mode 100644 index 00000000..275417bd --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/allocators.h @@ -0,0 +1,693 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ALLOCATORS_H_ +#define RAPIDJSON_ALLOCATORS_H_ + +#include "rapidjson.h" +#include "internal/meta.h" + +#include +#include + +#if RAPIDJSON_HAS_CXX11 +#include +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Allocator + +/*! \class rapidjson::Allocator + \brief Concept for allocating, resizing and freeing memory block. + + Note that Malloc() and Realloc() are non-static but Free() is static. + + So if an allocator need to support Free(), it needs to put its pointer in + the header of memory block. + +\code +concept Allocator { + static const bool kNeedFree; //!< Whether this allocator needs to call Free(). + + // Allocate a memory block. + // \param size of the memory block in bytes. + // \returns pointer to the memory block. + void* Malloc(size_t size); + + // Resize a memory block. + // \param originalPtr The pointer to current memory block. Null pointer is permitted. + // \param originalSize The current size in bytes. (Design issue: since some allocator may not book-keep this, explicitly pass to it can save memory.) + // \param newSize the new size in bytes. + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize); + + // Free a memory block. + // \param pointer to the memory block. Null pointer is permitted. + static void Free(void *ptr); +}; +\endcode +*/ + + +/*! \def RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User-defined kDefaultChunkCapacity definition. + + User can define this as any \c size that is a power of 2. +*/ + +#ifndef RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY +#define RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY (64 * 1024) +#endif + + +/////////////////////////////////////////////////////////////////////////////// +// CrtAllocator + +//! C-runtime library allocator. +/*! This class is just wrapper for standard C library memory routines. + \note implements Allocator concept +*/ +class CrtAllocator { +public: + static const bool kNeedFree = true; + void* Malloc(size_t size) { + if (size) // behavior of malloc(0) is implementation defined. + return RAPIDJSON_MALLOC(size); + else + return NULL; // standardize to returning NULL. + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + (void)originalSize; + if (newSize == 0) { + RAPIDJSON_FREE(originalPtr); + return NULL; + } + return RAPIDJSON_REALLOC(originalPtr, newSize); + } + static void Free(void *ptr) RAPIDJSON_NOEXCEPT { RAPIDJSON_FREE(ptr); } + + bool operator==(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { + return true; + } + bool operator!=(const CrtAllocator&) const RAPIDJSON_NOEXCEPT { + return false; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// MemoryPoolAllocator + +//! Default memory allocator used by the parser and DOM. +/*! This allocator allocate memory blocks from pre-allocated memory chunks. + + It does not free memory blocks. And Realloc() only allocate new memory. + + The memory chunks are allocated by BaseAllocator, which is CrtAllocator by default. + + User may also supply a buffer as the first chunk. + + If the user-buffer is full then additional chunks are allocated by BaseAllocator. + + The user-buffer is not deallocated by this allocator. + + \tparam BaseAllocator the allocator type for allocating memory chunks. Default is CrtAllocator. + \note implements Allocator concept +*/ +template +class MemoryPoolAllocator { + //! Chunk header for perpending to each chunk. + /*! Chunks are stored as a singly linked list. + */ + struct ChunkHeader { + size_t capacity; //!< Capacity of the chunk in bytes (excluding the header itself). + size_t size; //!< Current size of allocated memory in bytes. + ChunkHeader *next; //!< Next chunk in the linked list. + }; + + struct SharedData { + ChunkHeader *chunkHead; //!< Head of the chunk linked-list. Only the head chunk serves allocation. + BaseAllocator* ownBaseAllocator; //!< base allocator created by this object. + size_t refcount; + bool ownBuffer; + }; + + static const size_t SIZEOF_SHARED_DATA = RAPIDJSON_ALIGN(sizeof(SharedData)); + static const size_t SIZEOF_CHUNK_HEADER = RAPIDJSON_ALIGN(sizeof(ChunkHeader)); + + static inline ChunkHeader *GetChunkHead(SharedData *shared) + { + return reinterpret_cast(reinterpret_cast(shared) + SIZEOF_SHARED_DATA); + } + static inline uint8_t *GetChunkBuffer(SharedData *shared) + { + return reinterpret_cast(shared->chunkHead) + SIZEOF_CHUNK_HEADER; + } + + static const size_t kDefaultChunkCapacity = RAPIDJSON_ALLOCATOR_DEFAULT_CHUNK_CAPACITY; //!< Default chunk capacity. + +public: + static const bool kNeedFree = false; //!< Tell users that no need to call Free() with this allocator. (concept Allocator) + static const bool kRefCounted = true; //!< Tell users that this allocator is reference counted on copy + + //! Constructor with chunkSize. + /*! \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + explicit + MemoryPoolAllocator(size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunk_capacity_(chunkSize), + baseAllocator_(baseAllocator ? baseAllocator : RAPIDJSON_NEW(BaseAllocator)()), + shared_(static_cast(baseAllocator_ ? baseAllocator_->Malloc(SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER) : 0)) + { + RAPIDJSON_ASSERT(baseAllocator_ != 0); + RAPIDJSON_ASSERT(shared_ != 0); + if (baseAllocator) { + shared_->ownBaseAllocator = 0; + } + else { + shared_->ownBaseAllocator = baseAllocator_; + } + shared_->chunkHead = GetChunkHead(shared_); + shared_->chunkHead->capacity = 0; + shared_->chunkHead->size = 0; + shared_->chunkHead->next = 0; + shared_->ownBuffer = true; + shared_->refcount = 1; + } + + //! Constructor with user-supplied buffer. + /*! The user buffer will be used firstly. When it is full, memory pool allocates new chunk with chunk size. + + The user buffer will not be deallocated when this allocator is destructed. + + \param buffer User supplied buffer. + \param size Size of the buffer in bytes. It must at least larger than sizeof(ChunkHeader). + \param chunkSize The size of memory chunk. The default is kDefaultChunkSize. + \param baseAllocator The allocator for allocating memory chunks. + */ + MemoryPoolAllocator(void *buffer, size_t size, size_t chunkSize = kDefaultChunkCapacity, BaseAllocator* baseAllocator = 0) : + chunk_capacity_(chunkSize), + baseAllocator_(baseAllocator), + shared_(static_cast(AlignBuffer(buffer, size))) + { + RAPIDJSON_ASSERT(size >= SIZEOF_SHARED_DATA + SIZEOF_CHUNK_HEADER); + shared_->chunkHead = GetChunkHead(shared_); + shared_->chunkHead->capacity = size - SIZEOF_SHARED_DATA - SIZEOF_CHUNK_HEADER; + shared_->chunkHead->size = 0; + shared_->chunkHead->next = 0; + shared_->ownBaseAllocator = 0; + shared_->ownBuffer = false; + shared_->refcount = 1; + } + + MemoryPoolAllocator(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT : + chunk_capacity_(rhs.chunk_capacity_), + baseAllocator_(rhs.baseAllocator_), + shared_(rhs.shared_) + { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + ++shared_->refcount; + } + MemoryPoolAllocator& operator=(const MemoryPoolAllocator& rhs) RAPIDJSON_NOEXCEPT + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + ++rhs.shared_->refcount; + this->~MemoryPoolAllocator(); + baseAllocator_ = rhs.baseAllocator_; + chunk_capacity_ = rhs.chunk_capacity_; + shared_ = rhs.shared_; + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + MemoryPoolAllocator(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT : + chunk_capacity_(rhs.chunk_capacity_), + baseAllocator_(rhs.baseAllocator_), + shared_(rhs.shared_) + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + rhs.shared_ = 0; + } + MemoryPoolAllocator& operator=(MemoryPoolAllocator&& rhs) RAPIDJSON_NOEXCEPT + { + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + this->~MemoryPoolAllocator(); + baseAllocator_ = rhs.baseAllocator_; + chunk_capacity_ = rhs.chunk_capacity_; + shared_ = rhs.shared_; + rhs.shared_ = 0; + return *this; + } +#endif + + //! Destructor. + /*! This deallocates all memory chunks, excluding the user-supplied buffer. + */ + ~MemoryPoolAllocator() RAPIDJSON_NOEXCEPT { + if (!shared_) { + // do nothing if moved + return; + } + if (shared_->refcount > 1) { + --shared_->refcount; + return; + } + Clear(); + BaseAllocator *a = shared_->ownBaseAllocator; + if (shared_->ownBuffer) { + baseAllocator_->Free(shared_); + } + RAPIDJSON_DELETE(a); + } + + //! Deallocates all memory chunks, excluding the first/user one. + void Clear() RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + for (;;) { + ChunkHeader* c = shared_->chunkHead; + if (!c->next) { + break; + } + shared_->chunkHead = c->next; + baseAllocator_->Free(c); + } + shared_->chunkHead->size = 0; + } + + //! Computes the total capacity of allocated memory chunks. + /*! \return total capacity in bytes. + */ + size_t Capacity() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + size_t capacity = 0; + for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) + capacity += c->capacity; + return capacity; + } + + //! Computes the memory blocks allocated. + /*! \return total used bytes. + */ + size_t Size() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + size_t size = 0; + for (ChunkHeader* c = shared_->chunkHead; c != 0; c = c->next) + size += c->size; + return size; + } + + //! Whether the allocator is shared. + /*! \return true or false. + */ + bool Shared() const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + return shared_->refcount > 1; + } + + //! Allocates a memory block. (concept Allocator) + void* Malloc(size_t size) { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + if (!size) + return NULL; + + size = RAPIDJSON_ALIGN(size); + if (RAPIDJSON_UNLIKELY(shared_->chunkHead->size + size > shared_->chunkHead->capacity)) + if (!AddChunk(chunk_capacity_ > size ? chunk_capacity_ : size)) + return NULL; + + void *buffer = GetChunkBuffer(shared_) + shared_->chunkHead->size; + shared_->chunkHead->size += size; + return buffer; + } + + //! Resizes a memory block (concept Allocator) + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) { + if (originalPtr == 0) + return Malloc(newSize); + + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + if (newSize == 0) + return NULL; + + originalSize = RAPIDJSON_ALIGN(originalSize); + newSize = RAPIDJSON_ALIGN(newSize); + + // Do not shrink if new size is smaller than original + if (originalSize >= newSize) + return originalPtr; + + // Simply expand it if it is the last allocation and there is sufficient space + if (originalPtr == GetChunkBuffer(shared_) + shared_->chunkHead->size - originalSize) { + size_t increment = static_cast(newSize - originalSize); + if (shared_->chunkHead->size + increment <= shared_->chunkHead->capacity) { + shared_->chunkHead->size += increment; + return originalPtr; + } + } + + // Realloc process: allocate and copy memory, do not free original buffer. + if (void* newBuffer = Malloc(newSize)) { + if (originalSize) + std::memcpy(newBuffer, originalPtr, originalSize); + return newBuffer; + } + else + return NULL; + } + + //! Frees a memory block (concept Allocator) + static void Free(void *ptr) RAPIDJSON_NOEXCEPT { (void)ptr; } // Do nothing + + //! Compare (equality) with another MemoryPoolAllocator + bool operator==(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { + RAPIDJSON_NOEXCEPT_ASSERT(shared_->refcount > 0); + RAPIDJSON_NOEXCEPT_ASSERT(rhs.shared_->refcount > 0); + return shared_ == rhs.shared_; + } + //! Compare (inequality) with another MemoryPoolAllocator + bool operator!=(const MemoryPoolAllocator& rhs) const RAPIDJSON_NOEXCEPT { + return !operator==(rhs); + } + +private: + //! Creates a new chunk. + /*! \param capacity Capacity of the chunk in bytes. + \return true if success. + */ + bool AddChunk(size_t capacity) { + if (!baseAllocator_) + shared_->ownBaseAllocator = baseAllocator_ = RAPIDJSON_NEW(BaseAllocator)(); + if (ChunkHeader* chunk = static_cast(baseAllocator_->Malloc(SIZEOF_CHUNK_HEADER + capacity))) { + chunk->capacity = capacity; + chunk->size = 0; + chunk->next = shared_->chunkHead; + shared_->chunkHead = chunk; + return true; + } + else + return false; + } + + static inline void* AlignBuffer(void* buf, size_t &size) + { + RAPIDJSON_NOEXCEPT_ASSERT(buf != 0); + const uintptr_t mask = sizeof(void*) - 1; + const uintptr_t ubuf = reinterpret_cast(buf); + if (RAPIDJSON_UNLIKELY(ubuf & mask)) { + const uintptr_t abuf = (ubuf + mask) & ~mask; + RAPIDJSON_ASSERT(size >= abuf - ubuf); + buf = reinterpret_cast(abuf); + size -= abuf - ubuf; + } + return buf; + } + + size_t chunk_capacity_; //!< The minimum capacity of chunk when they are allocated. + BaseAllocator* baseAllocator_; //!< base allocator for allocating memory chunks. + SharedData *shared_; //!< The shared data of the allocator +}; + +namespace internal { + template + struct IsRefCounted : + public FalseType + { }; + template + struct IsRefCounted::Type> : + public TrueType + { }; +} + +template +inline T* Realloc(A& a, T* old_p, size_t old_n, size_t new_n) +{ + RAPIDJSON_NOEXCEPT_ASSERT(old_n <= (std::numeric_limits::max)() / sizeof(T) && new_n <= (std::numeric_limits::max)() / sizeof(T)); + return static_cast(a.Realloc(old_p, old_n * sizeof(T), new_n * sizeof(T))); +} + +template +inline T *Malloc(A& a, size_t n = 1) +{ + return Realloc(a, NULL, 0, n); +} + +template +inline void Free(A& a, T *p, size_t n = 1) +{ + static_cast(Realloc(a, p, n, 0)); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) // std::allocator can safely be inherited +#endif + +template +class StdAllocator : + public std::allocator +{ + typedef std::allocator allocator_type; +#if RAPIDJSON_HAS_CXX11 + typedef std::allocator_traits traits_type; +#else + typedef allocator_type traits_type; +#endif + +public: + typedef BaseAllocator BaseAllocatorType; + + StdAllocator() RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_() + { } + + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + template + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + StdAllocator(StdAllocator&& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(std::move(rhs)), + baseAllocator_(std::move(rhs.baseAllocator_)) + { } +#endif +#if RAPIDJSON_HAS_CXX11 + using propagate_on_container_move_assignment = std::true_type; + using propagate_on_container_swap = std::true_type; +#endif + + /* implicit */ + StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_(baseAllocator) + { } + + ~StdAllocator() RAPIDJSON_NOEXCEPT + { } + + template + struct rebind { + typedef StdAllocator other; + }; + + typedef typename traits_type::size_type size_type; + typedef typename traits_type::difference_type difference_type; + + typedef typename traits_type::value_type value_type; + typedef typename traits_type::pointer pointer; + typedef typename traits_type::const_pointer const_pointer; + +#if RAPIDJSON_HAS_CXX11 + + typedef typename std::add_lvalue_reference::type &reference; + typedef typename std::add_lvalue_reference::type>::type &const_reference; + + pointer address(reference r) const RAPIDJSON_NOEXCEPT + { + return std::addressof(r); + } + const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT + { + return std::addressof(r); + } + + size_type max_size() const RAPIDJSON_NOEXCEPT + { + return traits_type::max_size(*this); + } + + template + void construct(pointer p, Args&&... args) + { + traits_type::construct(*this, p, std::forward(args)...); + } + void destroy(pointer p) + { + traits_type::destroy(*this, p); + } + +#else // !RAPIDJSON_HAS_CXX11 + + typedef typename allocator_type::reference reference; + typedef typename allocator_type::const_reference const_reference; + + pointer address(reference r) const RAPIDJSON_NOEXCEPT + { + return allocator_type::address(r); + } + const_pointer address(const_reference r) const RAPIDJSON_NOEXCEPT + { + return allocator_type::address(r); + } + + size_type max_size() const RAPIDJSON_NOEXCEPT + { + return allocator_type::max_size(); + } + + void construct(pointer p, const_reference r) + { + allocator_type::construct(p, r); + } + void destroy(pointer p) + { + allocator_type::destroy(p); + } + +#endif // !RAPIDJSON_HAS_CXX11 + + template + U* allocate(size_type n = 1, const void* = 0) + { + return RAPIDJSON_NAMESPACE::Malloc(baseAllocator_, n); + } + template + void deallocate(U* p, size_type n = 1) + { + RAPIDJSON_NAMESPACE::Free(baseAllocator_, p, n); + } + + pointer allocate(size_type n = 1, const void* = 0) + { + return allocate(n); + } + void deallocate(pointer p, size_type n = 1) + { + deallocate(p, n); + } + +#if RAPIDJSON_HAS_CXX11 + using is_always_equal = std::is_empty; +#endif + + template + bool operator==(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT + { + return baseAllocator_ == rhs.baseAllocator_; + } + template + bool operator!=(const StdAllocator& rhs) const RAPIDJSON_NOEXCEPT + { + return !operator==(rhs); + } + + //! rapidjson Allocator concept + static const bool kNeedFree = BaseAllocator::kNeedFree; + static const bool kRefCounted = internal::IsRefCounted::Value; + void* Malloc(size_t size) + { + return baseAllocator_.Malloc(size); + } + void* Realloc(void* originalPtr, size_t originalSize, size_t newSize) + { + return baseAllocator_.Realloc(originalPtr, originalSize, newSize); + } + static void Free(void *ptr) RAPIDJSON_NOEXCEPT + { + BaseAllocator::Free(ptr); + } + +private: + template + friend class StdAllocator; // access to StdAllocator.* + + BaseAllocator baseAllocator_; +}; + +#if !RAPIDJSON_HAS_CXX17 // std::allocator deprecated in C++17 +template +class StdAllocator : + public std::allocator +{ + typedef std::allocator allocator_type; + +public: + typedef BaseAllocator BaseAllocatorType; + + StdAllocator() RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_() + { } + + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + template + StdAllocator(const StdAllocator& rhs) RAPIDJSON_NOEXCEPT : + allocator_type(rhs), + baseAllocator_(rhs.baseAllocator_) + { } + + /* implicit */ + StdAllocator(const BaseAllocator& baseAllocator) RAPIDJSON_NOEXCEPT : + allocator_type(), + baseAllocator_(baseAllocator) + { } + + ~StdAllocator() RAPIDJSON_NOEXCEPT + { } + + template + struct rebind { + typedef StdAllocator other; + }; + + typedef typename allocator_type::value_type value_type; + +private: + template + friend class StdAllocator; // access to StdAllocator.* + + BaseAllocator baseAllocator_; +}; +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/Minecraft.Server/vendor/rapidjson/cursorstreamwrapper.h b/Minecraft.Server/vendor/rapidjson/cursorstreamwrapper.h new file mode 100644 index 00000000..fd6513db --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/cursorstreamwrapper.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_CURSORSTREAMWRAPPER_H_ +#define RAPIDJSON_CURSORSTREAMWRAPPER_H_ + +#include "stream.h" + +#if defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + + +//! Cursor stream wrapper for counting line and column number if error exists. +/*! + \tparam InputStream Any stream that implements Stream Concept +*/ +template > +class CursorStreamWrapper : public GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + + CursorStreamWrapper(InputStream& is): + GenericStreamWrapper(is), line_(1), col_(0) {} + + // counting line and column number + Ch Take() { + Ch ch = this->is_.Take(); + if(ch == '\n') { + line_ ++; + col_ = 0; + } else { + col_ ++; + } + return ch; + } + + //! Get the error line number, if error exists. + size_t GetLine() const { return line_; } + //! Get the error column number, if error exists. + size_t GetColumn() const { return col_; } + +private: + size_t line_; //!< Current Line + size_t col_; //!< Current Column +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +#if defined(__GNUC__) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CURSORSTREAMWRAPPER_H_ diff --git a/Minecraft.Server/vendor/rapidjson/document.h b/Minecraft.Server/vendor/rapidjson/document.h new file mode 100644 index 00000000..4b2d7232 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/document.h @@ -0,0 +1,3044 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_DOCUMENT_H_ +#define RAPIDJSON_DOCUMENT_H_ + +/*! \file document.h */ + +#include "reader.h" +#include "internal/meta.h" +#include "internal/strfunc.h" +#include "memorystream.h" +#include "encodedstream.h" +#include // placement new +#include +#ifdef __cpp_lib_three_way_comparison +#include +#endif + +RAPIDJSON_DIAG_PUSH +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4244) // conversion from kXxxFlags to 'uint16_t', possible loss of data +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_OFF(effc++) +#endif // __GNUC__ + +#ifdef GetObject +// see https://github.com/Tencent/rapidjson/issues/1448 +// a former included windows.h might have defined a macro called GetObject, which affects +// GetObject defined here. This ensures the macro does not get applied +#pragma push_macro("GetObject") +#define RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#undef GetObject +#endif + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS +#include // std::random_access_iterator_tag +#endif + +#if RAPIDJSON_USE_MEMBERSMAP +#include // std::multimap +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +// Forward declaration. +template +class GenericValue; + +template +class GenericDocument; + +/*! \def RAPIDJSON_DEFAULT_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default allocator. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_ALLOCATOR +#define RAPIDJSON_DEFAULT_ALLOCATOR ::RAPIDJSON_NAMESPACE::MemoryPoolAllocator<::RAPIDJSON_NAMESPACE::CrtAllocator> +#endif + +/*! \def RAPIDJSON_DEFAULT_STACK_ALLOCATOR + \ingroup RAPIDJSON_CONFIG + \brief Allows to choose default stack allocator for Document. + + User can define this to use CrtAllocator or MemoryPoolAllocator. +*/ +#ifndef RAPIDJSON_DEFAULT_STACK_ALLOCATOR +#define RAPIDJSON_DEFAULT_STACK_ALLOCATOR ::RAPIDJSON_NAMESPACE::CrtAllocator +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultObjectCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY +// number of objects that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY 16 +#endif + +/*! \def RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY + \ingroup RAPIDJSON_CONFIG + \brief User defined kDefaultArrayCapacity value. + + User can define this as any natural number. +*/ +#ifndef RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY +// number of array elements that rapidjson::Value allocates memory for by default +#define RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY 16 +#endif + +//! Name-value pair in a JSON object value. +/*! + This class was internal to GenericValue. It used to be a inner struct. + But a compiler (IBM XL C/C++ for AIX) have reported to have problem with that so it moved as a namespace scope struct. + https://code.google.com/p/rapidjson/issues/detail?id=64 +*/ +template +class GenericMember { +public: + GenericValue name; //!< name of member (must be a string) + GenericValue value; //!< value of member. + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericMember(GenericMember&& rhs) RAPIDJSON_NOEXCEPT + : name(std::move(rhs.name)), + value(std::move(rhs.value)) + { + } + + //! Move assignment in C++11 + GenericMember& operator=(GenericMember&& rhs) RAPIDJSON_NOEXCEPT { + return *this = static_cast(rhs); + } +#endif + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. Its name and value will become a null value after assignment. + */ + GenericMember& operator=(GenericMember& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + name = rhs.name; + value = rhs.value; + } + return *this; + } + + // swap() for std::sort() and other potential use in STL. + friend inline void swap(GenericMember& a, GenericMember& b) RAPIDJSON_NOEXCEPT { + a.name.Swap(b.name); + a.value.Swap(b.value); + } + +private: + //! Copy constructor is not permitted. + GenericMember(const GenericMember& rhs); +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericMemberIterator + +#ifndef RAPIDJSON_NOMEMBERITERATORCLASS + +//! (Constant) member iterator for a JSON object value +/*! + \tparam Const Is this a constant iterator? + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. + + This class implements a Random Access Iterator for GenericMember elements + of a GenericValue, see ISO/IEC 14882:2003(E) C++ standard, 24.1 [lib.iterator.requirements]. + + \note This iterator implementation is mainly intended to avoid implicit + conversions from iterator values to \c NULL, + e.g. from GenericValue::FindMember. + + \note Define \c RAPIDJSON_NOMEMBERITERATORCLASS to fall back to a + pointer-based implementation, if your platform doesn't provide + the C++ header. + + \see GenericMember, GenericValue::MemberIterator, GenericValue::ConstMemberIterator + */ +template +class GenericMemberIterator { + + friend class GenericValue; + template friend class GenericMemberIterator; + + typedef GenericMember PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + +public: + //! Iterator type itself + typedef GenericMemberIterator Iterator; + //! Constant iterator type + typedef GenericMemberIterator ConstIterator; + //! Non-constant iterator type + typedef GenericMemberIterator NonConstIterator; + + /** \name std::iterator_traits support */ + //@{ + typedef ValueType value_type; + typedef ValueType * pointer; + typedef ValueType & reference; + typedef std::ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + //@} + + //! Pointer to (const) GenericMember + typedef pointer Pointer; + //! Reference to (const) GenericMember + typedef reference Reference; + //! Signed integer type (e.g. \c ptrdiff_t) + typedef difference_type DifferenceType; + + //! Default constructor (singular value) + /*! Creates an iterator pointing to no element. + \note All operations, except for comparisons, are undefined on such values. + */ + GenericMemberIterator() : ptr_() {} + + //! Iterator conversions to more const + /*! + \param it (Non-const) iterator to copy from + + Allows the creation of an iterator from another GenericMemberIterator + that is "less const". Especially, creating a non-constant iterator + from a constant iterator are disabled: + \li const -> non-const (not ok) + \li const -> const (ok) + \li non-const -> const (ok) + \li non-const -> non-const (ok) + + \note If the \c Const template parameter is already \c false, this + constructor effectively defines a regular copy-constructor. + Otherwise, the copy constructor is implicitly defined. + */ + GenericMemberIterator(const NonConstIterator & it) : ptr_(it.ptr_) {} + Iterator& operator=(const NonConstIterator & it) { ptr_ = it.ptr_; return *this; } + + //! @name stepping + //@{ + Iterator& operator++(){ ++ptr_; return *this; } + Iterator& operator--(){ --ptr_; return *this; } + Iterator operator++(int){ Iterator old(*this); ++ptr_; return old; } + Iterator operator--(int){ Iterator old(*this); --ptr_; return old; } + //@} + + //! @name increment/decrement + //@{ + Iterator operator+(DifferenceType n) const { return Iterator(ptr_+n); } + Iterator operator-(DifferenceType n) const { return Iterator(ptr_-n); } + + Iterator& operator+=(DifferenceType n) { ptr_+=n; return *this; } + Iterator& operator-=(DifferenceType n) { ptr_-=n; return *this; } + //@} + + //! @name relations + //@{ + template bool operator==(const GenericMemberIterator& that) const { return ptr_ == that.ptr_; } + template bool operator!=(const GenericMemberIterator& that) const { return ptr_ != that.ptr_; } + template bool operator<=(const GenericMemberIterator& that) const { return ptr_ <= that.ptr_; } + template bool operator>=(const GenericMemberIterator& that) const { return ptr_ >= that.ptr_; } + template bool operator< (const GenericMemberIterator& that) const { return ptr_ < that.ptr_; } + template bool operator> (const GenericMemberIterator& that) const { return ptr_ > that.ptr_; } + +#ifdef __cpp_lib_three_way_comparison + template std::strong_ordering operator<=>(const GenericMemberIterator& that) const { return ptr_ <=> that.ptr_; } +#endif + //@} + + //! @name dereference + //@{ + Reference operator*() const { return *ptr_; } + Pointer operator->() const { return ptr_; } + Reference operator[](DifferenceType n) const { return ptr_[n]; } + //@} + + //! Distance + DifferenceType operator-(ConstIterator that) const { return ptr_-that.ptr_; } + +private: + //! Internal constructor from plain pointer + explicit GenericMemberIterator(Pointer p) : ptr_(p) {} + + Pointer ptr_; //!< raw pointer +}; + +#else // RAPIDJSON_NOMEMBERITERATORCLASS + +// class-based member iterator implementation disabled, use plain pointers + +template +class GenericMemberIterator; + +//! non-const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain pointer as iterator type + typedef GenericMember* Iterator; +}; +//! const GenericMemberIterator +template +class GenericMemberIterator { +public: + //! use plain const pointer as iterator type + typedef const GenericMember* Iterator; +}; + +#endif // RAPIDJSON_NOMEMBERITERATORCLASS + +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array +#ifndef __clang__ // -Wdocumentation + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + template + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer +#ifndef __clang__ // -Wdocumentation + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ +#endif + explicit GenericStringRef(const CharType* str) + : s(str), length(NotNullStrLen(str)) {} + + //! Create constant string reference from pointer and length +#ifndef __clang__ // -Wdocumentation + /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ +#endif + GenericStringRef(const CharType* str, SizeType len) + : s(RAPIDJSON_LIKELY(str) ? str : emptyString), length(len) { RAPIDJSON_ASSERT(str != 0 || len == 0u); } + + GenericStringRef(const GenericStringRef& rhs) : s(rhs.s), length(rhs.length) {} + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* const s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + SizeType NotNullStrLen(const CharType* str) { + RAPIDJSON_ASSERT(str != 0); + return internal::StrLen(str); + } + + /// Empty string - used when passing in a NULL pointer + static const Ch emptyString[]; + + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; + //! Copy assignment operator not permitted - immutable type + GenericStringRef& operator=(const GenericStringRef& rhs) /* = delete */; +}; + +template +const CharType GenericStringRef::emptyString[] = { CharType() }; + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str); +} + +//! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + +#if RAPIDJSON_HAS_STDSTRING +//! Mark a string object as constant string +/*! Mark a string object (e.g. \c std::string) as a "string literal". + This function can be used to avoid copying a string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. +*/ +template +inline GenericStringRef StringRef(const std::basic_string& str) { + return GenericStringRef(str.data(), SizeType(str.size())); +} +#endif + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper {}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +#ifdef _MSC_VER +RAPIDJSON_STATIC_ASSERT(sizeof(long) == sizeof(int)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static long Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, long data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, long data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +RAPIDJSON_STATIC_ASSERT(sizeof(unsigned long) == sizeof(unsigned)); +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned long Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned long data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned long data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; +#endif + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +template +struct TypeHelper { + typedef const typename ValueType::Ch* StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType data) { return v.SetString(typename ValueType::StringRefType(data)); } + static ValueType& Set(ValueType& v, const StringType data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return StringType(v.GetString(), v.GetStringLength()); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +template +struct TypeHelper { + typedef typename ValueType::Array ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArrayType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } +}; + +template +struct TypeHelper { + typedef typename ValueType::Object ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v = data; } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ObjectType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } +}; + +} // namespace internal + +// Forward declarations +template class GenericArray; +template class GenericObject; + +/////////////////////////////////////////////////////////////////////////////// +// GenericValue + +//! Represents a JSON value. Use Value for UTF8 encoding and default allocator. +/*! + A JSON value can be one of 7 types. This class is a variant type supporting + these types. + + Use the Value if UTF8 and default allocator + + \tparam Encoding Encoding of the value. (Even non-string values need to have the same encoding in a document) + \tparam Allocator Allocator type for allocating memory of object, array and string. +*/ +template +class GenericValue { +public: + //! Name-value pair in an object. + typedef GenericMember Member; + typedef Encoding EncodingType; //!< Encoding type from template parameter. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string + typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. + typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. + typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. + typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. + typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray Array; + typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor creates a null value. + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents + } +#endif + +private: + //! Copy constructor is not permitted. + GenericValue(const GenericValue& rhs); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Moving from a GenericDocument is not permitted. + template + GenericValue(GenericDocument&& rhs); + + //! Move assignment from a GenericDocument is not permitted. + template + GenericValue& operator=(GenericDocument&& rhs); +#endif + +public: + + //! Constructor with JSON value type. + /*! This creates a Value of specified type with default content. + \param type Type of the value. + \note Default content for number is zero. + */ + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[] = { + kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, + kNumberAnyFlag + }; + RAPIDJSON_NOEXCEPT_ASSERT(type >= kNullType && type <= kNumberType); + data_.f.flags = defaultFlags[type]; + + // Use ShortString to store empty string. + if (type == kStringType) + data_.ss.SetLength(0); + } + + //! Explicit copy constructor (with allocator) + /*! Creates a copy of a Value by using the given Allocator + \tparam SourceAllocator allocator of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + \see CopyFrom() + */ + template + GenericValue(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + switch (rhs.GetType()) { + case kObjectType: + DoCopyMembers(rhs, allocator, copyConstStrings); + break; + case kArrayType: { + SizeType count = rhs.data_.a.size; + GenericValue* le = reinterpret_cast(allocator.Malloc(count * sizeof(GenericValue))); + const GenericValue* re = rhs.GetElementsPointer(); + for (SizeType i = 0; i < count; i++) + new (&le[i]) GenericValue(re[i], allocator, copyConstStrings); + data_.f.flags = kArrayFlag; + data_.a.size = data_.a.capacity = count; + SetElementsPointer(le); + } + break; + case kStringType: + if (rhs.data_.f.flags == kConstStringFlag && !copyConstStrings) { + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + } + else + SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); + break; + default: + data_.f.flags = rhs.data_.f.flags; + data_ = *reinterpret_cast(&rhs.data_); + break; + } + } + + //! Constructor for boolean value. + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT // See #472 +#else + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT +#endif + : data_() { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; + } + + //! Constructor for int value. + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; + } + + //! Constructor for unsigned value. + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); + } + + //! Constructor for int64_t value. + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; + if (i64 >= 0) { + data_.f.flags |= kNumberUint64Flag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for uint64_t value. + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { + data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) + data_.f.flags |= kInt64Flag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) + data_.f.flags |= kUintFlag; + if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) + data_.f.flags |= kIntFlag; + } + + //! Constructor for double value. + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for float value. + explicit GenericValue(float f) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = static_cast(f); data_.f.flags = kNumberDoubleFlag; } + + //! Constructor for constant string (i.e. do not make a copy of string) + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } + + //! Constructor for constant string (i.e. do not make a copy of string) + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } + + //! Constructor for copy-string (i.e. do make a copy of string) + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor for copy-string from a string object (i.e. do make a copy of string) + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } +#endif + + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_) { + a.value_.data_ = Data(); + a.value_.data_.f.flags = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_) { + o.value_.data_ = Data(); + o.value_.data_.f.flags = kObjectFlag; + } + + //! Destructor. + /*! Need to destruct elements of array, members of object, or copy-string. + */ + ~GenericValue() { + // With RAPIDJSON_USE_MEMBERSMAP, the maps need to be destroyed to release + // their Allocator if it's refcounted (e.g. MemoryPoolAllocator). + if (Allocator::kNeedFree || (RAPIDJSON_USE_MEMBERSMAP+0 && + internal::IsRefCounted::Value)) { + switch(data_.f.flags) { + case kArrayFlag: + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Allocator::Free(e); + } + } + break; + + case kObjectFlag: + DoFreeMembers(); + break; + + case kCopyStringFlag: + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Allocator::Free(const_cast(GetStringPointer())); + } + break; + + default: + break; // Do nothing for other types. + } + } + } + + //@} + + //!@name Assignment operators + //@{ + + //! Assignment with move semantics. + /*! \param rhs Source of the assignment. It will become a null value after assignment. + */ + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + if (RAPIDJSON_LIKELY(this != &rhs)) { + // Can't destroy "this" before assigning "rhs", otherwise "rhs" + // could be used after free if it's an sub-Value of "this", + // hence the temporary danse. + GenericValue temp; + temp.RawAssign(rhs); + this->~GenericValue(); + RawAssign(temp); + } + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { + return *this = rhs.Move(); + } +#endif + + //! Assignment of constant string reference (no copy) + /*! \param str Constant string reference to be assigned + \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. + \see GenericStringRef, operator=(T) + */ + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { + GenericValue s(str); + return *this = s; + } + + //! Assignment with primitive types. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + operator=(T value) { + GenericValue v(value); + return *this = v; + } + + //! Deep-copy assignment from Value + /*! Assigns a \b copy of the Value to the current Value object + \tparam SourceAllocator Allocator type of \c rhs + \param rhs Value to copy from (read-only) + \param allocator Allocator to use for copying + \param copyConstStrings Force copying of constant strings (e.g. referencing an in-situ buffer) + */ + template + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings = false) { + RAPIDJSON_ASSERT(static_cast(this) != static_cast(&rhs)); + this->~GenericValue(); + new (this) GenericValue(rhs, allocator, copyConstStrings); + return *this; + } + + //! Exchange the contents of this value with those of other. + /*! + \param other Another value. + \note Constant complexity. + */ + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { + GenericValue temp; + temp.RawAssign(*this); + RawAssign(other); + other.RawAssign(temp); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.value, b.value); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericValue& a, GenericValue& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } + //@} + + //!@name Equal-to and not-equal-to operators + //@{ + //! Equal-to operator + /*! + \note If an object contains duplicated named member, comparing equality with any object is always \c false. + \note Complexity is quadratic in Object's member number and linear for the rest (number of all values in the subtree and total lengths of all strings). + */ + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; + if (GetType() != rhs.GetType()) + return false; + + switch (GetType()) { + case kObjectType: // Warning: O(n^2) inner-loop + if (data_.o.size != rhs.data_.o.size) + return false; + for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + if (rhsMemberItr == rhs.MemberEnd() || (!(lhsMemberItr->value == rhsMemberItr->value))) + return false; + } + return true; + + case kArrayType: + if (data_.a.size != rhs.data_.a.size) + return false; + for (SizeType i = 0; i < data_.a.size; i++) + if (!((*this)[i] == rhs[i])) + return false; + return true; + + case kStringType: + return StringEqual(rhs); + + case kNumberType: + if (IsDouble() || rhs.IsDouble()) { + double a = GetDouble(); // May convert from integer to double. + double b = rhs.GetDouble(); // Ditto + return a >= b && a <= b; // Prevent -Wfloat-equal + } + else + return data_.n.u64 == rhs.data_.n.u64; + + default: + return true; + } + } + + //! Equal-to operator with const C-string pointer + bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } + +#if RAPIDJSON_HAS_STDSTRING + //! Equal-to operator with string object + /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + bool operator==(const std::basic_string& rhs) const { return *this == GenericValue(StringRef(rhs)); } +#endif + + //! Equal-to operator with primitive types + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + +#ifndef __cpp_impl_three_way_comparison + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + + //! Not-equal-to operator with arbitrary types + /*! \return !(*this == rhs) + */ + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + + //! Equal-to operator with arbitrary types (symmetric version) + /*! \return (rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + + //! Not-Equal-to operator with arbitrary types (symmetric version) + /*! \return !(rhs == lhs) + */ + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + //@} +#endif + + //!@name Type + //@{ + + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } + + // Checks whether a number can be losslessly converted to a double. + bool IsLosslessDouble() const { + if (!IsNumber()) return false; + if (IsUint64()) { + uint64_t u = GetUint64(); + volatile double d = static_cast(u); + return (d >= 0.0) + && (d < static_cast((std::numeric_limits::max)())) + && (u == static_cast(d)); + } + if (IsInt64()) { + int64_t i = GetInt64(); + volatile double d = static_cast(i); + return (d >= static_cast((std::numeric_limits::min)())) + && (d < static_cast((std::numeric_limits::max)())) + && (i == static_cast(d)); + } + return true; // double, int, uint are always lossless + } + + // Checks whether a number is a float (possible lossy). + bool IsFloat() const { + if ((data_.f.flags & kDoubleFlag) == 0) + return false; + double d = GetDouble(); + return d >= -3.4028234e38 && d <= 3.4028234e38; + } + // Checks whether a number can be losslessly converted to a float. + bool IsLosslessFloat() const { + if (!IsNumber()) return false; + double a = GetDouble(); + if (a < static_cast(-(std::numeric_limits::max)()) + || a > static_cast((std::numeric_limits::max)())) + return false; + double b = static_cast(static_cast(a)); + return a >= b && a <= b; // Prevent -Wfloat-equal + } + + //@} + + //!@name Null + //@{ + + GenericValue& SetNull() { this->~GenericValue(); new (this) GenericValue(); return *this; } + + //@} + + //!@name Bool + //@{ + + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ + GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } + + //@} + + //!@name Object + //@{ + + //! Set this value as an empty object. + /*! \post IsObject() == true */ + GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + + //! Get the number of members in the object. + SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } + + //! Get the capacity of object. + SizeType MemberCapacity() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.capacity; } + + //! Check whether the object is empty. + bool ObjectEmpty() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size == 0; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam T Either \c Ch or \c const \c Ch (template used for disambiguation with \ref operator[](SizeType)) + \note In version 0.1x, if the member is not found, this function returns a null value. This makes issue 7. + Since 0.2, if the name is not correct, it will assert. + If user is unsure whether a member exists, user should use HasMember() first. + A better approach is to use FindMember(). + \note Linear time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(GenericValue&)) operator[](T* name) { + GenericValue n(StringRef(name)); + return (*this)[n]; + } + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >),(const GenericValue&)) operator[](T* name) const { return const_cast(*this)[name]; } + + //! Get a value from an object associated with the name. + /*! \pre IsObject() == true + \tparam SourceAllocator Allocator of the \c name value + + \note Compared to \ref operator[](T*), this version is faster because it does not need a StrLen(). + And it can also handle strings with embedded null characters. + + \note Linear time complexity. + */ + template + GenericValue& operator[](const GenericValue& name) { + MemberIterator member = FindMember(name); + if (member != MemberEnd()) + return member->value; + else { + RAPIDJSON_ASSERT(false); // see above note + +#if RAPIDJSON_HAS_CXX11 + // Use thread-local storage to prevent races between threads. + // Use static buffer and placement-new to prevent destruction, with + // alignas() to ensure proper alignment. + alignas(GenericValue) thread_local static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); +#elif defined(_MSC_VER) && _MSC_VER < 1900 + // There's no way to solve both thread locality and proper alignment + // simultaneously. + __declspec(thread) static char buffer[sizeof(GenericValue)]; + return *new (buffer) GenericValue(); +#elif defined(__GNUC__) || defined(__clang__) + // This will generate -Wexit-time-destructors in clang, but that's + // better than having under-alignment. + __thread static GenericValue buffer; + return buffer; +#else + // Don't know what compiler this is, so don't know how to ensure + // thread-locality. + static GenericValue buffer; + return buffer; +#endif + } + } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + +#if RAPIDJSON_HAS_STDSTRING + //! Get a value from an object associated with name (string object). + GenericValue& operator[](const std::basic_string& name) { return (*this)[GenericValue(StringRef(name))]; } + const GenericValue& operator[](const std::basic_string& name) const { return (*this)[GenericValue(StringRef(name))]; } +#endif + + //! Const member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } + //! Const \em past-the-end member iterator + /*! \pre IsObject() == true */ + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } + //! Member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } + //! \em Past-the-end member iterator + /*! \pre IsObject() == true */ + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } + + //! Request the object to have enough capacity to store members. + /*! \param newCapacity The capacity that the object at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& MemberReserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsObject()); + DoReserveMembers(newCapacity, allocator); + return *this; + } + + //! Check whether a member exists in the object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const Ch* name) const { return FindMember(name) != MemberEnd(); } + +#if RAPIDJSON_HAS_STDSTRING + //! Check whether a member exists in the object with string object. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + bool HasMember(const std::basic_string& name) const { return FindMember(name) != MemberEnd(); } +#endif + + //! Check whether a member exists in the object with GenericValue name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Whether a member with that name exists. + \note It is better to use FindMember() directly if you need the obtain the value as well. + \note Linear time complexity. + */ + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + + //! Find member by name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + MemberIterator FindMember(const Ch* name) { + GenericValue n(StringRef(name)); + return FindMember(n); + } + + ConstMemberIterator FindMember(const Ch* name) const { return const_cast(*this).FindMember(name); } + + //! Find member by name. + /*! + This version is faster because it does not need a StrLen(). It can also handle string with null character. + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + + \note Earlier versions of Rapidjson returned a \c NULL pointer, in case + the requested member doesn't exist. For consistency with e.g. + \c std::map, this has been changed to MemberEnd() now. + \note Linear time complexity. + */ + template + MemberIterator FindMember(const GenericValue& name) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + return DoFindMember(name); + } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + +#if RAPIDJSON_HAS_STDSTRING + //! Find member by string object name. + /*! + \param name Member name to be searched. + \pre IsObject() == true + \return Iterator to member, if it exists. + Otherwise returns \ref MemberEnd(). + */ + MemberIterator FindMember(const std::basic_string& name) { return FindMember(GenericValue(StringRef(name))); } + ConstMemberIterator FindMember(const std::basic_string& name) const { return FindMember(GenericValue(StringRef(name))); } +#endif + + //! Add a member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(name.IsString()); + DoAddMember(name, value, allocator); + return *this; + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Add a string object as member (name-value pair) to the object. + /*! \param name A string value as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(GenericValue&,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(GenericValue& name, std::basic_string& value, Allocator& allocator) { + GenericValue v(value, allocator); + return AddMember(name, v, allocator); + } +#endif + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A string value as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(GenericValue& name, T value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + + //! Add a member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value Value of any type. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + \note Amortized Constant time complexity. + */ + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); + } + + //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized Constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } + + //! Remove all members in the object. + /*! This function do not deallocate memory in the object, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void RemoveAllMembers() { + RAPIDJSON_ASSERT(IsObject()); + DoClearMembers(); + } + + //! Remove a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Linear time complexity. + */ + bool RemoveMember(const Ch* name) { + GenericValue n(StringRef(name)); + return RemoveMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) { return RemoveMember(GenericValue(StringRef(name))); } +#endif + + template + bool RemoveMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + RemoveMember(m); + return true; + } + else + return false; + } + + //! Remove a member in object by iterator. + /*! \param m member iterator (obtained by FindMember() or MemberBegin()). + \return the new iterator after removal. + \note This function may reorder the object members. Use \ref + EraseMember(ConstMemberIterator) if you need to preserve the + relative order of the remaining members. + \note Constant time complexity. + */ + MemberIterator RemoveMember(MemberIterator m) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); + return DoRemoveMember(m); + } + + //! Remove a member from an object by iterator. + /*! \param pos iterator to the member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c pos < \ref MemberEnd() + \return Iterator following the removed element. + If the iterator \c pos refers to the last element, the \ref MemberEnd() iterator is returned. + \note This function preserves the relative order of the remaining object + members. If you do not need this, use the more efficient \ref RemoveMember(MemberIterator). + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator pos) { + return EraseMember(pos, pos +1); + } + + //! Remove members in the range [first, last) from an object. + /*! \param first iterator to the first member to remove + \param last iterator following the last member to remove + \pre IsObject() == true && \ref MemberBegin() <= \c first <= \c last <= \ref MemberEnd() + \return Iterator following the last removed element. + \note This function preserves the relative order of the remaining object + members. + \note Linear time complexity. + */ + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { + RAPIDJSON_ASSERT(IsObject()); + RAPIDJSON_ASSERT(data_.o.size > 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); + RAPIDJSON_ASSERT(first >= MemberBegin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= MemberEnd()); + return DoEraseMembers(first, last); + } + + //! Erase a member in object by its name. + /*! \param name Name of member to be removed. + \return Whether the member existed. + \note Linear time complexity. + */ + bool EraseMember(const Ch* name) { + GenericValue n(StringRef(name)); + return EraseMember(n); + } + +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) { return EraseMember(GenericValue(StringRef(name))); } +#endif + + template + bool EraseMember(const GenericValue& name) { + MemberIterator m = FindMember(name); + if (m != MemberEnd()) { + EraseMember(m); + return true; + } + else + return false; + } + + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + Object GetObj() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + ConstObject GetObj() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + + //@} + + //!@name Array + //@{ + + //! Set this value as an empty array. + /*! \post IsArray == true */ + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Get the number of elements in array. + SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } + + //! Get the capacity of array. + SizeType Capacity() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.capacity; } + + //! Check whether the array is empty. + bool Empty() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size == 0; } + + //! Remove all elements in the array. + /*! This function do not deallocate memory in the array, i.e. the capacity is unchanged. + \note Linear time complexity. + */ + void Clear() { + RAPIDJSON_ASSERT(IsArray()); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + data_.a.size = 0; + } + + //! Get an element from array by index. + /*! \pre IsArray() == true + \param index Zero-based index of element. + \see operator[](T*) + */ + GenericValue& operator[](SizeType index) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(index < data_.a.size); + return GetElementsPointer()[index]; + } + const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } + + //! Element iterator + /*! \pre IsArray() == true */ + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } + //! \em Past-the-end element iterator + /*! \pre IsArray() == true */ + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } + //! Constant element iterator + /*! \pre IsArray() == true */ + ConstValueIterator Begin() const { return const_cast(*this).Begin(); } + //! Constant \em past-the-end element iterator + /*! \pre IsArray() == true */ + ConstValueIterator End() const { return const_cast(*this).End(); } + + //! Request the array to have enough capacity to store elements. + /*! \param newCapacity The capacity that the array at least need to have. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note Linear time complexity. + */ + GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (newCapacity > data_.a.capacity) { + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); + data_.a.capacity = newCapacity; + } + return *this; + } + + //! Append a GenericValue at the end of the array. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + */ + GenericValue& PushBack(GenericValue& value, Allocator& allocator) { + RAPIDJSON_ASSERT(IsArray()); + if (data_.a.size >= data_.a.capacity) + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); + GetElementsPointer()[data_.a.size++].RawAssign(value); + return *this; + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Append a constant string reference at the end of the array. + /*! \param value Constant string reference to be appended. + \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \note Amortized constant time complexity. + \see GenericStringRef + */ + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + \note Amortized constant time complexity. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) + PushBack(T value, Allocator& allocator) { + GenericValue v(value); + return PushBack(v, allocator); + } + + //! Remove the last element in the array. + /*! + \note Constant time complexity. + */ + GenericValue& PopBack() { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(!Empty()); + GetElementsPointer()[--data_.a.size].~GenericValue(); + return *this; + } + + //! Remove an element of array by iterator. + /*! + \param pos iterator to the element to remove + \pre IsArray() == true && \ref Begin() <= \c pos < \ref End() + \return Iterator following the removed element. If the iterator pos refers to the last element, the End() iterator is returned. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator pos) { + return Erase(pos, pos + 1); + } + + //! Remove elements in the range [first, last) of the array. + /*! + \param first iterator to the first element to remove + \param last iterator following the last element to remove + \pre IsArray() == true && \ref Begin() <= \c first <= \c last <= \ref End() + \return Iterator following the last removed element. + \note Linear time complexity. + */ + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { + RAPIDJSON_ASSERT(IsArray()); + RAPIDJSON_ASSERT(data_.a.size > 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); + RAPIDJSON_ASSERT(first >= Begin()); + RAPIDJSON_ASSERT(first <= last); + RAPIDJSON_ASSERT(last <= End()); + ValueIterator pos = Begin() + (first - Begin()); + for (ValueIterator itr = pos; itr != last; ++itr) + itr->~GenericValue(); + std::memmove(static_cast(pos), last, static_cast(End() - last) * sizeof(GenericValue)); + data_.a.size -= static_cast(last - first); + return pos; + } + + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } + + //@} + + //!@name Number + //@{ + + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } + + //! Get the value as double type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. + */ + double GetDouble() const { + RAPIDJSON_ASSERT(IsNumber()); + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + } + + //! Get the value as float type. + /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. + */ + float GetFloat() const { + return static_cast(GetDouble()); + } + + GenericValue& SetInt(int i) { this->~GenericValue(); new (this) GenericValue(i); return *this; } + GenericValue& SetUint(unsigned u) { this->~GenericValue(); new (this) GenericValue(u); return *this; } + GenericValue& SetInt64(int64_t i64) { this->~GenericValue(); new (this) GenericValue(i64); return *this; } + GenericValue& SetUint64(uint64_t u64) { this->~GenericValue(); new (this) GenericValue(u64); return *this; } + GenericValue& SetDouble(double d) { this->~GenericValue(); new (this) GenericValue(d); return *this; } + GenericValue& SetFloat(float f) { this->~GenericValue(); new (this) GenericValue(static_cast(f)); return *this; } + + //@} + + //!@name String + //@{ + + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return DataString(data_); } + + //! Get the length of string. + /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). + */ + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return DataStringLength(data_); } + + //! Set this value as a string without copying source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string pointer. + \param length The length of source string, excluding the trailing null terminator. + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) + */ + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } + + //! Set this value as a string without copying source string. + /*! \param s source string reference + \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length + */ + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } + + //! Set this value as a string by copying from source string. + /*! This version has better performance with supplied length, and also support string containing null character. + \param s source string. + \param length The length of source string, excluding the trailing null terminator. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { return SetString(StringRef(s, length), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(StringRef(s), allocator); } + + //! Set this value as a string by copying from source string. + /*! \param s source string reference + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.s && strcmp(GetString(),s) == 0 && GetStringLength() == length + */ + GenericValue& SetString(StringRefType s, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, allocator); return *this; } + +#if RAPIDJSON_HAS_STDSTRING + //! Set this value as a string by copying from source string. + /*! \param s source string. + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \post IsString() == true && GetString() != s.data() && strcmp(GetString(),s.data() == 0 && GetStringLength() == s.size() + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + GenericValue& SetString(const std::basic_string& s, Allocator& allocator) { return SetString(StringRef(s), allocator); } +#endif + + //@} + + //!@name Array + //@{ + + //! Templated version for checking whether this value is type T. + /*! + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + T Get() { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + template + ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + + //@} + + //! Generate events of this value to a Handler. + /*! This function adopts the GoF visitor pattern. + Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. + It can also be used to deep clone this value via GenericDocument, which is also a Handler. + \tparam Handler type of handler. + \param handler An object implementing concept Handler. + */ + template + bool Accept(Handler& handler) const { + switch(GetType()) { + case kNullType: return handler.Null(); + case kFalseType: return handler.Bool(false); + case kTrueType: return handler.Bool(true); + + case kObjectType: + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + return false; + for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { + RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) + return false; + if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) + return false; + } + return handler.EndObject(data_.o.size); + + case kArrayType: + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + return false; + for (ConstValueIterator v = Begin(); v != End(); ++v) + if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) + return false; + return handler.EndArray(data_.a.size); + + case kStringType: + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); + + default: + RAPIDJSON_ASSERT(GetType() == kNumberType); + if (IsDouble()) return handler.Double(data_.n.d); + else if (IsInt()) return handler.Int(data_.n.i.i); + else if (IsUint()) return handler.Uint(data_.n.u.u); + else if (IsInt64()) return handler.Int64(data_.n.i64); + else return handler.Uint64(data_.n.u64); + } + } + +private: + template friend class GenericValue; + template friend class GenericDocument; + + enum { + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, + + // Initial flags of different types. + kNullFlag = kNullType, + // These casts are added to suppress the warning on MSVC about bitwise operations between enums of different types. + kTrueFlag = static_cast(kTrueType) | static_cast(kBoolFlag), + kFalseFlag = static_cast(kFalseType) | static_cast(kBoolFlag), + kNumberIntFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag), + kNumberUintFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kUintFlag | kUint64Flag | kInt64Flag), + kNumberInt64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kInt64Flag), + kNumberUint64Flag = static_cast(kNumberType) | static_cast(kNumberFlag | kUint64Flag), + kNumberDoubleFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kDoubleFlag), + kNumberAnyFlag = static_cast(kNumberType) | static_cast(kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag), + kConstStringFlag = static_cast(kStringType) | static_cast(kStringFlag), + kCopyStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag), + kShortStringFlag = static_cast(kStringType) | static_cast(kStringFlag | kCopyFlag | kInlineStrFlag), + kObjectFlag = kObjectType, + kArrayFlag = kArrayType, + + kTypeMask = 0x07 + }; + + static const SizeType kDefaultArrayCapacity = RAPIDJSON_VALUE_DEFAULT_ARRAY_CAPACITY; + static const SizeType kDefaultObjectCapacity = RAPIDJSON_VALUE_DEFAULT_OBJECT_CAPACITY; + + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + + struct String { + SizeType length; + SizeType hashcode; //!< reserved + const Ch* str; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as + // the string terminator as well. For getting the string length back from that value just use + // "MaxSize - str[LenPos]". + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). + struct ShortString { + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = static_cast(MaxSize - len); } + inline SizeType GetLength() const { return static_cast(MaxSize - str[LenPos]); } + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + // By using proper binary layout, retrieval of different integer types do not need conversions. + union Number { +#if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN + struct I { + int i; + char padding[4]; + }i; + struct U { + unsigned u; + char padding2[4]; + }u; +#else + struct I { + char padding[4]; + int i; + }i; + struct U { + char padding2[4]; + unsigned u; + }u; +#endif + int64_t i64; + uint64_t u64; + double d; + }; // 8 bytes + + struct ObjectData { + SizeType size; + SizeType capacity; + Member* members; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + struct ArrayData { + SizeType size; + SizeType capacity; + GenericValue* elements; + }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + + union Data { + String s; + ShortString ss; + Number n; + ObjectData o; + ArrayData a; + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + static RAPIDJSON_FORCEINLINE const Ch* DataString(const Data& data) { + return (data.f.flags & kInlineStrFlag) ? data.ss.str : RAPIDJSON_GETPOINTER(Ch, data.s.str); + } + static RAPIDJSON_FORCEINLINE SizeType DataStringLength(const Data& data) { + return (data.f.flags & kInlineStrFlag) ? data.ss.GetLength() : data.s.length; + } + + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + +#if RAPIDJSON_USE_MEMBERSMAP + + struct MapTraits { + struct Less { + bool operator()(const Data& s1, const Data& s2) const { + SizeType n1 = DataStringLength(s1), n2 = DataStringLength(s2); + int cmp = std::memcmp(DataString(s1), DataString(s2), sizeof(Ch) * (n1 < n2 ? n1 : n2)); + return cmp < 0 || (cmp == 0 && n1 < n2); + } + }; + typedef std::pair Pair; + typedef std::multimap > Map; + typedef typename Map::iterator Iterator; + }; + typedef typename MapTraits::Map Map; + typedef typename MapTraits::Less MapLess; + typedef typename MapTraits::Pair MapPair; + typedef typename MapTraits::Iterator MapIterator; + + // + // Layout of the members' map/array, re(al)located according to the needed capacity: + // + // {Map*}<>{capacity}<>{Member[capacity]}<>{MapIterator[capacity]} + // + // (where <> stands for the RAPIDJSON_ALIGN-ment, if needed) + // + + static RAPIDJSON_FORCEINLINE size_t GetMapLayoutSize(SizeType capacity) { + return RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType)) + + RAPIDJSON_ALIGN(capacity * sizeof(Member)) + + capacity * sizeof(MapIterator); + } + + static RAPIDJSON_FORCEINLINE SizeType &GetMapCapacity(Map* &map) { + return *reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*))); + } + + static RAPIDJSON_FORCEINLINE Member* GetMapMembers(Map* &map) { + return reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType))); + } + + static RAPIDJSON_FORCEINLINE MapIterator* GetMapIterators(Map* &map) { + return reinterpret_cast(reinterpret_cast(&map) + + RAPIDJSON_ALIGN(sizeof(Map*)) + + RAPIDJSON_ALIGN(sizeof(SizeType)) + + RAPIDJSON_ALIGN(GetMapCapacity(map) * sizeof(Member))); + } + + static RAPIDJSON_FORCEINLINE Map* &GetMap(Member* members) { + RAPIDJSON_ASSERT(members != 0); + return *reinterpret_cast(reinterpret_cast(members) - + RAPIDJSON_ALIGN(sizeof(SizeType)) - + RAPIDJSON_ALIGN(sizeof(Map*))); + } + + // Some compilers' debug mechanisms want all iterators to be destroyed, for their accounting.. + RAPIDJSON_FORCEINLINE MapIterator DropMapIterator(MapIterator& rhs) { +#if RAPIDJSON_HAS_CXX11 + MapIterator ret = std::move(rhs); +#else + MapIterator ret = rhs; +#endif + rhs.~MapIterator(); + return ret; + } + + Map* &DoReallocMap(Map** oldMap, SizeType newCapacity, Allocator& allocator) { + Map **newMap = static_cast(allocator.Malloc(GetMapLayoutSize(newCapacity))); + GetMapCapacity(*newMap) = newCapacity; + if (!oldMap) { + *newMap = new (allocator.Malloc(sizeof(Map))) Map(MapLess(), allocator); + } + else { + *newMap = *oldMap; + size_t count = (*oldMap)->size(); + std::memcpy(static_cast(GetMapMembers(*newMap)), + static_cast(GetMapMembers(*oldMap)), + count * sizeof(Member)); + MapIterator *oldIt = GetMapIterators(*oldMap), + *newIt = GetMapIterators(*newMap); + while (count--) { + new (&newIt[count]) MapIterator(DropMapIterator(oldIt[count])); + } + Allocator::Free(oldMap); + } + return *newMap; + } + + RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { + return GetMapMembers(DoReallocMap(0, capacity, allocator)); + } + + void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { + ObjectData& o = data_.o; + if (newCapacity > o.capacity) { + Member* oldMembers = GetMembersPointer(); + Map **oldMap = oldMembers ? &GetMap(oldMembers) : 0, + *&newMap = DoReallocMap(oldMap, newCapacity, allocator); + RAPIDJSON_SETPOINTER(Member, o.members, GetMapMembers(newMap)); + o.capacity = newCapacity; + } + } + + template + MemberIterator DoFindMember(const GenericValue& name) { + if (Member* members = GetMembersPointer()) { + Map* &map = GetMap(members); + MapIterator mit = map->find(reinterpret_cast(name.data_)); + if (mit != map->end()) { + return MemberIterator(&members[mit->second]); + } + } + return MemberEnd(); + } + + void DoClearMembers() { + if (Member* members = GetMembersPointer()) { + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + for (SizeType i = 0; i < data_.o.size; i++) { + map->erase(DropMapIterator(mit[i])); + members[i].~Member(); + } + data_.o.size = 0; + } + } + + void DoFreeMembers() { + if (Member* members = GetMembersPointer()) { + GetMap(members)->~Map(); + for (SizeType i = 0; i < data_.o.size; i++) { + members[i].~Member(); + } + if (Allocator::kNeedFree) { // Shortcut by Allocator's trait + Map** map = &GetMap(members); + Allocator::Free(*map); + Allocator::Free(map); + } + } + } + +#else // !RAPIDJSON_USE_MEMBERSMAP + + RAPIDJSON_FORCEINLINE Member* DoAllocMembers(SizeType capacity, Allocator& allocator) { + return Malloc(allocator, capacity); + } + + void DoReserveMembers(SizeType newCapacity, Allocator& allocator) { + ObjectData& o = data_.o; + if (newCapacity > o.capacity) { + Member* newMembers = Realloc(allocator, GetMembersPointer(), o.capacity, newCapacity); + RAPIDJSON_SETPOINTER(Member, o.members, newMembers); + o.capacity = newCapacity; + } + } + + template + MemberIterator DoFindMember(const GenericValue& name) { + MemberIterator member = MemberBegin(); + for ( ; member != MemberEnd(); ++member) + if (name.StringEqual(member->name)) + break; + return member; + } + + void DoClearMembers() { + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + data_.o.size = 0; + } + + void DoFreeMembers() { + for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) + m->~Member(); + Allocator::Free(GetMembersPointer()); + } + +#endif // !RAPIDJSON_USE_MEMBERSMAP + + void DoAddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { + ObjectData& o = data_.o; + if (o.size >= o.capacity) + DoReserveMembers(o.capacity ? (o.capacity + (o.capacity + 1) / 2) : kDefaultObjectCapacity, allocator); + Member* members = GetMembersPointer(); + Member* m = members + o.size; + m->name.RawAssign(name); + m->value.RawAssign(value); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + new (&mit[o.size]) MapIterator(map->insert(MapPair(m->name.data_, o.size))); +#endif + ++o.size; + } + + MemberIterator DoRemoveMember(MemberIterator m) { + ObjectData& o = data_.o; + Member* members = GetMembersPointer(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(members); + MapIterator* mit = GetMapIterators(map); + SizeType mpos = static_cast(&*m - members); + map->erase(DropMapIterator(mit[mpos])); +#endif + MemberIterator last(members + (o.size - 1)); + if (o.size > 1 && m != last) { +#if RAPIDJSON_USE_MEMBERSMAP + new (&mit[mpos]) MapIterator(DropMapIterator(mit[&*last - members])); + mit[mpos]->second = mpos; +#endif + *m = *last; // Move the last one to this place + } + else { + m->~Member(); // Only one left, just destroy + } + --o.size; + return m; + } + + MemberIterator DoEraseMembers(ConstMemberIterator first, ConstMemberIterator last) { + ObjectData& o = data_.o; + MemberIterator beg = MemberBegin(), + pos = beg + (first - beg), + end = MemberEnd(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(GetMembersPointer()); + MapIterator* mit = GetMapIterators(map); +#endif + for (MemberIterator itr = pos; itr != last; ++itr) { +#if RAPIDJSON_USE_MEMBERSMAP + map->erase(DropMapIterator(mit[itr - beg])); +#endif + itr->~Member(); + } +#if RAPIDJSON_USE_MEMBERSMAP + if (first != last) { + // Move remaining members/iterators + MemberIterator next = pos + (last - first); + for (MemberIterator itr = pos; next != end; ++itr, ++next) { + std::memcpy(static_cast(&*itr), &*next, sizeof(Member)); + SizeType mpos = static_cast(itr - beg); + new (&mit[mpos]) MapIterator(DropMapIterator(mit[next - beg])); + mit[mpos]->second = mpos; + } + } +#else + std::memmove(static_cast(&*pos), &*last, + static_cast(end - last) * sizeof(Member)); +#endif + o.size -= static_cast(last - first); + return pos; + } + + template + void DoCopyMembers(const GenericValue& rhs, Allocator& allocator, bool copyConstStrings) { + RAPIDJSON_ASSERT(rhs.GetType() == kObjectType); + + data_.f.flags = kObjectFlag; + SizeType count = rhs.data_.o.size; + Member* lm = DoAllocMembers(count, allocator); + const typename GenericValue::Member* rm = rhs.GetMembersPointer(); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(lm); + MapIterator* mit = GetMapIterators(map); +#endif + for (SizeType i = 0; i < count; i++) { + new (&lm[i].name) GenericValue(rm[i].name, allocator, copyConstStrings); + new (&lm[i].value) GenericValue(rm[i].value, allocator, copyConstStrings); +#if RAPIDJSON_USE_MEMBERSMAP + new (&mit[i]) MapIterator(map->insert(MapPair(lm[i].name.data_, i))); +#endif + } + data_.o.size = data_.o.capacity = count; + SetMembersPointer(lm); + } + + // Initialize this value as array with initial data, without calling destructor. + void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { + data_.f.flags = kArrayFlag; + if (count) { + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(static_cast(e), values, count * sizeof(GenericValue)); + } + else + SetElementsPointer(0); + data_.a.size = data_.a.capacity = count; + } + + //! Initialize this value as object with initial data, without calling destructor. + void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { + data_.f.flags = kObjectFlag; + if (count) { + Member* m = DoAllocMembers(count, allocator); + SetMembersPointer(m); + std::memcpy(static_cast(m), members, count * sizeof(Member)); +#if RAPIDJSON_USE_MEMBERSMAP + Map* &map = GetMap(m); + MapIterator* mit = GetMapIterators(map); + for (SizeType i = 0; i < count; i++) { + new (&mit[i]) MapIterator(map->insert(MapPair(m[i].name.data_, i))); + } +#endif + } + else + SetMembersPointer(0); + data_.o.size = data_.o.capacity = count; + } + + //! Initialize this value as constant string, without calling destructor. + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { + data_.f.flags = kConstStringFlag; + SetStringPointer(s); + data_.s.length = s.length; + } + + //! Initialize this value as copy string with initial data, without calling destructor. + void SetStringRaw(StringRefType s, Allocator& allocator) { + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; + data_.ss.SetLength(s.length); + str = data_.ss.str; + std::memmove(str, s, s.length * sizeof(Ch)); + } else { + data_.f.flags = kCopyStringFlag; + data_.s.length = s.length; + str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); + SetStringPointer(str); + std::memcpy(str, s, s.length * sizeof(Ch)); + } + str[s.length] = '\0'; + } + + //! Assignment without calling destructor + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { + data_ = rhs.data_; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; + } + + template + bool StringEqual(const GenericValue& rhs) const { + RAPIDJSON_ASSERT(IsString()); + RAPIDJSON_ASSERT(rhs.IsString()); + + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); + if(len1 != len2) { return false; } + + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); + if(str1 == str2) { return true; } // fast path for constant string + + return (std::memcmp(str1, str2, sizeof(Ch) * len1) == 0); + } + + Data data_; +}; + +//! GenericValue with UTF8 encoding +typedef GenericValue > Value; + +/////////////////////////////////////////////////////////////////////////////// +// GenericDocument + +//! A document for parsing JSON text as DOM. +/*! + \note implements Handler concept + \tparam Encoding Encoding for both parsing and string storage. + \tparam Allocator Allocator for allocating memory for the DOM + \tparam StackAllocator Allocator for allocating memory for stack during parsing. + \warning Although GenericDocument inherits from GenericValue, the API does \b not provide any virtual functions, especially no virtual destructor. To avoid memory leaks, do not \c delete a GenericDocument object via a pointer to a GenericValue. +*/ +template +class GenericDocument : public GenericValue { +public: + typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericValue ValueType; //!< Value type of the document. + typedef Allocator AllocatorType; //!< Allocator type from template parameter. + typedef StackAllocator StackAllocatorType; //!< StackAllocator type from template parameter. + + //! Constructor + /*! Creates an empty document of specified type. + \param type Mandatory type of object to create. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + explicit GenericDocument(Type type, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + GenericValue(type), allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + + //! Constructor + /*! Creates an empty document which type is Null. + \param allocator Optional allocator for allocating memory. + \param stackCapacity Optional initial capacity of stack in bytes. + \param stackAllocator Optional allocator for allocating memory for stack. + */ + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity, StackAllocator* stackAllocator = 0) : + allocator_(allocator), ownAllocator_(0), stack_(stackAllocator, stackCapacity), parseResult_() + { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericDocument(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + : ValueType(std::forward(rhs)), // explicit cast to avoid prohibited move from Document + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(std::move(rhs.stack_)), + parseResult_(rhs.parseResult_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + } +#endif + + ~GenericDocument() { + // Clear the ::ValueType before ownAllocator is destroyed, ~ValueType() + // runs last and may access its elements or members which would be freed + // with an allocator like MemoryPoolAllocator (CrtAllocator does not + // free its data when destroyed, but MemoryPoolAllocator does). + if (ownAllocator_) { + ValueType::SetNull(); + } + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move assignment in C++11 + GenericDocument& operator=(GenericDocument&& rhs) RAPIDJSON_NOEXCEPT + { + // The cast to ValueType is necessary here, because otherwise it would + // attempt to call GenericValue's templated assignment operator. + ValueType::operator=(std::forward(rhs)); + + // Calling the destructor here would prematurely call stack_'s destructor + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = std::move(rhs.stack_); + parseResult_ = rhs.parseResult_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.parseResult_ = ParseResult(); + + return *this; + } +#endif + + //! Exchange the contents of this document with those of another. + /*! + \param rhs Another document. + \note Constant complexity. + \see GenericValue::Swap + */ + GenericDocument& Swap(GenericDocument& rhs) RAPIDJSON_NOEXCEPT { + ValueType::Swap(rhs); + stack_.Swap(rhs.stack_); + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(parseResult_, rhs.parseResult_); + return *this; + } + + // Allow Swap with ValueType. + // Refer to Effective C++ 3rd Edition/Item 33: Avoid hiding inherited names. + using ValueType::Swap; + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.doc, b.doc); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericDocument& a, GenericDocument& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //! Populate this document by a generator which produces SAX events. + /*! \tparam Generator A functor with bool f(Handler) prototype. + \param g Generator functor which sends SAX events to the parameter. + \return The document itself for fluent API. + */ + template + GenericDocument& Populate(Generator& g) { + ClearStackOnExit scope(*this); + if (g(*this)) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //!@name Parse from stream + //!@{ + + //! Parse JSON text from an input stream (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam SourceEncoding Encoding of input stream + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + GenericReader reader( + stack_.HasAllocator() ? &stack_.GetAllocator() : 0); + ClearStackOnExit scope(*this); + parseResult_ = reader.template Parse(is, *this); + if (parseResult_) { + RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object + ValueType::operator=(*stack_.template Pop(1));// Move value from stack to document + } + return *this; + } + + //! Parse JSON text from an input stream + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + + //! Parse JSON text from an input stream (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \param is Input stream to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); + } + //!@} + + //!@name Parse in-place from mutable string + //!@{ + + //! Parse JSON text from a mutable string + /*! \tparam parseFlags Combination of \ref ParseFlag. + \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + template + GenericDocument& ParseInsitu(Ch* str) { + GenericInsituStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a mutable string (with \ref kParseDefaultFlags) + /*! \param str Mutable zero-terminated string to be parsed. + \return The document itself for fluent API. + */ + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); + } + //!@} + + //!@name Parse from read-only string + //!@{ + + //! Parse JSON text from a read-only string (with Encoding conversion) + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \tparam SourceEncoding Transcoding from input Encoding + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + GenericStringStream s(str); + return ParseStream(s); + } + + //! Parse JSON text from a read-only string + /*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag). + \param str Read-only zero-terminated string to be parsed. + */ + template + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + //! Parse JSON text from a read-only string (with \ref kParseDefaultFlags) + /*! \param str Read-only zero-terminated string to be parsed. + */ + GenericDocument& Parse(const Ch* str) { + return Parse(str); + } + + template + GenericDocument& Parse(const typename SourceEncoding::Ch* str, size_t length) { + RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); + MemoryStream ms(reinterpret_cast(str), length * sizeof(typename SourceEncoding::Ch)); + EncodedInputStream is(ms); + ParseStream(is); + return *this; + } + + template + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + + GenericDocument& Parse(const Ch* str, size_t length) { + return Parse(str, length); + } + +#if RAPIDJSON_HAS_STDSTRING + template + GenericDocument& Parse(const std::basic_string& str) { + // c_str() is constant complexity according to standard. Should be faster than Parse(const char*, size_t) + return Parse(str.c_str()); + } + + template + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str.c_str()); + } + + GenericDocument& Parse(const std::basic_string& str) { + return Parse(str); + } +#endif // RAPIDJSON_HAS_STDSTRING + + //!@} + + //!@name Handling parse errors + //!@{ + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseError() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + + //! Implicit conversion to get the last parse result +#ifndef __clang // -Wdocumentation + /*! \return \ref ParseResult of the last parse operation + + \code + Document doc; + ParseResult ok = doc.Parse(json); + if (!ok) + printf( "JSON parse error: %s (%u)\n", GetParseError_En(ok.Code()), ok.Offset()); + \endcode + */ +#endif + operator ParseResult() const { return parseResult_; } + //!@} + + //! Get the allocator of this document. + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + //! Get the capacity of stack in bytes. + size_t GetStackCapacity() const { return stack_.GetCapacity(); } + +private: + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericDocument& d) : d_(d) {} + ~ClearStackOnExit() { d_.ClearStack(); } + private: + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + GenericDocument& d_; + }; + + // callers of the following private Handler functions + // template friend class GenericReader; // for parsing + template friend class GenericValue; // for deep copying + +public: + // Implementation of Handler + bool Null() { new (stack_.template Push()) ValueType(); return true; } + bool Bool(bool b) { new (stack_.template Push()) ValueType(b); return true; } + bool Int(int i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint(unsigned i) { new (stack_.template Push()) ValueType(i); return true; } + bool Int64(int64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Uint64(uint64_t i) { new (stack_.template Push()) ValueType(i); return true; } + bool Double(double d) { new (stack_.template Push()) ValueType(d); return true; } + + bool RawNumber(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool String(const Ch* str, SizeType length, bool copy) { + if (copy) + new (stack_.template Push()) ValueType(str, length, GetAllocator()); + else + new (stack_.template Push()) ValueType(str, length); + return true; + } + + bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + + bool EndObject(SizeType memberCount) { + typename ValueType::Member* members = stack_.template Pop(memberCount); + stack_.template Top()->SetObjectRaw(members, memberCount, GetAllocator()); + return true; + } + + bool StartArray() { new (stack_.template Push()) ValueType(kArrayType); return true; } + + bool EndArray(SizeType elementCount) { + ValueType* elements = stack_.template Pop(elementCount); + stack_.template Top()->SetArrayRaw(elements, elementCount, GetAllocator()); + return true; + } + +private: + //! Prohibit copying + GenericDocument(const GenericDocument&); + //! Prohibit assignment + GenericDocument& operator=(const GenericDocument&); + + void ClearStack() { + if (Allocator::kNeedFree) + while (stack_.GetSize() > 0) // Here assumes all elements in stack array are GenericValue (Member is actually 2 GenericValue objects) + (stack_.template Pop(1))->~ValueType(); + else + stack_.Clear(); + stack_.ShrinkToFit(); + } + + void Destroy() { + RAPIDJSON_DELETE(ownAllocator_); + } + + static const size_t kDefaultStackCapacity = 1024; + Allocator* allocator_; + Allocator* ownAllocator_; + internal::Stack stack_; + ParseResult parseResult_; +}; + +//! GenericDocument with UTF8 encoding +typedef GenericDocument > Document; + + +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericArray { +public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueT* ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } + ~GenericArray() {} + + operator ValueType&() const { return value_; } + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } +#endif + +private: + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +//! Helper class for accessing Value of object type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetObject(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } + ~GenericObject() {} + + operator ValueType&() const { return value_; } + SizeType MemberCount() const { return value_.MemberCount(); } + SizeType MemberCapacity() const { return value_.MemberCapacity(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } +#endif + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + GenericObject MemberReserve(SizeType newCapacity, AllocatorType &allocator) const { value_.MemberReserve(newCapacity, allocator); return *this; } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { return value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { return value_.FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } +#endif + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_STDSTRING + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } +#endif + +private: + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#ifdef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#pragma pop_macro("GetObject") +#undef RAPIDJSON_WINDOWS_GETOBJECT_WORKAROUND_APPLIED +#endif + +#endif // RAPIDJSON_DOCUMENT_H_ diff --git a/Minecraft.Server/vendor/rapidjson/encodedstream.h b/Minecraft.Server/vendor/rapidjson/encodedstream.h new file mode 100644 index 00000000..cf046b89 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/encodedstream.h @@ -0,0 +1,299 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODEDSTREAM_H_ +#define RAPIDJSON_ENCODEDSTREAM_H_ + +#include "stream.h" +#include "memorystream.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Input byte stream wrapper with a statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam InputByteStream Type of input byte stream. For example, FileReadStream. +*/ +template +class EncodedInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedInputStream(InputByteStream& is) : is_(is) { + current_ = Encoding::TakeBOM(is_); + } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = Encoding::Take(is_); return c; } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); + + InputByteStream& is_; + Ch current_; +}; + +//! Specialized for UTF8 MemoryStream. +template <> +class EncodedInputStream, MemoryStream> { +public: + typedef UTF8<>::Ch Ch; + + EncodedInputStream(MemoryStream& is) : is_(is) { + if (static_cast(is_.Peek()) == 0xEFu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBBu) is_.Take(); + if (static_cast(is_.Peek()) == 0xBFu) is_.Take(); + } + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() const { return is_.Tell(); } + + // Not implemented + void Put(Ch) {} + void Flush() {} + Ch* PutBegin() { return 0; } + size_t PutEnd(Ch*) { return 0; } + + MemoryStream& is_; + +private: + EncodedInputStream(const EncodedInputStream&); + EncodedInputStream& operator=(const EncodedInputStream&); +}; + +//! Output byte stream wrapper with statically bound encoding. +/*! + \tparam Encoding The interpretation of encoding of the stream. Either UTF8, UTF16LE, UTF16BE, UTF32LE, UTF32BE. + \tparam OutputByteStream Type of input byte stream. For example, FileWriteStream. +*/ +template +class EncodedOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef typename Encoding::Ch Ch; + + EncodedOutputStream(OutputByteStream& os, bool putBOM = true) : os_(os) { + if (putBOM) + Encoding::PutBOM(os_); + } + + void Put(Ch c) { Encoding::Put(os_, c); } + void Flush() { os_.Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + EncodedOutputStream(const EncodedOutputStream&); + EncodedOutputStream& operator=(const EncodedOutputStream&); + + OutputByteStream& os_; +}; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + +//! Input stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for reading. + \tparam InputByteStream type of input byte stream to be wrapped. +*/ +template +class AutoUTFInputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param is input stream to be wrapped. + \param type UTF encoding type if it is not detected from the stream. + */ + AutoUTFInputStream(InputByteStream& is, UTFType type = kUTF8) : is_(&is), type_(type), hasBOM_(false) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + DetectType(); + static const TakeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Take) }; + takeFunc_ = f[type_]; + current_ = takeFunc_(*is_); + } + + UTFType GetType() const { return type_; } + bool HasBOM() const { return hasBOM_; } + + Ch Peek() const { return current_; } + Ch Take() { Ch c = current_; current_ = takeFunc_(*is_); return c; } + size_t Tell() const { return is_->Tell(); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFInputStream(const AutoUTFInputStream&); + AutoUTFInputStream& operator=(const AutoUTFInputStream&); + + // Detect encoding type with BOM or RFC 4627 + void DetectType() { + // BOM (Byte Order Mark): + // 00 00 FE FF UTF-32BE + // FF FE 00 00 UTF-32LE + // FE FF UTF-16BE + // FF FE UTF-16LE + // EF BB BF UTF-8 + + const unsigned char* c = reinterpret_cast(is_->Peek4()); + if (!c) + return; + + unsigned bom = static_cast(c[0] | (c[1] << 8) | (c[2] << 16) | (c[3] << 24)); + hasBOM_ = false; + if (bom == 0xFFFE0000) { type_ = kUTF32BE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if (bom == 0x0000FEFF) { type_ = kUTF32LE; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFFFE) { type_ = kUTF16BE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFF) == 0xFEFF) { type_ = kUTF16LE; hasBOM_ = true; is_->Take(); is_->Take(); } + else if ((bom & 0xFFFFFF) == 0xBFBBEF) { type_ = kUTF8; hasBOM_ = true; is_->Take(); is_->Take(); is_->Take(); } + + // RFC 4627: Section 3 + // "Since the first two characters of a JSON text will always be ASCII + // characters [RFC0020], it is possible to determine whether an octet + // stream is UTF-8, UTF-16 (BE or LE), or UTF-32 (BE or LE) by looking + // at the pattern of nulls in the first four octets." + // 00 00 00 xx UTF-32BE + // 00 xx 00 xx UTF-16BE + // xx 00 00 00 UTF-32LE + // xx 00 xx 00 UTF-16LE + // xx xx xx xx UTF-8 + + if (!hasBOM_) { + int pattern = (c[0] ? 1 : 0) | (c[1] ? 2 : 0) | (c[2] ? 4 : 0) | (c[3] ? 8 : 0); + switch (pattern) { + case 0x08: type_ = kUTF32BE; break; + case 0x0A: type_ = kUTF16BE; break; + case 0x01: type_ = kUTF32LE; break; + case 0x05: type_ = kUTF16LE; break; + case 0x0F: type_ = kUTF8; break; + default: break; // Use type defined by user. + } + } + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + } + + typedef Ch (*TakeFunc)(InputByteStream& is); + InputByteStream* is_; + UTFType type_; + Ch current_; + TakeFunc takeFunc_; + bool hasBOM_; +}; + +//! Output stream wrapper with dynamically bound encoding and automatic encoding detection. +/*! + \tparam CharType Type of character for writing. + \tparam OutputByteStream type of output byte stream to be wrapped. +*/ +template +class AutoUTFOutputStream { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); +public: + typedef CharType Ch; + + //! Constructor. + /*! + \param os output stream to be wrapped. + \param type UTF encoding type. + \param putBOM Whether to write BOM at the beginning of the stream. + */ + AutoUTFOutputStream(OutputByteStream& os, UTFType type, bool putBOM) : os_(&os), type_(type) { + RAPIDJSON_ASSERT(type >= kUTF8 && type <= kUTF32BE); + + // Runtime check whether the size of character type is sufficient. It only perform checks with assertion. + if (type_ == kUTF16LE || type_ == kUTF16BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 2); + if (type_ == kUTF32LE || type_ == kUTF32BE) RAPIDJSON_ASSERT(sizeof(Ch) >= 4); + + static const PutFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Put) }; + putFunc_ = f[type_]; + + if (putBOM) + PutBOM(); + } + + UTFType GetType() const { return type_; } + + void Put(Ch c) { putFunc_(*os_, c); } + void Flush() { os_->Flush(); } + + // Not implemented + Ch Peek() const { RAPIDJSON_ASSERT(false); return 0;} + Ch Take() { RAPIDJSON_ASSERT(false); return 0;} + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + AutoUTFOutputStream(const AutoUTFOutputStream&); + AutoUTFOutputStream& operator=(const AutoUTFOutputStream&); + + void PutBOM() { + typedef void (*PutBOMFunc)(OutputByteStream&); + static const PutBOMFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(PutBOM) }; + f[type_](*os_); + } + + typedef void (*PutFunc)(OutputByteStream&, Ch); + + OutputByteStream* os_; + UTFType type_; + PutFunc putFunc_; +}; + +#undef RAPIDJSON_ENCODINGS_FUNC + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/Minecraft.Server/vendor/rapidjson/encodings.h b/Minecraft.Server/vendor/rapidjson/encodings.h new file mode 100644 index 00000000..c453c0da --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/encodings.h @@ -0,0 +1,716 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ENCODINGS_H_ +#define RAPIDJSON_ENCODINGS_H_ + +#include "rapidjson.h" + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4244) // conversion from 'type1' to 'type2', possible loss of data +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#elif defined(__GNUC__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(overflow) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Encoding + +/*! \class rapidjson::Encoding + \brief Concept for encoding of Unicode characters. + +\code +concept Encoding { + typename Ch; //! Type of character. A "character" is actually a code unit in unicode's definition. + + enum { supportUnicode = 1 }; // or 0 if not supporting unicode + + //! \brief Encode a Unicode codepoint to an output stream. + //! \param os Output stream. + //! \param codepoint An unicode codepoint, ranging from 0x0 to 0x10FFFF inclusively. + template + static void Encode(OutputStream& os, unsigned codepoint); + + //! \brief Decode a Unicode codepoint from an input stream. + //! \param is Input stream. + //! \param codepoint Output of the unicode codepoint. + //! \return true if a valid codepoint can be decoded from the stream. + template + static bool Decode(InputStream& is, unsigned* codepoint); + + //! \brief Validate one Unicode codepoint from an encoded stream. + //! \param is Input stream to obtain codepoint. + //! \param os Output for copying one codepoint. + //! \return true if it is valid. + //! \note This function just validating and copying the codepoint without actually decode it. + template + static bool Validate(InputStream& is, OutputStream& os); + + // The following functions are deal with byte streams. + + //! Take a character from input byte stream, skip BOM if exist. + template + static CharType TakeBOM(InputByteStream& is); + + //! Take a character from input byte stream. + template + static Ch Take(InputByteStream& is); + + //! Put BOM to output byte stream. + template + static void PutBOM(OutputByteStream& os); + + //! Put a character to output byte stream. + template + static void Put(OutputByteStream& os, Ch c); +}; +\endcode +*/ + +/////////////////////////////////////////////////////////////////////////////// +// UTF8 + +//! UTF-8 encoding. +/*! http://en.wikipedia.org/wiki/UTF-8 + http://tools.ietf.org/html/rfc3629 + \tparam CharType Code unit for storing 8-bit UTF-8 data. Default is char. + \note implements Encoding concept +*/ +template +struct UTF8 { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + os.Put(static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + os.Put(static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + os.Put(static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + os.Put(static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + os.Put(static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + os.Put(static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + if (codepoint <= 0x7F) + PutUnsafe(os, static_cast(codepoint & 0xFF)); + else if (codepoint <= 0x7FF) { + PutUnsafe(os, static_cast(0xC0 | ((codepoint >> 6) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint & 0x3F)))); + } + else if (codepoint <= 0xFFFF) { + PutUnsafe(os, static_cast(0xE0 | ((codepoint >> 12) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, static_cast(0xF0 | ((codepoint >> 18) & 0xFF))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 12) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | ((codepoint >> 6) & 0x3F))); + PutUnsafe(os, static_cast(0x80 | (codepoint & 0x3F))); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { +#define RAPIDJSON_COPY() c = is.Take(); *codepoint = (*codepoint << 6) | (static_cast(c) & 0x3Fu) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + typename InputStream::Ch c = is.Take(); + if (!(c & 0x80)) { + *codepoint = static_cast(c); + return true; + } + + unsigned char type = GetRange(static_cast(c)); + if (type >= 32) { + *codepoint = 0; + } else { + *codepoint = (0xFFu >> type) & static_cast(c); + } + bool result = true; + switch (type) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + template + static bool Validate(InputStream& is, OutputStream& os) { +#define RAPIDJSON_COPY() if (c != '\0') os.Put(c = is.Take()) +#define RAPIDJSON_TRANS(mask) result &= ((GetRange(static_cast(c)) & mask) != 0) +#define RAPIDJSON_TAIL() RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x70) + Ch c = static_cast(-1); + RAPIDJSON_COPY(); + if (!(c & 0x80)) + return true; + + bool result = true; + switch (GetRange(static_cast(c))) { + case 2: RAPIDJSON_TAIL(); return result; + case 3: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 4: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x50); RAPIDJSON_TAIL(); return result; + case 5: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x10); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 6: RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + case 10: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x20); RAPIDJSON_TAIL(); return result; + case 11: RAPIDJSON_COPY(); RAPIDJSON_TRANS(0x60); RAPIDJSON_TAIL(); RAPIDJSON_TAIL(); return result; + default: return false; + } +#undef RAPIDJSON_COPY +#undef RAPIDJSON_TRANS +#undef RAPIDJSON_TAIL + } + + static unsigned char GetRange(unsigned char c) { + // Referring to DFA of http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + // With new mapping 1 -> 0x10, 7 -> 0x20, 9 -> 0x40, such that AND operation can test multiple types. + static const unsigned char type[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + }; + return type[c]; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + typename InputByteStream::Ch c = Take(is); + if (static_cast(c) != 0xEFu) return c; + c = is.Take(); + if (static_cast(c) != 0xBBu) return c; + c = is.Take(); + if (static_cast(c) != 0xBFu) return c; + c = is.Take(); + return c; + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xEFu)); + os.Put(static_cast(0xBBu)); + os.Put(static_cast(0xBFu)); + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF16 + +//! UTF-16 encoding. +/*! http://en.wikipedia.org/wiki/UTF-16 + http://tools.ietf.org/html/rfc2781 + \tparam CharType Type for storing 16-bit UTF-16 data. Default is wchar_t. C++11 may use char16_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF16LE and UTF16BE, which handle endianness. +*/ +template +struct UTF16 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 2); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + os.Put(static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + os.Put(static_cast((v >> 10) | 0xD800)); + os.Put(static_cast((v & 0x3FF) | 0xDC00)); + } + } + + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + if (codepoint <= 0xFFFF) { + RAPIDJSON_ASSERT(codepoint < 0xD800 || codepoint > 0xDFFF); // Code point itself cannot be surrogate pair + PutUnsafe(os, static_cast(codepoint)); + } + else { + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + unsigned v = codepoint - 0x10000; + PutUnsafe(os, static_cast((v >> 10) | 0xD800)); + PutUnsafe(os, static_cast((v & 0x3FF) | 0xDC00)); + } + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + typename InputStream::Ch c = is.Take(); + if (c < 0xD800 || c > 0xDFFF) { + *codepoint = static_cast(c); + return true; + } + else if (c <= 0xDBFF) { + *codepoint = (static_cast(c) & 0x3FF) << 10; + c = is.Take(); + *codepoint |= (static_cast(c) & 0x3FF); + *codepoint += 0x10000; + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 2); + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 2); + typename InputStream::Ch c; + os.Put(static_cast(c = is.Take())); + if (c < 0xD800 || c > 0xDFFF) + return true; + else if (c <= 0xDBFF) { + os.Put(c = is.Take()); + return c >= 0xDC00 && c <= 0xDFFF; + } + return false; + } +}; + +//! UTF-16 little endian encoding. +template +struct UTF16LE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(static_cast(c) & 0xFFu)); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + } +}; + +//! UTF-16 big endian encoding. +template +struct UTF16BE : UTF16 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0xFEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((static_cast(c) >> 8) & 0xFFu)); + os.Put(static_cast(static_cast(c) & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// UTF32 + +//! UTF-32 encoding. +/*! http://en.wikipedia.org/wiki/UTF-32 + \tparam CharType Type for storing 32-bit UTF-32 data. Default is unsigned. C++11 may use char32_t instead. + \note implements Encoding concept + + \note For in-memory access, no need to concern endianness. The code units and code points are represented by CPU's endianness. + For streaming, use UTF32LE and UTF32BE, which handle endianness. +*/ +template +struct UTF32 { + typedef CharType Ch; + RAPIDJSON_STATIC_ASSERT(sizeof(Ch) >= 4); + + enum { supportUnicode = 1 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + os.Put(codepoint); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputStream::Ch) >= 4); + RAPIDJSON_ASSERT(codepoint <= 0x10FFFF); + PutUnsafe(os, codepoint); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c = is.Take(); + *codepoint = c; + return c <= 0x10FFFF; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputStream::Ch) >= 4); + Ch c; + os.Put(c = is.Take()); + return c <= 0x10FFFF; + } +}; + +//! UTF-32 little endian enocoding. +template +struct UTF32LE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(is.Take()); + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 24; + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0xFFu)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 24) & 0xFFu)); + } +}; + +//! UTF-32 big endian encoding. +template +struct UTF32BE : UTF32 { + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + CharType c = Take(is); + return static_cast(c) == 0x0000FEFFu ? Take(is) : c; + } + + template + static CharType Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + unsigned c = static_cast(static_cast(is.Take())) << 24; + c |= static_cast(static_cast(is.Take())) << 16; + c |= static_cast(static_cast(is.Take())) << 8; + c |= static_cast(static_cast(is.Take())); + return static_cast(c); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0x00u)); + os.Put(static_cast(0xFEu)); + os.Put(static_cast(0xFFu)); + } + + template + static void Put(OutputByteStream& os, CharType c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast((c >> 24) & 0xFFu)); + os.Put(static_cast((c >> 16) & 0xFFu)); + os.Put(static_cast((c >> 8) & 0xFFu)); + os.Put(static_cast(c & 0xFFu)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// ASCII + +//! ASCII encoding. +/*! http://en.wikipedia.org/wiki/ASCII + \tparam CharType Code unit for storing 7-bit ASCII data. Default is char. + \note implements Encoding concept +*/ +template +struct ASCII { + typedef CharType Ch; + + enum { supportUnicode = 0 }; + + template + static void Encode(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + os.Put(static_cast(codepoint & 0xFF)); + } + + template + static void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + RAPIDJSON_ASSERT(codepoint <= 0x7F); + PutUnsafe(os, static_cast(codepoint & 0xFF)); + } + + template + static bool Decode(InputStream& is, unsigned* codepoint) { + uint8_t c = static_cast(is.Take()); + *codepoint = c; + return c <= 0X7F; + } + + template + static bool Validate(InputStream& is, OutputStream& os) { + uint8_t c = static_cast(is.Take()); + os.Put(static_cast(c)); + return c <= 0x7F; + } + + template + static CharType TakeBOM(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + uint8_t c = static_cast(Take(is)); + return static_cast(c); + } + + template + static Ch Take(InputByteStream& is) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename InputByteStream::Ch) == 1); + return static_cast(is.Take()); + } + + template + static void PutBOM(OutputByteStream& os) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + (void)os; + } + + template + static void Put(OutputByteStream& os, Ch c) { + RAPIDJSON_STATIC_ASSERT(sizeof(typename OutputByteStream::Ch) == 1); + os.Put(static_cast(c)); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// AutoUTF + +//! Runtime-specified UTF encoding type of a stream. +enum UTFType { + kUTF8 = 0, //!< UTF-8. + kUTF16LE = 1, //!< UTF-16 little endian. + kUTF16BE = 2, //!< UTF-16 big endian. + kUTF32LE = 3, //!< UTF-32 little endian. + kUTF32BE = 4 //!< UTF-32 big endian. +}; + +//! Dynamically select encoding according to stream's runtime-specified UTF encoding type. +/*! \note This class can be used with AutoUTFInputtStream and AutoUTFOutputStream, which provides GetType(). +*/ +template +struct AutoUTF { + typedef CharType Ch; + + enum { supportUnicode = 1 }; + +#define RAPIDJSON_ENCODINGS_FUNC(x) UTF8::x, UTF16LE::x, UTF16BE::x, UTF32LE::x, UTF32BE::x + + template + static RAPIDJSON_FORCEINLINE void Encode(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Encode) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE void EncodeUnsafe(OutputStream& os, unsigned codepoint) { + typedef void (*EncodeFunc)(OutputStream&, unsigned); + static const EncodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(EncodeUnsafe) }; + (*f[os.GetType()])(os, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Decode(InputStream& is, unsigned* codepoint) { + typedef bool (*DecodeFunc)(InputStream&, unsigned*); + static const DecodeFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Decode) }; + return (*f[is.GetType()])(is, codepoint); + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + typedef bool (*ValidateFunc)(InputStream&, OutputStream&); + static const ValidateFunc f[] = { RAPIDJSON_ENCODINGS_FUNC(Validate) }; + return (*f[is.GetType()])(is, os); + } + +#undef RAPIDJSON_ENCODINGS_FUNC +}; + +/////////////////////////////////////////////////////////////////////////////// +// Transcoder + +//! Encoding conversion. +template +struct Transcoder { + //! Take one Unicode codepoint from source encoding, convert it to target encoding and put it to the output stream. + template + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::Encode(os, codepoint); + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + unsigned codepoint; + if (!SourceEncoding::Decode(is, &codepoint)) + return false; + TargetEncoding::EncodeUnsafe(os, codepoint); + return true; + } + + //! Validate one Unicode codepoint from an encoded stream. + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Transcode(is, os); // Since source/target encoding is different, must transcode. + } +}; + +// Forward declaration. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c); + +//! Specialization of Transcoder with same source and target encoding. +template +struct Transcoder { + template + static RAPIDJSON_FORCEINLINE bool Transcode(InputStream& is, OutputStream& os) { + os.Put(is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool TranscodeUnsafe(InputStream& is, OutputStream& os) { + PutUnsafe(os, is.Take()); // Just copy one code unit. This semantic is different from primary template class. + return true; + } + + template + static RAPIDJSON_FORCEINLINE bool Validate(InputStream& is, OutputStream& os) { + return Encoding::Validate(is, os); // source/target encoding are the same + } +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || (defined(_MSC_VER) && !defined(__clang__)) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ENCODINGS_H_ diff --git a/Minecraft.Server/vendor/rapidjson/error/en.h b/Minecraft.Server/vendor/rapidjson/error/en.h new file mode 100644 index 00000000..c87b04eb --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/error/en.h @@ -0,0 +1,176 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_EN_H_ +#define RAPIDJSON_ERROR_EN_H_ + +#include "error.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +RAPIDJSON_DIAG_OFF(covered-switch-default) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Maps error code of parsing into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param parseErrorCode Error code obtained in parsing. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty."); + case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values."); + + case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoding in string."); + + case kParseErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to be stored in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error."); + case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +//! Maps error code of validation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param validateErrorCode Error code obtained from validator. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrors: return RAPIDJSON_ERROR_STRING("One or more validation errors have occurred"); + case kValidateErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kValidateErrorMultipleOf: return RAPIDJSON_ERROR_STRING("Number '%actual' is not a multiple of the 'multipleOf' value '%expected'."); + case kValidateErrorMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than the 'maximum' value '%expected'."); + case kValidateErrorExclusiveMaximum: return RAPIDJSON_ERROR_STRING("Number '%actual' is greater than or equal to the 'exclusiveMaximum' value '%expected'."); + case kValidateErrorMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than the 'minimum' value '%expected'."); + case kValidateErrorExclusiveMinimum: return RAPIDJSON_ERROR_STRING("Number '%actual' is less than or equal to the 'exclusiveMinimum' value '%expected'."); + + case kValidateErrorMaxLength: return RAPIDJSON_ERROR_STRING("String '%actual' is longer than the 'maxLength' value '%expected'."); + case kValidateErrorMinLength: return RAPIDJSON_ERROR_STRING("String '%actual' is shorter than the 'minLength' value '%expected'."); + case kValidateErrorPattern: return RAPIDJSON_ERROR_STRING("String '%actual' does not match the 'pattern' regular expression."); + + case kValidateErrorMaxItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is longer than the 'maxItems' value '%expected'."); + case kValidateErrorMinItems: return RAPIDJSON_ERROR_STRING("Array of length '%actual' is shorter than the 'minItems' value '%expected'."); + case kValidateErrorUniqueItems: return RAPIDJSON_ERROR_STRING("Array has duplicate items at indices '%duplicates' but 'uniqueItems' is true."); + case kValidateErrorAdditionalItems: return RAPIDJSON_ERROR_STRING("Array has an additional item at index '%disallowed' that is not allowed by the schema."); + + case kValidateErrorMaxProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is more than 'maxProperties' value '%expected'."); + case kValidateErrorMinProperties: return RAPIDJSON_ERROR_STRING("Object has '%actual' members which is less than 'minProperties' value '%expected'."); + case kValidateErrorRequired: return RAPIDJSON_ERROR_STRING("Object is missing the following members required by the schema: '%missing'."); + case kValidateErrorAdditionalProperties: return RAPIDJSON_ERROR_STRING("Object has an additional member '%disallowed' that is not allowed by the schema."); + case kValidateErrorPatternProperties: return RAPIDJSON_ERROR_STRING("Object has 'patternProperties' that are not allowed by the schema."); + case kValidateErrorDependencies: return RAPIDJSON_ERROR_STRING("Object has missing property or schema dependencies, refer to following errors."); + + case kValidateErrorEnum: return RAPIDJSON_ERROR_STRING("Property has a value that is not one of its allowed enumerated values."); + case kValidateErrorType: return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'."); + + case kValidateErrorOneOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors."); + case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf', indices '%matches'."); + case kValidateErrorAllOf: return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors."); + case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors."); + case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'."); + + case kValidateErrorReadOnly: return RAPIDJSON_ERROR_STRING("Property is read-only but has been provided when validation is for writing."); + case kValidateErrorWriteOnly: return RAPIDJSON_ERROR_STRING("Property is write-only but has been provided when validation is for reading."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +//! Maps error code of schema document compilation into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param schemaErrorCode Error code obtained from compiling the schema document. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ + inline const RAPIDJSON_ERROR_CHARTYPE* GetSchemaError_En(SchemaErrorCode schemaErrorCode) { + switch (schemaErrorCode) { + case kSchemaErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kSchemaErrorStartUnknown: return RAPIDJSON_ERROR_STRING("Pointer '%value' to start of schema does not resolve to a location in the document."); + case kSchemaErrorRefPlainName: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' must be a JSON pointer."); + case kSchemaErrorRefInvalid: return RAPIDJSON_ERROR_STRING("$ref must not be an empty string."); + case kSchemaErrorRefPointerInvalid: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' is not a valid JSON pointer at offset '%offset'."); + case kSchemaErrorRefUnknown: return RAPIDJSON_ERROR_STRING("$ref '%value' does not resolve to a location in the target document."); + case kSchemaErrorRefCyclical: return RAPIDJSON_ERROR_STRING("$ref '%value' is cyclical."); + case kSchemaErrorRefNoRemoteProvider: return RAPIDJSON_ERROR_STRING("$ref is remote but there is no remote provider."); + case kSchemaErrorRefNoRemoteSchema: return RAPIDJSON_ERROR_STRING("$ref '%value' is remote but the remote provider did not return a schema."); + case kSchemaErrorRegexInvalid: return RAPIDJSON_ERROR_STRING("Invalid regular expression '%value' in 'pattern' or 'patternProperties'."); + case kSchemaErrorSpecUnknown: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not recognized."); + case kSchemaErrorSpecUnsupported: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not supported."); + case kSchemaErrorSpecIllegal: return RAPIDJSON_ERROR_STRING("Both JSON schema draft and OpenAPI version found in document."); + case kSchemaErrorReadOnlyAndWriteOnly: return RAPIDJSON_ERROR_STRING("Property must not be both 'readOnly' and 'writeOnly'."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } + } + +//! Maps error code of pointer parse into error message. +/*! + \ingroup RAPIDJSON_ERRORS + \param pointerParseErrorCode Error code obtained from pointer parse. + \return the error message. + \note User can make a copy of this function for localization. + Using switch-case is safer for future modification of error codes. +*/ +inline const RAPIDJSON_ERROR_CHARTYPE* GetPointerParseError_En(PointerParseErrorCode pointerParseErrorCode) { + switch (pointerParseErrorCode) { + case kPointerParseErrorNone: return RAPIDJSON_ERROR_STRING("No error."); + + case kPointerParseErrorTokenMustBeginWithSolidus: return RAPIDJSON_ERROR_STRING("A token must begin with a '/'."); + case kPointerParseErrorInvalidEscape: return RAPIDJSON_ERROR_STRING("Invalid escape."); + case kPointerParseErrorInvalidPercentEncoding: return RAPIDJSON_ERROR_STRING("Invalid percent encoding in URI fragment."); + case kPointerParseErrorCharacterMustPercentEncode: return RAPIDJSON_ERROR_STRING("A character must be percent encoded in a URI fragment."); + + default: return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_EN_H_ diff --git a/Minecraft.Server/vendor/rapidjson/error/error.h b/Minecraft.Server/vendor/rapidjson/error/error.h new file mode 100644 index 00000000..cae345db --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/error/error.h @@ -0,0 +1,285 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ERROR_ERROR_H_ +#define RAPIDJSON_ERROR_ERROR_H_ + +#include "../rapidjson.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +/*! \file error.h */ + +/*! \defgroup RAPIDJSON_ERRORS RapidJSON error handling */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_CHARTYPE + +//! Character type of error messages. +/*! \ingroup RAPIDJSON_ERRORS + The default character type is \c char. + On Windows, user can define this macro as \c TCHAR for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ERROR_STRING + +//! Macro for converting string literal to \ref RAPIDJSON_ERROR_CHARTYPE[]. +/*! \ingroup RAPIDJSON_ERRORS + By default this conversion macro does nothing. + On Windows, user can define this macro as \c _T(x) for supporting both + unicode/non-unicode settings. +*/ +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseErrorCode + +//! Error code of parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericReader::Parse, GenericReader::GetParseErrorCode +*/ +enum ParseErrorCode { + kParseErrorNone = 0, //!< No error. + + kParseErrorDocumentEmpty, //!< The document is empty. + kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values. + + kParseErrorValueInvalid, //!< Invalid value. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Missing a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoding in string. + + kParseErrorNumberTooBig, //!< Number too big to be stored in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent, //!< Miss exponent in number. + + kParseErrorTermination, //!< Parsing was terminated. + kParseErrorUnspecificSyntaxError //!< Unspecific syntax error. +}; + +//! Result of parsing (wraps ParseErrorCode) +/*! + \ingroup RAPIDJSON_ERRORS + \code + Document doc; + ParseResult ok = doc.Parse("[42]"); + if (!ok) { + fprintf(stderr, "JSON parse error: %s (%u)", + GetParseError_En(ok.Code()), ok.Offset()); + exit(EXIT_FAILURE); + } + \endcode + \see GenericReader::Parse, GenericDocument::Parse +*/ +struct ParseResult { + //!! Unspecified boolean type + typedef bool (ParseResult::*BooleanType)() const; +public: + //! Default constructor, no error. + ParseResult() : code_(kParseErrorNone), offset_(0) {} + //! Constructor to set an error. + ParseResult(ParseErrorCode code, size_t offset) : code_(code), offset_(offset) {} + + //! Get the error code. + ParseErrorCode Code() const { return code_; } + //! Get the error offset, if \ref IsError(), 0 otherwise. + size_t Offset() const { return offset_; } + + //! Explicit conversion to \c bool, returns \c true, iff !\ref IsError(). + operator BooleanType() const { return !IsError() ? &ParseResult::IsError : NULL; } + //! Whether the result is an error. + bool IsError() const { return code_ != kParseErrorNone; } + + bool operator==(const ParseResult& that) const { return code_ == that.code_; } + bool operator==(ParseErrorCode code) const { return code_ == code; } + friend bool operator==(ParseErrorCode code, const ParseResult & err) { return code == err.code_; } + + bool operator!=(const ParseResult& that) const { return !(*this == that); } + bool operator!=(ParseErrorCode code) const { return !(*this == code); } + friend bool operator!=(ParseErrorCode code, const ParseResult & err) { return err != code; } + + //! Reset error code. + void Clear() { Set(kParseErrorNone); } + //! Update error code and offset. + void Set(ParseErrorCode code, size_t offset = 0) { code_ = code; offset_ = offset; } + +private: + ParseErrorCode code_; + size_t offset_; +}; + +//! Function pointer type of GetParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetParseErrorFunc GetParseError = GetParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// ValidateErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum ValidateErrorCode { + kValidateErrors = -1, //!< Top level error code when kValidateContinueOnErrorsFlag set. + kValidateErrorNone = 0, //!< No error. + + kValidateErrorMultipleOf, //!< Number is not a multiple of the 'multipleOf' value. + kValidateErrorMaximum, //!< Number is greater than the 'maximum' value. + kValidateErrorExclusiveMaximum, //!< Number is greater than or equal to the 'maximum' value. + kValidateErrorMinimum, //!< Number is less than the 'minimum' value. + kValidateErrorExclusiveMinimum, //!< Number is less than or equal to the 'minimum' value. + + kValidateErrorMaxLength, //!< String is longer than the 'maxLength' value. + kValidateErrorMinLength, //!< String is longer than the 'maxLength' value. + kValidateErrorPattern, //!< String does not match the 'pattern' regular expression. + + kValidateErrorMaxItems, //!< Array is longer than the 'maxItems' value. + kValidateErrorMinItems, //!< Array is shorter than the 'minItems' value. + kValidateErrorUniqueItems, //!< Array has duplicate items but 'uniqueItems' is true. + kValidateErrorAdditionalItems, //!< Array has additional items that are not allowed by the schema. + + kValidateErrorMaxProperties, //!< Object has more members than 'maxProperties' value. + kValidateErrorMinProperties, //!< Object has less members than 'minProperties' value. + kValidateErrorRequired, //!< Object is missing one or more members required by the schema. + kValidateErrorAdditionalProperties, //!< Object has additional members that are not allowed by the schema. + kValidateErrorPatternProperties, //!< See other errors. + kValidateErrorDependencies, //!< Object has missing property or schema dependencies. + + kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values. + kValidateErrorType, //!< Property has a type that is not allowed by the schema. + + kValidateErrorOneOf, //!< Property did not match any of the sub-schemas specified by 'oneOf'. + kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'. + kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'. + kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'. + kValidateErrorNot, //!< Property matched the sub-schema specified by 'not'. + + kValidateErrorReadOnly, //!< Property is read-only but has been provided when validation is for writing + kValidateErrorWriteOnly //!< Property is write-only but has been provided when validation is for reading +}; + +//! Function pointer type of GetValidateError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetValidateError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetValidateErrorFunc GetValidateError = GetValidateError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetValidateError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// SchemaErrorCode + +//! Error codes when validating. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericSchemaValidator +*/ +enum SchemaErrorCode { + kSchemaErrorNone = 0, //!< No error. + + kSchemaErrorStartUnknown, //!< Pointer to start of schema does not resolve to a location in the document + kSchemaErrorRefPlainName, //!< $ref fragment must be a JSON pointer + kSchemaErrorRefInvalid, //!< $ref must not be an empty string + kSchemaErrorRefPointerInvalid, //!< $ref fragment is not a valid JSON pointer at offset + kSchemaErrorRefUnknown, //!< $ref does not resolve to a location in the target document + kSchemaErrorRefCyclical, //!< $ref is cyclical + kSchemaErrorRefNoRemoteProvider, //!< $ref is remote but there is no remote provider + kSchemaErrorRefNoRemoteSchema, //!< $ref is remote but the remote provider did not return a schema + kSchemaErrorRegexInvalid, //!< Invalid regular expression in 'pattern' or 'patternProperties' + kSchemaErrorSpecUnknown, //!< JSON schema draft or OpenAPI version is not recognized + kSchemaErrorSpecUnsupported, //!< JSON schema draft or OpenAPI version is not supported + kSchemaErrorSpecIllegal, //!< Both JSON schema draft and OpenAPI version found in document + kSchemaErrorReadOnlyAndWriteOnly //!< Property must not be both 'readOnly' and 'writeOnly' +}; + +//! Function pointer type of GetSchemaError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetSchemaError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetSchemaErrorFunc GetSchemaError = GetSchemaError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetSchemaError(validator.GetInvalidSchemaCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetSchemaErrorFunc)(SchemaErrorCode); + +/////////////////////////////////////////////////////////////////////////////// +// PointerParseErrorCode + +//! Error code of JSON pointer parsing. +/*! \ingroup RAPIDJSON_ERRORS + \see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode +*/ +enum PointerParseErrorCode { + kPointerParseErrorNone = 0, //!< The parse is successful + + kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/' + kPointerParseErrorInvalidEscape, //!< Invalid escape + kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment + kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment +}; + +//! Function pointer type of GetPointerParseError(). +/*! \ingroup RAPIDJSON_ERRORS + + This is the prototype for \c GetPointerParseError_X(), where \c X is a locale. + User can dynamically change locale in runtime, e.g.: +\code + GetPointerParseErrorFunc GetPointerParseError = GetPointerParseError_En; // or whatever + const RAPIDJSON_ERROR_CHARTYPE* s = GetPointerParseError(pointer.GetParseErrorCode()); +\endcode +*/ +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetPointerParseErrorFunc)(PointerParseErrorCode); + + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_ERROR_ERROR_H_ diff --git a/Minecraft.Server/vendor/rapidjson/filereadstream.h b/Minecraft.Server/vendor/rapidjson/filereadstream.h new file mode 100644 index 00000000..f8bb43cb --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/filereadstream.h @@ -0,0 +1,99 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEREADSTREAM_H_ +#define RAPIDJSON_FILEREADSTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! File byte stream for input using fread(). +/*! + \note implements Stream concept +*/ +class FileReadStream { +public: + typedef char Ch; //!< Character type (byte). + + //! Constructor. + /*! + \param fp File pointer opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + FileReadStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(fp_ != 0); + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = std::fread(buffer_, 1, bufferSize_, fp_); + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (readCount_ < bufferSize_) { + buffer_[readCount_] = '\0'; + ++bufferLast_; + eof_ = true; + } + } + } + + std::FILE* fp_; + Ch *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/Minecraft.Server/vendor/rapidjson/filewritestream.h b/Minecraft.Server/vendor/rapidjson/filewritestream.h new file mode 100644 index 00000000..5d89588c --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/filewritestream.h @@ -0,0 +1,104 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FILEWRITESTREAM_H_ +#define RAPIDJSON_FILEWRITESTREAM_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of C file stream for output using fwrite(). +/*! + \note implements Stream concept +*/ +class FileWriteStream { +public: + typedef char Ch; //!< Character type. Only support char. + + FileWriteStream(std::FILE* fp, char* buffer, size_t bufferSize) : fp_(fp), buffer_(buffer), bufferEnd_(buffer + bufferSize), current_(buffer_) { + RAPIDJSON_ASSERT(fp_ != 0); + } + + void Put(char c) { + if (current_ >= bufferEnd_) + Flush(); + + *current_++ = c; + } + + void PutN(char c, size_t n) { + size_t avail = static_cast(bufferEnd_ - current_); + while (n > avail) { + std::memset(current_, c, avail); + current_ += avail; + Flush(); + n -= avail; + avail = static_cast(bufferEnd_ - current_); + } + + if (n > 0) { + std::memset(current_, c, n); + current_ += n; + } + } + + void Flush() { + if (current_ != buffer_) { + size_t result = std::fwrite(buffer_, 1, static_cast(current_ - buffer_), fp_); + if (result < static_cast(current_ - buffer_)) { + // failure deliberately ignored at this time + // added to avoid warn_unused_result build errors + } + current_ = buffer_; + } + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + // Prohibit copy constructor & assignment operator. + FileWriteStream(const FileWriteStream&); + FileWriteStream& operator=(const FileWriteStream&); + + std::FILE* fp_; + char *buffer_; + char *bufferEnd_; + char *current_; +}; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(FileWriteStream& stream, char c, size_t n) { + stream.PutN(c, n); +} + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_FILESTREAM_H_ diff --git a/Minecraft.Server/vendor/rapidjson/fwd.h b/Minecraft.Server/vendor/rapidjson/fwd.h new file mode 100644 index 00000000..d62f77f0 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/fwd.h @@ -0,0 +1,151 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_FWD_H_ +#define RAPIDJSON_FWD_H_ + +#include "rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN + +// encodings.h + +template struct UTF8; +template struct UTF16; +template struct UTF16BE; +template struct UTF16LE; +template struct UTF32; +template struct UTF32BE; +template struct UTF32LE; +template struct ASCII; +template struct AutoUTF; + +template +struct Transcoder; + +// allocators.h + +class CrtAllocator; + +template +class MemoryPoolAllocator; + +// stream.h + +template +struct GenericStringStream; + +typedef GenericStringStream > StringStream; + +template +struct GenericInsituStringStream; + +typedef GenericInsituStringStream > InsituStringStream; + +// stringbuffer.h + +template +class GenericStringBuffer; + +typedef GenericStringBuffer, CrtAllocator> StringBuffer; + +// filereadstream.h + +class FileReadStream; + +// filewritestream.h + +class FileWriteStream; + +// memorybuffer.h + +template +struct GenericMemoryBuffer; + +typedef GenericMemoryBuffer MemoryBuffer; + +// memorystream.h + +struct MemoryStream; + +// reader.h + +template +struct BaseReaderHandler; + +template +class GenericReader; + +typedef GenericReader, UTF8, CrtAllocator> Reader; + +// writer.h + +template +class Writer; + +// prettywriter.h + +template +class PrettyWriter; + +// document.h + +template +class GenericMember; + +template +class GenericMemberIterator; + +template +struct GenericStringRef; + +template +class GenericValue; + +typedef GenericValue, MemoryPoolAllocator > Value; + +template +class GenericDocument; + +typedef GenericDocument, MemoryPoolAllocator, CrtAllocator> Document; + +// pointer.h + +template +class GenericPointer; + +typedef GenericPointer Pointer; + +// schema.h + +template +class IGenericRemoteSchemaDocumentProvider; + +template +class GenericSchemaDocument; + +typedef GenericSchemaDocument SchemaDocument; +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +template < + typename SchemaDocumentType, + typename OutputHandler, + typename StateAllocator> +class GenericSchemaValidator; + +typedef GenericSchemaValidator, void>, CrtAllocator> SchemaValidator; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSONFWD_H_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/biginteger.h b/Minecraft.Server/vendor/rapidjson/internal/biginteger.h new file mode 100644 index 00000000..4930043d --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/biginteger.h @@ -0,0 +1,297 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_BIGINTEGER_H_ +#define RAPIDJSON_BIGINTEGER_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) && defined(_M_AMD64) +#include // for _umul128 +#if !defined(_ARM64EC_) +#pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class BigInteger { +public: + typedef uint64_t Type; + + BigInteger(const BigInteger& rhs) : count_(rhs.count_) { + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + + explicit BigInteger(uint64_t u) : count_(1) { + digits_[0] = u; + } + + template + BigInteger(const Ch* decimals, size_t length) : count_(1) { + RAPIDJSON_ASSERT(length > 0); + digits_[0] = 0; + size_t i = 0; + const size_t kMaxDigitPerIteration = 19; // 2^64 = 18446744073709551616 > 10^19 + while (length >= kMaxDigitPerIteration) { + AppendDecimal64(decimals + i, decimals + i + kMaxDigitPerIteration); + length -= kMaxDigitPerIteration; + i += kMaxDigitPerIteration; + } + + if (length > 0) + AppendDecimal64(decimals + i, decimals + i + length); + } + + BigInteger& operator=(const BigInteger &rhs) + { + if (this != &rhs) { + count_ = rhs.count_; + std::memcpy(digits_, rhs.digits_, count_ * sizeof(Type)); + } + return *this; + } + + BigInteger& operator=(uint64_t u) { + digits_[0] = u; + count_ = 1; + return *this; + } + + BigInteger& operator+=(uint64_t u) { + Type backup = digits_[0]; + digits_[0] += u; + for (size_t i = 0; i < count_ - 1; i++) { + if (digits_[i] >= backup) + return *this; // no carry + backup = digits_[i + 1]; + digits_[i + 1] += 1; + } + + // Last carry + if (digits_[count_ - 1] < backup) + PushBack(1); + + return *this; + } + + BigInteger& operator*=(uint64_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + uint64_t hi; + digits_[i] = MulAdd64(digits_[i], u, k, &hi); + k = hi; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator*=(uint32_t u) { + if (u == 0) return *this = 0; + if (u == 1) return *this; + if (*this == 1) return *this = u; + + uint64_t k = 0; + for (size_t i = 0; i < count_; i++) { + const uint64_t c = digits_[i] >> 32; + const uint64_t d = digits_[i] & 0xFFFFFFFF; + const uint64_t uc = u * c; + const uint64_t ud = u * d; + const uint64_t p0 = ud + k; + const uint64_t p1 = uc + (p0 >> 32); + digits_[i] = (p0 & 0xFFFFFFFF) | (p1 << 32); + k = p1 >> 32; + } + + if (k > 0) + PushBack(k); + + return *this; + } + + BigInteger& operator<<=(size_t shift) { + if (IsZero() || shift == 0) return *this; + + size_t offset = shift / kTypeBit; + size_t interShift = shift % kTypeBit; + RAPIDJSON_ASSERT(count_ + offset <= kCapacity); + + if (interShift == 0) { + std::memmove(digits_ + offset, digits_, count_ * sizeof(Type)); + count_ += offset; + } + else { + digits_[count_] = 0; + for (size_t i = count_; i > 0; i--) + digits_[i + offset] = (digits_[i] << interShift) | (digits_[i - 1] >> (kTypeBit - interShift)); + digits_[offset] = digits_[0] << interShift; + count_ += offset; + if (digits_[count_]) + count_++; + } + + std::memset(digits_, 0, offset * sizeof(Type)); + + return *this; + } + + bool operator==(const BigInteger& rhs) const { + return count_ == rhs.count_ && std::memcmp(digits_, rhs.digits_, count_ * sizeof(Type)) == 0; + } + + bool operator==(const Type rhs) const { + return count_ == 1 && digits_[0] == rhs; + } + + BigInteger& MultiplyPow5(unsigned exp) { + static const uint32_t kPow5[12] = { + 5, + 5 * 5, + 5 * 5 * 5, + 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5, + 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 + }; + if (exp == 0) return *this; + for (; exp >= 27; exp -= 27) *this *= RAPIDJSON_UINT64_C2(0X6765C793, 0XFA10079D); // 5^27 + for (; exp >= 13; exp -= 13) *this *= static_cast(1220703125u); // 5^13 + if (exp > 0) *this *= kPow5[exp - 1]; + return *this; + } + + // Compute absolute difference of this and rhs. + // Assume this != rhs + bool Difference(const BigInteger& rhs, BigInteger* out) const { + int cmp = Compare(rhs); + RAPIDJSON_ASSERT(cmp != 0); + const BigInteger *a, *b; // Makes a > b + bool ret; + if (cmp < 0) { a = &rhs; b = this; ret = true; } + else { a = this; b = &rhs; ret = false; } + + Type borrow = 0; + for (size_t i = 0; i < a->count_; i++) { + Type d = a->digits_[i] - borrow; + if (i < b->count_) + d -= b->digits_[i]; + borrow = (d > a->digits_[i]) ? 1 : 0; + out->digits_[i] = d; + if (d != 0) + out->count_ = i + 1; + } + + return ret; + } + + int Compare(const BigInteger& rhs) const { + if (count_ != rhs.count_) + return count_ < rhs.count_ ? -1 : 1; + + for (size_t i = count_; i-- > 0;) + if (digits_[i] != rhs.digits_[i]) + return digits_[i] < rhs.digits_[i] ? -1 : 1; + + return 0; + } + + size_t GetCount() const { return count_; } + Type GetDigit(size_t index) const { RAPIDJSON_ASSERT(index < count_); return digits_[index]; } + bool IsZero() const { return count_ == 1 && digits_[0] == 0; } + +private: + template + void AppendDecimal64(const Ch* begin, const Ch* end) { + uint64_t u = ParseUint64(begin, end); + if (IsZero()) + *this = u; + else { + unsigned exp = static_cast(end - begin); + (MultiplyPow5(exp) <<= exp) += u; // *this = *this * 10^exp + u + } + } + + void PushBack(Type digit) { + RAPIDJSON_ASSERT(count_ < kCapacity); + digits_[count_++] = digit; + } + + template + static uint64_t ParseUint64(const Ch* begin, const Ch* end) { + uint64_t r = 0; + for (const Ch* p = begin; p != end; ++p) { + RAPIDJSON_ASSERT(*p >= Ch('0') && *p <= Ch('9')); + r = r * 10u + static_cast(*p - Ch('0')); + } + return r; + } + + // Assume a * b + k < 2^128 + static uint64_t MulAdd64(uint64_t a, uint64_t b, uint64_t k, uint64_t* outHigh) { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t low = _umul128(a, b, outHigh) + k; + if (low < k) + (*outHigh)++; + return low; +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(a) * static_cast(b); + p += k; + *outHigh = static_cast(p >> 64); + return static_cast(p); +#else + const uint64_t a0 = a & 0xFFFFFFFF, a1 = a >> 32, b0 = b & 0xFFFFFFFF, b1 = b >> 32; + uint64_t x0 = a0 * b0, x1 = a0 * b1, x2 = a1 * b0, x3 = a1 * b1; + x1 += (x0 >> 32); // can't give carry + x1 += x2; + if (x1 < x2) + x3 += (static_cast(1) << 32); + uint64_t lo = (x1 << 32) + (x0 & 0xFFFFFFFF); + uint64_t hi = x3 + (x1 >> 32); + + lo += k; + if (lo < k) + hi++; + *outHigh = hi; + return lo; +#endif + } + + static const size_t kBitCount = 3328; // 64bit * 54 > 10^1000 + static const size_t kCapacity = kBitCount / sizeof(Type); + static const size_t kTypeBit = sizeof(Type) * 8; + + Type digits_[kCapacity]; + size_t count_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_BIGINTEGER_H_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/clzll.h b/Minecraft.Server/vendor/rapidjson/internal/clzll.h new file mode 100644 index 00000000..8fc5118a --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/clzll.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_CLZLL_H_ +#define RAPIDJSON_CLZLL_H_ + +#include "../rapidjson.h" + +#if defined(_MSC_VER) && !defined(UNDER_CE) +#include +#if defined(_WIN64) +#pragma intrinsic(_BitScanReverse64) +#else +#pragma intrinsic(_BitScanReverse) +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline uint32_t clzll(uint64_t x) { + // Passing 0 to __builtin_clzll is UB in GCC and results in an + // infinite loop in the software implementation. + RAPIDJSON_ASSERT(x != 0); + +#if defined(_MSC_VER) && !defined(UNDER_CE) + unsigned long r = 0; +#if defined(_WIN64) + _BitScanReverse64(&r, x); +#else + // Scan the high 32 bits. + if (_BitScanReverse(&r, static_cast(x >> 32))) + return 63 - (r + 32); + + // Scan the low 32 bits. + _BitScanReverse(&r, static_cast(x & 0xFFFFFFFF)); +#endif // _WIN64 + + return 63 - r; +#elif (defined(__GNUC__) && __GNUC__ >= 4) || RAPIDJSON_HAS_BUILTIN(__builtin_clzll) + // __builtin_clzll wrapper + return static_cast(__builtin_clzll(x)); +#else + // naive version + uint32_t r = 0; + while (!(x & (static_cast(1) << 63))) { + x <<= 1; + ++r; + } + + return r; +#endif // _MSC_VER +} + +#define RAPIDJSON_CLZLL RAPIDJSON_NAMESPACE::internal::clzll + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_CLZLL_H_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/diyfp.h b/Minecraft.Server/vendor/rapidjson/internal/diyfp.h new file mode 100644 index 00000000..1f60fb60 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/diyfp.h @@ -0,0 +1,261 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DIYFP_H_ +#define RAPIDJSON_DIYFP_H_ + +#include "../rapidjson.h" +#include "clzll.h" +#include + +#if defined(_MSC_VER) && defined(_M_AMD64) && !defined(__INTEL_COMPILER) +#include +#if !defined(_ARM64EC_) +#pragma intrinsic(_umul128) +#else +#pragma comment(lib,"softintrin") +#endif +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +struct DiyFp { + DiyFp() : f(), e() {} + + DiyFp(uint64_t fp, int exp) : f(fp), e(exp) {} + + explicit DiyFp(double d) { + union { + double d; + uint64_t u64; + } u = { d }; + + int biased_e = static_cast((u.u64 & kDpExponentMask) >> kDpSignificandSize); + uint64_t significand = (u.u64 & kDpSignificandMask); + if (biased_e != 0) { + f = significand + kDpHiddenBit; + e = biased_e - kDpExponentBias; + } + else { + f = significand; + e = kDpMinExponent + 1; + } + } + + DiyFp operator-(const DiyFp& rhs) const { + return DiyFp(f - rhs.f, e); + } + + DiyFp operator*(const DiyFp& rhs) const { +#if defined(_MSC_VER) && defined(_M_AMD64) + uint64_t h; + uint64_t l = _umul128(f, rhs.f, &h); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) && defined(__x86_64__) + __extension__ typedef unsigned __int128 uint128; + uint128 p = static_cast(f) * static_cast(rhs.f); + uint64_t h = static_cast(p >> 64); + uint64_t l = static_cast(p); + if (l & (uint64_t(1) << 63)) // rounding + h++; + return DiyFp(h, e + rhs.e + 64); +#else + const uint64_t M32 = 0xFFFFFFFF; + const uint64_t a = f >> 32; + const uint64_t b = f & M32; + const uint64_t c = rhs.f >> 32; + const uint64_t d = rhs.f & M32; + const uint64_t ac = a * c; + const uint64_t bc = b * c; + const uint64_t ad = a * d; + const uint64_t bd = b * d; + uint64_t tmp = (bd >> 32) + (ad & M32) + (bc & M32); + tmp += 1U << 31; /// mult_round + return DiyFp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), e + rhs.e + 64); +#endif + } + + DiyFp Normalize() const { + int s = static_cast(clzll(f)); + return DiyFp(f << s, e - s); + } + + DiyFp NormalizeBoundary() const { + DiyFp res = *this; + while (!(res.f & (kDpHiddenBit << 1))) { + res.f <<= 1; + res.e--; + } + res.f <<= (kDiySignificandSize - kDpSignificandSize - 2); + res.e = res.e - (kDiySignificandSize - kDpSignificandSize - 2); + return res; + } + + void NormalizedBoundaries(DiyFp* minus, DiyFp* plus) const { + DiyFp pl = DiyFp((f << 1) + 1, e - 1).NormalizeBoundary(); + DiyFp mi = (f == kDpHiddenBit) ? DiyFp((f << 2) - 1, e - 2) : DiyFp((f << 1) - 1, e - 1); + mi.f <<= mi.e - pl.e; + mi.e = pl.e; + *plus = pl; + *minus = mi; + } + + double ToDouble() const { + union { + double d; + uint64_t u64; + }u; + RAPIDJSON_ASSERT(f <= kDpHiddenBit + kDpSignificandMask); + if (e < kDpDenormalExponent) { + // Underflow. + return 0.0; + } + if (e >= kDpMaxExponent) { + // Overflow. + return std::numeric_limits::infinity(); + } + const uint64_t be = (e == kDpDenormalExponent && (f & kDpHiddenBit) == 0) ? 0 : + static_cast(e + kDpExponentBias); + u.u64 = (f & kDpSignificandMask) | (be << kDpSignificandSize); + return u.d; + } + + static const int kDiySignificandSize = 64; + static const int kDpSignificandSize = 52; + static const int kDpExponentBias = 0x3FF + kDpSignificandSize; + static const int kDpMaxExponent = 0x7FF - kDpExponentBias; + static const int kDpMinExponent = -kDpExponentBias; + static const int kDpDenormalExponent = -kDpExponentBias + 1; + static const uint64_t kDpExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kDpSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kDpHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + uint64_t f; + int e; +}; + +inline DiyFp GetCachedPowerByIndex(size_t index) { + // 10^-348, 10^-340, ..., 10^340 + static const uint64_t kCachedPowers_F[] = { + RAPIDJSON_UINT64_C2(0xfa8fd5a0, 0x081c0288), RAPIDJSON_UINT64_C2(0xbaaee17f, 0xa23ebf76), + RAPIDJSON_UINT64_C2(0x8b16fb20, 0x3055ac76), RAPIDJSON_UINT64_C2(0xcf42894a, 0x5dce35ea), + RAPIDJSON_UINT64_C2(0x9a6bb0aa, 0x55653b2d), RAPIDJSON_UINT64_C2(0xe61acf03, 0x3d1a45df), + RAPIDJSON_UINT64_C2(0xab70fe17, 0xc79ac6ca), RAPIDJSON_UINT64_C2(0xff77b1fc, 0xbebcdc4f), + RAPIDJSON_UINT64_C2(0xbe5691ef, 0x416bd60c), RAPIDJSON_UINT64_C2(0x8dd01fad, 0x907ffc3c), + RAPIDJSON_UINT64_C2(0xd3515c28, 0x31559a83), RAPIDJSON_UINT64_C2(0x9d71ac8f, 0xada6c9b5), + RAPIDJSON_UINT64_C2(0xea9c2277, 0x23ee8bcb), RAPIDJSON_UINT64_C2(0xaecc4991, 0x4078536d), + RAPIDJSON_UINT64_C2(0x823c1279, 0x5db6ce57), RAPIDJSON_UINT64_C2(0xc2109436, 0x4dfb5637), + RAPIDJSON_UINT64_C2(0x9096ea6f, 0x3848984f), RAPIDJSON_UINT64_C2(0xd77485cb, 0x25823ac7), + RAPIDJSON_UINT64_C2(0xa086cfcd, 0x97bf97f4), RAPIDJSON_UINT64_C2(0xef340a98, 0x172aace5), + RAPIDJSON_UINT64_C2(0xb23867fb, 0x2a35b28e), RAPIDJSON_UINT64_C2(0x84c8d4df, 0xd2c63f3b), + RAPIDJSON_UINT64_C2(0xc5dd4427, 0x1ad3cdba), RAPIDJSON_UINT64_C2(0x936b9fce, 0xbb25c996), + RAPIDJSON_UINT64_C2(0xdbac6c24, 0x7d62a584), RAPIDJSON_UINT64_C2(0xa3ab6658, 0x0d5fdaf6), + RAPIDJSON_UINT64_C2(0xf3e2f893, 0xdec3f126), RAPIDJSON_UINT64_C2(0xb5b5ada8, 0xaaff80b8), + RAPIDJSON_UINT64_C2(0x87625f05, 0x6c7c4a8b), RAPIDJSON_UINT64_C2(0xc9bcff60, 0x34c13053), + RAPIDJSON_UINT64_C2(0x964e858c, 0x91ba2655), RAPIDJSON_UINT64_C2(0xdff97724, 0x70297ebd), + RAPIDJSON_UINT64_C2(0xa6dfbd9f, 0xb8e5b88f), RAPIDJSON_UINT64_C2(0xf8a95fcf, 0x88747d94), + RAPIDJSON_UINT64_C2(0xb9447093, 0x8fa89bcf), RAPIDJSON_UINT64_C2(0x8a08f0f8, 0xbf0f156b), + RAPIDJSON_UINT64_C2(0xcdb02555, 0x653131b6), RAPIDJSON_UINT64_C2(0x993fe2c6, 0xd07b7fac), + RAPIDJSON_UINT64_C2(0xe45c10c4, 0x2a2b3b06), RAPIDJSON_UINT64_C2(0xaa242499, 0x697392d3), + RAPIDJSON_UINT64_C2(0xfd87b5f2, 0x8300ca0e), RAPIDJSON_UINT64_C2(0xbce50864, 0x92111aeb), + RAPIDJSON_UINT64_C2(0x8cbccc09, 0x6f5088cc), RAPIDJSON_UINT64_C2(0xd1b71758, 0xe219652c), + RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), RAPIDJSON_UINT64_C2(0xe8d4a510, 0x00000000), + RAPIDJSON_UINT64_C2(0xad78ebc5, 0xac620000), RAPIDJSON_UINT64_C2(0x813f3978, 0xf8940984), + RAPIDJSON_UINT64_C2(0xc097ce7b, 0xc90715b3), RAPIDJSON_UINT64_C2(0x8f7e32ce, 0x7bea5c70), + RAPIDJSON_UINT64_C2(0xd5d238a4, 0xabe98068), RAPIDJSON_UINT64_C2(0x9f4f2726, 0x179a2245), + RAPIDJSON_UINT64_C2(0xed63a231, 0xd4c4fb27), RAPIDJSON_UINT64_C2(0xb0de6538, 0x8cc8ada8), + RAPIDJSON_UINT64_C2(0x83c7088e, 0x1aab65db), RAPIDJSON_UINT64_C2(0xc45d1df9, 0x42711d9a), + RAPIDJSON_UINT64_C2(0x924d692c, 0xa61be758), RAPIDJSON_UINT64_C2(0xda01ee64, 0x1a708dea), + RAPIDJSON_UINT64_C2(0xa26da399, 0x9aef774a), RAPIDJSON_UINT64_C2(0xf209787b, 0xb47d6b85), + RAPIDJSON_UINT64_C2(0xb454e4a1, 0x79dd1877), RAPIDJSON_UINT64_C2(0x865b8692, 0x5b9bc5c2), + RAPIDJSON_UINT64_C2(0xc83553c5, 0xc8965d3d), RAPIDJSON_UINT64_C2(0x952ab45c, 0xfa97a0b3), + RAPIDJSON_UINT64_C2(0xde469fbd, 0x99a05fe3), RAPIDJSON_UINT64_C2(0xa59bc234, 0xdb398c25), + RAPIDJSON_UINT64_C2(0xf6c69a72, 0xa3989f5c), RAPIDJSON_UINT64_C2(0xb7dcbf53, 0x54e9bece), + RAPIDJSON_UINT64_C2(0x88fcf317, 0xf22241e2), RAPIDJSON_UINT64_C2(0xcc20ce9b, 0xd35c78a5), + RAPIDJSON_UINT64_C2(0x98165af3, 0x7b2153df), RAPIDJSON_UINT64_C2(0xe2a0b5dc, 0x971f303a), + RAPIDJSON_UINT64_C2(0xa8d9d153, 0x5ce3b396), RAPIDJSON_UINT64_C2(0xfb9b7cd9, 0xa4a7443c), + RAPIDJSON_UINT64_C2(0xbb764c4c, 0xa7a44410), RAPIDJSON_UINT64_C2(0x8bab8eef, 0xb6409c1a), + RAPIDJSON_UINT64_C2(0xd01fef10, 0xa657842c), RAPIDJSON_UINT64_C2(0x9b10a4e5, 0xe9913129), + RAPIDJSON_UINT64_C2(0xe7109bfb, 0xa19c0c9d), RAPIDJSON_UINT64_C2(0xac2820d9, 0x623bf429), + RAPIDJSON_UINT64_C2(0x80444b5e, 0x7aa7cf85), RAPIDJSON_UINT64_C2(0xbf21e440, 0x03acdd2d), + RAPIDJSON_UINT64_C2(0x8e679c2f, 0x5e44ff8f), RAPIDJSON_UINT64_C2(0xd433179d, 0x9c8cb841), + RAPIDJSON_UINT64_C2(0x9e19db92, 0xb4e31ba9), RAPIDJSON_UINT64_C2(0xeb96bf6e, 0xbadf77d9), + RAPIDJSON_UINT64_C2(0xaf87023b, 0x9bf0ee6b) + }; + static const int16_t kCachedPowers_E[] = { + -1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, + -954, -927, -901, -874, -847, -821, -794, -768, -741, -715, + -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, + -422, -396, -369, -343, -316, -289, -263, -236, -210, -183, + -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, + 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, + 375, 402, 428, 455, 481, 508, 534, 561, 588, 614, + 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, + 907, 933, 960, 986, 1013, 1039, 1066 + }; + RAPIDJSON_ASSERT(index < 87); + return DiyFp(kCachedPowers_F[index], kCachedPowers_E[index]); +} + +inline DiyFp GetCachedPower(int e, int* K) { + + //int k = static_cast(ceil((-61 - e) * 0.30102999566398114)) + 374; + double dk = (-61 - e) * 0.30102999566398114 + 347; // dk must be positive, so can do ceiling in positive + int k = static_cast(dk); + if (dk - k > 0.0) + k++; + + unsigned index = static_cast((k >> 3) + 1); + *K = -(-348 + static_cast(index << 3)); // decimal exponent no need lookup table + + return GetCachedPowerByIndex(index); +} + +inline DiyFp GetCachedPower10(int exp, int *outExp) { + RAPIDJSON_ASSERT(exp >= -348); + unsigned index = static_cast(exp + 348) / 8u; + *outExp = -348 + static_cast(index) * 8; + return GetCachedPowerByIndex(index); +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +RAPIDJSON_DIAG_OFF(padded) +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DIYFP_H_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/dtoa.h b/Minecraft.Server/vendor/rapidjson/internal/dtoa.h new file mode 100644 index 00000000..cd456721 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/dtoa.h @@ -0,0 +1,249 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +// This is a C++ header-only implementation of Grisu2 algorithm from the publication: +// Loitsch, Florian. "Printing floating-point numbers quickly and accurately with +// integers." ACM Sigplan Notices 45.6 (2010): 233-243. + +#ifndef RAPIDJSON_DTOA_ +#define RAPIDJSON_DTOA_ + +#include "itoa.h" // GetDigitsLut() +#include "diyfp.h" +#include "ieee754.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +RAPIDJSON_DIAG_OFF(array-bounds) // some gcc versions generate wrong warnings https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59124 +#endif + +inline void GrisuRound(char* buffer, int len, uint64_t delta, uint64_t rest, uint64_t ten_kappa, uint64_t wp_w) { + while (rest < wp_w && delta - rest >= ten_kappa && + (rest + ten_kappa < wp_w || /// closer + wp_w - rest > rest + ten_kappa - wp_w)) { + buffer[len - 1]--; + rest += ten_kappa; + } +} + +inline int CountDecimalDigit32(uint32_t n) { + // Simple pure C++ implementation was faster than __builtin_clz version in this situation. + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + // Will not reach 10 digits in DigitGen() + //if (n < 1000000000) return 9; + //return 10; + return 9; +} + +inline void DigitGen(const DiyFp& W, const DiyFp& Mp, uint64_t delta, char* buffer, int* len, int* K) { + static const uint64_t kPow10[] = { 1ULL, 10ULL, 100ULL, 1000ULL, 10000ULL, 100000ULL, 1000000ULL, 10000000ULL, 100000000ULL, + 1000000000ULL, 10000000000ULL, 100000000000ULL, 1000000000000ULL, + 10000000000000ULL, 100000000000000ULL, 1000000000000000ULL, + 10000000000000000ULL, 100000000000000000ULL, 1000000000000000000ULL, + 10000000000000000000ULL }; + const DiyFp one(uint64_t(1) << -Mp.e, Mp.e); + const DiyFp wp_w = Mp - W; + uint32_t p1 = static_cast(Mp.f >> -one.e); + uint64_t p2 = Mp.f & (one.f - 1); + int kappa = CountDecimalDigit32(p1); // kappa in [0, 9] + *len = 0; + + while (kappa > 0) { + uint32_t d = 0; + switch (kappa) { + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default:; + } + if (d || *len) + buffer[(*len)++] = static_cast('0' + static_cast(d)); + kappa--; + uint64_t tmp = (static_cast(p1) << -one.e) + p2; + if (tmp <= delta) { + *K += kappa; + GrisuRound(buffer, *len, delta, tmp, kPow10[kappa] << -one.e, wp_w.f); + return; + } + } + + // kappa = 0 + for (;;) { + p2 *= 10; + delta *= 10; + char d = static_cast(p2 >> -one.e); + if (d || *len) + buffer[(*len)++] = static_cast('0' + d); + p2 &= one.f - 1; + kappa--; + if (p2 < delta) { + *K += kappa; + int index = -kappa; + GrisuRound(buffer, *len, delta, p2, one.f, wp_w.f * (index < 20 ? kPow10[index] : 0)); + return; + } + } +} + +inline void Grisu2(double value, char* buffer, int* length, int* K) { + const DiyFp v(value); + DiyFp w_m, w_p; + v.NormalizedBoundaries(&w_m, &w_p); + + const DiyFp c_mk = GetCachedPower(w_p.e, K); + const DiyFp W = v.Normalize() * c_mk; + DiyFp Wp = w_p * c_mk; + DiyFp Wm = w_m * c_mk; + Wm.f++; + Wp.f--; + DigitGen(W, Wp, Wp.f - Wm.f, buffer, length, K); +} + +inline char* WriteExponent(int K, char* buffer) { + if (K < 0) { + *buffer++ = '-'; + K = -K; + } + + if (K >= 100) { + *buffer++ = static_cast('0' + static_cast(K / 100)); + K %= 100; + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else if (K >= 10) { + const char* d = GetDigitsLut() + K * 2; + *buffer++ = d[0]; + *buffer++ = d[1]; + } + else + *buffer++ = static_cast('0' + static_cast(K)); + + return buffer; +} + +inline char* Prettify(char* buffer, int length, int k, int maxDecimalPlaces) { + const int kk = length + k; // 10^(kk-1) <= v < 10^kk + + if (0 <= k && kk <= 21) { + // 1234e7 -> 12340000000 + for (int i = length; i < kk; i++) + buffer[i] = '0'; + buffer[kk] = '.'; + buffer[kk + 1] = '0'; + return &buffer[kk + 2]; + } + else if (0 < kk && kk <= 21) { + // 1234e-2 -> 12.34 + std::memmove(&buffer[kk + 1], &buffer[kk], static_cast(length - kk)); + buffer[kk] = '.'; + if (0 > k + maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 1.2345 -> 1.23, 1.102 -> 1.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = kk + maxDecimalPlaces; i > kk + 1; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[kk + 2]; // Reserve one zero + } + else + return &buffer[length + 1]; + } + else if (-6 < kk && kk <= 0) { + // 1234e-6 -> 0.001234 + const int offset = 2 - kk; + std::memmove(&buffer[offset], &buffer[0], static_cast(length)); + buffer[0] = '0'; + buffer[1] = '.'; + for (int i = 2; i < offset; i++) + buffer[i] = '0'; + if (length - kk > maxDecimalPlaces) { + // When maxDecimalPlaces = 2, 0.123 -> 0.12, 0.102 -> 0.1 + // Remove extra trailing zeros (at least one) after truncation. + for (int i = maxDecimalPlaces + 1; i > 2; i--) + if (buffer[i] != '0') + return &buffer[i + 1]; + return &buffer[3]; // Reserve one zero + } + else + return &buffer[length + offset]; + } + else if (kk < -maxDecimalPlaces) { + // Truncate to zero + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else if (length == 1) { + // 1e30 + buffer[1] = 'e'; + return WriteExponent(kk - 1, &buffer[2]); + } + else { + // 1234e30 -> 1.234e33 + std::memmove(&buffer[2], &buffer[1], static_cast(length - 1)); + buffer[1] = '.'; + buffer[length + 1] = 'e'; + return WriteExponent(kk - 1, &buffer[0 + length + 2]); + } +} + +inline char* dtoa(double value, char* buffer, int maxDecimalPlaces = 324) { + RAPIDJSON_ASSERT(maxDecimalPlaces >= 1); + Double d(value); + if (d.IsZero()) { + if (d.Sign()) + *buffer++ = '-'; // -0.0, Issue #289 + buffer[0] = '0'; + buffer[1] = '.'; + buffer[2] = '0'; + return &buffer[3]; + } + else { + if (value < 0) { + *buffer++ = '-'; + value = -value; + } + int length, K; + Grisu2(value, buffer, &length, &K); + return Prettify(buffer, length, K, maxDecimalPlaces); + } +} + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_DTOA_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/ieee754.h b/Minecraft.Server/vendor/rapidjson/internal/ieee754.h new file mode 100644 index 00000000..68c9e966 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/ieee754.h @@ -0,0 +1,78 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_IEEE754_ +#define RAPIDJSON_IEEE754_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +class Double { +public: + Double() {} + Double(double d) : d_(d) {} + Double(uint64_t u) : u_(u) {} + + double Value() const { return d_; } + uint64_t Uint64Value() const { return u_; } + + double NextPositiveDouble() const { + RAPIDJSON_ASSERT(!Sign()); + return Double(u_ + 1).Value(); + } + + bool Sign() const { return (u_ & kSignMask) != 0; } + uint64_t Significand() const { return u_ & kSignificandMask; } + int Exponent() const { return static_cast(((u_ & kExponentMask) >> kSignificandSize) - kExponentBias); } + + bool IsNan() const { return (u_ & kExponentMask) == kExponentMask && Significand() != 0; } + bool IsInf() const { return (u_ & kExponentMask) == kExponentMask && Significand() == 0; } + bool IsNanOrInf() const { return (u_ & kExponentMask) == kExponentMask; } + bool IsNormal() const { return (u_ & kExponentMask) != 0 || Significand() == 0; } + bool IsZero() const { return (u_ & (kExponentMask | kSignificandMask)) == 0; } + + uint64_t IntegerSignificand() const { return IsNormal() ? Significand() | kHiddenBit : Significand(); } + int IntegerExponent() const { return (IsNormal() ? Exponent() : kDenormalExponent) - kSignificandSize; } + uint64_t ToBias() const { return (u_ & kSignMask) ? ~u_ + 1 : u_ | kSignMask; } + + static int EffectiveSignificandSize(int order) { + if (order >= -1021) + return 53; + else if (order <= -1074) + return 0; + else + return order + 1074; + } + +private: + static const int kSignificandSize = 52; + static const int kExponentBias = 0x3FF; + static const int kDenormalExponent = 1 - kExponentBias; + static const uint64_t kSignMask = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + static const uint64_t kExponentMask = RAPIDJSON_UINT64_C2(0x7FF00000, 0x00000000); + static const uint64_t kSignificandMask = RAPIDJSON_UINT64_C2(0x000FFFFF, 0xFFFFFFFF); + static const uint64_t kHiddenBit = RAPIDJSON_UINT64_C2(0x00100000, 0x00000000); + + union { + double d_; + uint64_t u_; + }; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_IEEE754_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/itoa.h b/Minecraft.Server/vendor/rapidjson/internal/itoa.h new file mode 100644 index 00000000..9fe8c932 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/itoa.h @@ -0,0 +1,308 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ITOA_ +#define RAPIDJSON_ITOA_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline const char* GetDigitsLut() { + static const char cDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' + }; + return cDigitsLut; +} + +inline char* u32toa(uint32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + + const char* cDigitsLut = GetDigitsLut(); + + if (value < 10000) { + const uint32_t d1 = (value / 100) << 1; + const uint32_t d2 = (value % 100) << 1; + + if (value >= 1000) + *buffer++ = cDigitsLut[d1]; + if (value >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else if (value < 100000000) { + // value = bbbbcccc + const uint32_t b = value / 10000; + const uint32_t c = value % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + else { + // value = aabbbbcccc in decimal + + const uint32_t a = value / 100000000; // 1 to 42 + value %= 100000000; + + if (a >= 10) { + const unsigned i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else + *buffer++ = static_cast('0' + static_cast(a)); + + const uint32_t b = value / 10000; // 0 to 9999 + const uint32_t c = value % 10000; // 0 to 9999 + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + return buffer; +} + +inline char* i32toa(int32_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + uint32_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u32toa(u, buffer); +} + +inline char* u64toa(uint64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + const char* cDigitsLut = GetDigitsLut(); + const uint64_t kTen8 = 100000000; + const uint64_t kTen9 = kTen8 * 10; + const uint64_t kTen10 = kTen8 * 100; + const uint64_t kTen11 = kTen8 * 1000; + const uint64_t kTen12 = kTen8 * 10000; + const uint64_t kTen13 = kTen8 * 100000; + const uint64_t kTen14 = kTen8 * 1000000; + const uint64_t kTen15 = kTen8 * 10000000; + const uint64_t kTen16 = kTen8 * kTen8; + + if (value < kTen8) { + uint32_t v = static_cast(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buffer++ = cDigitsLut[d1]; + if (v >= 100) + *buffer++ = cDigitsLut[d1 + 1]; + if (v >= 10) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buffer++ = cDigitsLut[d1]; + if (value >= 1000000) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= 100000) + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + } + } + else if (value < kTen16) { + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + if (value >= kTen15) + *buffer++ = cDigitsLut[d1]; + if (value >= kTen14) + *buffer++ = cDigitsLut[d1 + 1]; + if (value >= kTen13) + *buffer++ = cDigitsLut[d2]; + if (value >= kTen12) + *buffer++ = cDigitsLut[d2 + 1]; + if (value >= kTen11) + *buffer++ = cDigitsLut[d3]; + if (value >= kTen10) + *buffer++ = cDigitsLut[d3 + 1]; + if (value >= kTen9) + *buffer++ = cDigitsLut[d4]; + + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + else { + const uint32_t a = static_cast(value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) + *buffer++ = static_cast('0' + static_cast(a)); + else if (a < 100) { + const uint32_t i = a << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else if (a < 1000) { + *buffer++ = static_cast('0' + static_cast(a / 100)); + + const uint32_t i = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + } + else { + const uint32_t i = (a / 100) << 1; + const uint32_t j = (a % 100) << 1; + *buffer++ = cDigitsLut[i]; + *buffer++ = cDigitsLut[i + 1]; + *buffer++ = cDigitsLut[j]; + *buffer++ = cDigitsLut[j + 1]; + } + + const uint32_t v0 = static_cast(value / kTen8); + const uint32_t v1 = static_cast(value % kTen8); + + const uint32_t b0 = v0 / 10000; + const uint32_t c0 = v0 % 10000; + + const uint32_t d1 = (b0 / 100) << 1; + const uint32_t d2 = (b0 % 100) << 1; + + const uint32_t d3 = (c0 / 100) << 1; + const uint32_t d4 = (c0 % 100) << 1; + + const uint32_t b1 = v1 / 10000; + const uint32_t c1 = v1 % 10000; + + const uint32_t d5 = (b1 / 100) << 1; + const uint32_t d6 = (b1 % 100) << 1; + + const uint32_t d7 = (c1 / 100) << 1; + const uint32_t d8 = (c1 % 100) << 1; + + *buffer++ = cDigitsLut[d1]; + *buffer++ = cDigitsLut[d1 + 1]; + *buffer++ = cDigitsLut[d2]; + *buffer++ = cDigitsLut[d2 + 1]; + *buffer++ = cDigitsLut[d3]; + *buffer++ = cDigitsLut[d3 + 1]; + *buffer++ = cDigitsLut[d4]; + *buffer++ = cDigitsLut[d4 + 1]; + *buffer++ = cDigitsLut[d5]; + *buffer++ = cDigitsLut[d5 + 1]; + *buffer++ = cDigitsLut[d6]; + *buffer++ = cDigitsLut[d6 + 1]; + *buffer++ = cDigitsLut[d7]; + *buffer++ = cDigitsLut[d7 + 1]; + *buffer++ = cDigitsLut[d8]; + *buffer++ = cDigitsLut[d8 + 1]; + } + + return buffer; +} + +inline char* i64toa(int64_t value, char* buffer) { + RAPIDJSON_ASSERT(buffer != 0); + uint64_t u = static_cast(value); + if (value < 0) { + *buffer++ = '-'; + u = ~u + 1; + } + + return u64toa(u, buffer); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ITOA_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/meta.h b/Minecraft.Server/vendor/rapidjson/internal/meta.h new file mode 100644 index 00000000..27092dc0 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/meta.h @@ -0,0 +1,186 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_META_H_ +#define RAPIDJSON_INTERNAL_META_H_ + +#include "../rapidjson.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#if RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif + +//@cond RAPIDJSON_INTERNAL +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; + +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst +template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; +template struct RemoveConst { typedef T Type; }; +template struct RemoveConst { typedef T Type; }; + + +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; + +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; + +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#if RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); + }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; +}; + +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + + +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// +template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { /* empty */ }; + +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; + +template +struct EnableIf : EnableIfCond {}; + +template +struct DisableIf : DisableIfCond {}; + +// SFINAE helpers +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; + +#define RAPIDJSON_REMOVEFPTR_(type) \ + typename ::RAPIDJSON_NAMESPACE::internal::RemoveSfinaeTag \ + < ::RAPIDJSON_NAMESPACE::internal::SfinaeTag&(*) type>::Type + +#define RAPIDJSON_ENABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type * = NULL + +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::EnableIf \ + ::Type + +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::RAPIDJSON_NAMESPACE::internal::DisableIf \ + ::Type + +} // namespace internal +RAPIDJSON_NAMESPACE_END +//@endcond + +#if defined(_MSC_VER) && !defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_META_H_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/pow10.h b/Minecraft.Server/vendor/rapidjson/internal/pow10.h new file mode 100644 index 00000000..eae1a43e --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/pow10.h @@ -0,0 +1,55 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POW10_ +#define RAPIDJSON_POW10_ + +#include "../rapidjson.h" + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Computes integer powers of 10 in double (10.0^n). +/*! This function uses lookup table for fast and accurate results. + \param n non-negative exponent. Must <= 308. + \return 10.0^n +*/ +inline double Pow10(int n) { + static const double e[] = { // 1e-0...1e308: 309 * 8 bytes = 2472 bytes + 1e+0, + 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11, 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, + 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29, 1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39, 1e+40, + 1e+41, 1e+42, 1e+43, 1e+44, 1e+45, 1e+46, 1e+47, 1e+48, 1e+49, 1e+50, 1e+51, 1e+52, 1e+53, 1e+54, 1e+55, 1e+56, 1e+57, 1e+58, 1e+59, 1e+60, + 1e+61, 1e+62, 1e+63, 1e+64, 1e+65, 1e+66, 1e+67, 1e+68, 1e+69, 1e+70, 1e+71, 1e+72, 1e+73, 1e+74, 1e+75, 1e+76, 1e+77, 1e+78, 1e+79, 1e+80, + 1e+81, 1e+82, 1e+83, 1e+84, 1e+85, 1e+86, 1e+87, 1e+88, 1e+89, 1e+90, 1e+91, 1e+92, 1e+93, 1e+94, 1e+95, 1e+96, 1e+97, 1e+98, 1e+99, 1e+100, + 1e+101,1e+102,1e+103,1e+104,1e+105,1e+106,1e+107,1e+108,1e+109,1e+110,1e+111,1e+112,1e+113,1e+114,1e+115,1e+116,1e+117,1e+118,1e+119,1e+120, + 1e+121,1e+122,1e+123,1e+124,1e+125,1e+126,1e+127,1e+128,1e+129,1e+130,1e+131,1e+132,1e+133,1e+134,1e+135,1e+136,1e+137,1e+138,1e+139,1e+140, + 1e+141,1e+142,1e+143,1e+144,1e+145,1e+146,1e+147,1e+148,1e+149,1e+150,1e+151,1e+152,1e+153,1e+154,1e+155,1e+156,1e+157,1e+158,1e+159,1e+160, + 1e+161,1e+162,1e+163,1e+164,1e+165,1e+166,1e+167,1e+168,1e+169,1e+170,1e+171,1e+172,1e+173,1e+174,1e+175,1e+176,1e+177,1e+178,1e+179,1e+180, + 1e+181,1e+182,1e+183,1e+184,1e+185,1e+186,1e+187,1e+188,1e+189,1e+190,1e+191,1e+192,1e+193,1e+194,1e+195,1e+196,1e+197,1e+198,1e+199,1e+200, + 1e+201,1e+202,1e+203,1e+204,1e+205,1e+206,1e+207,1e+208,1e+209,1e+210,1e+211,1e+212,1e+213,1e+214,1e+215,1e+216,1e+217,1e+218,1e+219,1e+220, + 1e+221,1e+222,1e+223,1e+224,1e+225,1e+226,1e+227,1e+228,1e+229,1e+230,1e+231,1e+232,1e+233,1e+234,1e+235,1e+236,1e+237,1e+238,1e+239,1e+240, + 1e+241,1e+242,1e+243,1e+244,1e+245,1e+246,1e+247,1e+248,1e+249,1e+250,1e+251,1e+252,1e+253,1e+254,1e+255,1e+256,1e+257,1e+258,1e+259,1e+260, + 1e+261,1e+262,1e+263,1e+264,1e+265,1e+266,1e+267,1e+268,1e+269,1e+270,1e+271,1e+272,1e+273,1e+274,1e+275,1e+276,1e+277,1e+278,1e+279,1e+280, + 1e+281,1e+282,1e+283,1e+284,1e+285,1e+286,1e+287,1e+288,1e+289,1e+290,1e+291,1e+292,1e+293,1e+294,1e+295,1e+296,1e+297,1e+298,1e+299,1e+300, + 1e+301,1e+302,1e+303,1e+304,1e+305,1e+306,1e+307,1e+308 + }; + RAPIDJSON_ASSERT(n >= 0 && n <= 308); + return e[n]; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_POW10_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/regex.h b/Minecraft.Server/vendor/rapidjson/internal/regex.h new file mode 100644 index 00000000..7740dcd5 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/regex.h @@ -0,0 +1,739 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_REGEX_H_ +#define RAPIDJSON_INTERNAL_REGEX_H_ + +#include "../allocators.h" +#include "../stream.h" +#include "stack.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifndef RAPIDJSON_REGEX_VERBOSE +#define RAPIDJSON_REGEX_VERBOSE 0 +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// DecodedStream + +template +class DecodedStream { +public: + DecodedStream(SourceStream& ss) : ss_(ss), codepoint_() { Decode(); } + unsigned Peek() { return codepoint_; } + unsigned Take() { + unsigned c = codepoint_; + if (c) // No further decoding when '\0' + Decode(); + return c; + } + +private: + void Decode() { + if (!Encoding::Decode(ss_, &codepoint_)) + codepoint_ = 0; + } + + SourceStream& ss_; + unsigned codepoint_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericRegex + +static const SizeType kRegexInvalidState = ~SizeType(0); //!< Represents an invalid index in GenericRegex::State::out, out1 +static const SizeType kRegexInvalidRange = ~SizeType(0); + +template +class GenericRegexSearch; + +//! Regular expression engine with subset of ECMAscript grammar. +/*! + Supported regular expression syntax: + - \c ab Concatenation + - \c a|b Alternation + - \c a? Zero or one + - \c a* Zero or more + - \c a+ One or more + - \c a{3} Exactly 3 times + - \c a{3,} At least 3 times + - \c a{3,5} 3 to 5 times + - \c (ab) Grouping + - \c ^a At the beginning + - \c a$ At the end + - \c . Any character + - \c [abc] Character classes + - \c [a-c] Character class range + - \c [a-z0-9_] Character class combination + - \c [^abc] Negated character classes + - \c [^a-c] Negated character class range + - \c [\b] Backspace (U+0008) + - \c \\| \\\\ ... Escape characters + - \c \\f Form feed (U+000C) + - \c \\n Line feed (U+000A) + - \c \\r Carriage return (U+000D) + - \c \\t Tab (U+0009) + - \c \\v Vertical tab (U+000B) + + \note This is a Thompson NFA engine, implemented with reference to + Cox, Russ. "Regular Expression Matching Can Be Simple And Fast (but is slow in Java, Perl, PHP, Python, Ruby,...).", + https://swtch.com/~rsc/regexp/regexp1.html +*/ +template +class GenericRegex { +public: + typedef Encoding EncodingType; + typedef typename Encoding::Ch Ch; + template friend class GenericRegexSearch; + + GenericRegex(const Ch* source, Allocator* allocator = 0) : + ownAllocator_(allocator ? 0 : RAPIDJSON_NEW(Allocator)()), allocator_(allocator ? allocator : ownAllocator_), + states_(allocator_, 256), ranges_(allocator_, 256), root_(kRegexInvalidState), stateCount_(), rangeCount_(), + anchorBegin_(), anchorEnd_() + { + GenericStringStream ss(source); + DecodedStream, Encoding> ds(ss); + Parse(ds); + } + + ~GenericRegex() + { + RAPIDJSON_DELETE(ownAllocator_); + } + + bool IsValid() const { + return root_ != kRegexInvalidState; + } + +private: + enum Operator { + kZeroOrOne, + kZeroOrMore, + kOneOrMore, + kConcatenation, + kAlternation, + kLeftParenthesis + }; + + static const unsigned kAnyCharacterClass = 0xFFFFFFFF; //!< For '.' + static const unsigned kRangeCharacterClass = 0xFFFFFFFE; + static const unsigned kRangeNegationFlag = 0x80000000; + + struct Range { + unsigned start; // + unsigned end; + SizeType next; + }; + + struct State { + SizeType out; //!< Equals to kInvalid for matching state + SizeType out1; //!< Equals to non-kInvalid for split + SizeType rangeStart; + unsigned codepoint; + }; + + struct Frag { + Frag(SizeType s, SizeType o, SizeType m) : start(s), out(o), minIndex(m) {} + SizeType start; + SizeType out; //!< link-list of all output states + SizeType minIndex; + }; + + State& GetState(SizeType index) { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + const State& GetState(SizeType index) const { + RAPIDJSON_ASSERT(index < stateCount_); + return states_.template Bottom()[index]; + } + + Range& GetRange(SizeType index) { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + const Range& GetRange(SizeType index) const { + RAPIDJSON_ASSERT(index < rangeCount_); + return ranges_.template Bottom()[index]; + } + + template + void Parse(DecodedStream& ds) { + Stack operandStack(allocator_, 256); // Frag + Stack operatorStack(allocator_, 256); // Operator + Stack atomCountStack(allocator_, 256); // unsigned (Atom per parenthesis) + + *atomCountStack.template Push() = 0; + + unsigned codepoint; + while (ds.Peek() != 0) { + switch (codepoint = ds.Take()) { + case '^': + anchorBegin_ = true; + break; + + case '$': + anchorEnd_ = true; + break; + + case '|': + while (!operatorStack.Empty() && *operatorStack.template Top() < kAlternation) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + *operatorStack.template Push() = kAlternation; + *atomCountStack.template Top() = 0; + break; + + case '(': + *operatorStack.template Push() = kLeftParenthesis; + *atomCountStack.template Push() = 0; + break; + + case ')': + while (!operatorStack.Empty() && *operatorStack.template Top() != kLeftParenthesis) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + if (operatorStack.Empty()) + return; + operatorStack.template Pop(1); + atomCountStack.template Pop(1); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '?': + if (!Eval(operandStack, kZeroOrOne)) + return; + break; + + case '*': + if (!Eval(operandStack, kZeroOrMore)) + return; + break; + + case '+': + if (!Eval(operandStack, kOneOrMore)) + return; + break; + + case '{': + { + unsigned n, m; + if (!ParseUnsigned(ds, &n)) + return; + + if (ds.Peek() == ',') { + ds.Take(); + if (ds.Peek() == '}') + m = kInfinityQuantifier; + else if (!ParseUnsigned(ds, &m) || m < n) + return; + } + else + m = n; + + if (!EvalQuantifier(operandStack, n, m) || ds.Peek() != '}') + return; + ds.Take(); + } + break; + + case '.': + PushOperand(operandStack, kAnyCharacterClass); + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '[': + { + SizeType range; + if (!ParseRange(ds, &range)) + return; + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, kRangeCharacterClass); + GetState(s).rangeStart = range; + *operandStack.template Push() = Frag(s, s, s); + } + ImplicitConcatenation(atomCountStack, operatorStack); + break; + + case '\\': // Escape character + if (!CharacterEscape(ds, &codepoint)) + return; // Unsupported escape character + // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: // Pattern character + PushOperand(operandStack, codepoint); + ImplicitConcatenation(atomCountStack, operatorStack); + } + } + + while (!operatorStack.Empty()) + if (!Eval(operandStack, *operatorStack.template Pop(1))) + return; + + // Link the operand to matching state. + if (operandStack.GetSize() == sizeof(Frag)) { + Frag* e = operandStack.template Pop(1); + Patch(e->out, NewState(kRegexInvalidState, kRegexInvalidState, 0)); + root_ = e->start; + +#if RAPIDJSON_REGEX_VERBOSE + printf("root: %d\n", root_); + for (SizeType i = 0; i < stateCount_ ; i++) { + State& s = GetState(i); + printf("[%2d] out: %2d out1: %2d c: '%c'\n", i, s.out, s.out1, (char)s.codepoint); + } + printf("\n"); +#endif + } + } + + SizeType NewState(SizeType out, SizeType out1, unsigned codepoint) { + State* s = states_.template Push(); + s->out = out; + s->out1 = out1; + s->codepoint = codepoint; + s->rangeStart = kRegexInvalidRange; + return stateCount_++; + } + + void PushOperand(Stack& operandStack, unsigned codepoint) { + SizeType s = NewState(kRegexInvalidState, kRegexInvalidState, codepoint); + *operandStack.template Push() = Frag(s, s, s); + } + + void ImplicitConcatenation(Stack& atomCountStack, Stack& operatorStack) { + if (*atomCountStack.template Top()) + *operatorStack.template Push() = kConcatenation; + (*atomCountStack.template Top())++; + } + + SizeType Append(SizeType l1, SizeType l2) { + SizeType old = l1; + while (GetState(l1).out != kRegexInvalidState) + l1 = GetState(l1).out; + GetState(l1).out = l2; + return old; + } + + void Patch(SizeType l, SizeType s) { + for (SizeType next; l != kRegexInvalidState; l = next) { + next = GetState(l).out; + GetState(l).out = s; + } + } + + bool Eval(Stack& operandStack, Operator op) { + switch (op) { + case kConcatenation: + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag) * 2); + { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + Patch(e1.out, e2.start); + *operandStack.template Push() = Frag(e1.start, e2.out, Min(e1.minIndex, e2.minIndex)); + } + return true; + + case kAlternation: + if (operandStack.GetSize() >= sizeof(Frag) * 2) { + Frag e2 = *operandStack.template Pop(1); + Frag e1 = *operandStack.template Pop(1); + SizeType s = NewState(e1.start, e2.start, 0); + *operandStack.template Push() = Frag(s, Append(e1.out, e2.out), Min(e1.minIndex, e2.minIndex)); + return true; + } + return false; + + case kZeroOrOne: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + *operandStack.template Push() = Frag(s, Append(e.out, s), e.minIndex); + return true; + } + return false; + + case kZeroOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(s, s, e.minIndex); + return true; + } + return false; + + case kOneOrMore: + if (operandStack.GetSize() >= sizeof(Frag)) { + Frag e = *operandStack.template Pop(1); + SizeType s = NewState(kRegexInvalidState, e.start, 0); + Patch(e.out, s); + *operandStack.template Push() = Frag(e.start, s, e.minIndex); + return true; + } + return false; + + default: + // syntax error (e.g. unclosed kLeftParenthesis) + return false; + } + } + + bool EvalQuantifier(Stack& operandStack, unsigned n, unsigned m) { + RAPIDJSON_ASSERT(n <= m); + RAPIDJSON_ASSERT(operandStack.GetSize() >= sizeof(Frag)); + + if (n == 0) { + if (m == 0) // a{0} not support + return false; + else if (m == kInfinityQuantifier) + Eval(operandStack, kZeroOrMore); // a{0,} -> a* + else { + Eval(operandStack, kZeroOrOne); // a{0,5} -> a? + for (unsigned i = 0; i < m - 1; i++) + CloneTopOperand(operandStack); // a{0,5} -> a? a? a? a? a? + for (unsigned i = 0; i < m - 1; i++) + Eval(operandStack, kConcatenation); // a{0,5} -> a?a?a?a?a? + } + return true; + } + + for (unsigned i = 0; i < n - 1; i++) // a{3} -> a a a + CloneTopOperand(operandStack); + + if (m == kInfinityQuantifier) + Eval(operandStack, kOneOrMore); // a{3,} -> a a a+ + else if (m > n) { + CloneTopOperand(operandStack); // a{3,5} -> a a a a + Eval(operandStack, kZeroOrOne); // a{3,5} -> a a a a? + for (unsigned i = n; i < m - 1; i++) + CloneTopOperand(operandStack); // a{3,5} -> a a a a? a? + for (unsigned i = n; i < m; i++) + Eval(operandStack, kConcatenation); // a{3,5} -> a a aa?a? + } + + for (unsigned i = 0; i < n - 1; i++) + Eval(operandStack, kConcatenation); // a{3} -> aaa, a{3,} -> aaa+, a{3.5} -> aaaa?a? + + return true; + } + + static SizeType Min(SizeType a, SizeType b) { return a < b ? a : b; } + + void CloneTopOperand(Stack& operandStack) { + const Frag src = *operandStack.template Top(); // Copy constructor to prevent invalidation + SizeType count = stateCount_ - src.minIndex; // Assumes top operand contains states in [src->minIndex, stateCount_) + State* s = states_.template Push(count); + memcpy(s, &GetState(src.minIndex), count * sizeof(State)); + for (SizeType j = 0; j < count; j++) { + if (s[j].out != kRegexInvalidState) + s[j].out += count; + if (s[j].out1 != kRegexInvalidState) + s[j].out1 += count; + } + *operandStack.template Push() = Frag(src.start + count, src.out + count, src.minIndex + count); + stateCount_ += count; + } + + template + bool ParseUnsigned(DecodedStream& ds, unsigned* u) { + unsigned r = 0; + if (ds.Peek() < '0' || ds.Peek() > '9') + return false; + while (ds.Peek() >= '0' && ds.Peek() <= '9') { + if (r >= 429496729 && ds.Peek() > '5') // 2^32 - 1 = 4294967295 + return false; // overflow + r = r * 10 + (ds.Take() - '0'); + } + *u = r; + return true; + } + + template + bool ParseRange(DecodedStream& ds, SizeType* range) { + bool isBegin = true; + bool negate = false; + int step = 0; + SizeType start = kRegexInvalidRange; + SizeType current = kRegexInvalidRange; + unsigned codepoint; + while ((codepoint = ds.Take()) != 0) { + if (isBegin) { + isBegin = false; + if (codepoint == '^') { + negate = true; + continue; + } + } + + switch (codepoint) { + case ']': + if (start == kRegexInvalidRange) + return false; // Error: nothing inside [] + if (step == 2) { // Add trailing '-' + SizeType r = NewRange('-'); + RAPIDJSON_ASSERT(current != kRegexInvalidRange); + GetRange(current).next = r; + } + if (negate) + GetRange(start).start |= kRangeNegationFlag; + *range = start; + return true; + + case '\\': + if (ds.Peek() == 'b') { + ds.Take(); + codepoint = 0x0008; // Escape backspace character + } + else if (!CharacterEscape(ds, &codepoint)) + return false; + // fall through to default + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + default: + switch (step) { + case 1: + if (codepoint == '-') { + step++; + break; + } + // fall through to step 0 for other characters + RAPIDJSON_DELIBERATE_FALLTHROUGH; + + case 0: + { + SizeType r = NewRange(codepoint); + if (current != kRegexInvalidRange) + GetRange(current).next = r; + if (start == kRegexInvalidRange) + start = r; + current = r; + } + step = 1; + break; + + default: + RAPIDJSON_ASSERT(step == 2); + GetRange(current).end = codepoint; + step = 0; + } + } + } + return false; + } + + SizeType NewRange(unsigned codepoint) { + Range* r = ranges_.template Push(); + r->start = r->end = codepoint; + r->next = kRegexInvalidRange; + return rangeCount_++; + } + + template + bool CharacterEscape(DecodedStream& ds, unsigned* escapedCodepoint) { + unsigned codepoint; + switch (codepoint = ds.Take()) { + case '^': + case '$': + case '|': + case '(': + case ')': + case '?': + case '*': + case '+': + case '.': + case '[': + case ']': + case '{': + case '}': + case '\\': + *escapedCodepoint = codepoint; return true; + case 'f': *escapedCodepoint = 0x000C; return true; + case 'n': *escapedCodepoint = 0x000A; return true; + case 'r': *escapedCodepoint = 0x000D; return true; + case 't': *escapedCodepoint = 0x0009; return true; + case 'v': *escapedCodepoint = 0x000B; return true; + default: + return false; // Unsupported escape character + } + } + + Allocator* ownAllocator_; + Allocator* allocator_; + Stack states_; + Stack ranges_; + SizeType root_; + SizeType stateCount_; + SizeType rangeCount_; + + static const unsigned kInfinityQuantifier = ~0u; + + // For SearchWithAnchoring() + bool anchorBegin_; + bool anchorEnd_; +}; + +template +class GenericRegexSearch { +public: + typedef typename RegexType::EncodingType Encoding; + typedef typename Encoding::Ch Ch; + + GenericRegexSearch(const RegexType& regex, Allocator* allocator = 0) : + regex_(regex), allocator_(allocator), ownAllocator_(0), + state0_(allocator, 0), state1_(allocator, 0), stateSet_() + { + RAPIDJSON_ASSERT(regex_.IsValid()); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + stateSet_ = static_cast(allocator_->Malloc(GetStateSetSize())); + state0_.template Reserve(regex_.stateCount_); + state1_.template Reserve(regex_.stateCount_); + } + + ~GenericRegexSearch() { + Allocator::Free(stateSet_); + RAPIDJSON_DELETE(ownAllocator_); + } + + template + bool Match(InputStream& is) { + return SearchWithAnchoring(is, true, true); + } + + bool Match(const Ch* s) { + GenericStringStream is(s); + return Match(is); + } + + template + bool Search(InputStream& is) { + return SearchWithAnchoring(is, regex_.anchorBegin_, regex_.anchorEnd_); + } + + bool Search(const Ch* s) { + GenericStringStream is(s); + return Search(is); + } + +private: + typedef typename RegexType::State State; + typedef typename RegexType::Range Range; + + template + bool SearchWithAnchoring(InputStream& is, bool anchorBegin, bool anchorEnd) { + DecodedStream ds(is); + + state0_.Clear(); + Stack *current = &state0_, *next = &state1_; + const size_t stateSetSize = GetStateSetSize(); + std::memset(stateSet_, 0, stateSetSize); + + bool matched = AddState(*current, regex_.root_); + unsigned codepoint; + while (!current->Empty() && (codepoint = ds.Take()) != 0) { + std::memset(stateSet_, 0, stateSetSize); + next->Clear(); + matched = false; + for (const SizeType* s = current->template Bottom(); s != current->template End(); ++s) { + const State& sr = regex_.GetState(*s); + if (sr.codepoint == codepoint || + sr.codepoint == RegexType::kAnyCharacterClass || + (sr.codepoint == RegexType::kRangeCharacterClass && MatchRange(sr.rangeStart, codepoint))) + { + matched = AddState(*next, sr.out) || matched; + if (!anchorEnd && matched) + return true; + } + if (!anchorBegin) + AddState(*next, regex_.root_); + } + internal::Swap(current, next); + } + + return matched; + } + + size_t GetStateSetSize() const { + return (regex_.stateCount_ + 31) / 32 * 4; + } + + // Return whether the added states is a match state + bool AddState(Stack& l, SizeType index) { + RAPIDJSON_ASSERT(index != kRegexInvalidState); + + const State& s = regex_.GetState(index); + if (s.out1 != kRegexInvalidState) { // Split + bool matched = AddState(l, s.out); + return AddState(l, s.out1) || matched; + } + else if (!(stateSet_[index >> 5] & (1u << (index & 31)))) { + stateSet_[index >> 5] |= (1u << (index & 31)); + *l.template PushUnsafe() = index; + } + return s.out == kRegexInvalidState; // by using PushUnsafe() above, we can ensure s is not validated due to reallocation. + } + + bool MatchRange(SizeType rangeIndex, unsigned codepoint) const { + bool yes = (regex_.GetRange(rangeIndex).start & RegexType::kRangeNegationFlag) == 0; + while (rangeIndex != kRegexInvalidRange) { + const Range& r = regex_.GetRange(rangeIndex); + if (codepoint >= (r.start & ~RegexType::kRangeNegationFlag) && codepoint <= r.end) + return yes; + rangeIndex = r.next; + } + return !yes; + } + + const RegexType& regex_; + Allocator* allocator_; + Allocator* ownAllocator_; + Stack state0_; + Stack state1_; + uint32_t* stateSet_; +}; + +typedef GenericRegex > Regex; +typedef GenericRegexSearch RegexSearch; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_REGEX_H_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/stack.h b/Minecraft.Server/vendor/rapidjson/internal/stack.h new file mode 100644 index 00000000..73abd706 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/stack.h @@ -0,0 +1,232 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STACK_H_ +#define RAPIDJSON_INTERNAL_STACK_H_ + +#include "../allocators.h" +#include "swap.h" +#include + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +/////////////////////////////////////////////////////////////////////////////// +// Stack + +//! A type-unsafe stack for storing different types of data. +/*! \tparam Allocator Allocator for allocating stack memory. +*/ +template +class Stack { +public: + // Optimization note: Do not allocate memory for stack_ in constructor. + // Do it lazily when first Push() -> Expand() -> Resize(). + Stack(Allocator* allocator, size_t stackCapacity) : allocator_(allocator), ownAllocator_(0), stack_(0), stackTop_(0), stackEnd_(0), initialCapacity_(stackCapacity) { + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack(Stack&& rhs) + : allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + stack_(rhs.stack_), + stackTop_(rhs.stackTop_), + stackEnd_(rhs.stackEnd_), + initialCapacity_(rhs.initialCapacity_) + { + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } +#endif + + ~Stack() { + Destroy(); + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Stack& operator=(Stack&& rhs) { + if (&rhs != this) + { + Destroy(); + + allocator_ = rhs.allocator_; + ownAllocator_ = rhs.ownAllocator_; + stack_ = rhs.stack_; + stackTop_ = rhs.stackTop_; + stackEnd_ = rhs.stackEnd_; + initialCapacity_ = rhs.initialCapacity_; + + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.stack_ = 0; + rhs.stackTop_ = 0; + rhs.stackEnd_ = 0; + rhs.initialCapacity_ = 0; + } + return *this; + } +#endif + + void Swap(Stack& rhs) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, rhs.allocator_); + internal::Swap(ownAllocator_, rhs.ownAllocator_); + internal::Swap(stack_, rhs.stack_); + internal::Swap(stackTop_, rhs.stackTop_); + internal::Swap(stackEnd_, rhs.stackEnd_); + internal::Swap(initialCapacity_, rhs.initialCapacity_); + } + + void Clear() { stackTop_ = stack_; } + + void ShrinkToFit() { + if (Empty()) { + // If the stack is empty, completely deallocate the memory. + Allocator::Free(stack_); // NOLINT (+clang-analyzer-unix.Malloc) + stack_ = 0; + stackTop_ = 0; + stackEnd_ = 0; + } + else + Resize(GetSize()); + } + + // Optimization note: try to minimize the size of this function for force inline. + // Expansion is run very infrequently, so it is moved to another (probably non-inline) function. + template + RAPIDJSON_FORCEINLINE void Reserve(size_t count = 1) { + // Expand the stack if needed + if (RAPIDJSON_UNLIKELY(static_cast(sizeof(T) * count) > (stackEnd_ - stackTop_))) + Expand(count); + } + + template + RAPIDJSON_FORCEINLINE T* Push(size_t count = 1) { + Reserve(count); + return PushUnsafe(count); + } + + template + RAPIDJSON_FORCEINLINE T* PushUnsafe(size_t count = 1) { + RAPIDJSON_ASSERT(stackTop_); + RAPIDJSON_ASSERT(static_cast(sizeof(T) * count) <= (stackEnd_ - stackTop_)); + T* ret = reinterpret_cast(stackTop_); + stackTop_ += sizeof(T) * count; + return ret; + } + + template + T* Pop(size_t count) { + RAPIDJSON_ASSERT(GetSize() >= count * sizeof(T)); + stackTop_ -= count * sizeof(T); + return reinterpret_cast(stackTop_); + } + + template + T* Top() { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + const T* Top() const { + RAPIDJSON_ASSERT(GetSize() >= sizeof(T)); + return reinterpret_cast(stackTop_ - sizeof(T)); + } + + template + T* End() { return reinterpret_cast(stackTop_); } + + template + const T* End() const { return reinterpret_cast(stackTop_); } + + template + T* Bottom() { return reinterpret_cast(stack_); } + + template + const T* Bottom() const { return reinterpret_cast(stack_); } + + bool HasAllocator() const { + return allocator_ != 0; + } + + Allocator& GetAllocator() { + RAPIDJSON_ASSERT(allocator_); + return *allocator_; + } + + bool Empty() const { return stackTop_ == stack_; } + size_t GetSize() const { return static_cast(stackTop_ - stack_); } + size_t GetCapacity() const { return static_cast(stackEnd_ - stack_); } + +private: + template + void Expand(size_t count) { + // Only expand the capacity if the current stack exists. Otherwise just create a stack with initial capacity. + size_t newCapacity; + if (stack_ == 0) { + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + newCapacity = initialCapacity_; + } else { + newCapacity = GetCapacity(); + newCapacity += (newCapacity + 1) / 2; + } + size_t newSize = GetSize() + sizeof(T) * count; + if (newCapacity < newSize) + newCapacity = newSize; + + Resize(newCapacity); + } + + void Resize(size_t newCapacity) { + const size_t size = GetSize(); // Backup the current size + stack_ = static_cast(allocator_->Realloc(stack_, GetCapacity(), newCapacity)); + stackTop_ = stack_ + size; + stackEnd_ = stack_ + newCapacity; + } + + void Destroy() { + Allocator::Free(stack_); + RAPIDJSON_DELETE(ownAllocator_); // Only delete if it is owned by the stack + } + + // Prohibit copy constructor & assignment operator. + Stack(const Stack&); + Stack& operator=(const Stack&); + + Allocator* allocator_; + Allocator* ownAllocator_; + char *stack_; + char *stackTop_; + char *stackEnd_; + size_t initialCapacity_; +}; + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STACK_H_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/strfunc.h b/Minecraft.Server/vendor/rapidjson/internal/strfunc.h new file mode 100644 index 00000000..b698a8f4 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/strfunc.h @@ -0,0 +1,83 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_STRFUNC_H_ +#define RAPIDJSON_INTERNAL_STRFUNC_H_ + +#include "../stream.h" +#include + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom strlen() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s Null-terminated input string. + \return Number of characters in the string. + \note This has the same semantics as strlen(), the return value is not number of Unicode codepoints. +*/ +template +inline SizeType StrLen(const Ch* s) { + RAPIDJSON_ASSERT(s != 0); + const Ch* p = s; + while (*p) ++p; + return SizeType(p - s); +} + +template <> +inline SizeType StrLen(const char* s) { + return SizeType(std::strlen(s)); +} + +template <> +inline SizeType StrLen(const wchar_t* s) { + return SizeType(std::wcslen(s)); +} + +//! Custom strcmpn() which works on different character types. +/*! \tparam Ch Character type (e.g. char, wchar_t, short) + \param s1 Null-terminated input string. + \param s2 Null-terminated input string. + \return 0 if equal +*/ +template +inline int StrCmp(const Ch* s1, const Ch* s2) { + RAPIDJSON_ASSERT(s1 != 0); + RAPIDJSON_ASSERT(s2 != 0); + while(*s1 && (*s1 == *s2)) { s1++; s2++; } + return static_cast(*s1) < static_cast(*s2) ? -1 : static_cast(*s1) > static_cast(*s2); +} + +//! Returns number of code points in a encoded string. +template +bool CountStringCodePoint(const typename Encoding::Ch* s, SizeType length, SizeType* outCount) { + RAPIDJSON_ASSERT(s != 0); + RAPIDJSON_ASSERT(outCount != 0); + GenericStringStream is(s); + const typename Encoding::Ch* end = s + length; + SizeType count = 0; + while (is.src_ < end) { + unsigned codepoint; + if (!Encoding::Decode(is, &codepoint)) + return false; + count++; + } + *outCount = count; + return true; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_INTERNAL_STRFUNC_H_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/strtod.h b/Minecraft.Server/vendor/rapidjson/internal/strtod.h new file mode 100644 index 00000000..57c8418b --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/strtod.h @@ -0,0 +1,293 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRTOD_ +#define RAPIDJSON_STRTOD_ + +#include "ieee754.h" +#include "biginteger.h" +#include "diyfp.h" +#include "pow10.h" +#include +#include + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +inline double FastPath(double significand, int exp) { + if (exp < -308) + return 0.0; + else if (exp >= 0) + return significand * internal::Pow10(exp); + else + return significand / internal::Pow10(-exp); +} + +inline double StrtodNormalPrecision(double d, int p) { + if (p < -308) { + // Prevent expSum < -308, making Pow10(p) = 0 + d = FastPath(d, -308); + d = FastPath(d, p + 308); + } + else + d = FastPath(d, p); + return d; +} + +template +inline T Min3(T a, T b, T c) { + T m = a; + if (m > b) m = b; + if (m > c) m = c; + return m; +} + +inline int CheckWithinHalfULP(double b, const BigInteger& d, int dExp) { + const Double db(b); + const uint64_t bInt = db.IntegerSignificand(); + const int bExp = db.IntegerExponent(); + const int hExp = bExp - 1; + + int dS_Exp2 = 0, dS_Exp5 = 0, bS_Exp2 = 0, bS_Exp5 = 0, hS_Exp2 = 0, hS_Exp5 = 0; + + // Adjust for decimal exponent + if (dExp >= 0) { + dS_Exp2 += dExp; + dS_Exp5 += dExp; + } + else { + bS_Exp2 -= dExp; + bS_Exp5 -= dExp; + hS_Exp2 -= dExp; + hS_Exp5 -= dExp; + } + + // Adjust for binary exponent + if (bExp >= 0) + bS_Exp2 += bExp; + else { + dS_Exp2 -= bExp; + hS_Exp2 -= bExp; + } + + // Adjust for half ulp exponent + if (hExp >= 0) + hS_Exp2 += hExp; + else { + dS_Exp2 -= hExp; + bS_Exp2 -= hExp; + } + + // Remove common power of two factor from all three scaled values + int common_Exp2 = Min3(dS_Exp2, bS_Exp2, hS_Exp2); + dS_Exp2 -= common_Exp2; + bS_Exp2 -= common_Exp2; + hS_Exp2 -= common_Exp2; + + BigInteger dS = d; + dS.MultiplyPow5(static_cast(dS_Exp5)) <<= static_cast(dS_Exp2); + + BigInteger bS(bInt); + bS.MultiplyPow5(static_cast(bS_Exp5)) <<= static_cast(bS_Exp2); + + BigInteger hS(1); + hS.MultiplyPow5(static_cast(hS_Exp5)) <<= static_cast(hS_Exp2); + + BigInteger delta(0); + dS.Difference(bS, &delta); + + return delta.Compare(hS); +} + +inline bool StrtodFast(double d, int p, double* result) { + // Use fast path for string-to-double conversion if possible + // see http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ + if (p > 22 && p < 22 + 16) { + // Fast Path Cases In Disguise + d *= internal::Pow10(p - 22); + p = 22; + } + + if (p >= -22 && p <= 22 && d <= 9007199254740991.0) { // 2^53 - 1 + *result = FastPath(d, p); + return true; + } + else + return false; +} + +// Compute an approximation and see if it is within 1/2 ULP +template +inline bool StrtodDiyFp(const Ch* decimals, int dLen, int dExp, double* result) { + uint64_t significand = 0; + int i = 0; // 2^64 - 1 = 18446744073709551615, 1844674407370955161 = 0x1999999999999999 + for (; i < dLen; i++) { + if (significand > RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || + (significand == RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) && decimals[i] >= Ch('5'))) + break; + significand = significand * 10u + static_cast(decimals[i] - Ch('0')); + } + + if (i < dLen && decimals[i] >= Ch('5')) // Rounding + significand++; + + int remaining = dLen - i; + const int kUlpShift = 3; + const int kUlp = 1 << kUlpShift; + int64_t error = (remaining == 0) ? 0 : kUlp / 2; + + DiyFp v(significand, 0); + v = v.Normalize(); + error <<= -v.e; + + dExp += remaining; + + int actualExp; + DiyFp cachedPower = GetCachedPower10(dExp, &actualExp); + if (actualExp != dExp) { + static const DiyFp kPow10[] = { + DiyFp(RAPIDJSON_UINT64_C2(0xa0000000, 0x00000000), -60), // 10^1 + DiyFp(RAPIDJSON_UINT64_C2(0xc8000000, 0x00000000), -57), // 10^2 + DiyFp(RAPIDJSON_UINT64_C2(0xfa000000, 0x00000000), -54), // 10^3 + DiyFp(RAPIDJSON_UINT64_C2(0x9c400000, 0x00000000), -50), // 10^4 + DiyFp(RAPIDJSON_UINT64_C2(0xc3500000, 0x00000000), -47), // 10^5 + DiyFp(RAPIDJSON_UINT64_C2(0xf4240000, 0x00000000), -44), // 10^6 + DiyFp(RAPIDJSON_UINT64_C2(0x98968000, 0x00000000), -40) // 10^7 + }; + int adjustment = dExp - actualExp; + RAPIDJSON_ASSERT(adjustment >= 1 && adjustment < 8); + v = v * kPow10[adjustment - 1]; + if (dLen + adjustment > 19) // has more digits than decimal digits in 64-bit + error += kUlp / 2; + } + + v = v * cachedPower; + + error += kUlp + (error == 0 ? 0 : 1); + + const int oldExp = v.e; + v = v.Normalize(); + error <<= oldExp - v.e; + + const int effectiveSignificandSize = Double::EffectiveSignificandSize(64 + v.e); + int precisionSize = 64 - effectiveSignificandSize; + if (precisionSize + kUlpShift >= 64) { + int scaleExp = (precisionSize + kUlpShift) - 63; + v.f >>= scaleExp; + v.e += scaleExp; + error = (error >> scaleExp) + 1 + kUlp; + precisionSize -= scaleExp; + } + + DiyFp rounded(v.f >> precisionSize, v.e + precisionSize); + const uint64_t precisionBits = (v.f & ((uint64_t(1) << precisionSize) - 1)) * kUlp; + const uint64_t halfWay = (uint64_t(1) << (precisionSize - 1)) * kUlp; + if (precisionBits >= halfWay + static_cast(error)) { + rounded.f++; + if (rounded.f & (DiyFp::kDpHiddenBit << 1)) { // rounding overflows mantissa (issue #340) + rounded.f >>= 1; + rounded.e++; + } + } + + *result = rounded.ToDouble(); + + return halfWay - static_cast(error) >= precisionBits || precisionBits >= halfWay + static_cast(error); +} + +template +inline double StrtodBigInteger(double approx, const Ch* decimals, int dLen, int dExp) { + RAPIDJSON_ASSERT(dLen >= 0); + const BigInteger dInt(decimals, static_cast(dLen)); + Double a(approx); + int cmp = CheckWithinHalfULP(a.Value(), dInt, dExp); + if (cmp < 0) + return a.Value(); // within half ULP + else if (cmp == 0) { + // Round towards even + if (a.Significand() & 1) + return a.NextPositiveDouble(); + else + return a.Value(); + } + else // adjustment + return a.NextPositiveDouble(); +} + +template +inline double StrtodFullPrecision(double d, int p, const Ch* decimals, size_t length, size_t decimalPosition, int exp) { + RAPIDJSON_ASSERT(d >= 0.0); + RAPIDJSON_ASSERT(length >= 1); + + double result = 0.0; + if (StrtodFast(d, p, &result)) + return result; + + RAPIDJSON_ASSERT(length <= INT_MAX); + int dLen = static_cast(length); + + RAPIDJSON_ASSERT(length >= decimalPosition); + RAPIDJSON_ASSERT(length - decimalPosition <= INT_MAX); + int dExpAdjust = static_cast(length - decimalPosition); + + RAPIDJSON_ASSERT(exp >= INT_MIN + dExpAdjust); + int dExp = exp - dExpAdjust; + + // Make sure length+dExp does not overflow + RAPIDJSON_ASSERT(dExp <= INT_MAX - dLen); + + // Trim leading zeros + while (dLen > 0 && *decimals == '0') { + dLen--; + decimals++; + } + + // Trim trailing zeros + while (dLen > 0 && decimals[dLen - 1] == '0') { + dLen--; + dExp++; + } + + if (dLen == 0) { // Buffer only contains zeros. + return 0.0; + } + + // Trim right-most digits + const int kMaxDecimalDigit = 767 + 1; + if (dLen > kMaxDecimalDigit) { + dExp += dLen - kMaxDecimalDigit; + dLen = kMaxDecimalDigit; + } + + // If too small, underflow to zero. + // Any x <= 10^-324 is interpreted as zero. + if (dLen + dExp <= -324) + return 0.0; + + // If too large, overflow to infinity. + // Any x >= 10^309 is interpreted as +infinity. + if (dLen + dExp > 309) + return std::numeric_limits::infinity(); + + if (StrtodDiyFp(decimals, dLen, dExp, &result)) + return result; + + // Use approximation from StrtodDiyFp and make adjustment with BigInteger comparison + return StrtodBigInteger(result, decimals, dLen, dExp); +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STRTOD_ diff --git a/Minecraft.Server/vendor/rapidjson/internal/swap.h b/Minecraft.Server/vendor/rapidjson/internal/swap.h new file mode 100644 index 00000000..2cf92f93 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/internal/swap.h @@ -0,0 +1,46 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_INTERNAL_SWAP_H_ +#define RAPIDJSON_INTERNAL_SWAP_H_ + +#include "../rapidjson.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN +namespace internal { + +//! Custom swap() to avoid dependency on C++ header +/*! \tparam T Type of the arguments to swap, should be instantiated with primitive C++ types only. + \note This has the same semantics as std::swap(). +*/ +template +inline void Swap(T& a, T& b) RAPIDJSON_NOEXCEPT { + T tmp = a; + a = b; + b = tmp; +} + +} // namespace internal +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_INTERNAL_SWAP_H_ diff --git a/Minecraft.Server/vendor/rapidjson/istreamwrapper.h b/Minecraft.Server/vendor/rapidjson/istreamwrapper.h new file mode 100644 index 00000000..01437ec0 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/istreamwrapper.h @@ -0,0 +1,128 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_ISTREAMWRAPPER_H_ +#define RAPIDJSON_ISTREAMWRAPPER_H_ + +#include "stream.h" +#include +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4351) // new behavior: elements of array 'array' will be default initialized +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_istream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::istringstream + - \c std::stringstream + - \c std::wistringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wifstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_istream. +*/ + +template +class BasicIStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + + //! Constructor. + /*! + \param stream stream opened for read. + */ + BasicIStreamWrapper(StreamType &stream) : stream_(stream), buffer_(peekBuffer_), bufferSize_(4), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + Read(); + } + + //! Constructor. + /*! + \param stream stream opened for read. + \param buffer user-supplied buffer. + \param bufferSize size of buffer in bytes. Must >=4 bytes. + */ + BasicIStreamWrapper(StreamType &stream, char* buffer, size_t bufferSize) : stream_(stream), buffer_(buffer), bufferSize_(bufferSize), bufferLast_(0), current_(buffer_), readCount_(0), count_(0), eof_(false) { + RAPIDJSON_ASSERT(bufferSize >= 4); + Read(); + } + + Ch Peek() const { return *current_; } + Ch Take() { Ch c = *current_; Read(); return c; } + size_t Tell() const { return count_ + static_cast(current_ - buffer_); } + + // Not implemented + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return (current_ + 4 - !eof_ <= bufferLast_) ? current_ : 0; + } + +private: + BasicIStreamWrapper(); + BasicIStreamWrapper(const BasicIStreamWrapper&); + BasicIStreamWrapper& operator=(const BasicIStreamWrapper&); + + void Read() { + if (current_ < bufferLast_) + ++current_; + else if (!eof_) { + count_ += readCount_; + readCount_ = bufferSize_; + bufferLast_ = buffer_ + readCount_ - 1; + current_ = buffer_; + + if (!stream_.read(buffer_, static_cast(bufferSize_))) { + readCount_ = static_cast(stream_.gcount()); + *(bufferLast_ = buffer_ + readCount_) = '\0'; + eof_ = true; + } + } + } + + StreamType &stream_; + Ch peekBuffer_[4], *buffer_; + size_t bufferSize_; + Ch *bufferLast_; + Ch *current_; + size_t readCount_; + size_t count_; //!< Number of characters read + bool eof_; +}; + +typedef BasicIStreamWrapper IStreamWrapper; +typedef BasicIStreamWrapper WIStreamWrapper; + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_ISTREAMWRAPPER_H_ diff --git a/Minecraft.Server/vendor/rapidjson/memorybuffer.h b/Minecraft.Server/vendor/rapidjson/memorybuffer.h new file mode 100644 index 00000000..ffbc41ed --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/memorybuffer.h @@ -0,0 +1,70 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYBUFFER_H_ +#define RAPIDJSON_MEMORYBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output byte stream. +/*! + This class is mainly for being wrapped by EncodedOutputStream or AutoUTFOutputStream. + + It is similar to FileWriteBuffer but the destination is an in-memory buffer instead of a file. + + Differences between MemoryBuffer and StringBuffer: + 1. StringBuffer has Encoding but MemoryBuffer is only a byte buffer. + 2. StringBuffer::GetString() returns a null-terminated string. MemoryBuffer::GetBuffer() returns a buffer without terminator. + + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +struct GenericMemoryBuffer { + typedef char Ch; // byte + + GenericMemoryBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + + void Put(Ch c) { *stack_.template Push() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { stack_.ShrinkToFit(); } + Ch* Push(size_t count) { return stack_.template Push(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetBuffer() const { + return stack_.template Bottom(); + } + + size_t GetSize() const { return stack_.GetSize(); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; +}; + +typedef GenericMemoryBuffer<> MemoryBuffer; + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(MemoryBuffer& memoryBuffer, char c, size_t n) { + std::memset(memoryBuffer.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/Minecraft.Server/vendor/rapidjson/memorystream.h b/Minecraft.Server/vendor/rapidjson/memorystream.h new file mode 100644 index 00000000..77af6c99 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/memorystream.h @@ -0,0 +1,71 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_MEMORYSTREAM_H_ +#define RAPIDJSON_MEMORYSTREAM_H_ + +#include "stream.h" + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(missing-noreturn) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory input byte stream. +/*! + This class is mainly for being wrapped by EncodedInputStream or AutoUTFInputStream. + + It is similar to FileReadBuffer but the source is an in-memory buffer instead of a file. + + Differences between MemoryStream and StringStream: + 1. StringStream has encoding but MemoryStream is a byte stream. + 2. MemoryStream needs size of the source buffer and the buffer don't need to be null terminated. StringStream assume null-terminated string as source. + 3. MemoryStream supports Peek4() for encoding detection. StringStream is specified with an encoding so it should not have Peek4(). + \note implements Stream concept +*/ +struct MemoryStream { + typedef char Ch; // byte + + MemoryStream(const Ch *src, size_t size) : src_(src), begin_(src), end_(src + size), size_(size) {} + + Ch Peek() const { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_; } + Ch Take() { return RAPIDJSON_UNLIKELY(src_ == end_) ? '\0' : *src_++; } + size_t Tell() const { return static_cast(src_ - begin_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + // For encoding detection only. + const Ch* Peek4() const { + return Tell() + 4 <= size_ ? src_ : 0; + } + + const Ch* src_; //!< Current read position. + const Ch* begin_; //!< Original head of the string. + const Ch* end_; //!< End of stream. + size_t size_; //!< Size of the stream. +}; + +RAPIDJSON_NAMESPACE_END + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_MEMORYBUFFER_H_ diff --git a/Minecraft.Server/vendor/rapidjson/msinttypes/inttypes.h b/Minecraft.Server/vendor/rapidjson/msinttypes/inttypes.h new file mode 100644 index 00000000..18111286 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/msinttypes/inttypes.h @@ -0,0 +1,316 @@ +// ISO C9x compliant inttypes.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_INTTYPES_H_ // [ +#define _MSC_INTTYPES_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include "stdint.h" + +// miloyip: VC supports inttypes.h since VC2013 +#if _MSC_VER >= 1800 +#include +#else + +// 7.8 Format conversion of integer types + +typedef struct { + intmax_t quot; + intmax_t rem; +} imaxdiv_t; + +// 7.8.1 Macros for format specifiers + +#if !defined(__cplusplus) || defined(__STDC_FORMAT_MACROS) // [ See footnote 185 at page 198 + +// The fprintf macros for signed integers are: +#define PRId8 "d" +#define PRIi8 "i" +#define PRIdLEAST8 "d" +#define PRIiLEAST8 "i" +#define PRIdFAST8 "d" +#define PRIiFAST8 "i" + +#define PRId16 "hd" +#define PRIi16 "hi" +#define PRIdLEAST16 "hd" +#define PRIiLEAST16 "hi" +#define PRIdFAST16 "hd" +#define PRIiFAST16 "hi" + +#define PRId32 "I32d" +#define PRIi32 "I32i" +#define PRIdLEAST32 "I32d" +#define PRIiLEAST32 "I32i" +#define PRIdFAST32 "I32d" +#define PRIiFAST32 "I32i" + +#define PRId64 "I64d" +#define PRIi64 "I64i" +#define PRIdLEAST64 "I64d" +#define PRIiLEAST64 "I64i" +#define PRIdFAST64 "I64d" +#define PRIiFAST64 "I64i" + +#define PRIdMAX "I64d" +#define PRIiMAX "I64i" + +#define PRIdPTR "Id" +#define PRIiPTR "Ii" + +// The fprintf macros for unsigned integers are: +#define PRIo8 "o" +#define PRIu8 "u" +#define PRIx8 "x" +#define PRIX8 "X" +#define PRIoLEAST8 "o" +#define PRIuLEAST8 "u" +#define PRIxLEAST8 "x" +#define PRIXLEAST8 "X" +#define PRIoFAST8 "o" +#define PRIuFAST8 "u" +#define PRIxFAST8 "x" +#define PRIXFAST8 "X" + +#define PRIo16 "ho" +#define PRIu16 "hu" +#define PRIx16 "hx" +#define PRIX16 "hX" +#define PRIoLEAST16 "ho" +#define PRIuLEAST16 "hu" +#define PRIxLEAST16 "hx" +#define PRIXLEAST16 "hX" +#define PRIoFAST16 "ho" +#define PRIuFAST16 "hu" +#define PRIxFAST16 "hx" +#define PRIXFAST16 "hX" + +#define PRIo32 "I32o" +#define PRIu32 "I32u" +#define PRIx32 "I32x" +#define PRIX32 "I32X" +#define PRIoLEAST32 "I32o" +#define PRIuLEAST32 "I32u" +#define PRIxLEAST32 "I32x" +#define PRIXLEAST32 "I32X" +#define PRIoFAST32 "I32o" +#define PRIuFAST32 "I32u" +#define PRIxFAST32 "I32x" +#define PRIXFAST32 "I32X" + +#define PRIo64 "I64o" +#define PRIu64 "I64u" +#define PRIx64 "I64x" +#define PRIX64 "I64X" +#define PRIoLEAST64 "I64o" +#define PRIuLEAST64 "I64u" +#define PRIxLEAST64 "I64x" +#define PRIXLEAST64 "I64X" +#define PRIoFAST64 "I64o" +#define PRIuFAST64 "I64u" +#define PRIxFAST64 "I64x" +#define PRIXFAST64 "I64X" + +#define PRIoMAX "I64o" +#define PRIuMAX "I64u" +#define PRIxMAX "I64x" +#define PRIXMAX "I64X" + +#define PRIoPTR "Io" +#define PRIuPTR "Iu" +#define PRIxPTR "Ix" +#define PRIXPTR "IX" + +// The fscanf macros for signed integers are: +#define SCNd8 "d" +#define SCNi8 "i" +#define SCNdLEAST8 "d" +#define SCNiLEAST8 "i" +#define SCNdFAST8 "d" +#define SCNiFAST8 "i" + +#define SCNd16 "hd" +#define SCNi16 "hi" +#define SCNdLEAST16 "hd" +#define SCNiLEAST16 "hi" +#define SCNdFAST16 "hd" +#define SCNiFAST16 "hi" + +#define SCNd32 "ld" +#define SCNi32 "li" +#define SCNdLEAST32 "ld" +#define SCNiLEAST32 "li" +#define SCNdFAST32 "ld" +#define SCNiFAST32 "li" + +#define SCNd64 "I64d" +#define SCNi64 "I64i" +#define SCNdLEAST64 "I64d" +#define SCNiLEAST64 "I64i" +#define SCNdFAST64 "I64d" +#define SCNiFAST64 "I64i" + +#define SCNdMAX "I64d" +#define SCNiMAX "I64i" + +#ifdef _WIN64 // [ +# define SCNdPTR "I64d" +# define SCNiPTR "I64i" +#else // _WIN64 ][ +# define SCNdPTR "ld" +# define SCNiPTR "li" +#endif // _WIN64 ] + +// The fscanf macros for unsigned integers are: +#define SCNo8 "o" +#define SCNu8 "u" +#define SCNx8 "x" +#define SCNX8 "X" +#define SCNoLEAST8 "o" +#define SCNuLEAST8 "u" +#define SCNxLEAST8 "x" +#define SCNXLEAST8 "X" +#define SCNoFAST8 "o" +#define SCNuFAST8 "u" +#define SCNxFAST8 "x" +#define SCNXFAST8 "X" + +#define SCNo16 "ho" +#define SCNu16 "hu" +#define SCNx16 "hx" +#define SCNX16 "hX" +#define SCNoLEAST16 "ho" +#define SCNuLEAST16 "hu" +#define SCNxLEAST16 "hx" +#define SCNXLEAST16 "hX" +#define SCNoFAST16 "ho" +#define SCNuFAST16 "hu" +#define SCNxFAST16 "hx" +#define SCNXFAST16 "hX" + +#define SCNo32 "lo" +#define SCNu32 "lu" +#define SCNx32 "lx" +#define SCNX32 "lX" +#define SCNoLEAST32 "lo" +#define SCNuLEAST32 "lu" +#define SCNxLEAST32 "lx" +#define SCNXLEAST32 "lX" +#define SCNoFAST32 "lo" +#define SCNuFAST32 "lu" +#define SCNxFAST32 "lx" +#define SCNXFAST32 "lX" + +#define SCNo64 "I64o" +#define SCNu64 "I64u" +#define SCNx64 "I64x" +#define SCNX64 "I64X" +#define SCNoLEAST64 "I64o" +#define SCNuLEAST64 "I64u" +#define SCNxLEAST64 "I64x" +#define SCNXLEAST64 "I64X" +#define SCNoFAST64 "I64o" +#define SCNuFAST64 "I64u" +#define SCNxFAST64 "I64x" +#define SCNXFAST64 "I64X" + +#define SCNoMAX "I64o" +#define SCNuMAX "I64u" +#define SCNxMAX "I64x" +#define SCNXMAX "I64X" + +#ifdef _WIN64 // [ +# define SCNoPTR "I64o" +# define SCNuPTR "I64u" +# define SCNxPTR "I64x" +# define SCNXPTR "I64X" +#else // _WIN64 ][ +# define SCNoPTR "lo" +# define SCNuPTR "lu" +# define SCNxPTR "lx" +# define SCNXPTR "lX" +#endif // _WIN64 ] + +#endif // __STDC_FORMAT_MACROS ] + +// 7.8.2 Functions for greatest-width integer types + +// 7.8.2.1 The imaxabs function +#define imaxabs _abs64 + +// 7.8.2.2 The imaxdiv function + +// This is modified version of div() function from Microsoft's div.c found +// in %MSVC.NET%\crt\src\div.c +#ifdef STATIC_IMAXDIV // [ +static +#else // STATIC_IMAXDIV ][ +_inline +#endif // STATIC_IMAXDIV ] +imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom) +{ + imaxdiv_t result; + + result.quot = numer / denom; + result.rem = numer % denom; + + if (numer < 0 && result.rem > 0) { + // did division wrong; must fix up + ++result.quot; + result.rem -= denom; + } + + return result; +} + +// 7.8.2.3 The strtoimax and strtoumax functions +#define strtoimax _strtoi64 +#define strtoumax _strtoui64 + +// 7.8.2.4 The wcstoimax and wcstoumax functions +#define wcstoimax _wcstoi64 +#define wcstoumax _wcstoui64 + +#endif // _MSC_VER >= 1800 + +#endif // _MSC_INTTYPES_H_ ] diff --git a/Minecraft.Server/vendor/rapidjson/msinttypes/stdint.h b/Minecraft.Server/vendor/rapidjson/msinttypes/stdint.h new file mode 100644 index 00000000..3d4477b9 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/msinttypes/stdint.h @@ -0,0 +1,300 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2013 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. Neither the name of the product nor the names of its contributors may +// be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +// The above software in this distribution may have been modified by +// THL A29 Limited ("Tencent Modifications"). +// All Tencent Modifications are Copyright (C) 2015 THL A29 Limited. + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +// miloyip: Originally Visual Studio 2010 uses its own stdint.h. However it generates warning with INT64_C(), so change to use this file for vs2010. +#if _MSC_VER >= 1600 // [ +#include + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +#undef INT8_C +#undef INT16_C +#undef INT32_C +#undef INT64_C +#undef UINT8_C +#undef UINT16_C +#undef UINT32_C +#undef UINT64_C + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#else // ] _MSC_VER >= 1700 [ + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we have to wrap include with 'extern "C++" {}' +// or compiler would give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#if defined(__cplusplus) && !defined(_M_ARM) +extern "C" { +#endif +# include +#if defined(__cplusplus) && !defined(_M_ARM) +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +// These #ifndef's are needed to prevent collisions with . +// Check out Issue 9 for the details. +#ifndef INTMAX_C // [ +# define INTMAX_C INT64_C +#endif // INTMAX_C ] +#ifndef UINTMAX_C // [ +# define UINTMAX_C UINT64_C +#endif // UINTMAX_C ] + +#endif // __STDC_CONSTANT_MACROS ] + +#endif // _MSC_VER >= 1600 ] + +#endif // _MSC_STDINT_H_ ] diff --git a/Minecraft.Server/vendor/rapidjson/ostreamwrapper.h b/Minecraft.Server/vendor/rapidjson/ostreamwrapper.h new file mode 100644 index 00000000..11ed4d33 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/ostreamwrapper.h @@ -0,0 +1,81 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_OSTREAMWRAPPER_H_ +#define RAPIDJSON_OSTREAMWRAPPER_H_ + +#include "stream.h" +#include + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Wrapper of \c std::basic_ostream into RapidJSON's Stream concept. +/*! + The classes can be wrapped including but not limited to: + + - \c std::ostringstream + - \c std::stringstream + - \c std::wpstringstream + - \c std::wstringstream + - \c std::ifstream + - \c std::fstream + - \c std::wofstream + - \c std::wfstream + + \tparam StreamType Class derived from \c std::basic_ostream. +*/ + +template +class BasicOStreamWrapper { +public: + typedef typename StreamType::char_type Ch; + BasicOStreamWrapper(StreamType& stream) : stream_(stream) {} + + void Put(Ch c) { + stream_.put(c); + } + + void Flush() { + stream_.flush(); + } + + // Not implemented + char Peek() const { RAPIDJSON_ASSERT(false); return 0; } + char Take() { RAPIDJSON_ASSERT(false); return 0; } + size_t Tell() const { RAPIDJSON_ASSERT(false); return 0; } + char* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + size_t PutEnd(char*) { RAPIDJSON_ASSERT(false); return 0; } + +private: + BasicOStreamWrapper(const BasicOStreamWrapper&); + BasicOStreamWrapper& operator=(const BasicOStreamWrapper&); + + StreamType& stream_; +}; + +typedef BasicOStreamWrapper OStreamWrapper; +typedef BasicOStreamWrapper WOStreamWrapper; + +#ifdef __clang__ +RAPIDJSON_DIAG_POP +#endif + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_OSTREAMWRAPPER_H_ diff --git a/Minecraft.Server/vendor/rapidjson/pointer.h b/Minecraft.Server/vendor/rapidjson/pointer.h new file mode 100644 index 00000000..355929ed --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/pointer.h @@ -0,0 +1,1482 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_POINTER_H_ +#define RAPIDJSON_POINTER_H_ + +#include "document.h" +#include "uri.h" +#include "internal/itoa.h" +#include "error/error.h" // PointerParseErrorCode + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +#if defined(RAPIDJSON_CPLUSPLUS) && RAPIDJSON_CPLUSPLUS >= 201703L +#define RAPIDJSON_IF_CONSTEXPR if constexpr +#else +#define RAPIDJSON_IF_CONSTEXPR if +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token + +/////////////////////////////////////////////////////////////////////////////// +// GenericPointer + +//! Represents a JSON Pointer. Use Pointer for UTF8 encoding and default allocator. +/*! + This class implements RFC 6901 "JavaScript Object Notation (JSON) Pointer" + (https://tools.ietf.org/html/rfc6901). + + A JSON pointer is for identifying a specific value in a JSON document + (GenericDocument). It can simplify coding of DOM tree manipulation, because it + can access multiple-level depth of DOM tree with single API call. + + After it parses a string representation (e.g. "/foo/0" or URI fragment + representation (e.g. "#/foo/0") into its internal representation (tokens), + it can be used to resolve a specific value in multiple documents, or sub-tree + of documents. + + Contrary to GenericValue, Pointer can be copy constructed and copy assigned. + Apart from assignment, a Pointer cannot be modified after construction. + + Although Pointer is very convenient, please aware that constructing Pointer + involves parsing and dynamic memory allocation. A special constructor with user- + supplied tokens eliminates these. + + GenericPointer depends on GenericDocument and GenericValue. + + \tparam ValueType The value type of the DOM tree. E.g. GenericValue > + \tparam Allocator The allocator type for allocating memory for internal representation. + + \note GenericPointer uses same encoding of ValueType. + However, Allocator of GenericPointer is independent of Allocator of Value. +*/ +template +class GenericPointer { +public: + typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value + typedef typename ValueType::Ch Ch; //!< Character type from Value + typedef GenericUri UriType; + + + //! A token is the basic units of internal representation. + /*! + A JSON pointer string representation "/foo/123" is parsed to two tokens: + "foo" and 123. 123 will be represented in both numeric form and string form. + They are resolved according to the actual value type (object or array). + + For token that are not numbers, or the numeric value is out of bound + (greater than limits of SizeType), they are only treated as string form + (i.e. the token's index will be equal to kPointerInvalidIndex). + + This struct is public so that user can create a Pointer without parsing and + allocation, using a special constructor. + */ + struct Token { + const Ch* name; //!< Name of the token. It has null character at the end but it can contain null character. + SizeType length; //!< Length of the name. + SizeType index; //!< A valid array index, if it is not equal to kPointerInvalidIndex. + }; + + //!@name Constructors and destructor. + //@{ + + //! Default constructor. + GenericPointer(Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A null-terminated, string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + */ + explicit GenericPointer(const Ch* source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, internal::StrLen(source)); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Constructor that parses a string or URI fragment representation. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. + */ + explicit GenericPointer(const std::basic_string& source, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source.c_str(), source.size()); + } +#endif + + //! Constructor that parses a string or URI fragment representation, with length of the source string. + /*! + \param source A string or URI fragment representation of JSON pointer. + \param length Length of source. + \param allocator User supplied allocator for this pointer. If no allocator is provided, it creates a self-owned one. + \note Slightly faster than the overload without length. + */ + GenericPointer(const Ch* source, size_t length, Allocator* allocator = 0) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + Parse(source, length); + } + + //! Constructor with user-supplied tokens. + /*! + This constructor let user supplies const array of tokens. + This prevents the parsing process and eliminates allocation. + This is preferred for memory constrained environments. + + \param tokens An constant array of tokens representing the JSON pointer. + \param tokenCount Number of tokens. + + \b Example + \code + #define NAME(s) { s, sizeof(s) / sizeof(s[0]) - 1, kPointerInvalidIndex } + #define INDEX(i) { #i, sizeof(#i) - 1, i } + + static const Pointer::Token kTokens[] = { NAME("foo"), INDEX(123) }; + static const Pointer p(kTokens, sizeof(kTokens) / sizeof(kTokens[0])); + // Equivalent to static const Pointer p("/foo/123"); + + #undef NAME + #undef INDEX + \endcode + */ + GenericPointer(const Token* tokens, size_t tokenCount) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(const_cast(tokens)), tokenCount_(tokenCount), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) {} + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs) : allocator_(), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Copy constructor. + GenericPointer(const GenericPointer& rhs, Allocator* allocator) : allocator_(allocator), ownAllocator_(), nameBuffer_(), tokens_(), tokenCount_(), parseErrorOffset_(), parseErrorCode_(kPointerParseErrorNone) { + *this = rhs; + } + + //! Destructor. + ~GenericPointer() { + if (nameBuffer_) // If user-supplied tokens constructor is used, nameBuffer_ is nullptr and tokens_ are not deallocated. + Allocator::Free(tokens_); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator. + GenericPointer& operator=(const GenericPointer& rhs) { + if (this != &rhs) { + // Do not delete ownAllcator + if (nameBuffer_) + Allocator::Free(tokens_); + + tokenCount_ = rhs.tokenCount_; + parseErrorOffset_ = rhs.parseErrorOffset_; + parseErrorCode_ = rhs.parseErrorCode_; + + if (rhs.nameBuffer_) + CopyFromRaw(rhs); // Normally parsed tokens. + else { + tokens_ = rhs.tokens_; // User supplied const tokens. + nameBuffer_ = 0; + } + } + return *this; + } + + //! Swap the content of this pointer with an other. + /*! + \param other The pointer to swap with. + \note Constant complexity. + */ + GenericPointer& Swap(GenericPointer& other) RAPIDJSON_NOEXCEPT { + internal::Swap(allocator_, other.allocator_); + internal::Swap(ownAllocator_, other.ownAllocator_); + internal::Swap(nameBuffer_, other.nameBuffer_); + internal::Swap(tokens_, other.tokens_); + internal::Swap(tokenCount_, other.tokenCount_); + internal::Swap(parseErrorOffset_, other.parseErrorOffset_); + internal::Swap(parseErrorCode_, other.parseErrorCode_); + return *this; + } + + //! free-standing swap function helper + /*! + Helper function to enable support for common swap implementation pattern based on \c std::swap: + \code + void swap(MyClass& a, MyClass& b) { + using std::swap; + swap(a.pointer, b.pointer); + // ... + } + \endcode + \see Swap() + */ + friend inline void swap(GenericPointer& a, GenericPointer& b) RAPIDJSON_NOEXCEPT { a.Swap(b); } + + //@} + + //!@name Append token + //@{ + + //! Append a token and return a new Pointer + /*! + \param token Token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Token& token, Allocator* allocator = 0) const { + GenericPointer r; + r.allocator_ = allocator; + Ch *p = r.CopyFromRaw(*this, 1, token.length + 1); + std::memcpy(p, token.name, (token.length + 1) * sizeof(Ch)); + r.tokens_[tokenCount_].name = p; + r.tokens_[tokenCount_].length = token.length; + r.tokens_[tokenCount_].index = token.index; + return r; + } + + //! Append a name token with length, and return a new Pointer + /*! + \param name Name to be appended. + \param length Length of name. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const Ch* name, SizeType length, Allocator* allocator = 0) const { + Token token = { name, length, kPointerInvalidIndex }; + return Append(token, allocator); + } + + //! Append a name token without length, and return a new Pointer + /*! + \param name Name (const Ch*) to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::NotExpr::Type, Ch> >), (GenericPointer)) + Append(T* name, Allocator* allocator = 0) const { + return Append(name, internal::StrLen(name), allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Append a name token, and return a new Pointer + /*! + \param name Name to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const std::basic_string& name, Allocator* allocator = 0) const { + return Append(name.c_str(), static_cast(name.size()), allocator); + } +#endif + + //! Append a index token, and return a new Pointer + /*! + \param index Index to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(SizeType index, Allocator* allocator = 0) const { + char buffer[21]; + char* end = sizeof(SizeType) == 4 ? internal::u32toa(index, buffer) : internal::u64toa(index, buffer); + SizeType length = static_cast(end - buffer); + buffer[length] = '\0'; + + RAPIDJSON_IF_CONSTEXPR (sizeof(Ch) == 1) { + Token token = { reinterpret_cast(buffer), length, index }; + return Append(token, allocator); + } + else { + Ch name[21]; + for (size_t i = 0; i <= length; i++) + name[i] = static_cast(buffer[i]); + Token token = { name, length, index }; + return Append(token, allocator); + } + } + + //! Append a token by value, and return a new Pointer + /*! + \param token token to be appended. + \param allocator Allocator for the newly return Pointer. + \return A new Pointer with appended token. + */ + GenericPointer Append(const ValueType& token, Allocator* allocator = 0) const { + if (token.IsString()) + return Append(token.GetString(), token.GetStringLength(), allocator); + else { + RAPIDJSON_ASSERT(token.IsUint64()); + RAPIDJSON_ASSERT(token.GetUint64() <= SizeType(~0)); + return Append(static_cast(token.GetUint64()), allocator); + } + } + + //!@name Handling Parse Error + //@{ + + //! Check whether this is a valid pointer. + bool IsValid() const { return parseErrorCode_ == kPointerParseErrorNone; } + + //! Get the parsing error offset in code unit. + size_t GetParseErrorOffset() const { return parseErrorOffset_; } + + //! Get the parsing error code. + PointerParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } + + //@} + + //! Get the allocator of this pointer. + Allocator& GetAllocator() { return *allocator_; } + + //!@name Tokens + //@{ + + //! Get the token array (const version only). + const Token* GetTokens() const { return tokens_; } + + //! Get the number of tokens. + size_t GetTokenCount() const { return tokenCount_; } + + //@} + + //!@name Equality/inequality operators + //@{ + + //! Equality operator. + /*! + \note When any pointers are invalid, always returns false. + */ + bool operator==(const GenericPointer& rhs) const { + if (!IsValid() || !rhs.IsValid() || tokenCount_ != rhs.tokenCount_) + return false; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index || + tokens_[i].length != rhs.tokens_[i].length || + (tokens_[i].length != 0 && std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch)* tokens_[i].length) != 0)) + { + return false; + } + } + + return true; + } + + //! Inequality operator. + /*! + \note When any pointers are invalid, always returns true. + */ + bool operator!=(const GenericPointer& rhs) const { return !(*this == rhs); } + + //! Less than operator. + /*! + \note Invalid pointers are always greater than valid ones. + */ + bool operator<(const GenericPointer& rhs) const { + if (!IsValid()) + return false; + if (!rhs.IsValid()) + return true; + + if (tokenCount_ != rhs.tokenCount_) + return tokenCount_ < rhs.tokenCount_; + + for (size_t i = 0; i < tokenCount_; i++) { + if (tokens_[i].index != rhs.tokens_[i].index) + return tokens_[i].index < rhs.tokens_[i].index; + + if (tokens_[i].length != rhs.tokens_[i].length) + return tokens_[i].length < rhs.tokens_[i].length; + + if (int cmp = std::memcmp(tokens_[i].name, rhs.tokens_[i].name, sizeof(Ch) * tokens_[i].length)) + return cmp < 0; + } + + return false; + } + + //@} + + //!@name Stringify + //@{ + + //! Stringify the pointer into string representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + return Stringify(os); + } + + //! Stringify the pointer into URI fragment representation. + /*! + \tparam OutputStream Type of output stream. + \param os The output stream. + */ + template + bool StringifyUriFragment(OutputStream& os) const { + return Stringify(os); + } + + //@} + + //!@name Create value + //@{ + + //! Create a value in a subtree. + /*! + If the value is not exist, it creates all parent values and a JSON Null value. + So it always succeed and return the newly created or existing value. + + Remind that it may change types of parents according to tokens, so it + potentially removes previously stored values. For example, if a document + was an array, and "/foo" is used to create a value, then the document + will be changed to an object, and all existing array elements are lost. + + \param root Root value of a DOM subtree to be resolved. It can be any value other than document root. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created (a JSON Null value), or already exists value. + */ + ValueType& Create(ValueType& root, typename ValueType::AllocatorType& allocator, bool* alreadyExist = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + bool exist = true; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + if (v->IsArray() && t->name[0] == '-' && t->length == 1) { + v->PushBack(ValueType().Move(), allocator); + v = &((*v)[v->Size() - 1]); + exist = false; + } + else { + if (t->index == kPointerInvalidIndex) { // must be object name + if (!v->IsObject()) + v->SetObject(); // Change to Object + } + else { // object name or array index + if (!v->IsArray() && !v->IsObject()) + v->SetArray(); // Change to Array + } + + if (v->IsArray()) { + if (t->index >= v->Size()) { + v->Reserve(t->index + 1, allocator); + while (t->index >= v->Size()) + v->PushBack(ValueType().Move(), allocator); + exist = false; + } + v = &((*v)[t->index]); + } + else { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) { + v->AddMember(ValueType(t->name, t->length, allocator).Move(), ValueType().Move(), allocator); + m = v->MemberEnd(); + v = &(--m)->value; // Assumes AddMember() appends at the end + exist = false; + } + else + v = &m->value; + } + } + } + + if (alreadyExist) + *alreadyExist = exist; + + return *v; + } + + //! Creates a value in a document. + /*! + \param document A document to be resolved. + \param alreadyExist If non-null, it stores whether the resolved value is already exist. + \return The resolved newly created, or already exists value. + */ + template + ValueType& Create(GenericDocument& document, bool* alreadyExist = 0) const { + return Create(document, document.GetAllocator(), alreadyExist); + } + + //@} + + //!@name Compute URI + //@{ + + //! Compute the in-scope URI for a subtree. + // For use with JSON pointers into JSON schema documents. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param rootUri Root URI + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \param allocator Allocator for Uris + \return Uri if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a URI cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { + static const Ch kIdString[] = { 'i', 'd', '\0' }; + static const ValueType kIdValue(kIdString, 2); + UriType base = UriType(rootUri, allocator); + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + // See if we have an id, and if so resolve with the current base + typename ValueType::MemberIterator m = v->FindMember(kIdValue); + if (m != v->MemberEnd() && (m->value).IsString()) { + UriType here = UriType(m->value, allocator).Resolve(base, allocator); + base = here; + } + m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return UriType(allocator); + } + return base; + } + + UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0, Allocator* allocator = 0) const { + return GetUri(const_cast(root), rootUri, unresolvedTokenIndex, allocator); + } + + + //!@name Query value + //@{ + + //! Query a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token. + \return Pointer to the value if it can be resolved. Otherwise null. + + \note + There are only 3 situations when a value cannot be resolved: + 1. A value in the path is not an array nor object. + 2. An object value does not contain the token. + 3. A token is out of range of an array value. + + Use unresolvedTokenIndex to retrieve the token index. + */ + ValueType* Get(ValueType& root, size_t* unresolvedTokenIndex = 0) const { + RAPIDJSON_ASSERT(IsValid()); + ValueType* v = &root; + for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + break; + v = &m->value; + } + continue; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + break; + v = &((*v)[t->index]); + continue; + default: + break; + } + + // Error: unresolved token + if (unresolvedTokenIndex) + *unresolvedTokenIndex = static_cast(t - tokens_); + return 0; + } + return v; + } + + //! Query a const value in a const subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Pointer to the value if it can be resolved. Otherwise null. + */ + const ValueType* Get(const ValueType& root, size_t* unresolvedTokenIndex = 0) const { + return Get(const_cast(root), unresolvedTokenIndex); + } + + //@} + + //!@name Query a value with default + //@{ + + //! Query a value in a subtree with default value. + /*! + Similar to Get(), but if the specified value do not exists, it creates all parents and clone the default value. + So that this function always succeed. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param defaultValue Default value to be cloned if the value was not exists. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& GetWithDefault(ValueType& root, const ValueType& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.CopyFrom(defaultValue, allocator); + } + + //! Query a value in a subtree with default null-terminated string. + ValueType& GetWithDefault(ValueType& root, const Ch* defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a subtree with default std::basic_string. + ValueType& GetWithDefault(ValueType& root, const std::basic_string& defaultValue, typename ValueType::AllocatorType& allocator) const { + bool alreadyExist; + ValueType& v = Create(root, allocator, &alreadyExist); + return alreadyExist ? v : v.SetString(defaultValue, allocator); + } +#endif + + //! Query a value in a subtree with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(ValueType& root, T defaultValue, typename ValueType::AllocatorType& allocator) const { + return GetWithDefault(root, ValueType(defaultValue).Move(), allocator); + } + + //! Query a value in a document with default value. + template + ValueType& GetWithDefault(GenericDocument& document, const ValueType& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //! Query a value in a document with default null-terminated string. + template + ValueType& GetWithDefault(GenericDocument& document, const Ch* defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Query a value in a document with default std::basic_string. + template + ValueType& GetWithDefault(GenericDocument& document, const std::basic_string& defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } +#endif + + //! Query a value in a document with default primitive value. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + GetWithDefault(GenericDocument& document, T defaultValue) const { + return GetWithDefault(document, defaultValue, document.GetAllocator()); + } + + //@} + + //!@name Set a value + //@{ + + //! Set a value in a subtree, with move semantics. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be set. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Set(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = value; + } + + //! Set a value in a subtree, with copy semantics. + ValueType& Set(ValueType& root, const ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).CopyFrom(value, allocator); + } + + //! Set a null-terminated string in a subtree. + ValueType& Set(ValueType& root, const Ch* value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Set a std::basic_string in a subtree. + ValueType& Set(ValueType& root, const std::basic_string& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value, allocator).Move(); + } +#endif + + //! Set a primitive value in a subtree. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(ValueType& root, T value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator) = ValueType(value).Move(); + } + + //! Set a value in a document, with move semantics. + template + ValueType& Set(GenericDocument& document, ValueType& value) const { + return Create(document) = value; + } + + //! Set a value in a document, with copy semantics. + template + ValueType& Set(GenericDocument& document, const ValueType& value) const { + return Create(document).CopyFrom(value, document.GetAllocator()); + } + + //! Set a null-terminated string in a document. + template + ValueType& Set(GenericDocument& document, const Ch* value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } + +#if RAPIDJSON_HAS_STDSTRING + //! Sets a std::basic_string in a document. + template + ValueType& Set(GenericDocument& document, const std::basic_string& value) const { + return Create(document) = ValueType(value, document.GetAllocator()).Move(); + } +#endif + + //! Set a primitive value in a document. + /*! + \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c bool + */ + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) + Set(GenericDocument& document, T value) const { + return Create(document) = value; + } + + //@} + + //!@name Swap a value + //@{ + + //! Swap a value with a value in a subtree. + /*! + It creates all parents if they are not exist or types are different to the tokens. + So this function always succeeds but potentially remove existing values. + + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \param value Value to be swapped. + \param allocator Allocator for creating the values if the specified value or its parents are not exist. + \see Create() + */ + ValueType& Swap(ValueType& root, ValueType& value, typename ValueType::AllocatorType& allocator) const { + return Create(root, allocator).Swap(value); + } + + //! Swap a value with a value in a document. + template + ValueType& Swap(GenericDocument& document, ValueType& value) const { + return Create(document).Swap(value); + } + + //@} + + //! Erase a value in a subtree. + /*! + \param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root. + \return Whether the resolved value is found and erased. + + \note Erasing with an empty pointer \c Pointer(""), i.e. the root, always fail and return false. + */ + bool Erase(ValueType& root) const { + RAPIDJSON_ASSERT(IsValid()); + if (tokenCount_ == 0) // Cannot erase the root + return false; + + ValueType* v = &root; + const Token* last = tokens_ + (tokenCount_ - 1); + for (const Token *t = tokens_; t != last; ++t) { + switch (v->GetType()) { + case kObjectType: + { + typename ValueType::MemberIterator m = v->FindMember(GenericValue(GenericStringRef(t->name, t->length))); + if (m == v->MemberEnd()) + return false; + v = &m->value; + } + break; + case kArrayType: + if (t->index == kPointerInvalidIndex || t->index >= v->Size()) + return false; + v = &((*v)[t->index]); + break; + default: + return false; + } + } + + switch (v->GetType()) { + case kObjectType: + return v->EraseMember(GenericStringRef(last->name, last->length)); + case kArrayType: + if (last->index == kPointerInvalidIndex || last->index >= v->Size()) + return false; + v->Erase(v->Begin() + last->index); + return true; + default: + return false; + } + } + +private: + //! Clone the content from rhs to this. + /*! + \param rhs Source pointer. + \param extraToken Extra tokens to be allocated. + \param extraNameBufferSize Extra name buffer size (in number of Ch) to be allocated. + \return Start of non-occupied name buffer, for storing extra names. + */ + Ch* CopyFromRaw(const GenericPointer& rhs, size_t extraToken = 0, size_t extraNameBufferSize = 0) { + if (!allocator_) // allocator is independently owned. + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + size_t nameBufferSize = rhs.tokenCount_; // null terminators for tokens + for (Token *t = rhs.tokens_; t != rhs.tokens_ + rhs.tokenCount_; ++t) + nameBufferSize += t->length; + + tokenCount_ = rhs.tokenCount_ + extraToken; + tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + (nameBufferSize + extraNameBufferSize) * sizeof(Ch))); + nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + if (rhs.tokenCount_ > 0) { + std::memcpy(tokens_, rhs.tokens_, rhs.tokenCount_ * sizeof(Token)); + } + if (nameBufferSize > 0) { + std::memcpy(nameBuffer_, rhs.nameBuffer_, nameBufferSize * sizeof(Ch)); + } + + // The names of each token point to a string in the nameBuffer_. The + // previous memcpy copied over string pointers into the rhs.nameBuffer_, + // but they should point to the strings in the new nameBuffer_. + for (size_t i = 0; i < rhs.tokenCount_; ++i) { + // The offset between the string address and the name buffer should + // still be constant, so we can just get this offset and set each new + // token name according the new buffer start + the known offset. + std::ptrdiff_t name_offset = rhs.tokens_[i].name - rhs.nameBuffer_; + tokens_[i].name = nameBuffer_ + name_offset; + } + + return nameBuffer_ + nameBufferSize; + } + + //! Check whether a character should be percent-encoded. + /*! + According to RFC 3986 2.3 Unreserved Characters. + \param c The character (code unit) to be tested. + */ + bool NeedPercentEncode(Ch c) const { + return !((c >= '0' && c <= '9') || (c >= 'A' && c <='Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '.' || c == '_' || c =='~'); + } + + //! Parse a JSON String or its URI fragment representation into tokens. +#ifndef __clang__ // -Wdocumentation + /*! + \param source Either a JSON Pointer string, or its URI fragment representation. Not need to be null terminated. + \param length Length of the source string. + \note Source cannot be JSON String Representation of JSON Pointer, e.g. In "/\u0000", \u0000 will not be unescaped. + */ +#endif + void Parse(const Ch* source, size_t length) { + RAPIDJSON_ASSERT(source != NULL); + RAPIDJSON_ASSERT(nameBuffer_ == 0); + RAPIDJSON_ASSERT(tokens_ == 0); + + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Count number of '/' as tokenCount + tokenCount_ = 0; + for (const Ch* s = source; s != source + length; s++) + if (*s == '/') + tokenCount_++; + + Token* token = tokens_ = static_cast(allocator_->Malloc(tokenCount_ * sizeof(Token) + length * sizeof(Ch))); + Ch* name = nameBuffer_ = reinterpret_cast(tokens_ + tokenCount_); + size_t i = 0; + + // Detect if it is a URI fragment + bool uriFragment = false; + if (source[i] == '#') { + uriFragment = true; + i++; + } + + if (i != length && source[i] != '/') { + parseErrorCode_ = kPointerParseErrorTokenMustBeginWithSolidus; + goto error; + } + + while (i < length) { + RAPIDJSON_ASSERT(source[i] == '/'); + i++; // consumes '/' + + token->name = name; + bool isNumber = true; + + while (i < length && source[i] != '/') { + Ch c = source[i]; + if (uriFragment) { + // Decoding percent-encoding for URI fragment + if (c == '%') { + PercentDecodeStream is(&source[i], source + length); + GenericInsituStringStream os(name); + Ch* begin = os.PutBegin(); + if (!Transcoder, EncodingType>().Validate(is, os) || !is.IsValid()) { + parseErrorCode_ = kPointerParseErrorInvalidPercentEncoding; + goto error; + } + size_t len = os.PutEnd(begin); + i += is.Tell() - 1; + if (len == 1) + c = *name; + else { + name += len; + isNumber = false; + i++; + continue; + } + } + else if (NeedPercentEncode(c)) { + parseErrorCode_ = kPointerParseErrorCharacterMustPercentEncode; + goto error; + } + } + + i++; + + // Escaping "~0" -> '~', "~1" -> '/' + if (c == '~') { + if (i < length) { + c = source[i]; + if (c == '0') c = '~'; + else if (c == '1') c = '/'; + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + i++; + } + else { + parseErrorCode_ = kPointerParseErrorInvalidEscape; + goto error; + } + } + + // First check for index: all of characters are digit + if (c < '0' || c > '9') + isNumber = false; + + *name++ = c; + } + token->length = static_cast(name - token->name); + if (token->length == 0) + isNumber = false; + *name++ = '\0'; // Null terminator + + // Second check for index: more than one digit cannot have leading zero + if (isNumber && token->length > 1 && token->name[0] == '0') + isNumber = false; + + // String to SizeType conversion + SizeType n = 0; + if (isNumber) { + for (size_t j = 0; j < token->length; j++) { + SizeType m = n * 10 + static_cast(token->name[j] - '0'); + if (m < n) { // overflow detection + isNumber = false; + break; + } + n = m; + } + } + + token->index = isNumber ? n : kPointerInvalidIndex; + token++; + } + + RAPIDJSON_ASSERT(name <= nameBuffer_ + length); // Should not overflow buffer + parseErrorCode_ = kPointerParseErrorNone; + return; + + error: + Allocator::Free(tokens_); + nameBuffer_ = 0; + tokens_ = 0; + tokenCount_ = 0; + parseErrorOffset_ = i; + return; + } + + //! Stringify to string or URI fragment representation. + /*! + \tparam uriFragment True for stringifying to URI fragment representation. False for string representation. + \tparam OutputStream type of output stream. + \param os The output stream. + */ + template + bool Stringify(OutputStream& os) const { + RAPIDJSON_ASSERT(IsValid()); + + if (uriFragment) + os.Put('#'); + + for (Token *t = tokens_; t != tokens_ + tokenCount_; ++t) { + os.Put('/'); + for (size_t j = 0; j < t->length; j++) { + Ch c = t->name[j]; + if (c == '~') { + os.Put('~'); + os.Put('0'); + } + else if (c == '/') { + os.Put('~'); + os.Put('1'); + } + else if (uriFragment && NeedPercentEncode(c)) { + // Transcode to UTF8 sequence + GenericStringStream source(&t->name[j]); + PercentEncodeStream target(os); + if (!Transcoder >().Validate(source, target)) + return false; + j += source.Tell() - 1; + } + else + os.Put(c); + } + } + return true; + } + + //! A helper stream for decoding a percent-encoded sequence into code unit. + /*! + This stream decodes %XY triplet into code unit (0-255). + If it encounters invalid characters, it sets output code unit as 0 and + mark invalid, and to be checked by IsValid(). + */ + class PercentDecodeStream { + public: + typedef typename ValueType::Ch Ch; + + //! Constructor + /*! + \param source Start of the stream + \param end Past-the-end of the stream. + */ + PercentDecodeStream(const Ch* source, const Ch* end) : src_(source), head_(source), end_(end), valid_(true) {} + + Ch Take() { + if (*src_ != '%' || src_ + 3 > end_) { // %XY triplet + valid_ = false; + return 0; + } + src_++; + Ch c = 0; + for (int j = 0; j < 2; j++) { + c = static_cast(c << 4); + Ch h = *src_; + if (h >= '0' && h <= '9') c = static_cast(c + h - '0'); + else if (h >= 'A' && h <= 'F') c = static_cast(c + h - 'A' + 10); + else if (h >= 'a' && h <= 'f') c = static_cast(c + h - 'a' + 10); + else { + valid_ = false; + return 0; + } + src_++; + } + return c; + } + + size_t Tell() const { return static_cast(src_ - head_); } + bool IsValid() const { return valid_; } + + private: + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. + const Ch* end_; //!< Past-the-end position. + bool valid_; //!< Whether the parsing is valid. + }; + + //! A helper stream to encode character (UTF-8 code unit) into percent-encoded sequence. + template + class PercentEncodeStream { + public: + PercentEncodeStream(OutputStream& os) : os_(os) {} + void Put(char c) { // UTF-8 must be byte + unsigned char u = static_cast(c); + static const char hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + os_.Put('%'); + os_.Put(static_cast(hexDigits[u >> 4])); + os_.Put(static_cast(hexDigits[u & 15])); + } + private: + OutputStream& os_; + }; + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Pointer. + Ch* nameBuffer_; //!< A buffer containing all names in tokens. + Token* tokens_; //!< A list of tokens. + size_t tokenCount_; //!< Number of tokens in tokens_. + size_t parseErrorOffset_; //!< Offset in code unit when parsing fail. + PointerParseErrorCode parseErrorCode_; //!< Parsing error code. +}; + +//! GenericPointer for Value (UTF-8, default allocator). +typedef GenericPointer Pointer; + +//!@name Helper functions for GenericPointer +//@{ + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& CreateValueByPointer(T& root, const GenericPointer& pointer, typename T::AllocatorType& a) { + return pointer.Create(root, a); +} + +template +typename T::ValueType& CreateValueByPointer(T& root, const CharType(&source)[N], typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Create(root, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const GenericPointer& pointer) { + return pointer.Create(document); +} + +template +typename DocumentType::ValueType& CreateValueByPointer(DocumentType& document, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Create(document); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType* GetValueByPointer(T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const GenericPointer& pointer, size_t* unresolvedTokenIndex = 0) { + return pointer.Get(root, unresolvedTokenIndex); +} + +template +typename T::ValueType* GetValueByPointer(T& root, const CharType (&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +template +const typename T::ValueType* GetValueByPointer(const T& root, const CharType(&source)[N], size_t* unresolvedTokenIndex = 0) { + return GenericPointer(source, N - 1).Get(root, unresolvedTokenIndex); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const GenericPointer& pointer, T2 defaultValue, typename T::AllocatorType& a) { + return pointer.GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::ValueType& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const typename T::Ch* defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& GetValueByPointerWithDefault(T& root, const CharType(&source)[N], const std::basic_string& defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +GetValueByPointerWithDefault(T& root, const CharType(&source)[N], T2 defaultValue, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).GetWithDefault(root, defaultValue, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, const std::basic_string& defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const GenericPointer& pointer, T2 defaultValue) { + return pointer.GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], const std::basic_string& defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +GetValueByPointerWithDefault(DocumentType& document, const CharType(&source)[N], T2 defaultValue) { + return GenericPointer(source, N - 1).GetWithDefault(document, defaultValue); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const typename T::Ch* value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const GenericPointer& pointer, const std::basic_string& value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const GenericPointer& pointer, T2 value, typename T::AllocatorType& a) { + return pointer.Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const typename T::Ch* value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename T::ValueType& SetValueByPointer(T& root, const CharType(&source)[N], const std::basic_string& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename T::ValueType&)) +SetValueByPointer(T& root, const CharType(&source)[N], T2 value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Set(root, value, a); +} + +// No allocator parameter + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::ValueType& value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const typename DocumentType::Ch* value) { + return pointer.Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const GenericPointer& pointer, const std::basic_string& value) { + return pointer.Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const GenericPointer& pointer, T2 value) { + return pointer.Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const typename DocumentType::Ch* value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +#if RAPIDJSON_HAS_STDSTRING +template +typename DocumentType::ValueType& SetValueByPointer(DocumentType& document, const CharType(&source)[N], const std::basic_string& value) { + return GenericPointer(source, N - 1).Set(document, value); +} +#endif + +template +RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (typename DocumentType::ValueType&)) +SetValueByPointer(DocumentType& document, const CharType(&source)[N], T2 value) { + return GenericPointer(source, N - 1).Set(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +typename T::ValueType& SwapValueByPointer(T& root, const GenericPointer& pointer, typename T::ValueType& value, typename T::AllocatorType& a) { + return pointer.Swap(root, value, a); +} + +template +typename T::ValueType& SwapValueByPointer(T& root, const CharType(&source)[N], typename T::ValueType& value, typename T::AllocatorType& a) { + return GenericPointer(source, N - 1).Swap(root, value, a); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const GenericPointer& pointer, typename DocumentType::ValueType& value) { + return pointer.Swap(document, value); +} + +template +typename DocumentType::ValueType& SwapValueByPointer(DocumentType& document, const CharType(&source)[N], typename DocumentType::ValueType& value) { + return GenericPointer(source, N - 1).Swap(document, value); +} + +////////////////////////////////////////////////////////////////////////////// + +template +bool EraseValueByPointer(T& root, const GenericPointer& pointer) { + return pointer.Erase(root); +} + +template +bool EraseValueByPointer(T& root, const CharType(&source)[N]) { + return GenericPointer(source, N - 1).Erase(root); +} + +//@} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_POINTER_H_ diff --git a/Minecraft.Server/vendor/rapidjson/prettywriter.h b/Minecraft.Server/vendor/rapidjson/prettywriter.h new file mode 100644 index 00000000..fe45df1d --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/prettywriter.h @@ -0,0 +1,277 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_PRETTYWRITER_H_ +#define RAPIDJSON_PRETTYWRITER_H_ + +#include "writer.h" + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Combination of PrettyWriter format flags. +/*! \see PrettyWriter::SetFormatOptions + */ +enum PrettyFormatOptions { + kFormatDefault = 0, //!< Default pretty formatting. + kFormatSingleLineArray = 1 //!< Format arrays on a single line. +}; + +//! Writer with indentation and spacing. +/*! + \tparam OutputStream Type of output os. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class PrettyWriter : public Writer { +public: + typedef Writer Base; + typedef typename Base::Ch Ch; + + //! Constructor + /*! \param os Output stream. + \param allocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit PrettyWriter(OutputStream& os, StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(os, allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + + + explicit PrettyWriter(StackAllocator* allocator = 0, size_t levelDepth = Base::kDefaultLevelDepth) : + Base(allocator, levelDepth), indentChar_(' '), indentCharCount_(4), formatOptions_(kFormatDefault) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + PrettyWriter(PrettyWriter&& rhs) : + Base(std::forward(rhs)), indentChar_(rhs.indentChar_), indentCharCount_(rhs.indentCharCount_), formatOptions_(rhs.formatOptions_) {} +#endif + + //! Set custom indentation. + /*! \param indentChar Character for indentation. Must be whitespace character (' ', '\\t', '\\n', '\\r'). + \param indentCharCount Number of indent characters for each indentation level. + \note The default indentation is 4 spaces. + */ + PrettyWriter& SetIndent(Ch indentChar, unsigned indentCharCount) { + RAPIDJSON_ASSERT(indentChar == ' ' || indentChar == '\t' || indentChar == '\n' || indentChar == '\r'); + indentChar_ = indentChar; + indentCharCount_ = indentCharCount; + return *this; + } + + //! Set pretty writer formatting options. + /*! \param options Formatting options. + */ + PrettyWriter& SetFormatOptions(PrettyFormatOptions options) { + formatOptions_ = options; + return *this; + } + + /*! @name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { PrettyPrefix(kNullType); return Base::EndValue(Base::WriteNull()); } + bool Bool(bool b) { PrettyPrefix(b ? kTrueType : kFalseType); return Base::EndValue(Base::WriteBool(b)); } + bool Int(int i) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt(i)); } + bool Uint(unsigned u) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint(u)); } + bool Int64(int64_t i64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteInt64(i64)); } + bool Uint64(uint64_t u64) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteUint64(u64)); } + bool Double(double d) { PrettyPrefix(kNumberType); return Base::EndValue(Base::WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kNumberType); + return Base::EndValue(Base::WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + PrettyPrefix(kStringType); + return Base::EndValue(Base::WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + PrettyPrefix(kObjectType); + new (Base::level_stack_.template Push()) typename Base::Level(false); + return Base::WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); // not inside an Object + RAPIDJSON_ASSERT(!Base::level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == Base::level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndObject()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::Flush(); + return true; + } + + bool StartArray() { + PrettyPrefix(kArrayType); + new (Base::level_stack_.template Push()) typename Base::Level(true); + return Base::WriteStartArray(); + } + + bool EndArray(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); + RAPIDJSON_ASSERT(Base::level_stack_.template Top()->inArray); + bool empty = Base::level_stack_.template Pop(1)->valueCount == 0; + + if (!empty && !(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + bool ret = Base::EndValue(Base::WriteEndArray()); + (void)ret; + RAPIDJSON_ASSERT(ret == true); + if (Base::level_stack_.Empty()) // end of json text + Base::Flush(); + return true; + } + + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + \note When using PrettyWriter::RawValue(), the result json may not be indented correctly. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + PrettyPrefix(type); + return Base::EndValue(Base::WriteRawValue(json, length)); + } + +protected: + void PrettyPrefix(Type type) { + (void)type; + if (Base::level_stack_.GetSize() != 0) { // this value is not at root + typename Base::Level* level = Base::level_stack_.template Top(); + + if (level->inArray) { + if (level->valueCount > 0) { + Base::os_->Put(','); // add comma if it is not the first element in array + if (formatOptions_ & kFormatSingleLineArray) + Base::os_->Put(' '); + } + + if (!(formatOptions_ & kFormatSingleLineArray)) { + Base::os_->Put('\n'); + WriteIndent(); + } + } + else { // in object + if (level->valueCount > 0) { + if (level->valueCount % 2 == 0) { + Base::os_->Put(','); + Base::os_->Put('\n'); + } + else { + Base::os_->Put(':'); + Base::os_->Put(' '); + } + } + else + Base::os_->Put('\n'); + + if (level->valueCount % 2 == 0) + WriteIndent(); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root. + Base::hasRoot_ = true; + } + } + + void WriteIndent() { + size_t count = (Base::level_stack_.GetSize() / sizeof(typename Base::Level)) * indentCharCount_; + PutN(*Base::os_, static_cast(indentChar_), count); + } + + Ch indentChar_; + unsigned indentCharCount_; + PrettyFormatOptions formatOptions_; + +private: + // Prohibit copy constructor & assignment operator. + PrettyWriter(const PrettyWriter&); + PrettyWriter& operator=(const PrettyWriter&); +}; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/Minecraft.Server/vendor/rapidjson/rapidjson.h b/Minecraft.Server/vendor/rapidjson/rapidjson.h new file mode 100644 index 00000000..247b8e68 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/rapidjson.h @@ -0,0 +1,741 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_RAPIDJSON_H_ +#define RAPIDJSON_RAPIDJSON_H_ + +/*!\file rapidjson.h + \brief common definitions and configuration + + \see RAPIDJSON_CONFIG + */ + +/*! \defgroup RAPIDJSON_CONFIG RapidJSON configuration + \brief Configuration macros for library features + + Some RapidJSON features are configurable to adapt the library to a wide + variety of platforms, environments and usage scenarios. Most of the + features can be configured in terms of overridden or predefined + preprocessor macros at compile-time. + + Some additional customization is available in the \ref RAPIDJSON_ERRORS APIs. + + \note These macros should be given on the compiler command-line + (where applicable) to avoid inconsistent values when compiling + different translation units of a single application. + */ + +#include // malloc(), realloc(), free(), size_t +#include // memset(), memcpy(), memmove(), memcmp() + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_VERSION_STRING +// +// ALWAYS synchronize the following 3 macros with corresponding variables in /CMakeLists.txt. +// + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +// token stringification +#define RAPIDJSON_STRINGIFY(x) RAPIDJSON_DO_STRINGIFY(x) +#define RAPIDJSON_DO_STRINGIFY(x) #x + +// token concatenation +#define RAPIDJSON_JOIN(X, Y) RAPIDJSON_DO_JOIN(X, Y) +#define RAPIDJSON_DO_JOIN(X, Y) RAPIDJSON_DO_JOIN2(X, Y) +#define RAPIDJSON_DO_JOIN2(X, Y) X##Y +//!@endcond + +/*! \def RAPIDJSON_MAJOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Major version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_MINOR_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Minor version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_PATCH_VERSION + \ingroup RAPIDJSON_CONFIG + \brief Patch version of RapidJSON in integer. +*/ +/*! \def RAPIDJSON_VERSION_STRING + \ingroup RAPIDJSON_CONFIG + \brief Version of RapidJSON in ".." string format. +*/ +#define RAPIDJSON_MAJOR_VERSION 1 +#define RAPIDJSON_MINOR_VERSION 1 +#define RAPIDJSON_PATCH_VERSION 0 +#define RAPIDJSON_VERSION_STRING \ + RAPIDJSON_STRINGIFY(RAPIDJSON_MAJOR_VERSION.RAPIDJSON_MINOR_VERSION.RAPIDJSON_PATCH_VERSION) + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NAMESPACE_(BEGIN|END) +/*! \def RAPIDJSON_NAMESPACE + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace + + In order to avoid symbol clashes and/or "One Definition Rule" errors + between multiple inclusions of (different versions of) RapidJSON in + a single binary, users can customize the name of the main RapidJSON + namespace. + + In case of a single nesting level, defining \c RAPIDJSON_NAMESPACE + to a custom name (e.g. \c MyRapidJSON) is sufficient. If multiple + levels are needed, both \ref RAPIDJSON_NAMESPACE_BEGIN and \ref + RAPIDJSON_NAMESPACE_END need to be defined as well: + + \code + // in some .cpp file + #define RAPIDJSON_NAMESPACE my::rapidjson + #define RAPIDJSON_NAMESPACE_BEGIN namespace my { namespace rapidjson { + #define RAPIDJSON_NAMESPACE_END } } + #include "rapidjson/..." + \endcode + + \see rapidjson + */ +/*! \def RAPIDJSON_NAMESPACE_BEGIN + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (opening expression) + \see RAPIDJSON_NAMESPACE +*/ +/*! \def RAPIDJSON_NAMESPACE_END + \ingroup RAPIDJSON_CONFIG + \brief provide custom rapidjson namespace (closing expression) + \see RAPIDJSON_NAMESPACE +*/ +#ifndef RAPIDJSON_NAMESPACE +#define RAPIDJSON_NAMESPACE rapidjson +#endif +#ifndef RAPIDJSON_NAMESPACE_BEGIN +#define RAPIDJSON_NAMESPACE_BEGIN namespace RAPIDJSON_NAMESPACE { +#endif +#ifndef RAPIDJSON_NAMESPACE_END +#define RAPIDJSON_NAMESPACE_END } +#endif + +/////////////////////////////////////////////////////////////////////////////// +// __cplusplus macro + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#if defined(_MSC_VER) +#define RAPIDJSON_CPLUSPLUS _MSVC_LANG +#else +#define RAPIDJSON_CPLUSPLUS __cplusplus +#endif + +//!@endcond + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_HAS_STDSTRING + +#ifndef RAPIDJSON_HAS_STDSTRING +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default +#endif +/*! \def RAPIDJSON_HAS_STDSTRING + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for \c std::string + + By defining this preprocessor symbol to \c 1, several convenience functions for using + \ref rapidjson::GenericValue with \c std::string are enabled, especially + for construction and comparison. + + \hideinitializer +*/ +#endif // !defined(RAPIDJSON_HAS_STDSTRING) + +#if RAPIDJSON_HAS_STDSTRING +#include +#endif // RAPIDJSON_HAS_STDSTRING + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_USE_MEMBERSMAP + +/*! \def RAPIDJSON_USE_MEMBERSMAP + \ingroup RAPIDJSON_CONFIG + \brief Enable RapidJSON support for object members handling in a \c std::multimap + + By defining this preprocessor symbol to \c 1, \ref rapidjson::GenericValue object + members are stored in a \c std::multimap for faster lookup and deletion times, a + trade off with a slightly slower insertion time and a small object allocat(or)ed + memory overhead. + + \hideinitializer +*/ +#ifndef RAPIDJSON_USE_MEMBERSMAP +#define RAPIDJSON_USE_MEMBERSMAP 0 // not by default +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_INT64DEFINE + +/*! \def RAPIDJSON_NO_INT64DEFINE + \ingroup RAPIDJSON_CONFIG + \brief Use external 64-bit integer types. + + RapidJSON requires the 64-bit integer types \c int64_t and \c uint64_t types + to be available at global scope. + + If users have their own definition, define RAPIDJSON_NO_INT64DEFINE to + prevent RapidJSON from defining its own types. +*/ +#ifndef RAPIDJSON_NO_INT64DEFINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && (_MSC_VER < 1800) // Visual Studio 2013 +#include "msinttypes/stdint.h" +#include "msinttypes/inttypes.h" +#else +// Other compilers should have this. +#include +#include +#endif +//!@endcond +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_INT64DEFINE +#endif +#endif // RAPIDJSON_NO_INT64TYPEDEF + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_FORCEINLINE + +#ifndef RAPIDJSON_FORCEINLINE +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#if defined(_MSC_VER) && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __forceinline +#elif defined(__GNUC__) && __GNUC__ >= 4 && defined(NDEBUG) +#define RAPIDJSON_FORCEINLINE __attribute__((always_inline)) +#else +#define RAPIDJSON_FORCEINLINE +#endif +//!@endcond +#endif // RAPIDJSON_FORCEINLINE + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ENDIAN +#define RAPIDJSON_LITTLEENDIAN 0 //!< Little endian machine +#define RAPIDJSON_BIGENDIAN 1 //!< Big endian machine + +//! Endianness of the machine. +/*! + \def RAPIDJSON_ENDIAN + \ingroup RAPIDJSON_CONFIG + + GCC 4.6 provided macro for detecting endianness of the target machine. But other + compilers may not have this. User can define RAPIDJSON_ENDIAN to either + \ref RAPIDJSON_LITTLEENDIAN or \ref RAPIDJSON_BIGENDIAN. + + Default detection implemented with reference to + \li https://gcc.gnu.org/onlinedocs/gcc-4.6.0/cpp/Common-Predefined-Macros.html + \li http://www.boost.org/doc/libs/1_42_0/boost/detail/endian.hpp +*/ +#ifndef RAPIDJSON_ENDIAN +// Detect with GCC 4.6's macro +# ifdef __BYTE_ORDER__ +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __BYTE_ORDER__ +// Detect with GLIBC's endian.h +# elif defined(__GLIBC__) +# include +# if (__BYTE_ORDER == __LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif (__BYTE_ORDER == __BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif // __GLIBC__ +// Detect with _LITTLE_ENDIAN and _BIG_ENDIAN macro +# elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +// Detect with architecture macros +# elif defined(__sparc) || defined(__sparc__) || defined(_POWER) || defined(__powerpc__) || defined(__ppc__) || defined(__ppc64__) || defined(__hpux) || defined(__hppa) || defined(_MIPSEB) || defined(_POWER) || defined(__s390__) +# define RAPIDJSON_ENDIAN RAPIDJSON_BIGENDIAN +# elif defined(__i386__) || defined(__alpha__) || defined(__ia64) || defined(__ia64__) || defined(_M_IX86) || defined(_M_IA64) || defined(_M_ALPHA) || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || defined(__bfin__) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(_MSC_VER) && (defined(_M_ARM) || defined(_M_ARM64)) +# define RAPIDJSON_ENDIAN RAPIDJSON_LITTLEENDIAN +# elif defined(RAPIDJSON_DOXYGEN_RUNNING) +# define RAPIDJSON_ENDIAN +# else +# error Unknown machine endianness detected. User needs to define RAPIDJSON_ENDIAN. +# endif +#endif // RAPIDJSON_ENDIAN + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_64BIT + +//! Whether using 64-bit architecture +#ifndef RAPIDJSON_64BIT +#if defined(__LP64__) || (defined(__x86_64__) && defined(__ILP32__)) || defined(_WIN64) || defined(__EMSCRIPTEN__) +#define RAPIDJSON_64BIT 1 +#else +#define RAPIDJSON_64BIT 0 +#endif +#endif // RAPIDJSON_64BIT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ALIGN + +//! Data alignment of the machine. +/*! \ingroup RAPIDJSON_CONFIG + \param x pointer to align + + Some machines require strict data alignment. The default is 8 bytes. + User can customize by defining the RAPIDJSON_ALIGN function macro. +*/ +#ifndef RAPIDJSON_ALIGN +#define RAPIDJSON_ALIGN(x) (((x) + static_cast(7u)) & ~static_cast(7u)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_UINT64_C2 + +//! Construct a 64-bit literal by a pair of 32-bit integer. +/*! + 64-bit literal with or without ULL suffix is prone to compiler warnings. + UINT64_C() is C macro which cause compilation problems. + Use this macro to define 64-bit constants by a pair of 32-bit integer. +*/ +#ifndef RAPIDJSON_UINT64_C2 +#define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_NEON/RAPIDJSON_SIMD + +/*! \def RAPIDJSON_SIMD + \ingroup RAPIDJSON_CONFIG + \brief Enable SSE2/SSE4.2/Neon optimization. + + RapidJSON supports optimized implementations for some parsing operations + based on the SSE2, SSE4.2 or NEon SIMD extensions on modern Intel + or ARM compatible processors. + + To enable these optimizations, three different symbols can be defined; + \code + // Enable SSE2 optimization. + #define RAPIDJSON_SSE2 + + // Enable SSE4.2 optimization. + #define RAPIDJSON_SSE42 + \endcode + + // Enable ARM Neon optimization. + #define RAPIDJSON_NEON + \endcode + + \c RAPIDJSON_SSE42 takes precedence over SSE2, if both are defined. + + If any of these symbols is defined, RapidJSON defines the macro + \c RAPIDJSON_SIMD to indicate the availability of the optimized code. +*/ +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) \ + || defined(RAPIDJSON_NEON) || defined(RAPIDJSON_DOXYGEN_RUNNING) +#define RAPIDJSON_SIMD +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NO_SIZETYPEDEFINE + +#ifndef RAPIDJSON_NO_SIZETYPEDEFINE +/*! \def RAPIDJSON_NO_SIZETYPEDEFINE + \ingroup RAPIDJSON_CONFIG + \brief User-provided \c SizeType definition. + + In order to avoid using 32-bit size types for indexing strings and arrays, + define this preprocessor symbol and provide the type rapidjson::SizeType + before including RapidJSON: + \code + #define RAPIDJSON_NO_SIZETYPEDEFINE + namespace rapidjson { typedef ::std::size_t SizeType; } + #include "rapidjson/..." + \endcode + + \see rapidjson::SizeType +*/ +#ifdef RAPIDJSON_DOXYGEN_RUNNING +#define RAPIDJSON_NO_SIZETYPEDEFINE +#endif +RAPIDJSON_NAMESPACE_BEGIN +//! Size type (for string lengths, array sizes, etc.) +/*! RapidJSON uses 32-bit array/string indices even on 64-bit platforms, + instead of using \c size_t. Users may override the SizeType by defining + \ref RAPIDJSON_NO_SIZETYPEDEFINE. +*/ +typedef unsigned SizeType; +RAPIDJSON_NAMESPACE_END +#endif + +// always import std::size_t to rapidjson namespace +RAPIDJSON_NAMESPACE_BEGIN +using std::size_t; +RAPIDJSON_NAMESPACE_END + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_ASSERT + +//! Assertion. +/*! \ingroup RAPIDJSON_CONFIG + By default, rapidjson uses C \c assert() for internal assertions. + User can override it by defining RAPIDJSON_ASSERT(x) macro. + + \note Parsing errors are handled and can be customized by the + \ref RAPIDJSON_ERRORS APIs. +*/ +#ifndef RAPIDJSON_ASSERT +#include +#define RAPIDJSON_ASSERT(x) assert(x) +#endif // RAPIDJSON_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_STATIC_ASSERT + +// Prefer C++11 static_assert, if available +#ifndef RAPIDJSON_STATIC_ASSERT +#if RAPIDJSON_CPLUSPLUS >= 201103L || ( defined(_MSC_VER) && _MSC_VER >= 1800 ) +#define RAPIDJSON_STATIC_ASSERT(x) \ + static_assert(x, RAPIDJSON_STRINGIFY(x)) +#endif // C++11 +#endif // RAPIDJSON_STATIC_ASSERT + +// Adopt C++03 implementation from boost +#ifndef RAPIDJSON_STATIC_ASSERT +#ifndef __clang__ +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#endif +RAPIDJSON_NAMESPACE_BEGIN +template struct STATIC_ASSERTION_FAILURE; +template <> struct STATIC_ASSERTION_FAILURE { enum { value = 1 }; }; +template struct StaticAssertTest {}; +RAPIDJSON_NAMESPACE_END + +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE __attribute__((unused)) +#else +#define RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif +#ifndef __clang__ +//!@endcond +#endif + +/*! \def RAPIDJSON_STATIC_ASSERT + \brief (Internal) macro to check for conditions at compile-time + \param x compile-time condition + \hideinitializer + */ +#define RAPIDJSON_STATIC_ASSERT(x) \ + typedef ::RAPIDJSON_NAMESPACE::StaticAssertTest< \ + sizeof(::RAPIDJSON_NAMESPACE::STATIC_ASSERTION_FAILURE)> \ + RAPIDJSON_JOIN(StaticAssertTypedef, __LINE__) RAPIDJSON_STATIC_ASSERT_UNUSED_ATTRIBUTE +#endif // RAPIDJSON_STATIC_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_LIKELY, RAPIDJSON_UNLIKELY + +//! Compiler branching hint for expression with high probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression likely to be true. +*/ +#ifndef RAPIDJSON_LIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_LIKELY(x) __builtin_expect(!!(x), 1) +#else +#define RAPIDJSON_LIKELY(x) (x) +#endif +#endif + +//! Compiler branching hint for expression with low probability to be true. +/*! + \ingroup RAPIDJSON_CONFIG + \param x Boolean expression unlikely to be true. +*/ +#ifndef RAPIDJSON_UNLIKELY +#if defined(__GNUC__) || defined(__clang__) +#define RAPIDJSON_UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define RAPIDJSON_UNLIKELY(x) (x) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Helpers + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN + +#define RAPIDJSON_MULTILINEMACRO_BEGIN do { +#define RAPIDJSON_MULTILINEMACRO_END \ +} while((void)0, 0) + +// adopted from Boost +#define RAPIDJSON_VERSION_CODE(x,y,z) \ + (((x)*100000) + ((y)*100) + (z)) + +#if defined(__has_builtin) +#define RAPIDJSON_HAS_BUILTIN(x) __has_builtin(x) +#else +#define RAPIDJSON_HAS_BUILTIN(x) 0 +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF + +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) + +#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) +#define RAPIDJSON_DIAG_OFF(x) \ + RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) + +// push/pop support in Clang and GCC>=4.6 +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) +#else // GCC >= 4.2, < 4.6 +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ +#endif + +#elif defined(_MSC_VER) + +// pragma (MSVC specific) +#define RAPIDJSON_PRAGMA(x) __pragma(x) +#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(warning(x)) + +#define RAPIDJSON_DIAG_OFF(x) RAPIDJSON_DIAG_PRAGMA(disable: x) +#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) +#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) + +#else + +#define RAPIDJSON_DIAG_OFF(x) /* ignored */ +#define RAPIDJSON_DIAG_PUSH /* ignored */ +#define RAPIDJSON_DIAG_POP /* ignored */ + +#endif // RAPIDJSON_DIAG_* + +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11 (RAPIDJSON_CPLUSPLUS >= 201103L) +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#elif defined(__clang__) +#if __has_feature(cxx_rvalue_references) && \ + (defined(_MSC_VER) || defined(_LIBCPP_VERSION) || defined(__GLIBCXX__) && __GLIBCXX__ >= 20080306) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) + +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if RAPIDJSON_HAS_CXX11 +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#elif defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1900) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#ifndef RAPIDJSON_NOEXCEPT +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT throw() +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT +#endif + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#if (defined(_MSC_VER) && _MSC_VER >= 1700) +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 1 +#else +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif +#endif + +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1700) || \ + (defined(__SUNPRO_CC) && __SUNPRO_CC >= 0x5140 && defined(__GXX_EXPERIMENTAL_CXX0X__)) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + +/////////////////////////////////////////////////////////////////////////////// +// C++17 features + +#ifndef RAPIDJSON_HAS_CXX17 +#define RAPIDJSON_HAS_CXX17 (RAPIDJSON_CPLUSPLUS >= 201703L) +#endif + +#if RAPIDJSON_HAS_CXX17 +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[fallthrough]] +#elif defined(__has_cpp_attribute) +# if __has_cpp_attribute(clang::fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH [[clang::fallthrough]] +# elif __has_cpp_attribute(fallthrough) +# define RAPIDJSON_DELIBERATE_FALLTHROUGH __attribute__((fallthrough)) +# else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +# endif +#else +# define RAPIDJSON_DELIBERATE_FALLTHROUGH +#endif + +//!@endcond + +//! Assertion (in non-throwing contexts). + /*! \ingroup RAPIDJSON_CONFIG + Some functions provide a \c noexcept guarantee, if the compiler supports it. + In these cases, the \ref RAPIDJSON_ASSERT macro cannot be overridden to + throw an exception. This macro adds a separate customization point for + such cases. + + Defaults to C \c assert() (as \ref RAPIDJSON_ASSERT), if \c noexcept is + supported, and to \ref RAPIDJSON_ASSERT otherwise. + */ + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_NOEXCEPT_ASSERT + +#ifndef RAPIDJSON_NOEXCEPT_ASSERT +#ifdef RAPIDJSON_ASSERT_THROWS +#include +#define RAPIDJSON_NOEXCEPT_ASSERT(x) assert(x) +#else +#define RAPIDJSON_NOEXCEPT_ASSERT(x) RAPIDJSON_ASSERT(x) +#endif // RAPIDJSON_ASSERT_THROWS +#endif // RAPIDJSON_NOEXCEPT_ASSERT + +/////////////////////////////////////////////////////////////////////////////// +// malloc/realloc/free + +#ifndef RAPIDJSON_MALLOC +///! customization point for global \c malloc +#define RAPIDJSON_MALLOC(size) std::malloc(size) +#endif +#ifndef RAPIDJSON_REALLOC +///! customization point for global \c realloc +#define RAPIDJSON_REALLOC(ptr, new_size) std::realloc(ptr, new_size) +#endif +#ifndef RAPIDJSON_FREE +///! customization point for global \c free +#define RAPIDJSON_FREE(ptr) std::free(ptr) +#endif + +/////////////////////////////////////////////////////////////////////////////// +// new/delete + +#ifndef RAPIDJSON_NEW +///! customization point for global \c new +#define RAPIDJSON_NEW(TypeName) new TypeName +#endif +#ifndef RAPIDJSON_DELETE +///! customization point for global \c delete +#define RAPIDJSON_DELETE(x) delete x +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Type + +/*! \namespace rapidjson + \brief main RapidJSON namespace + \see RAPIDJSON_NAMESPACE +*/ +RAPIDJSON_NAMESPACE_BEGIN + +//! Type of JSON value +enum Type { + kNullType = 0, //!< null + kFalseType = 1, //!< false + kTrueType = 2, //!< true + kObjectType = 3, //!< object + kArrayType = 4, //!< array + kStringType = 5, //!< string + kNumberType = 6 //!< number +}; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/Minecraft.Server/vendor/rapidjson/reader.h b/Minecraft.Server/vendor/rapidjson/reader.h new file mode 100644 index 00000000..f7ef6102 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/reader.h @@ -0,0 +1,2246 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_READER_H_ +#define RAPIDJSON_READER_H_ + +/*! \file reader.h */ + +#include "allocators.h" +#include "stream.h" +#include "encodedstream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strtod.h" +#include + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(old-style-cast) +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(switch-enum) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +RAPIDJSON_DIAG_OFF(4702) // unreachable code +#endif + +#ifdef __GNUC__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(effc++) +#endif + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define RAPIDJSON_NOTHING /* deliberately empty */ +#ifndef RAPIDJSON_PARSE_ERROR_EARLY_RETURN +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN(value) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + if (RAPIDJSON_UNLIKELY(HasParseError())) { return value; } \ + RAPIDJSON_MULTILINEMACRO_END +#endif +#define RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(RAPIDJSON_NOTHING) +//!@endcond + +/*! \def RAPIDJSON_PARSE_ERROR_NORETURN + \ingroup RAPIDJSON_ERRORS + \brief Macro to indicate a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + This macros can be used as a customization point for the internal + error handling mechanism of RapidJSON. + + A common usage model is to throw an exception instead of requiring the + caller to explicitly check the \ref rapidjson::GenericReader::Parse's + return value: + + \code + #define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode,offset) \ + throw ParseException(parseErrorCode, #parseErrorCode, offset) + + #include // std::runtime_error + #include "rapidjson/error/error.h" // rapidjson::ParseResult + + struct ParseException : std::runtime_error, rapidjson::ParseResult { + ParseException(rapidjson::ParseErrorCode code, const char* msg, size_t offset) + : std::runtime_error(msg), ParseResult(code, offset) {} + }; + + #include "rapidjson/reader.h" + \endcode + + \see RAPIDJSON_PARSE_ERROR, rapidjson::GenericReader::Parse + */ +#ifndef RAPIDJSON_PARSE_ERROR_NORETURN +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_ASSERT(!HasParseError()); /* Error can only be assigned once */ \ + SetParseError(parseErrorCode, offset); \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +/*! \def RAPIDJSON_PARSE_ERROR + \ingroup RAPIDJSON_ERRORS + \brief (Internal) macro to indicate and handle a parse error. + \param parseErrorCode \ref rapidjson::ParseErrorCode of the error + \param offset position of the error in JSON input (\c size_t) + + Invokes RAPIDJSON_PARSE_ERROR_NORETURN and stops the parsing. + + \see RAPIDJSON_PARSE_ERROR_NORETURN + \hideinitializer + */ +#ifndef RAPIDJSON_PARSE_ERROR +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ + RAPIDJSON_MULTILINEMACRO_BEGIN \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; \ + RAPIDJSON_MULTILINEMACRO_END +#endif + +#include "error/error.h" // ParseErrorCode, ParseResult + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// ParseFlag + +/*! \def RAPIDJSON_PARSE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kParseDefaultFlags definition. + + User can define this as any \c ParseFlag combinations. +*/ +#ifndef RAPIDJSON_PARSE_DEFAULT_FLAGS +#define RAPIDJSON_PARSE_DEFAULT_FLAGS kParseNoFlags +#endif + +//! Combination of parseFlags +/*! \see Reader::Parse, Document::Parse, Document::ParseInsitu, Document::ParseStream + */ +enum ParseFlag { + kParseNoFlags = 0, //!< No flags are set. + kParseInsituFlag = 1, //!< In-situ(destructive) parsing. + kParseValidateEncodingFlag = 2, //!< Validate encoding of JSON strings. + kParseIterativeFlag = 4, //!< Iterative(constant complexity in terms of function call stack size) parsing. + kParseStopWhenDoneFlag = 8, //!< After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate kParseErrorDocumentRootNotSingular error. + kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower). + kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments. + kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings. + kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays. + kParseNanAndInfFlag = 256, //!< Allow parsing NaN, Inf, Infinity, -Inf and -Infinity as doubles. + kParseEscapedApostropheFlag = 512, //!< Allow escaped apostrophe in strings. + kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Handler + +/*! \class rapidjson::Handler + \brief Concept for receiving events from GenericReader upon parsing. + The functions return true if no error occurs. If they return false, + the event publisher should terminate the process. +\code +concept Handler { + typename Ch; + + bool Null(); + bool Bool(bool b); + bool Int(int i); + bool Uint(unsigned i); + bool Int64(int64_t i); + bool Uint64(uint64_t i); + bool Double(double d); + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType length, bool copy); + bool String(const Ch* str, SizeType length, bool copy); + bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); + bool EndObject(SizeType memberCount); + bool StartArray(); + bool EndArray(SizeType elementCount); +}; +\endcode +*/ +/////////////////////////////////////////////////////////////////////////////// +// BaseReaderHandler + +//! Default implementation of Handler. +/*! This can be used as base class of any reader handler. + \note implements Handler concept +*/ +template, typename Derived = void> +struct BaseReaderHandler { + typedef typename Encoding::Ch Ch; + + typedef typename internal::SelectIf, BaseReaderHandler, Derived>::Type Override; + + bool Default() { return true; } + bool Null() { return static_cast(*this).Default(); } + bool Bool(bool) { return static_cast(*this).Default(); } + bool Int(int) { return static_cast(*this).Default(); } + bool Uint(unsigned) { return static_cast(*this).Default(); } + bool Int64(int64_t) { return static_cast(*this).Default(); } + bool Uint64(uint64_t) { return static_cast(*this).Default(); } + bool Double(double) { return static_cast(*this).Default(); } + /// enabled via kParseNumbersAsStringsFlag, string is not null-terminated (use length) + bool RawNumber(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } + bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } + bool EndObject(SizeType) { return static_cast(*this).Default(); } + bool StartArray() { return static_cast(*this).Default(); } + bool EndArray(SizeType) { return static_cast(*this).Default(); } +}; + +/////////////////////////////////////////////////////////////////////////////// +// StreamLocalCopy + +namespace internal { + +template::copyOptimization> +class StreamLocalCopy; + +//! Do copy optimization. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original), original_(original) {} + ~StreamLocalCopy() { original_ = s; } + + Stream s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; + + Stream& original_; +}; + +//! Keep reference. +template +class StreamLocalCopy { +public: + StreamLocalCopy(Stream& original) : s(original) {} + + Stream& s; + +private: + StreamLocalCopy& operator=(const StreamLocalCopy&) /* = delete */; +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// SkipWhitespace + +//! Skip the JSON white spaces in a stream. +/*! \param is A input stream for skipping white spaces. + \note This function has SSE2/SSE4.2 specialization. +*/ +template +void SkipWhitespace(InputStream& is) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + typename InputStream::Ch c; + while ((c = s.Peek()) == ' ' || c == '\n' || c == '\r' || c == '\t') + s.Take(); +} + +inline const char* SkipWhitespace(const char* p, const char* end) { + while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + return p; +} + +#ifdef RAPIDJSON_SSE42 +//! Skip whitespace with SSE 4.2 pcmpistrm instruction, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The middle of string using SIMD + static const char whitespace[16] = " \n\r\t"; + const __m128i w = _mm_loadu_si128(reinterpret_cast(&whitespace[0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + const int r = _mm_cmpistri(w, s, _SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); + if (r != 16) // some of characters is non-whitespace + return p + r; + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_SSE2) + +//! Skip whitespace with SSE2 instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + // The rest of string + #define C16(c) { c, c, c, c, c, c, c, c, c, c, c, c, c, c, c, c } + static const char whitespaces[4][16] = { C16(' '), C16('\n'), C16('\r'), C16('\t') }; + #undef C16 + + const __m128i w0 = _mm_loadu_si128(reinterpret_cast(&whitespaces[0][0])); + const __m128i w1 = _mm_loadu_si128(reinterpret_cast(&whitespaces[1][0])); + const __m128i w2 = _mm_loadu_si128(reinterpret_cast(&whitespaces[2][0])); + const __m128i w3 = _mm_loadu_si128(reinterpret_cast(&whitespaces[3][0])); + + for (; p <= end - 16; p += 16) { + const __m128i s = _mm_loadu_si128(reinterpret_cast(p)); + __m128i x = _mm_cmpeq_epi8(s, w0); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w1)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w2)); + x = _mm_or_si128(x, _mm_cmpeq_epi8(s, w3)); + unsigned short r = static_cast(~_mm_movemask_epi8(x)); + if (r != 0) { // some of characters may be non-whitespace +#ifdef _MSC_VER // Find the index of first non-whitespace + unsigned long offset; + _BitScanForward(&offset, r); + return p + offset; +#else + return p + __builtin_ffs(r) - 1; +#endif + } + } + + return SkipWhitespace(p, end); +} + +#elif defined(RAPIDJSON_NEON) + +//! Skip whitespace with ARM Neon instructions, testing 16 8-byte characters at once. +inline const char *SkipWhitespace_SIMD(const char* p) { + // Fast return for single non-whitespace + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + // 16-byte align to the next boundary + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } +} + +inline const char *SkipWhitespace_SIMD(const char* p, const char* end) { + // Fast return for single non-whitespace + if (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) + ++p; + else + return p; + + const uint8x16_t w0 = vmovq_n_u8(' '); + const uint8x16_t w1 = vmovq_n_u8('\n'); + const uint8x16_t w2 = vmovq_n_u8('\r'); + const uint8x16_t w3 = vmovq_n_u8('\t'); + + for (; p <= end - 16; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, w0); + x = vorrq_u8(x, vceqq_u8(s, w1)); + x = vorrq_u8(x, vceqq_u8(s, w2)); + x = vorrq_u8(x, vceqq_u8(s, w3)); + + x = vmvnq_u8(x); // Negate + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + return p + 8 + (lz >> 3); + } + } else { + uint32_t lz = internal::clzll(low); + return p + (lz >> 3); + } + } + + return SkipWhitespace(p, end); +} + +#endif // RAPIDJSON_NEON + +#ifdef RAPIDJSON_SIMD +//! Template function specialization for InsituStringStream +template<> inline void SkipWhitespace(InsituStringStream& is) { + is.src_ = const_cast(SkipWhitespace_SIMD(is.src_)); +} + +//! Template function specialization for StringStream +template<> inline void SkipWhitespace(StringStream& is) { + is.src_ = SkipWhitespace_SIMD(is.src_); +} + +template<> inline void SkipWhitespace(EncodedInputStream, MemoryStream>& is) { + is.is_.src_ = SkipWhitespace_SIMD(is.is_.src_, is.is_.end_); +} +#endif // RAPIDJSON_SIMD + +/////////////////////////////////////////////////////////////////////////////// +// GenericReader + +//! SAX-style JSON parser. Use \ref Reader for UTF8 encoding and default allocator. +/*! GenericReader parses JSON text from a stream, and send events synchronously to an + object implementing Handler concept. + + It needs to allocate a stack for storing a single decoded string during + non-destructive parsing. + + For in-situ parsing, the decoded string is directly written to the source + text string, no temporary buffer is required. + + A GenericReader object can be reused for parsing multiple JSON text. + + \tparam SourceEncoding Encoding of the input stream. + \tparam TargetEncoding Encoding of the parse output. + \tparam StackAllocator Allocator type for stack. +*/ +template +class GenericReader { +public: + typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type + + //! Constructor. + /*! \param stackAllocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) + \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) + */ + GenericReader(StackAllocator* stackAllocator = 0, size_t stackCapacity = kDefaultStackCapacity) : + stack_(stackAllocator, stackCapacity), parseResult_(), state_(IterativeParsingStartState) {} + + //! Parse JSON text. + /*! \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + if (parseFlags & kParseIterativeFlag) + return IterativeParse(is, handler); + + parseResult_.Clear(); + + ClearStackOnExit scope(*this); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + else { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (!(parseFlags & kParseStopWhenDoneFlag)) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + + if (RAPIDJSON_UNLIKELY(is.Peek() != '\0')) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + } + } + + return parseResult_; + } + + //! Parse JSON text (with \ref kParseDefaultFlags) + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + ParseResult Parse(InputStream& is, Handler& handler) { + return Parse(is, handler); + } + + //! Initialize JSON text token-by-token parsing + /*! + */ + void IterativeParseInit() { + parseResult_.Clear(); + state_ = IterativeParsingStartState; + } + + //! Parse one token from JSON text + /*! \tparam InputStream Type of input stream, implementing Stream concept + \tparam Handler Type of handler, implementing Handler concept. + \param is Input stream to be parsed. + \param handler The handler to receive events. + \return Whether the parsing is successful. + */ + template + bool IterativeParseNext(InputStream& is, Handler& handler) { + while (RAPIDJSON_LIKELY(is.Peek() != '\0')) { + SkipWhitespaceAndComments(is); + + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state_, t); + IterativeParsingState d = Transit(state_, t, n, is, handler); + + // If we've finished or hit an error... + if (RAPIDJSON_UNLIKELY(IsIterativeParsingCompleteState(d))) { + // Report errors. + if (d == IterativeParsingErrorState) { + HandleError(state_, is); + return false; + } + + // Transition to the finish state. + RAPIDJSON_ASSERT(d == IterativeParsingFinishState); + state_ = d; + + // If StopWhenDone is not set... + if (!(parseFlags & kParseStopWhenDoneFlag)) { + // ... and extra non-whitespace data is found... + SkipWhitespaceAndComments(is); + if (is.Peek() != '\0') { + // ... this is considered an error. + HandleError(state_, is); + return false; + } + } + + // Success! We are done! + return true; + } + + // Transition to the new state. + state_ = d; + + // If we parsed anything other than a delimiter, we invoked the handler, so we can return true now. + if (!IsIterativeParsingDelimiterState(n)) + return true; + } + + // We reached the end of file. + stack_.Clear(); + + if (state_ != IterativeParsingFinishState) { + HandleError(state_, is); + return false; + } + + return true; + } + + //! Check if token-by-token parsing JSON text is complete + /*! \return Whether the JSON has been fully decoded. + */ + RAPIDJSON_FORCEINLINE bool IterativeParseComplete() const { + return IsIterativeParsingCompleteState(state_); + } + + //! Whether a parse error has occurred in the last parsing. + bool HasParseError() const { return parseResult_.IsError(); } + + //! Get the \ref ParseErrorCode of last parsing. + ParseErrorCode GetParseErrorCode() const { return parseResult_.Code(); } + + //! Get the position of last parsing error in input, 0 otherwise. + size_t GetErrorOffset() const { return parseResult_.Offset(); } + +protected: + void SetParseError(ParseErrorCode code, size_t offset) { parseResult_.Set(code, offset); } + +private: + // Prohibit copy constructor & assignment operator. + GenericReader(const GenericReader&); + GenericReader& operator=(const GenericReader&); + + void ClearStack() { stack_.Clear(); } + + // clear stack on any exit from ParseStream, e.g. due to exception + struct ClearStackOnExit { + explicit ClearStackOnExit(GenericReader& r) : r_(r) {} + ~ClearStackOnExit() { r_.ClearStack(); } + private: + GenericReader& r_; + ClearStackOnExit(const ClearStackOnExit&); + ClearStackOnExit& operator=(const ClearStackOnExit&); + }; + + template + void SkipWhitespaceAndComments(InputStream& is) { + SkipWhitespace(is); + + if (parseFlags & kParseCommentsFlag) { + while (RAPIDJSON_UNLIKELY(Consume(is, '/'))) { + if (Consume(is, '*')) { + while (true) { + if (RAPIDJSON_UNLIKELY(is.Peek() == '\0')) + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + else if (Consume(is, '*')) { + if (Consume(is, '/')) + break; + } + else + is.Take(); + } + } + else if (RAPIDJSON_LIKELY(Consume(is, '/'))) + while (is.Peek() != '\0' && is.Take() != '\n') {} + else + RAPIDJSON_PARSE_ERROR(kParseErrorUnspecificSyntaxError, is.Tell()); + + SkipWhitespace(is); + } + } + } + + // Parse object: { string : value, ... } + template + void ParseObject(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '{'); + is.Take(); // Skip '{' + + if (RAPIDJSON_UNLIKELY(!handler.StartObject())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, '}')) { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(0))) // empty object + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType memberCount = 0;;) { + if (RAPIDJSON_UNLIKELY(is.Peek() != '"')) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); + + ParseString(is, handler, true); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (RAPIDJSON_UNLIKELY(!Consume(is, ':'))) + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++memberCount; + + switch (is.Peek()) { + case ',': + is.Take(); + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + break; + case '}': + is.Take(); + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + default: + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); break; // This useless break is only for making warning and coverage happy + } + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == '}') { + if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + // Parse array: [ value, ... ] + template + void ParseArray(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == '['); + is.Take(); // Skip '[' + + if (RAPIDJSON_UNLIKELY(!handler.StartArray())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(0))) // empty array + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + + for (SizeType elementCount = 0;;) { + ParseValue(is, handler); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + ++elementCount; + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + + if (Consume(is, ',')) { + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + } + else if (Consume(is, ']')) { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + return; + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); + + if (parseFlags & kParseTrailingCommasFlag) { + if (is.Peek() == ']') { + if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + is.Take(); + return; + } + } + } + } + + template + void ParseNull(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'n'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'u') && Consume(is, 'l') && Consume(is, 'l'))) { + if (RAPIDJSON_UNLIKELY(!handler.Null())) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseTrue(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 't'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'r') && Consume(is, 'u') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(true))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + void ParseFalse(InputStream& is, Handler& handler) { + RAPIDJSON_ASSERT(is.Peek() == 'f'); + is.Take(); + + if (RAPIDJSON_LIKELY(Consume(is, 'a') && Consume(is, 'l') && Consume(is, 's') && Consume(is, 'e'))) { + if (RAPIDJSON_UNLIKELY(!handler.Bool(false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell()); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); + } + + template + RAPIDJSON_FORCEINLINE static bool Consume(InputStream& is, typename InputStream::Ch expect) { + if (RAPIDJSON_LIKELY(is.Peek() == expect)) { + is.Take(); + return true; + } + else + return false; + } + + // Helper function to parse four hexadecimal digits in \uXXXX in ParseString(). + template + unsigned ParseHex4(InputStream& is, size_t escapeOffset) { + unsigned codepoint = 0; + for (int i = 0; i < 4; i++) { + Ch c = is.Peek(); + codepoint <<= 4; + codepoint += static_cast(c); + if (c >= '0' && c <= '9') + codepoint -= '0'; + else if (c >= 'A' && c <= 'F') + codepoint -= 'A' - 10; + else if (c >= 'a' && c <= 'f') + codepoint -= 'a' - 10; + else { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(0); + } + is.Take(); + } + return codepoint; + } + + template + class StackStream { + public: + typedef CharType Ch; + + StackStream(internal::Stack& stack) : stack_(stack), length_(0) {} + RAPIDJSON_FORCEINLINE void Put(Ch c) { + *stack_.template Push() = c; + ++length_; + } + + RAPIDJSON_FORCEINLINE void* Push(SizeType count) { + length_ += count; + return stack_.template Push(count); + } + + size_t Length() const { return length_; } + + Ch* Pop() { + return stack_.template Pop(length_); + } + + private: + StackStream(const StackStream&); + StackStream& operator=(const StackStream&); + + internal::Stack& stack_; + SizeType length_; + }; + + // Parse string and generate String event. Different code paths for kParseInsituFlag. + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { + internal::StreamLocalCopy copy(is); + InputStream& s(copy.s); + + RAPIDJSON_ASSERT(s.Peek() == '\"'); + s.Take(); // Skip '\"' + + bool success = false; + if (parseFlags & kParseInsituFlag) { + typename InputStream::Ch *head = s.PutBegin(); + ParseStringToStream(s, s); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + size_t length = s.PutEnd(head) - 1; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); + } + else { + StackStream stackStream(stack_); + ParseStringToStream(s, stackStream); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + SizeType length = static_cast(stackStream.Length()) - 1; + const typename TargetEncoding::Ch* const str = stackStream.Pop(); + success = (isKey ? handler.Key(str, length, true) : handler.String(str, length, true)); + } + if (RAPIDJSON_UNLIKELY(!success)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } + + // Parse string to an output is + // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. + template + RAPIDJSON_FORCEINLINE void ParseStringToStream(InputStream& is, OutputStream& os) { +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + static const char escape[256] = { + Z16, Z16, 0, 0,'\"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '/', + Z16, Z16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, + 0, 0,'\b', 0, 0, 0,'\f', 0, 0, 0, 0, 0, 0, 0,'\n', 0, + 0, 0,'\r', 0,'\t', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 + }; +#undef Z16 +//!@endcond + + for (;;) { + // Scan and copy string before "\\\"" or < 0x20. This is an optional optimzation. + if (!(parseFlags & kParseValidateEncodingFlag)) + ScanCopyUnescapedString(is, os); + + Ch c = is.Peek(); + if (RAPIDJSON_UNLIKELY(c == '\\')) { // Escape + size_t escapeOffset = is.Tell(); // For invalid escaping, report the initial '\\' as error offset + is.Take(); + Ch e = is.Peek(); + if ((sizeof(Ch) == 1 || unsigned(e) < 256) && RAPIDJSON_LIKELY(escape[static_cast(e)])) { + is.Take(); + os.Put(static_cast(escape[static_cast(e)])); + } + else if ((parseFlags & kParseEscapedApostropheFlag) && RAPIDJSON_LIKELY(e == '\'')) { // Allow escaped apostrophe + is.Take(); + os.Put('\''); + } + else if (RAPIDJSON_LIKELY(e == 'u')) { // Unicode + is.Take(); + unsigned codepoint = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint >= 0xD800 && codepoint <= 0xDFFF)) { + // high surrogate, check if followed by valid low surrogate + if (RAPIDJSON_LIKELY(codepoint <= 0xDBFF)) { + // Handle UTF-16 surrogate pair + if (RAPIDJSON_UNLIKELY(!Consume(is, '\\') || !Consume(is, 'u'))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + unsigned codepoint2 = ParseHex4(is, escapeOffset); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; + if (RAPIDJSON_UNLIKELY(codepoint2 < 0xDC00 || codepoint2 > 0xDFFF)) + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; + } + // single low surrogate + else + { + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, escapeOffset); + } + } + TEncoding::Encode(os, codepoint); + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, escapeOffset); + } + else if (RAPIDJSON_UNLIKELY(c == '"')) { // Closing double quote + is.Take(); + os.Put('\0'); // null-terminate the string + return; + } + else if (RAPIDJSON_UNLIKELY(static_cast(c) < 0x20)) { // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + if (c == '\0') + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell()); + else + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); + } + else { + size_t offset = is.Tell(); + if (RAPIDJSON_UNLIKELY((parseFlags & kParseValidateEncodingFlag ? + !Transcoder::Validate(is, os) : + !Transcoder::Transcode(is, os)))) + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, offset); + } + } + } + + template + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InputStream&, OutputStream&) { + // Do nothing for generic version + } + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType length; + #ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; + #else + length = static_cast(__builtin_ffs(r) - 1); + #endif + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16, q += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + for (const char* pend = p + length; p != pend; ) + *q++ = *p++; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (;; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + size_t length; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + length = offset; +#else + length = static_cast(__builtin_ffs(r) - 1); +#endif + p += length; + break; + } + } + + is.src_ = is.dst_ = p; + } +#elif defined(RAPIDJSON_NEON) + // StringStream -> StackStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(StringStream& is, StackStream& os) { + const char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + return; + } + else + os.Put(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + if (length != 0) { + char* q = reinterpret_cast(os.Push(length)); + for (size_t i = 0; i < length; i++) + q[i] = p[i]; + + p += length; + } + break; + } + vst1q_u8(reinterpret_cast(os.Push(16)), s); + } + + is.src_ = p; + } + + // InsituStringStream -> InsituStringStream + static RAPIDJSON_FORCEINLINE void ScanCopyUnescapedString(InsituStringStream& is, InsituStringStream& os) { + RAPIDJSON_ASSERT(&is == &os); + (void)os; + + if (is.src_ == is.dst_) { + SkipUnescapedString(is); + return; + } + + char* p = is.src_; + char *q = is.dst_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + while (p != nextAligned) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = p; + is.dst_ = q; + return; + } + else + *q++ = *p++; + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16, q += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType length = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + length = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + length = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + for (const char* pend = p + length; p != pend; ) { + *q++ = *p++; + } + break; + } + vst1q_u8(reinterpret_cast(q), s); + } + + is.src_ = p; + is.dst_ = q; + } + + // When read/write pointers are the same for insitu stream, just skip unescaped characters + static RAPIDJSON_FORCEINLINE void SkipUnescapedString(InsituStringStream& is) { + RAPIDJSON_ASSERT(is.src_ == is.dst_); + char* p = is.src_; + + // Scan one by one until alignment (unaligned load may cross page boundary and cause crash) + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + for (; p != nextAligned; p++) + if (RAPIDJSON_UNLIKELY(*p == '\"') || RAPIDJSON_UNLIKELY(*p == '\\') || RAPIDJSON_UNLIKELY(static_cast(*p) < 0x20)) { + is.src_ = is.dst_ = p; + return; + } + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (;; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + p += 8 + (lz >> 3); + break; + } + } else { + uint32_t lz = internal::clzll(low); + p += lz >> 3; + break; + } + } + + is.src_ = is.dst_ = p; + } +#endif // RAPIDJSON_NEON + + template + class NumberStream; + + template + class NumberStream { + public: + typedef typename InputStream::Ch Ch; + + NumberStream(GenericReader& reader, InputStream& s) : is(s) { (void)reader; } + + RAPIDJSON_FORCEINLINE Ch Peek() const { return is.Peek(); } + RAPIDJSON_FORCEINLINE Ch TakePush() { return is.Take(); } + RAPIDJSON_FORCEINLINE Ch Take() { return is.Take(); } + RAPIDJSON_FORCEINLINE void Push(char) {} + + size_t Tell() { return is.Tell(); } + size_t Length() { return 0; } + const StackCharacter* Pop() { return 0; } + + protected: + NumberStream& operator=(const NumberStream&); + + InputStream& is; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s), stackStream(reader.stack_) {} + + RAPIDJSON_FORCEINLINE Ch TakePush() { + stackStream.Put(static_cast(Base::is.Peek())); + return Base::is.Take(); + } + + RAPIDJSON_FORCEINLINE void Push(StackCharacter c) { + stackStream.Put(c); + } + + size_t Length() { return stackStream.Length(); } + + const StackCharacter* Pop() { + stackStream.Put('\0'); + return stackStream.Pop(); + } + + private: + StackStream stackStream; + }; + + template + class NumberStream : public NumberStream { + typedef NumberStream Base; + public: + NumberStream(GenericReader& reader, InputStream& s) : Base(reader, s) {} + + RAPIDJSON_FORCEINLINE Ch Take() { return Base::TakePush(); } + }; + + template + void ParseNumber(InputStream& is, Handler& handler) { + typedef typename internal::SelectIf, typename TargetEncoding::Ch, char>::Type NumberCharacter; + + internal::StreamLocalCopy copy(is); + NumberStream s(*this, copy.s); + + size_t startOffset = s.Tell(); + double d = 0.0; + bool useNanOrInf = false; + + // Parse minus + bool minus = Consume(s, '-'); + + // Parse int: zero / ( digit1-9 *DIGIT ) + unsigned i = 0; + uint64_t i64 = 0; + bool use64bit = false; + int significandDigit = 0; + if (RAPIDJSON_UNLIKELY(s.Peek() == '0')) { + i = 0; + s.TakePush(); + } + else if (RAPIDJSON_LIKELY(s.Peek() >= '1' && s.Peek() <= '9')) { + i = static_cast(s.TakePush() - '0'); + + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 214748364)) { // 2^31 = 2147483648 + if (RAPIDJSON_LIKELY(i != 214748364 || s.Peek() > '8')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i >= 429496729)) { // 2^32 - 1 = 4294967295 + if (RAPIDJSON_LIKELY(i != 429496729 || s.Peek() > '5')) { + i64 = i; + use64bit = true; + break; + } + } + i = i * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + // Parse NaN or Infinity here + else if ((parseFlags & kParseNanAndInfFlag) && RAPIDJSON_LIKELY((s.Peek() == 'I' || s.Peek() == 'N'))) { + if (Consume(s, 'N')) { + if (Consume(s, 'a') && Consume(s, 'N')) { + d = std::numeric_limits::quiet_NaN(); + useNanOrInf = true; + } + } + else if (RAPIDJSON_LIKELY(Consume(s, 'I'))) { + if (Consume(s, 'n') && Consume(s, 'f')) { + d = (minus ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + useNanOrInf = true; + + if (RAPIDJSON_UNLIKELY(s.Peek() == 'i' && !(Consume(s, 'i') && Consume(s, 'n') + && Consume(s, 'i') && Consume(s, 't') && Consume(s, 'y')))) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + } + + if (RAPIDJSON_UNLIKELY(!useNanOrInf)) { + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, s.Tell()); + + // Parse 64bit int + bool useDouble = false; + if (use64bit) { + if (minus) + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC))) // 2^63 = 9223372036854775808 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x0CCCCCCC, 0xCCCCCCCC) || s.Peek() > '8')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + else + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (RAPIDJSON_UNLIKELY(i64 >= RAPIDJSON_UINT64_C2(0x19999999, 0x99999999))) // 2^64 - 1 = 18446744073709551615 + if (RAPIDJSON_LIKELY(i64 != RAPIDJSON_UINT64_C2(0x19999999, 0x99999999) || s.Peek() > '5')) { + d = static_cast(i64); + useDouble = true; + break; + } + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + significandDigit++; + } + } + + // Force double for big integer + if (useDouble) { + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + d = d * 10 + (s.TakePush() - '0'); + } + } + + // Parse frac = decimal-point 1*DIGIT + int expFrac = 0; + size_t decimalPosition; + if (!useNanOrInf && Consume(s, '.')) { + decimalPosition = s.Length(); + + if (RAPIDJSON_UNLIKELY(!(s.Peek() >= '0' && s.Peek() <= '9'))) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, s.Tell()); + + if (!useDouble) { +#if RAPIDJSON_64BIT + // Use i64 to store significand in 64-bit architecture + if (!use64bit) + i64 = i; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (i64 > RAPIDJSON_UINT64_C2(0x1FFFFF, 0xFFFFFFFF)) // 2^53 - 1 for fast path + break; + else { + i64 = i64 * 10 + static_cast(s.TakePush() - '0'); + --expFrac; + if (i64 != 0) + significandDigit++; + } + } + + d = static_cast(i64); +#else + // Use double to store significand in 32-bit architecture + d = static_cast(use64bit ? i64 : i); +#endif + useDouble = true; + } + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + if (significandDigit < 17) { + d = d * 10.0 + (s.TakePush() - '0'); + --expFrac; + if (RAPIDJSON_LIKELY(d > 0.0)) + significandDigit++; + } + else + s.TakePush(); + } + } + else + decimalPosition = s.Length(); // decimal position at the end of integer. + + // Parse exp = e [ minus / plus ] 1*DIGIT + int exp = 0; + if (!useNanOrInf && (Consume(s, 'e') || Consume(s, 'E'))) { + if (!useDouble) { + d = static_cast(use64bit ? i64 : i); + useDouble = true; + } + + bool expMinus = false; + if (Consume(s, '+')) + ; + else if (Consume(s, '-')) + expMinus = true; + + if (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = static_cast(s.Take() - '0'); + if (expMinus) { + // (exp + expFrac) must not underflow int => we're detecting when -exp gets + // dangerously close to INT_MIN (a pessimistic next digit 9 would push it into + // underflow territory): + // + // -(exp * 10 + 9) + expFrac >= INT_MIN + // <=> exp <= (expFrac - INT_MIN - 9) / 10 + RAPIDJSON_ASSERT(expFrac <= 0); + int maxExp = (expFrac + 2147483639) / 10; + + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) { + while (RAPIDJSON_UNLIKELY(s.Peek() >= '0' && s.Peek() <= '9')) // Consume the rest of exponent + s.Take(); + } + } + } + else { // positive exp + int maxExp = 308 - expFrac; + while (RAPIDJSON_LIKELY(s.Peek() >= '0' && s.Peek() <= '9')) { + exp = exp * 10 + static_cast(s.Take() - '0'); + if (RAPIDJSON_UNLIKELY(exp > maxExp)) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + } + } + else + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); + + if (expMinus) + exp = -exp; + } + + // Finish parsing, call event according to the type of number. + bool cont = true; + + if (parseFlags & kParseNumbersAsStringsFlag) { + if (parseFlags & kParseInsituFlag) { + s.Pop(); // Pop stack no matter if it will be used or not. + typename InputStream::Ch* head = is.PutBegin(); + const size_t length = s.Tell() - startOffset; + RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); + // unable to insert the \0 character here, it will erase the comma after this number + const typename TargetEncoding::Ch* const str = reinterpret_cast(head); + cont = handler.RawNumber(str, SizeType(length), false); + } + else { + SizeType numCharsToCopy = static_cast(s.Length()); + GenericStringStream > srcStream(s.Pop()); + StackStream dstStream(stack_); + while (numCharsToCopy--) { + Transcoder, TargetEncoding>::Transcode(srcStream, dstStream); + } + dstStream.Put('\0'); + const typename TargetEncoding::Ch* str = dstStream.Pop(); + const SizeType length = static_cast(dstStream.Length()) - 1; + cont = handler.RawNumber(str, SizeType(length), true); + } + } + else { + size_t length = s.Length(); + const NumberCharacter* decimal = s.Pop(); // Pop stack no matter if it will be used or not. + + if (useDouble) { + int p = exp + expFrac; + if (parseFlags & kParseFullPrecisionFlag) + d = internal::StrtodFullPrecision(d, p, decimal, length, decimalPosition, exp); + else + d = internal::StrtodNormalPrecision(d, p); + + // Use > max, instead of == inf, to fix bogus warning -Wfloat-equal + if (d > (std::numeric_limits::max)()) { + // Overflow + // TODO: internal::StrtodX should report overflow (or underflow) + RAPIDJSON_PARSE_ERROR(kParseErrorNumberTooBig, startOffset); + } + + cont = handler.Double(minus ? -d : d); + } + else if (useNanOrInf) { + cont = handler.Double(d); + } + else { + if (use64bit) { + if (minus) + cont = handler.Int64(static_cast(~i64 + 1)); + else + cont = handler.Uint64(i64); + } + else { + if (minus) + cont = handler.Int(static_cast(~i + 1)); + else + cont = handler.Uint(i); + } + } + } + if (RAPIDJSON_UNLIKELY(!cont)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, startOffset); + } + + // Parse any JSON value + template + void ParseValue(InputStream& is, Handler& handler) { + switch (is.Peek()) { + case 'n': ParseNull (is, handler); break; + case 't': ParseTrue (is, handler); break; + case 'f': ParseFalse (is, handler); break; + case '"': ParseString(is, handler); break; + case '{': ParseObject(is, handler); break; + case '[': ParseArray (is, handler); break; + default : + ParseNumber(is, handler); + break; + + } + } + + // Iterative Parsing + + // States + enum IterativeParsingState { + IterativeParsingFinishState = 0, // sink states at top + IterativeParsingErrorState, // sink states at top + IterativeParsingStartState, + + // Object states + IterativeParsingObjectInitialState, + IterativeParsingMemberKeyState, + IterativeParsingMemberValueState, + IterativeParsingObjectFinishState, + + // Array states + IterativeParsingArrayInitialState, + IterativeParsingElementState, + IterativeParsingArrayFinishState, + + // Single value state + IterativeParsingValueState, + + // Delimiter states (at bottom) + IterativeParsingElementDelimiterState, + IterativeParsingMemberDelimiterState, + IterativeParsingKeyValueDelimiterState, + + cIterativeParsingStateCount + }; + + // Tokens + enum Token { + LeftBracketToken = 0, + RightBracketToken, + + LeftCurlyBracketToken, + RightCurlyBracketToken, + + CommaToken, + ColonToken, + + StringToken, + FalseToken, + TrueToken, + NullToken, + NumberToken, + + kTokenCount + }; + + RAPIDJSON_FORCEINLINE Token Tokenize(Ch c) const { + +//!@cond RAPIDJSON_HIDDEN_FROM_DOXYGEN +#define N NumberToken +#define N16 N,N,N,N,N,N,N,N,N,N,N,N,N,N,N,N + // Maps from ASCII to Token + static const unsigned char tokenMap[256] = { + N16, // 00~0F + N16, // 10~1F + N, N, StringToken, N, N, N, N, N, N, N, N, N, CommaToken, N, N, N, // 20~2F + N, N, N, N, N, N, N, N, N, N, ColonToken, N, N, N, N, N, // 30~3F + N16, // 40~4F + N, N, N, N, N, N, N, N, N, N, N, LeftBracketToken, N, RightBracketToken, N, N, // 50~5F + N, N, N, N, N, N, FalseToken, N, N, N, N, N, N, N, NullToken, N, // 60~6F + N, N, N, N, TrueToken, N, N, N, N, N, N, LeftCurlyBracketToken, N, RightCurlyBracketToken, N, N, // 70~7F + N16, N16, N16, N16, N16, N16, N16, N16 // 80~FF + }; +#undef N +#undef N16 +//!@endcond + + if (sizeof(Ch) == 1 || static_cast(c) < 256) + return static_cast(tokenMap[static_cast(c)]); + else + return NumberToken; + } + + RAPIDJSON_FORCEINLINE IterativeParsingState Predict(IterativeParsingState state, Token token) const { + // current state x one lookahead token -> new state + static const char G[cIterativeParsingStateCount][kTokenCount] = { + // Finish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Error(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Start + { + IterativeParsingArrayInitialState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingValueState, // String + IterativeParsingValueState, // False + IterativeParsingValueState, // True + IterativeParsingValueState, // Null + IterativeParsingValueState // Number + }, + // ObjectInitial + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberKey + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingKeyValueDelimiterState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // MemberValue + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingMemberDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ObjectFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ArrayInitial + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // Element + { + IterativeParsingErrorState, // Left bracket + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingErrorState, // Right curly bracket + IterativeParsingElementDelimiterState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingErrorState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // ArrayFinish(sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // Single Value (sink state) + { + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, + IterativeParsingErrorState + }, + // ElementDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push Element state) + IterativeParsingArrayFinishState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push Element state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingElementState, // String + IterativeParsingElementState, // False + IterativeParsingElementState, // True + IterativeParsingElementState, // Null + IterativeParsingElementState // Number + }, + // MemberDelimiter + { + IterativeParsingErrorState, // Left bracket + IterativeParsingErrorState, // Right bracket + IterativeParsingErrorState, // Left curly bracket + IterativeParsingObjectFinishState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberKeyState, // String + IterativeParsingErrorState, // False + IterativeParsingErrorState, // True + IterativeParsingErrorState, // Null + IterativeParsingErrorState // Number + }, + // KeyValueDelimiter + { + IterativeParsingArrayInitialState, // Left bracket(push MemberValue state) + IterativeParsingErrorState, // Right bracket + IterativeParsingObjectInitialState, // Left curly bracket(push MemberValue state) + IterativeParsingErrorState, // Right curly bracket + IterativeParsingErrorState, // Comma + IterativeParsingErrorState, // Colon + IterativeParsingMemberValueState, // String + IterativeParsingMemberValueState, // False + IterativeParsingMemberValueState, // True + IterativeParsingMemberValueState, // Null + IterativeParsingMemberValueState // Number + }, + }; // End of G + + return static_cast(G[state][token]); + } + + // Make an advance in the token stream and state based on the candidate destination state which was returned by Transit(). + // May return a new state on state pop. + template + RAPIDJSON_FORCEINLINE IterativeParsingState Transit(IterativeParsingState src, Token token, IterativeParsingState dst, InputStream& is, Handler& handler) { + (void)token; + + switch (dst) { + case IterativeParsingErrorState: + return dst; + + case IterativeParsingObjectInitialState: + case IterativeParsingArrayInitialState: + { + // Push the state(Element or MemeberValue) if we are nested in another array or value of member. + // In this way we can get the correct state on ObjectFinish or ArrayFinish by frame pop. + IterativeParsingState n = src; + if (src == IterativeParsingArrayInitialState || src == IterativeParsingElementDelimiterState) + n = IterativeParsingElementState; + else if (src == IterativeParsingKeyValueDelimiterState) + n = IterativeParsingMemberValueState; + // Push current state. + *stack_.template Push(1) = n; + // Initialize and push the member/element count. + *stack_.template Push(1) = 0; + // Call handler + bool hr = (dst == IterativeParsingObjectInitialState) ? handler.StartObject() : handler.StartArray(); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return dst; + } + } + + case IterativeParsingMemberKeyState: + ParseString(is, handler, true); + if (HasParseError()) + return IterativeParsingErrorState; + else + return dst; + + case IterativeParsingKeyValueDelimiterState: + RAPIDJSON_ASSERT(token == ColonToken); + is.Take(); + return dst; + + case IterativeParsingMemberValueState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingElementState: + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return dst; + + case IterativeParsingMemberDelimiterState: + case IterativeParsingElementDelimiterState: + is.Take(); + // Update member/element count. + *stack_.template Top() = *stack_.template Top() + 1; + return dst; + + case IterativeParsingObjectFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingMemberDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorObjectMissName, is.Tell()); + return IterativeParsingErrorState; + } + // Get member count. + SizeType c = *stack_.template Pop(1); + // If the object is not empty, count the last member. + if (src == IterativeParsingMemberValueState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndObject(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + case IterativeParsingArrayFinishState: + { + // Transit from delimiter is only allowed when trailing commas are enabled + if (!(parseFlags & kParseTrailingCommasFlag) && src == IterativeParsingElementDelimiterState) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorValueInvalid, is.Tell()); + return IterativeParsingErrorState; + } + // Get element count. + SizeType c = *stack_.template Pop(1); + // If the array is not empty, count the last element. + if (src == IterativeParsingElementState) + ++c; + // Restore the state. + IterativeParsingState n = static_cast(*stack_.template Pop(1)); + // Transit to Finish state if this is the topmost scope. + if (n == IterativeParsingStartState) + n = IterativeParsingFinishState; + // Call handler + bool hr = handler.EndArray(c); + // On handler short circuits the parsing. + if (!hr) { + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorTermination, is.Tell()); + return IterativeParsingErrorState; + } + else { + is.Take(); + return n; + } + } + + default: + // This branch is for IterativeParsingValueState actually. + // Use `default:` rather than + // `case IterativeParsingValueState:` is for code coverage. + + // The IterativeParsingStartState is not enumerated in this switch-case. + // It is impossible for that case. And it can be caught by following assertion. + + // The IterativeParsingFinishState is not enumerated in this switch-case either. + // It is a "derivative" state which cannot triggered from Predict() directly. + // Therefore it cannot happen here. And it can be caught by following assertion. + RAPIDJSON_ASSERT(dst == IterativeParsingValueState); + + // Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state. + ParseValue(is, handler); + if (HasParseError()) { + return IterativeParsingErrorState; + } + return IterativeParsingFinishState; + } + } + + template + void HandleError(IterativeParsingState src, InputStream& is) { + if (HasParseError()) { + // Error flag has been set. + return; + } + + switch (src) { + case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell()); return; + case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell()); return; + case IterativeParsingObjectInitialState: + case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); return; + case IterativeParsingMemberKeyState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); return; + case IterativeParsingMemberValueState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); return; + case IterativeParsingKeyValueDelimiterState: + case IterativeParsingArrayInitialState: + case IterativeParsingElementDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); return; + default: RAPIDJSON_ASSERT(src == IterativeParsingElementState); RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); return; + } + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingDelimiterState(IterativeParsingState s) const { + return s >= IterativeParsingElementDelimiterState; + } + + RAPIDJSON_FORCEINLINE bool IsIterativeParsingCompleteState(IterativeParsingState s) const { + return s <= IterativeParsingErrorState; + } + + template + ParseResult IterativeParse(InputStream& is, Handler& handler) { + parseResult_.Clear(); + ClearStackOnExit scope(*this); + IterativeParsingState state = IterativeParsingStartState; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + while (is.Peek() != '\0') { + Token t = Tokenize(is.Peek()); + IterativeParsingState n = Predict(state, t); + IterativeParsingState d = Transit(state, t, n, is, handler); + + if (d == IterativeParsingErrorState) { + HandleError(state, is); + break; + } + + state = d; + + // Do not further consume streams if a root JSON has been parsed. + if ((parseFlags & kParseStopWhenDoneFlag) && state == IterativeParsingFinishState) + break; + + SkipWhitespaceAndComments(is); + RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_); + } + + // Handle the end of file. + if (state != IterativeParsingFinishState) + HandleError(state, is); + + return parseResult_; + } + + static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. + internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. + ParseResult parseResult_; + IterativeParsingState state_; +}; // class GenericReader + +//! Reader with UTF8 encoding and default allocator. +typedef GenericReader, UTF8<> > Reader; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) || defined(_MSC_VER) +RAPIDJSON_DIAG_POP +#endif + + +#ifdef __GNUC__ +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_READER_H_ diff --git a/Minecraft.Server/vendor/rapidjson/schema.h b/Minecraft.Server/vendor/rapidjson/schema.h new file mode 100644 index 00000000..f049285f --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/schema.h @@ -0,0 +1,3261 @@ +// Tencent is pleased to support the open source community by making RapidJSON available-> +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved-> +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License-> You may obtain a copy of the License at +// +// http://opensource->org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied-> See the License for the +// specific language governing permissions and limitations under the License-> + +#ifndef RAPIDJSON_SCHEMA_H_ +#define RAPIDJSON_SCHEMA_H_ + +#include "document.h" +#include "pointer.h" +#include "stringbuffer.h" +#include "error/en.h" +#include "uri.h" +#include // abs, floor + +#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX) +#define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1 +#endif + +#if !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) || !(__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)) +#define RAPIDJSON_SCHEMA_USE_STDREGEX 0 +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX +#include "internal/regex.h" +#elif RAPIDJSON_SCHEMA_USE_STDREGEX +#include +#endif + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX +#define RAPIDJSON_SCHEMA_HAS_REGEX 1 +#else +#define RAPIDJSON_SCHEMA_HAS_REGEX 0 +#endif + +#ifndef RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_VERBOSE 0 +#endif + +RAPIDJSON_DIAG_PUSH + +#if defined(__GNUC__) +RAPIDJSON_DIAG_OFF(effc++) +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_OFF(weak-vtables) +RAPIDJSON_DIAG_OFF(exit-time-destructors) +RAPIDJSON_DIAG_OFF(c++98-compat-pedantic) +RAPIDJSON_DIAG_OFF(variadic-macros) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Verbose Utilities + +#if RAPIDJSON_SCHEMA_VERBOSE + +namespace internal { + +inline void PrintInvalidKeywordData(const char* keyword) { + printf(" Fail keyword: '%s'\n", keyword); +} + +inline void PrintInvalidKeywordData(const wchar_t* keyword) { + wprintf(L" Fail keyword: '%ls'\n", keyword); +} + +inline void PrintInvalidDocumentData(const char* document) { + printf(" Fail document: '%s'\n", document); +} + +inline void PrintInvalidDocumentData(const wchar_t* document) { + wprintf(L" Fail document: '%ls'\n", document); +} + +inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) { + printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d); +} + +inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) { + wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d); +} + +inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) { + printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved); +} + +inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) { + wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved); +} + +inline void PrintMethodData(const char* method) { + printf("%s\n", method); +} + +inline void PrintMethodData(const char* method, bool b) { + printf("%s, Data: '%s'\n", method, b ? "true" : "false"); +} + +inline void PrintMethodData(const char* method, int64_t i) { + printf("%s, Data: '%" PRId64 "'\n", method, i); +} + +inline void PrintMethodData(const char* method, uint64_t u) { + printf("%s, Data: '%" PRIu64 "'\n", method, u); +} + +inline void PrintMethodData(const char* method, double d) { + printf("%s, Data: '%lf'\n", method, d); +} + +inline void PrintMethodData(const char* method, const char* s) { + printf("%s, Data: '%s'\n", method, s); +} + +inline void PrintMethodData(const char* method, const wchar_t* s) { + wprintf(L"%hs, Data: '%ls'\n", method, s); +} + +inline void PrintMethodData(const char* method, const char* s1, const char* s2) { + printf("%s, Data: '%s', '%s'\n", method, s1, s2); +} + +inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) { + wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2); +} + +} // namespace internal + +#endif // RAPIDJSON_SCHEMA_VERBOSE + +#ifndef RAPIDJSON_SCHEMA_PRINT +#if RAPIDJSON_SCHEMA_VERBOSE +#define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__) +#else +#define RAPIDJSON_SCHEMA_PRINT(name, ...) +#endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_INVALID_KEYWORD_RETURN + +#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\ +RAPIDJSON_MULTILINEMACRO_BEGIN\ + context.invalidCode = code;\ + context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\ + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\ + return false;\ +RAPIDJSON_MULTILINEMACRO_END + +/////////////////////////////////////////////////////////////////////////////// +// ValidateFlag + +/*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kValidateDefaultFlags definition. + + User can define this as any \c ValidateFlag combinations. +*/ +#ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS +#define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags +#endif + +//! Combination of validate flags +enum ValidateFlag { + kValidateNoFlags = 0, //!< No flags are set. + kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error. + kValidateReadFlag = 2, //!< Validation is for a read semantic. + kValidateWriteFlag = 4, //!< Validation is for a write semantic. + kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS +}; + +/////////////////////////////////////////////////////////////////////////////// +// Specification +enum SchemaDraft { + kDraftUnknown = -1, + kDraftNone = 0, + kDraft03 = 3, + kDraftMin = 4, //!< Current minimum supported draft + kDraft04 = 4, + kDraft05 = 5, + kDraftMax = 5, //!< Current maximum supported draft + kDraft06 = 6, + kDraft07 = 7, + kDraft2019_09 = 8, + kDraft2020_12 = 9 +}; + +enum OpenApiVersion { + kVersionUnknown = -1, + kVersionNone = 0, + kVersionMin = 2, //!< Current minimum supported version + kVersion20 = 2, + kVersion30 = 3, + kVersionMax = 3, //!< Current maximum supported version + kVersion31 = 4, +}; + +struct Specification { + Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {} + Specification(OpenApiVersion o) : oapi(o) { + if (oapi == kVersion20) draft = kDraft04; + else if (oapi == kVersion30) draft = kDraft05; + else if (oapi == kVersion31) draft = kDraft2020_12; + else draft = kDraft04; + } + ~Specification() {} + bool IsSupported() const { + return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax))); + } + SchemaDraft draft; + OpenApiVersion oapi; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Forward declarations + +template +class GenericSchemaDocument; + +namespace internal { + +template +class Schema; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaValidator + +class ISchemaValidator { +public: + virtual ~ISchemaValidator() {} + virtual bool IsValid() const = 0; + virtual void SetValidateFlags(unsigned flags) = 0; + virtual unsigned GetValidateFlags() const = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// ISchemaStateFactory + +template +class ISchemaStateFactory { +public: + virtual ~ISchemaStateFactory() {} + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0; + virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0; + virtual void* CreateHasher() = 0; + virtual uint64_t GetHashCode(void* hasher) = 0; + virtual void DestroryHasher(void* hasher) = 0; + virtual void* MallocState(size_t size) = 0; + virtual void FreeState(void* p) = 0; +}; + +/////////////////////////////////////////////////////////////////////////////// +// IValidationErrorHandler + +template +class IValidationErrorHandler { +public: + typedef typename SchemaType::Ch Ch; + typedef typename SchemaType::SValue SValue; + + virtual ~IValidationErrorHandler() {} + + virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0; + virtual void NotMultipleOf(double actual, const SValue& expected) = 0; + virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0; + virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0; + + virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0; + virtual void DoesNotMatch(const Ch* str, SizeType length) = 0; + + virtual void DisallowedItem(SizeType index) = 0; + virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0; + virtual void DuplicateItems(SizeType index1, SizeType index2) = 0; + + virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0; + virtual void StartMissingProperties() = 0; + virtual void AddMissingProperty(const SValue& name) = 0; + virtual bool EndMissingProperties() = 0; + virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void DisallowedProperty(const Ch* name, SizeType length) = 0; + + virtual void StartDependencyErrors() = 0; + virtual void StartMissingDependentProperties() = 0; + virtual void AddMissingDependentProperty(const SValue& targetName) = 0; + virtual void EndMissingDependentProperties(const SValue& sourceName) = 0; + virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0; + virtual bool EndDependencyErrors() = 0; + + virtual void DisallowedValue(const ValidateErrorCode code) = 0; + virtual void StartDisallowedType() = 0; + virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0; + virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0; + virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0; + virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0; + virtual void Disallowed() = 0; + virtual void DisallowedWhenWriting() = 0; + virtual void DisallowedWhenReading() = 0; +}; + + +/////////////////////////////////////////////////////////////////////////////// +// Hasher + +// For comparison of compound value +template +class Hasher { +public: + typedef typename Encoding::Ch Ch; + + Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {} + + bool Null() { return WriteType(kNullType); } + bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); } + bool Int(int i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast(i); return WriteNumber(n); } + bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast(u); return WriteNumber(n); } + bool Double(double d) { + Number n; + if (d < 0) n.u.i = static_cast(d); + else n.u.u = static_cast(d); + n.d = d; + return WriteNumber(n); + } + + bool RawNumber(const Ch* str, SizeType len, bool) { + WriteBuffer(kNumberType, str, len * sizeof(Ch)); + return true; + } + + bool String(const Ch* str, SizeType len, bool) { + WriteBuffer(kStringType, str, len * sizeof(Ch)); + return true; + } + + bool StartObject() { return true; } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType memberCount) { + uint64_t h = Hash(0, kObjectType); + uint64_t* kv = stack_.template Pop(memberCount * 2); + for (SizeType i = 0; i < memberCount; i++) + // Issue #2205 + // Hasing the key to avoid key=value cases with bug-prone zero-value hash + h ^= Hash(Hash(0, kv[i * 2]), kv[i * 2 + 1]); // Use xor to achieve member order insensitive + *stack_.template Push() = h; + return true; + } + + bool StartArray() { return true; } + bool EndArray(SizeType elementCount) { + uint64_t h = Hash(0, kArrayType); + uint64_t* e = stack_.template Pop(elementCount); + for (SizeType i = 0; i < elementCount; i++) + h = Hash(h, e[i]); // Use hash to achieve element order sensitive + *stack_.template Push() = h; + return true; + } + + bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); } + + uint64_t GetHashCode() const { + RAPIDJSON_ASSERT(IsValid()); + return *stack_.template Top(); + } + +private: + static const size_t kDefaultSize = 256; + struct Number { + union U { + uint64_t u; + int64_t i; + }u; + double d; + }; + + bool WriteType(Type type) { return WriteBuffer(type, 0, 0); } + + bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); } + + bool WriteBuffer(Type type, const void* data, size_t len) { + // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/ + uint64_t h = Hash(RAPIDJSON_UINT64_C2(0xcbf29ce4, 0x84222325), type); + const unsigned char* d = static_cast(data); + for (size_t i = 0; i < len; i++) + h = Hash(h, d[i]); + *stack_.template Push() = h; + return true; + } + + static uint64_t Hash(uint64_t h, uint64_t d) { + static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3); + h ^= d; + h *= kPrime; + return h; + } + + Stack stack_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidationContext + +template +struct SchemaValidationContext { + typedef Schema SchemaType; + typedef ISchemaStateFactory SchemaValidatorFactoryType; + typedef IValidationErrorHandler ErrorHandlerType; + typedef typename SchemaType::ValueType ValueType; + typedef typename ValueType::Ch Ch; + + enum PatternValidatorType { + kPatternValidatorOnly, + kPatternValidatorWithProperty, + kPatternValidatorWithAdditionalProperty + }; + + SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) : + factory(f), + error_handler(eh), + schema(s), + flags(fl), + valueSchema(), + invalidKeyword(), + invalidCode(), + hasher(), + arrayElementHashCodes(), + validators(), + validatorCount(), + patternPropertiesValidators(), + patternPropertiesValidatorCount(), + patternPropertiesSchemas(), + patternPropertiesSchemaCount(), + valuePatternValidatorType(kPatternValidatorOnly), + propertyExist(), + inArray(false), + valueUniqueness(false), + arrayUniqueness(false) + { + } + + ~SchemaValidationContext() { + if (hasher) + factory.DestroryHasher(hasher); + if (validators) { + for (SizeType i = 0; i < validatorCount; i++) { + if (validators[i]) { + factory.DestroySchemaValidator(validators[i]); + } + } + factory.FreeState(validators); + } + if (patternPropertiesValidators) { + for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) { + if (patternPropertiesValidators[i]) { + factory.DestroySchemaValidator(patternPropertiesValidators[i]); + } + } + factory.FreeState(patternPropertiesValidators); + } + if (patternPropertiesSchemas) + factory.FreeState(patternPropertiesSchemas); + if (propertyExist) + factory.FreeState(propertyExist); + } + + SchemaValidatorFactoryType& factory; + ErrorHandlerType& error_handler; + const SchemaType* schema; + unsigned flags; + const SchemaType* valueSchema; + const Ch* invalidKeyword; + ValidateErrorCode invalidCode; + void* hasher; // Only validator access + void* arrayElementHashCodes; // Only validator access this + ISchemaValidator** validators; + SizeType validatorCount; + ISchemaValidator** patternPropertiesValidators; + SizeType patternPropertiesValidatorCount; + const SchemaType** patternPropertiesSchemas; + SizeType patternPropertiesSchemaCount; + PatternValidatorType valuePatternValidatorType; + PatternValidatorType objectPatternValidatorType; + SizeType arrayElementIndex; + bool* propertyExist; + bool inArray; + bool valueUniqueness; + bool arrayUniqueness; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Schema + +template +class Schema { +public: + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef SchemaValidationContext Context; + typedef Schema SchemaType; + typedef GenericValue SValue; + typedef IValidationErrorHandler ErrorHandler; + typedef GenericUri UriType; + friend class GenericSchemaDocument; + + Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) : + allocator_(allocator), + uri_(schemaDocument->GetURI(), *allocator), + id_(id, allocator), + spec_(schemaDocument->GetSpecification()), + pointer_(p, allocator), + typeless_(schemaDocument->GetTypeless()), + enum_(), + enumCount_(), + not_(), + type_((1 << kTotalSchemaType) - 1), // typeless + validatorCount_(), + notValidatorIndex_(), + properties_(), + additionalPropertiesSchema_(), + patternProperties_(), + patternPropertyCount_(), + propertyCount_(), + minProperties_(), + maxProperties_(SizeType(~0)), + additionalProperties_(true), + hasDependencies_(), + hasRequired_(), + hasSchemaDependencies_(), + additionalItemsSchema_(), + itemsList_(), + itemsTuple_(), + itemsTupleCount_(), + minItems_(), + maxItems_(SizeType(~0)), + additionalItems_(true), + uniqueItems_(false), + pattern_(), + minLength_(0), + maxLength_(~SizeType(0)), + exclusiveMinimum_(false), + exclusiveMaximum_(false), + defaultValueLength_(0), + readOnly_(false), + writeOnly_(false), + nullable_(false) + { + GenericStringBuffer sb; + p.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString()); + + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::ConstMemberIterator ConstMemberIterator; + + // PR #1393 + // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite + // recursion (with recursive schemas), since schemaDocument->getSchema() is always + // checked before creating a new one. Don't cache typeless_, though. + if (this != typeless_) { + typedef typename SchemaDocumentType::SchemaEntry SchemaEntry; + SchemaEntry *entry = schemaDocument->schemaMap_.template Push(); + new (entry) SchemaEntry(pointer_, this, true, allocator_); + schemaDocument->AddSchemaRefs(this); + } + + if (!value.IsObject()) + return; + + // If we have an id property, resolve it with the in-scope id + // Not supported for open api 2.0 or 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetIdString())) { + if (v->IsString()) { + UriType local(*v, allocator); + id_ = local.Resolve(id_, allocator); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString()); + } + } + + if (const ValueType* v = GetMember(value, GetTypeString())) { + type_ = 0; + if (v->IsString()) + AddType(*v); + else if (v->IsArray()) + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) + AddType(*itr); + } + + if (const ValueType* v = GetMember(value, GetEnumString())) { + if (v->IsArray() && v->Size() > 0) { + enum_ = static_cast(allocator_->Malloc(sizeof(uint64_t) * v->Size())); + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) { + typedef Hasher > EnumHasherType; + char buffer[256u + 24]; + MemoryPoolAllocator hasherAllocator(buffer, sizeof(buffer)); + EnumHasherType h(&hasherAllocator, 256); + itr->Accept(h); + enum_[enumCount_++] = h.GetHashCode(); + } + } + } + + if (schemaDocument) + AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document); + + // AnyOf, OneOf, Not not supported for open api 2.0 + if (schemaDocument && spec_.oapi != kVersion20) { + AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document); + AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document); + + if (const ValueType* v = GetMember(value, GetNotString())) { + schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document, id_); + notValidatorIndex_ = validatorCount_; + validatorCount_++; + } + } + + // Object + + const ValueType* properties = GetMember(value, GetPropertiesString()); + const ValueType* required = GetMember(value, GetRequiredString()); + const ValueType* dependencies = GetMember(value, GetDependenciesString()); + { + // Gather properties from properties/required/dependencies + SValue allProperties(kArrayType); + + if (properties && properties->IsObject()) + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) + AddUniqueElement(allProperties, itr->name); + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) + AddUniqueElement(allProperties, *itr); + + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (dependencies && dependencies->IsObject()) + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + AddUniqueElement(allProperties, itr->name); + if (itr->value.IsArray()) + for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i) + if (i->IsString()) + AddUniqueElement(allProperties, *i); + } + + if (allProperties.Size() > 0) { + propertyCount_ = allProperties.Size(); + properties_ = static_cast(allocator_->Malloc(sizeof(Property) * propertyCount_)); + for (SizeType i = 0; i < propertyCount_; i++) { + new (&properties_[i]) Property(); + properties_[i].name = allProperties[i]; + properties_[i].schema = typeless_; + } + } + } + + if (properties && properties->IsObject()) { + PointerType q = p.Append(GetPropertiesString(), allocator_); + for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) { + SizeType index; + if (FindPropertyIndex(itr->name, &index)) + schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_); + } + } + + // PatternProperties not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) { + PointerType q = p.Append(GetPatternPropertiesString(), allocator_); + patternProperties_ = static_cast(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount())); + patternPropertyCount_ = 0; + + for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) { + new (&patternProperties_[patternPropertyCount_]) PatternProperty(); + PointerType r = q.Append(itr->name, allocator_); + patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name, schemaDocument, r); + schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, r, itr->value, document, id_); + patternPropertyCount_++; + } + } + + if (required && required->IsArray()) + for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr) + if (itr->IsString()) { + SizeType index; + if (FindPropertyIndex(*itr, &index)) { + properties_[index].required = true; + hasRequired_ = true; + } + } + + // Dependencies not supported for open api 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (dependencies && dependencies->IsObject()) { + PointerType q = p.Append(GetDependenciesString(), allocator_); + hasDependencies_ = true; + for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) { + SizeType sourceIndex; + if (FindPropertyIndex(itr->name, &sourceIndex)) { + if (itr->value.IsArray()) { + properties_[sourceIndex].dependencies = static_cast(allocator_->Malloc(sizeof(bool) * propertyCount_)); + std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_); + for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) { + SizeType targetIndex; + if (FindPropertyIndex(*targetItr, &targetIndex)) + properties_[sourceIndex].dependencies[targetIndex] = true; + } + } + else if (itr->value.IsObject()) { + hasSchemaDependencies_ = true; + schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_); + properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_; + validatorCount_++; + } + } + } + } + + if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) { + if (v->IsBool()) + additionalProperties_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_); + } + + AssignIfExist(minProperties_, value, GetMinPropertiesString()); + AssignIfExist(maxProperties_, value, GetMaxPropertiesString()); + + // Array + if (const ValueType* v = GetMember(value, GetItemsString())) { + PointerType q = p.Append(GetItemsString(), allocator_); + if (v->IsObject()) // List validation + schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_); + else if (v->IsArray()) { // Tuple validation + itemsTuple_ = static_cast(allocator_->Malloc(sizeof(const Schema*) * v->Size())); + SizeType index = 0; + for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++) + schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_); + } + } + + AssignIfExist(minItems_, value, GetMinItemsString()); + AssignIfExist(maxItems_, value, GetMaxItemsString()); + + // AdditionalItems not supported for openapi 2.0 and 3.0 + if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30) + if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) { + if (v->IsBool()) + additionalItems_ = v->GetBool(); + else if (v->IsObject()) + schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_); + } + + AssignIfExist(uniqueItems_, value, GetUniqueItemsString()); + + // String + AssignIfExist(minLength_, value, GetMinLengthString()); + AssignIfExist(maxLength_, value, GetMaxLengthString()); + + if (const ValueType* v = GetMember(value, GetPatternString())) + pattern_ = CreatePattern(*v, schemaDocument, p.Append(GetPatternString(), allocator_)); + + // Number + if (const ValueType* v = GetMember(value, GetMinimumString())) + if (v->IsNumber()) + minimum_.CopyFrom(*v, *allocator_); + + if (const ValueType* v = GetMember(value, GetMaximumString())) + if (v->IsNumber()) + maximum_.CopyFrom(*v, *allocator_); + + AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString()); + AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString()); + + if (const ValueType* v = GetMember(value, GetMultipleOfString())) + if (v->IsNumber() && v->GetDouble() > 0.0) + multipleOf_.CopyFrom(*v, *allocator_); + + // Default + if (const ValueType* v = GetMember(value, GetDefaultValueString())) + if (v->IsString()) + defaultValueLength_ = v->GetStringLength(); + + // ReadOnly - open api only (until draft 7 supported) + // WriteOnly - open api 3 only (until draft 7 supported) + // Both can't be true + if (spec_.oapi != kVersionNone) + AssignIfExist(readOnly_, value, GetReadOnlyString()); + if (spec_.oapi >= kVersion30) + AssignIfExist(writeOnly_, value, GetWriteOnlyString()); + if (readOnly_ && writeOnly_) + schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p); + + // Nullable - open api 3 only + // If true add 'null' as allowable type + if (spec_.oapi >= kVersion30) { + AssignIfExist(nullable_, value, GetNullableString()); + if (nullable_) + AddType(GetNullString()); + } + } + + ~Schema() { + AllocatorType::Free(enum_); + if (properties_) { + for (SizeType i = 0; i < propertyCount_; i++) + properties_[i].~Property(); + AllocatorType::Free(properties_); + } + if (patternProperties_) { + for (SizeType i = 0; i < patternPropertyCount_; i++) + patternProperties_[i].~PatternProperty(); + AllocatorType::Free(patternProperties_); + } + AllocatorType::Free(itemsTuple_); +#if RAPIDJSON_SCHEMA_HAS_REGEX + if (pattern_) { + pattern_->~RegexType(); + AllocatorType::Free(pattern_); + } +#endif + } + + const SValue& GetURI() const { + return uri_; + } + + const UriType& GetId() const { + return id_; + } + + const Specification& GetSpecification() const { + return spec_; + } + + const PointerType& GetPointer() const { + return pointer_; + } + + bool BeginValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue"); + if (context.inArray) { + if (uniqueItems_) + context.valueUniqueness = true; + + if (itemsList_) + context.valueSchema = itemsList_; + else if (itemsTuple_) { + if (context.arrayElementIndex < itemsTupleCount_) + context.valueSchema = itemsTuple_[context.arrayElementIndex]; + else if (additionalItemsSchema_) + context.valueSchema = additionalItemsSchema_; + else if (additionalItems_) + context.valueSchema = typeless_; + else { + context.error_handler.DisallowedItem(context.arrayElementIndex); + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set + context.arrayElementIndex++; + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems); + } + } + else + context.valueSchema = typeless_; + + context.arrayElementIndex++; + } + return true; + } + + RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue"); + // Only check pattern properties if we have validators + if (context.patternPropertiesValidatorCount > 0) { + bool otherValid = false; + SizeType count = context.patternPropertiesValidatorCount; + if (context.objectPatternValidatorType != Context::kPatternValidatorOnly) + otherValid = context.patternPropertiesValidators[--count]->IsValid(); + + bool patternValid = true; + for (SizeType i = 0; i < count; i++) + if (!context.patternPropertiesValidators[i]->IsValid()) { + patternValid = false; + break; + } + + if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) { + if (!patternValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) { + if (!patternValid || !otherValid) { + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty) + context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties); + } + } + + // For enums only check if we have a hasher + if (enum_ && context.hasher) { + const uint64_t h = context.factory.GetHashCode(context.hasher); + for (SizeType i = 0; i < enumCount_; i++) + if (enum_[i] == h) + goto foundEnum; + context.error_handler.DisallowedValue(kValidateErrorEnum); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum); + foundEnum:; + } + + // Only check allOf etc if we have validators + if (context.validatorCount > 0) { + if (allOf_.schemas) + for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++) + if (!context.validators[i]->IsValid()) { + context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf); + } + + if (anyOf_.schemas) { + for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++) + if (context.validators[i]->IsValid()) + goto foundAny; + context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf); + foundAny:; + } + + if (oneOf_.schemas) { + bool oneValid = false; + SizeType firstMatch = 0; + for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++) + if (context.validators[i]->IsValid()) { + if (oneValid) { + context.error_handler.MultipleOneOf(firstMatch, i - oneOf_.begin); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch); + } else { + oneValid = true; + firstMatch = i - oneOf_.begin; + } + } + if (!oneValid) { + context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf); + } + } + + if (not_ && context.validators[notValidatorIndex_]->IsValid()) { + context.error_handler.Disallowed(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot); + } + } + + return true; + } + + bool Null(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null"); + if (!(type_ & (1 << kNullSchemaType))) { + DisallowedType(context, GetNullString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return CreateParallelValidator(context); + } + + bool Bool(Context& context, bool b) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b); + if (!CheckBool(context, b)) + return false; + return CreateParallelValidator(context); + } + + bool Int(Context& context, int i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i); + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint(Context& context, unsigned u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u); + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Int64(Context& context, int64_t i) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i); + if (!CheckInt(context, i)) + return false; + return CreateParallelValidator(context); + } + + bool Uint64(Context& context, uint64_t u) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u); + if (!CheckUint(context, u)) + return false; + return CreateParallelValidator(context); + } + + bool Double(Context& context, double d) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d); + if (!(type_ & (1 << kNumberSchemaType))) { + DisallowedType(context, GetNumberString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d)) + return false; + + if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d)) + return false; + + if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d)) + return false; + + return CreateParallelValidator(context); + } + + bool String(Context& context, const Ch* str, SizeType length, bool) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str); + if (!(type_ & (1 << kStringSchemaType))) { + DisallowedType(context, GetStringString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (minLength_ != 0 || maxLength_ != SizeType(~0)) { + SizeType count; + if (internal::CountStringCodePoint(str, length, &count)) { + if (count < minLength_) { + context.error_handler.TooShort(str, length, minLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength); + } + if (count > maxLength_) { + context.error_handler.TooLong(str, length, maxLength_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength); + } + } + } + + if (pattern_ && !IsPatternMatch(pattern_, str, length)) { + context.error_handler.DoesNotMatch(str, length); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern); + } + + return CreateParallelValidator(context); + } + + bool StartObject(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject"); + if (!(type_ & (1 << kObjectSchemaType))) { + DisallowedType(context, GetObjectString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (hasDependencies_ || hasRequired_) { + context.propertyExist = static_cast(context.factory.MallocState(sizeof(bool) * propertyCount_)); + std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_); + } + + if (patternProperties_) { // pre-allocate schema array + SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType + context.patternPropertiesSchemas = static_cast(context.factory.MallocState(sizeof(const SchemaType*) * count)); + context.patternPropertiesSchemaCount = 0; + std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count); + } + + return CreateParallelValidator(context); + } + + bool Key(Context& context, const Ch* str, SizeType len, bool) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str); + + if (patternProperties_) { + context.patternPropertiesSchemaCount = 0; + for (SizeType i = 0; i < patternPropertyCount_; i++) + if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema; + context.valueSchema = typeless_; + } + } + + SizeType index = 0; + if (FindPropertyIndex(ValueType(str, len).Move(), &index)) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithProperty; + } + else + context.valueSchema = properties_[index].schema; + + if (context.propertyExist) + context.propertyExist[index] = true; + + return true; + } + + if (additionalPropertiesSchema_) { + if (context.patternPropertiesSchemaCount > 0) { + context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_; + context.valueSchema = typeless_; + context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty; + } + else + context.valueSchema = additionalPropertiesSchema_; + return true; + } + else if (additionalProperties_) { + context.valueSchema = typeless_; + return true; + } + + if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties + // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error + context.valueSchema = typeless_; + context.error_handler.DisallowedProperty(str, len); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties); + } + + return true; + } + + bool EndObject(Context& context, SizeType memberCount) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject"); + if (hasRequired_) { + context.error_handler.StartMissingProperties(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].required && !context.propertyExist[index]) + if (properties_[index].schema->defaultValueLength_ == 0 ) + context.error_handler.AddMissingProperty(properties_[index].name); + if (context.error_handler.EndMissingProperties()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired); + } + + if (memberCount < minProperties_) { + context.error_handler.TooFewProperties(memberCount, minProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties); + } + + if (memberCount > maxProperties_) { + context.error_handler.TooManyProperties(memberCount, maxProperties_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties); + } + + if (hasDependencies_) { + context.error_handler.StartDependencyErrors(); + for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) { + const Property& source = properties_[sourceIndex]; + if (context.propertyExist[sourceIndex]) { + if (source.dependencies) { + context.error_handler.StartMissingDependentProperties(); + for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++) + if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex]) + context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name); + context.error_handler.EndMissingDependentProperties(source.name); + } + else if (source.dependenciesSchema) { + ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex]; + if (!dependenciesValidator->IsValid()) + context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator); + } + } + } + if (context.error_handler.EndDependencyErrors()) + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies); + } + + return true; + } + + bool StartArray(Context& context) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray"); + context.arrayElementIndex = 0; + context.inArray = true; // Ensure we note that we are in an array + + if (!(type_ & (1 << kArraySchemaType))) { + DisallowedType(context, GetArrayString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + return CreateParallelValidator(context); + } + + bool EndArray(Context& context, SizeType elementCount) const { + RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray"); + context.inArray = false; + + if (elementCount < minItems_) { + context.error_handler.TooFewItems(elementCount, minItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems); + } + + if (elementCount > maxItems_) { + context.error_handler.TooManyItems(elementCount, maxItems_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems); + } + + return true; + } + + static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) { + switch (validateErrorCode) { + case kValidateErrorMultipleOf: return GetMultipleOfString(); + case kValidateErrorMaximum: return GetMaximumString(); + case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same + case kValidateErrorMinimum: return GetMinimumString(); + case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same + + case kValidateErrorMaxLength: return GetMaxLengthString(); + case kValidateErrorMinLength: return GetMinLengthString(); + case kValidateErrorPattern: return GetPatternString(); + + case kValidateErrorMaxItems: return GetMaxItemsString(); + case kValidateErrorMinItems: return GetMinItemsString(); + case kValidateErrorUniqueItems: return GetUniqueItemsString(); + case kValidateErrorAdditionalItems: return GetAdditionalItemsString(); + + case kValidateErrorMaxProperties: return GetMaxPropertiesString(); + case kValidateErrorMinProperties: return GetMinPropertiesString(); + case kValidateErrorRequired: return GetRequiredString(); + case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString(); + case kValidateErrorPatternProperties: return GetPatternPropertiesString(); + case kValidateErrorDependencies: return GetDependenciesString(); + + case kValidateErrorEnum: return GetEnumString(); + case kValidateErrorType: return GetTypeString(); + + case kValidateErrorOneOf: return GetOneOfString(); + case kValidateErrorOneOfMatch: return GetOneOfString(); // Same + case kValidateErrorAllOf: return GetAllOfString(); + case kValidateErrorAnyOf: return GetAnyOfString(); + case kValidateErrorNot: return GetNotString(); + + case kValidateErrorReadOnly: return GetReadOnlyString(); + case kValidateErrorWriteOnly: return GetWriteOnlyString(); + + default: return GetNullString(); + } + } + + + // Generate functions for string literal according to Ch +#define RAPIDJSON_STRING_(name, ...) \ + static const ValueType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const ValueType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1));\ + return v;\ + } + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n') + RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't') + RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y') + RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g') + RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r') + RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r') + RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e') + RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm') + RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f') + RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f') + RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f') + RAPIDJSON_STRING_(Not, 'n', 'o', 't') + RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd') + RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's') + RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's') + RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's') + RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h') + RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n') + RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm') + RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f') + RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't') + RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a') + RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f') + RAPIDJSON_STRING_(Id, 'i', 'd') + RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r') + RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i') + RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e') + +#undef RAPIDJSON_STRING_ + +private: + enum SchemaValueType { + kNullSchemaType, + kBooleanSchemaType, + kObjectSchemaType, + kArraySchemaType, + kStringSchemaType, + kNumberSchemaType, + kIntegerSchemaType, + kTotalSchemaType + }; + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + typedef internal::GenericRegex RegexType; +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + typedef std::basic_regex RegexType; +#else + typedef char RegexType; +#endif + + struct SchemaArray { + SchemaArray() : schemas(), count() {} + ~SchemaArray() { AllocatorType::Free(schemas); } + const SchemaType** schemas; + SizeType begin; // begin index of context.validators + SizeType count; + }; + + template + void AddUniqueElement(V1& a, const V2& v) { + for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr) + if (*itr == v) + return; + V1 c(v, *allocator_); + a.PushBack(c, *allocator_); + } + + static const ValueType* GetMember(const ValueType& value, const ValueType& name) { + typename ValueType::ConstMemberIterator itr = value.FindMember(name); + return itr != value.MemberEnd() ? &(itr->value) : 0; + } + + static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsBool()) + out = v->GetBool(); + } + + static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) { + if (const ValueType* v = GetMember(value, name)) + if (v->IsUint64() && v->GetUint64() <= SizeType(~0)) + out = static_cast(v->GetUint64()); + } + + void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) { + if (const ValueType* v = GetMember(value, name)) { + if (v->IsArray() && v->Size() > 0) { + PointerType q = p.Append(name, allocator_); + out.count = v->Size(); + out.schemas = static_cast(allocator_->Malloc(out.count * sizeof(const Schema*))); + memset(out.schemas, 0, sizeof(Schema*)* out.count); + for (SizeType i = 0; i < out.count; i++) + schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_); + out.begin = validatorCount_; + validatorCount_ += out.count; + } + } + } + +#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX + template + RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { + if (value.IsString()) { + RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_); + if (!r->IsValid()) { + sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); + r->~RegexType(); + AllocatorType::Free(r); + r = 0; + } + return r; + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) { + GenericRegexSearch rs(*pattern); + return rs.Search(str); + } +#elif RAPIDJSON_SCHEMA_USE_STDREGEX + template + RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) { + if (value.IsString()) { + RegexType *r = static_cast(allocator_->Malloc(sizeof(RegexType))); + try { + return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript); + } + catch (const std::regex_error& e) { + sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength()); + AllocatorType::Free(r); + } + } + return 0; + } + + static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) { + std::match_results r; + return std::regex_search(str, str + length, r, *pattern); + } +#else + template + RegexType* CreatePattern(const ValueType&) { + return 0; + } + + static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; } +#endif // RAPIDJSON_SCHEMA_USE_STDREGEX + + void AddType(const ValueType& type) { + if (type == GetNullString() ) type_ |= 1 << kNullSchemaType; + else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType; + else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType; + else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType; + else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType; + else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType; + else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType); + } + + // Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required. + // Also creates a hasher for enums and array uniqueness, if required. + // Also a useful place to add type-independent error checks. + bool CreateParallelValidator(Context& context) const { + if (enum_ || context.arrayUniqueness) + context.hasher = context.factory.CreateHasher(); + + if (validatorCount_) { + RAPIDJSON_ASSERT(context.validators == 0); + context.validators = static_cast(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_)); + std::memset(context.validators, 0, sizeof(ISchemaValidator*) * validatorCount_); + context.validatorCount = validatorCount_; + + // Always return after first failure for these sub-validators + if (allOf_.schemas) + CreateSchemaValidators(context, allOf_, false); + + if (anyOf_.schemas) + CreateSchemaValidators(context, anyOf_, false); + + if (oneOf_.schemas) + CreateSchemaValidators(context, oneOf_, false); + + if (not_) + context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false); + + if (hasSchemaDependencies_) { + for (SizeType i = 0; i < propertyCount_; i++) + if (properties_[i].dependenciesSchema) + context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false); + } + } + + // Add any other type-independent checks here + if (readOnly_ && (context.flags & kValidateWriteFlag)) { + context.error_handler.DisallowedWhenWriting(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly); + } + if (writeOnly_ && (context.flags & kValidateReadFlag)) { + context.error_handler.DisallowedWhenReading(); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly); + } + + return true; + } + + void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const { + for (SizeType i = 0; i < schemas.count; i++) + context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors); + } + + // O(n) + bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const { + SizeType len = name.GetStringLength(); + const Ch* str = name.GetString(); + for (SizeType index = 0; index < propertyCount_; index++) + if (properties_[index].name.GetStringLength() == len && + (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0)) + { + *outIndex = index; + return true; + } + return false; + } + + bool CheckBool(Context& context, bool) const { + if (!(type_ & (1 << kBooleanSchemaType))) { + DisallowedType(context, GetBooleanString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + return true; + } + + bool CheckInt(Context& context, int64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsInt64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64() + } + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsInt64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsUint64()) { } + /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64() + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (static_cast(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckUint(Context& context, uint64_t i) const { + if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) { + DisallowedType(context, GetIntegerString()); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType); + } + + if (!minimum_.IsNull()) { + if (minimum_.IsUint64()) { + if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) { + context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + } + else if (minimum_.IsInt64()) + /* do nothing */; // i >= 0 > minimum.Getint64() + else if (!CheckDoubleMinimum(context, static_cast(i))) + return false; + } + + if (!maximum_.IsNull()) { + if (maximum_.IsUint64()) { + if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + } + else if (maximum_.IsInt64()) { + context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_ + } + else if (!CheckDoubleMaximum(context, static_cast(i))) + return false; + } + + if (!multipleOf_.IsNull()) { + if (multipleOf_.IsUint64()) { + if (i % multipleOf_.GetUint64() != 0) { + context.error_handler.NotMultipleOf(i, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + } + else if (!CheckDoubleMultipleOf(context, static_cast(i))) + return false; + } + + return true; + } + + bool CheckDoubleMinimum(Context& context, double d) const { + if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) { + context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); + } + return true; + } + + bool CheckDoubleMaximum(Context& context, double d) const { + if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) { + context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_); + RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); + } + return true; + } + + bool CheckDoubleMultipleOf(Context& context, double d) const { + double a = std::abs(d), b = std::abs(multipleOf_.GetDouble()); + double q = a / b; + double qRounded = std::floor(q + 0.5); + double scaledEpsilon = (q + qRounded) * std::numeric_limits::epsilon(); + double difference = std::abs(qRounded - q); + bool isMultiple = difference <= scaledEpsilon || difference < (std::numeric_limits::min)(); + if (!isMultiple) { + context.error_handler.NotMultipleOf(d, multipleOf_); + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf); + } + return true; + } + + void DisallowedType(Context& context, const ValueType& actualType) const { + ErrorHandler& eh = context.error_handler; + eh.StartDisallowedType(); + + if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString()); + if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString()); + if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString()); + if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString()); + if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString()); + + if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString()); + else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString()); + + eh.EndDisallowedType(actualType); + } + + struct Property { + Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {} + ~Property() { AllocatorType::Free(dependencies); } + SValue name; + const SchemaType* schema; + const SchemaType* dependenciesSchema; + SizeType dependenciesValidatorIndex; + bool* dependencies; + bool required; + }; + + struct PatternProperty { + PatternProperty() : schema(), pattern() {} + ~PatternProperty() { + if (pattern) { + pattern->~RegexType(); + AllocatorType::Free(pattern); + } + } + const SchemaType* schema; + RegexType* pattern; + }; + + AllocatorType* allocator_; + SValue uri_; + UriType id_; + Specification spec_; + PointerType pointer_; + const SchemaType* typeless_; + uint64_t* enum_; + SizeType enumCount_; + SchemaArray allOf_; + SchemaArray anyOf_; + SchemaArray oneOf_; + const SchemaType* not_; + unsigned type_; // bitmask of kSchemaType + SizeType validatorCount_; + SizeType notValidatorIndex_; + + Property* properties_; + const SchemaType* additionalPropertiesSchema_; + PatternProperty* patternProperties_; + SizeType patternPropertyCount_; + SizeType propertyCount_; + SizeType minProperties_; + SizeType maxProperties_; + bool additionalProperties_; + bool hasDependencies_; + bool hasRequired_; + bool hasSchemaDependencies_; + + const SchemaType* additionalItemsSchema_; + const SchemaType* itemsList_; + const SchemaType** itemsTuple_; + SizeType itemsTupleCount_; + SizeType minItems_; + SizeType maxItems_; + bool additionalItems_; + bool uniqueItems_; + + RegexType* pattern_; + SizeType minLength_; + SizeType maxLength_; + + SValue minimum_; + SValue maximum_; + SValue multipleOf_; + bool exclusiveMinimum_; + bool exclusiveMaximum_; + + SizeType defaultValueLength_; + + bool readOnly_; + bool writeOnly_; + bool nullable_; +}; + +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + *documentStack.template Push() = '/'; + char buffer[21]; + size_t length = static_cast((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer); + for (size_t i = 0; i < length; i++) + *documentStack.template Push() = static_cast(buffer[i]); + } +}; + +// Partial specialized version for char to prevent buffer copying. +template +struct TokenHelper { + RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) { + RAPIDJSON_IF_CONSTEXPR (sizeof(SizeType) == 4) { + char *buffer = documentStack.template Push(1 + 10); // '/' + uint + *buffer++ = '/'; + const char* end = internal::u32toa(index, buffer); + documentStack.template Pop(static_cast(10 - (end - buffer))); + } + else { + char *buffer = documentStack.template Push(1 + 20); // '/' + uint64 + *buffer++ = '/'; + const char* end = internal::u64toa(index, buffer); + documentStack.template Pop(static_cast(20 - (end - buffer))); + } + } +}; + +} // namespace internal + +/////////////////////////////////////////////////////////////////////////////// +// IGenericRemoteSchemaDocumentProvider + +template +class IGenericRemoteSchemaDocumentProvider { +public: + typedef typename SchemaDocumentType::Ch Ch; + typedef typename SchemaDocumentType::ValueType ValueType; + typedef typename SchemaDocumentType::AllocatorType AllocatorType; + + virtual ~IGenericRemoteSchemaDocumentProvider() {} + virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0; + virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri uri, Specification& spec) { + // Default implementation just calls through for compatibility + // Following line suppresses unused parameter warning + (void)spec; + // printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi); + return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaDocument + +//! JSON schema document. +/*! + A JSON schema document is a compiled version of a JSON schema. + It is basically a tree of internal::Schema. + + \note This is an immutable class (i.e. its instance cannot be modified after construction). + \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding. + \tparam Allocator Allocator type for allocating memory of this document. +*/ +template +class GenericSchemaDocument { +public: + typedef ValueT ValueType; + typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProviderType; + typedef Allocator AllocatorType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename EncodingType::Ch Ch; + typedef internal::Schema SchemaType; + typedef GenericPointer PointerType; + typedef GenericValue GValue; + typedef GenericUri UriType; + typedef GenericStringRef StringRefType; + friend class internal::Schema; + template + friend class GenericSchemaValidator; + + //! Constructor. + /*! + Compile a JSON document into schema document. + + \param document A JSON document as source. + \param uri The base URI of this schema document for purposes of violation reporting. + \param uriLength Length of \c name, in code points. + \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null. + \param allocator An optional allocator instance for allocating memory. Can be null. + \param pointer An optional JSON pointer to the start of the schema document + \param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04. + */ + explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0, + IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0, + const PointerType& pointer = PointerType(), // PR #1393 + const Specification& spec = Specification(kDraft04)) : + remoteProvider_(remoteProvider), + allocator_(allocator), + ownAllocator_(), + root_(), + typeless_(), + schemaMap_(allocator, kInitialSchemaMapSize), + schemaRef_(allocator, kInitialSchemaRefSize), + spec_(spec), + error_(kObjectType), + currentError_() + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument"); + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + Ch noUri[1] = {0}; + uri_.SetString(uri ? uri : noUri, uriLength, *allocator_); + docId_ = UriType(uri_, allocator_); + + typeless_ = static_cast(allocator_->Malloc(sizeof(SchemaType))); + new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_); + + // Establish the schema draft or open api version. + // We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document. + SetSchemaSpecification(document); + + // Generate root schema, it will call CreateSchema() to create sub-schemas, + // And call HandleRefSchema() if there are $ref. + // PR #1393 use input pointer if supplied + root_ = typeless_; + if (pointer.GetTokenCount() == 0) { + CreateSchemaRecursive(&root_, pointer, document, document, docId_); + } + else if (const ValueType* v = pointer.Get(document)) { + CreateSchema(&root_, pointer, *v, document, docId_); + } + else { + GenericStringBuffer sb; + pointer.StringifyUriFragment(sb); + SchemaErrorValue(kSchemaErrorStartUnknown, PointerType(), sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch))); + } + + RAPIDJSON_ASSERT(root_ != 0); + + schemaRef_.ShrinkToFit(); // Deallocate all memory for ref + } + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Move constructor in C++11 + GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT : + remoteProvider_(rhs.remoteProvider_), + allocator_(rhs.allocator_), + ownAllocator_(rhs.ownAllocator_), + root_(rhs.root_), + typeless_(rhs.typeless_), + schemaMap_(std::move(rhs.schemaMap_)), + schemaRef_(std::move(rhs.schemaRef_)), + uri_(std::move(rhs.uri_)), + docId_(std::move(rhs.docId_)), + spec_(rhs.spec_), + error_(std::move(rhs.error_)), + currentError_(std::move(rhs.currentError_)) + { + rhs.remoteProvider_ = 0; + rhs.allocator_ = 0; + rhs.ownAllocator_ = 0; + rhs.typeless_ = 0; + } +#endif + + //! Destructor + ~GenericSchemaDocument() { + while (!schemaMap_.Empty()) + schemaMap_.template Pop(1)->~SchemaEntry(); + + if (typeless_) { + typeless_->~SchemaType(); + Allocator::Free(typeless_); + } + + // these may contain some allocator data so clear before deleting ownAllocator_ + uri_.SetNull(); + error_.SetNull(); + currentError_.SetNull(); + + RAPIDJSON_DELETE(ownAllocator_); + } + + const GValue& GetURI() const { return uri_; } + + const Specification& GetSpecification() const { return spec_; } + bool IsSupportedSpecification() const { return spec_.IsSupported(); } + + //! Static method to get the specification of any schema document + // Returns kDraftNone if document is silent + static const Specification GetSpecification(const ValueType& document) { + SchemaDraft draft = GetSchemaDraft(document); + if (draft != kDraftNone) + return Specification(draft); + else { + OpenApiVersion oapi = GetOpenApiVersion(document); + if (oapi != kVersionNone) + return Specification(oapi); + } + return Specification(kDraftNone); + } + + //! Get the root schema. + const SchemaType& GetRoot() const { return *root_; } + + //! Gets the error object. + GValue& GetError() { return error_; } + const GValue& GetError() const { return error_; } + + static const StringRefType& GetSchemaErrorKeyword(SchemaErrorCode schemaErrorCode) { + switch (schemaErrorCode) { + case kSchemaErrorStartUnknown: return GetStartUnknownString(); + case kSchemaErrorRefPlainName: return GetRefPlainNameString(); + case kSchemaErrorRefInvalid: return GetRefInvalidString(); + case kSchemaErrorRefPointerInvalid: return GetRefPointerInvalidString(); + case kSchemaErrorRefUnknown: return GetRefUnknownString(); + case kSchemaErrorRefCyclical: return GetRefCyclicalString(); + case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString(); + case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString(); + case kSchemaErrorRegexInvalid: return GetRegexInvalidString(); + case kSchemaErrorSpecUnknown: return GetSpecUnknownString(); + case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString(); + case kSchemaErrorSpecIllegal: return GetSpecIllegalString(); + case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString(); + default: return GetNullString(); + } + } + + //! Default error method + void SchemaError(const SchemaErrorCode code, const PointerType& location) { + currentError_ = GValue(kObjectType); + AddCurrentError(code, location); + } + + //! Method for error with single string value insert + void SchemaErrorValue(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length) { + currentError_ = GValue(kObjectType); + currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); + AddCurrentError(code, location); + } + + //! Method for error with invalid pointer + void SchemaErrorPointer(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length, const PointerType& pointer) { + currentError_ = GValue(kObjectType); + currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_); + currentError_.AddMember(GetOffsetString(), static_cast(pointer.GetParseErrorOffset() / sizeof(Ch)), *allocator_); + AddCurrentError(code, location); + } + + private: + //! Prohibit copying + GenericSchemaDocument(const GenericSchemaDocument&); + //! Prohibit assignment + GenericSchemaDocument& operator=(const GenericSchemaDocument&); + + typedef const PointerType* SchemaRefPtr; // PR #1393 + + struct SchemaEntry { + SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {} + ~SchemaEntry() { + if (owned) { + schema->~SchemaType(); + Allocator::Free(schema); + } + } + PointerType pointer; + SchemaType* schema; + bool owned; + }; + + void AddErrorInstanceLocation(GValue& result, const PointerType& location) { + GenericStringBuffer sb; + location.StringifyUriFragment(sb); + GValue instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), *allocator_); + result.AddMember(GetInstanceRefString(), instanceRef, *allocator_); + } + + void AddError(GValue& keyword, GValue& error) { + typename GValue::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, *allocator_); + else { + if (member->value.IsObject()) { + GValue errors(kArrayType); + errors.PushBack(member->value, *allocator_); + member->value = errors; + } + member->value.PushBack(error, *allocator_); + } + } + + void AddCurrentError(const SchemaErrorCode code, const PointerType& location) { + RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code)); + currentError_.AddMember(GetErrorCodeString(), code, *allocator_); + AddErrorInstanceLocation(currentError_, location); + AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(Value, 'v', 'a', 'l', 'u', 'e') + RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't') + + RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l') + RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd') + RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', 'l') + RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e') + RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + RAPIDJSON_STRING_(RefPointerInvalid, 'R', 'e', 'f', 'P', 'o', 'i', 'n', 't', 'e', 'r', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + RAPIDJSON_STRING_(RefUnknown, 'R', 'e', 'f', 'U', 'n', 'k', 'n', 'o', 'w', 'n') + RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l') + RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r') + RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a') + RAPIDJSON_STRING_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y') + RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd') + +#undef RAPIDJSON_STRING_ + + // Static method to get schema draft of any schema document + static SchemaDraft GetSchemaDraft(const ValueType& document) { + static const Ch kDraft03String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '3', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft04String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft05String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '5', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft06String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '6', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft07String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '7', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' }; + static const Ch kDraft2019_09String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '1', '9', '-', '0', '9', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + static const Ch kDraft2020_12String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '2', '0', '-', '1', '2', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' }; + + if (!document.IsObject()) { + return kDraftNone; + } + + // Get the schema draft from the $schema keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kDraftUnknown; + const UriType draftUri(itr->value); + // Check base uri for match + if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04; + if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05; + if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06; + if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07; + if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03; + if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09; + if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12; + return kDraftUnknown; + } + // $schema not found + return kDraftNone; + } + + + // Get open api version of any schema document + static OpenApiVersion GetOpenApiVersion(const ValueType& document) { + static const Ch kVersion20String[] = { '2', '.', '0', '\0' }; + static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level + static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level + static SizeType len = internal::StrLen(kVersion30String); + + if (!document.IsObject()) { + return kVersionNone; + } + + // Get the open api version from the swagger / openapi keyword at the supplied location + typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString()); + if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString()); + if (itr != document.MemberEnd()) { + if (!itr->value.IsString()) return kVersionUnknown; + const ValueType kVersion20Value(kVersion20String); + if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly + const ValueType kVersion30Value(kVersion30String); + if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x + const ValueType kVersion31Value(kVersion31String); + if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x + return kVersionUnknown; + } + // swagger or openapi not found + return kVersionNone; + } + + // Get the draft of the schema or the open api version (which implies the draft). + // Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on. + void SetSchemaSpecification(const ValueType& document) { + // Look for '$schema', 'swagger' or 'openapi' keyword at document root + SchemaDraft docDraft = GetSchemaDraft(document); + OpenApiVersion docOapi = GetOpenApiVersion(document); + // Error if both in document + if (docDraft != kDraftNone && docOapi != kVersionNone) + SchemaError(kSchemaErrorSpecIllegal, PointerType()); + // Use document draft or open api version if present or use spec from constructor + if (docDraft != kDraftNone) + spec_ = Specification(docDraft); + else if (docOapi != kVersionNone) + spec_ = Specification(docOapi); + // Error if draft or version unknown + if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown) + SchemaError(kSchemaErrorSpecUnknown, PointerType()); + else if (!spec_.IsSupported()) + SchemaError(kSchemaErrorSpecUnsupported, PointerType()); + } + + // Changed by PR #1393 + void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { + if (v.GetType() == kObjectType) { + UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_); + + for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr) + CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid); + } + else if (v.GetType() == kArrayType) + for (SizeType i = 0; i < v.Size(); i++) + CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id); + } + + // Changed by PR #1393 + const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) { + RAPIDJSON_ASSERT(pointer.IsValid()); + GenericStringBuffer sb; + pointer.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString()); + if (v.IsObject()) { + if (const SchemaType* sc = GetSchema(pointer)) { + if (schema) + *schema = sc; + AddSchemaRefs(const_cast(sc)); + } + else if (!HandleRefSchema(pointer, schema, v, document, id)) { + // The new schema constructor adds itself and its $ref(s) to schemaMap_ + SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id); + if (schema) + *schema = s; + return s->GetId(); + } + } + else { + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + } + return id; + } + + // Changed by PR #1393 + // TODO should this return a UriType& ? + bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) { + typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString()); + if (itr == v.MemberEnd()) + return false; + + GenericStringBuffer sb; + source.StringifyUriFragment(sb); + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString()); + // Resolve the source pointer to the $ref'ed schema (finally) + new (schemaRef_.template Push()) SchemaRefPtr(&source); + + if (itr->value.IsString()) { + SizeType len = itr->value.GetStringLength(); + if (len == 0) + SchemaError(kSchemaErrorRefInvalid, source); + else { + // First resolve $ref against the in-scope id + UriType scopeId = UriType(id, allocator_); + UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_); + RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString()); + // See if the resolved $ref minus the fragment matches a resolved id in this document + // Search from the root. Returns the subschema in the document and its absolute JSON pointer. + PointerType basePointer = PointerType(); + const ValueType *base = FindId(document, ref, basePointer, docId_, false); + if (!base) { + // Remote reference - call the remote document provider + if (!remoteProvider_) + SchemaError(kSchemaErrorRefNoRemoteProvider, source); + else { + if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) { + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, absolute in the remote schema + const PointerType pointer(s, len, allocator_); + if (!pointer.IsValid()) + SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, pointer); + else { + // Get the subschema + if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) { + if (schema) + *schema = sc; + AddSchemaRefs(const_cast(sc)); + return true; + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } else + // Plain name fragment, not allowed in remote schema + SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); + } else + SchemaErrorValue(kSchemaErrorRefNoRemoteSchema, source, ref.GetString(), ref.GetStringLength()); + } + } + else { // Local reference + const Ch* s = ref.GetFragString(); + len = ref.GetFragStringLength(); + if (len <= 1 || s[1] == '/') { + // JSON pointer fragment, relative to the resolved URI + const PointerType relPointer(s, len, allocator_); + if (!relPointer.IsValid()) + SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, relPointer); + else { + // Get the subschema + if (const ValueType *pv = relPointer.Get(*base)) { + // Now get the absolute JSON pointer by adding relative to base + PointerType pointer(basePointer, allocator_); + for (SizeType i = 0; i < relPointer.GetTokenCount(); i++) + pointer = pointer.Append(relPointer.GetTokens()[i], allocator_); + if (IsCyclicRef(pointer)) + SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); + else { + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); + CreateSchema(schema, pointer, *pv, document, scopeId); + return true; + } + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } else { + // Plain name fragment, relative to the resolved URI + // Not supported in open api 2.0 and 3.0 + PointerType pointer(allocator_); + if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30) + SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len); + // See if the fragment matches an id in this document. + // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer. + else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) { + if (IsCyclicRef(pointer)) + SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength()); + else { + // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there + // TODO: cache pointer <-> id mapping + size_t unresolvedTokenIndex; + scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_); + CreateSchema(schema, pointer, *pv, document, scopeId); + return true; + } + } else + SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength()); + } + } + } + } + + // Invalid/Unknown $ref + if (schema) + *schema = typeless_; + AddSchemaRefs(typeless_); + return true; + } + + //! Find the first subschema with a resolved 'id' that matches the specified URI. + // If full specified use all URI else ignore fragment. + // If found, return a pointer to the subschema and its JSON pointer. + // TODO cache pointer <-> id mapping + ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const { + SizeType i = 0; + ValueType* resval = 0; + UriType tempuri = UriType(finduri, allocator_); + UriType localuri = UriType(baseuri, allocator_); + if (doc.GetType() == kObjectType) { + // Establish the base URI of this object + typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString()); + if (m != doc.MemberEnd() && m->value.GetType() == kStringType) { + localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_); + } + // See if it matches + if (localuri.Match(finduri, full)) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString()); + resval = const_cast(&doc); + resptr = here; + return resval; + } + // No match, continue looking + for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) { + if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) { + resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_)); + } + if (resval) break; + } + } else if (doc.GetType() == kArrayType) { + // Continue looking + for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) { + if (v->GetType() == kObjectType || v->GetType() == kArrayType) { + resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_)); + } + if (resval) break; + i++; + } + } + return resval; + } + + // Added by PR #1393 + void AddSchemaRefs(SchemaType* schema) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs"); + while (!schemaRef_.Empty()) { + SchemaRefPtr *ref = schemaRef_.template Pop(1); + SchemaEntry *entry = schemaMap_.template Push(); + new (entry) SchemaEntry(**ref, schema, false, allocator_); + } + } + + // Added by PR #1393 + bool IsCyclicRef(const PointerType& pointer) const { + for (const SchemaRefPtr* ref = schemaRef_.template Bottom(); ref != schemaRef_.template End(); ++ref) + if (pointer == **ref) + return true; + return false; + } + + const SchemaType* GetSchema(const PointerType& pointer) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (pointer == target->pointer) + return target->schema; + return 0; + } + + PointerType GetPointer(const SchemaType* schema) const { + for (const SchemaEntry* target = schemaMap_.template Bottom(); target != schemaMap_.template End(); ++target) + if (schema == target->schema) + return target->pointer; + return PointerType(); + } + + const SchemaType* GetTypeless() const { return typeless_; } + + static const size_t kInitialSchemaMapSize = 64; + static const size_t kInitialSchemaRefSize = 64; + + IRemoteSchemaDocumentProviderType* remoteProvider_; + Allocator *allocator_; + Allocator *ownAllocator_; + const SchemaType* root_; //!< Root schema. + SchemaType* typeless_; + internal::Stack schemaMap_; // Stores created Pointer -> Schemas + internal::Stack schemaRef_; // Stores Pointer(s) from $ref(s) until resolved + GValue uri_; // Schema document URI + UriType docId_; + Specification spec_; + GValue error_; + GValue currentError_; +}; + +//! GenericSchemaDocument using Value type. +typedef GenericSchemaDocument SchemaDocument; +//! IGenericRemoteSchemaDocumentProvider using SchemaDocument. +typedef IGenericRemoteSchemaDocumentProvider IRemoteSchemaDocumentProvider; + +/////////////////////////////////////////////////////////////////////////////// +// GenericSchemaValidator + +//! JSON Schema Validator. +/*! + A SAX style JSON schema validator. + It uses a \c GenericSchemaDocument to validate SAX events. + It delegates the incoming SAX events to an output handler. + The default output handler does nothing. + It can be reused multiple times by calling \c Reset(). + + \tparam SchemaDocumentType Type of schema document. + \tparam OutputHandler Type of output handler. Default handler does nothing. + \tparam StateAllocator Allocator for storing the internal validation states. +*/ +template < + typename SchemaDocumentType, + typename OutputHandler = BaseReaderHandler, + typename StateAllocator = CrtAllocator> +class GenericSchemaValidator : + public internal::ISchemaStateFactory, + public internal::ISchemaValidator, + public internal::IValidationErrorHandler { +public: + typedef typename SchemaDocumentType::SchemaType SchemaType; + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename SchemaType::EncodingType EncodingType; + typedef typename SchemaType::SValue SValue; + typedef typename EncodingType::Ch Ch; + typedef GenericStringRef StringRefType; + typedef GenericValue ValueType; + + //! Constructor without output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(0) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator"); + } + + //! Constructor with output handler. + /*! + \param schemaDocument The schema document to conform to. + \param allocator Optional allocator for storing internal validation states. + \param schemaStackCapacity Optional initial capacity of schema path stack. + \param documentStackCapacity Optional initial capacity of document path stack. + */ + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + OutputHandler& outputHandler, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(schemaDocument.GetRoot()), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(&outputHandler), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(0) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)"); + } + + //! Destructor. + ~GenericSchemaValidator() { + Reset(); + RAPIDJSON_DELETE(ownStateAllocator_); + } + + //! Reset the internal states. + void Reset() { + while (!schemaStack_.Empty()) + PopSchema(); + documentStack_.Clear(); + ResetError(); + } + + //! Reset the error state. + void ResetError() { + error_.SetObject(); + currentError_.SetNull(); + missingDependents_.SetNull(); + valid_ = true; + } + + //! Implementation of ISchemaValidator + void SetValidateFlags(unsigned flags) { + flags_ = flags; + } + virtual unsigned GetValidateFlags() const { + return flags_; + } + + virtual bool IsValid() const { + if (!valid_) return false; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false; + return true; + } + //! End of Implementation of ISchemaValidator + + //! Gets the error object. + ValueType& GetError() { return error_; } + const ValueType& GetError() const { return error_; } + + //! Gets the JSON pointer pointed to the invalid schema. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidSchemaPointer() const { + return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer(); + } + + //! Gets the keyword of invalid schema. + // If reporting all errors, the stack will be empty, so return "errors". + const Ch* GetInvalidSchemaKeyword() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return static_cast(GetErrorsString()); + return 0; + } + + //! Gets the error code of invalid schema. + // If reporting all errors, the stack will be empty, so return kValidateErrors. + ValidateErrorCode GetInvalidSchemaCode() const { + if (!schemaStack_.Empty()) return CurrentContext().invalidCode; + if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors; + return kValidateErrorNone; + } + + //! Gets the JSON pointer pointed to the invalid value. + // If reporting all errors, the stack will be empty. + PointerType GetInvalidDocumentPointer() const { + if (documentStack_.Empty()) { + return PointerType(); + } + else { + return PointerType(documentStack_.template Bottom(), documentStack_.GetSize() / sizeof(Ch)); + } + } + + void NotMultipleOf(int64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(uint64_t actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void NotMultipleOf(double actual, const SValue& expected) { + AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected); + } + void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void AboveMaximum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMaximumString : 0); + } + void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + void BelowMinimum(double actual, const SValue& expected, bool exclusive) { + AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected, + exclusive ? &SchemaType::GetExclusiveMinimumString : 0); + } + + void TooLong(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMaxLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void TooShort(const Ch* str, SizeType length, SizeType expected) { + AddNumberError(kValidateErrorMinLength, + ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move()); + } + void DoesNotMatch(const Ch* str, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorPattern); + } + + void DisallowedItem(SizeType index) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalItems, true); + } + void TooFewItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooManyItems(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxItems, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void DuplicateItems(SizeType index1, SizeType index2) { + ValueType duplicates(kArrayType); + duplicates.PushBack(index1, GetStateAllocator()); + duplicates.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator()); + AddCurrentError(kValidateErrorUniqueItems, true); + } + + void TooManyProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMaxProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void TooFewProperties(SizeType actualCount, SizeType expectedCount) { + AddNumberError(kValidateErrorMinProperties, + ValueType(actualCount).Move(), SValue(expectedCount).Move()); + } + void StartMissingProperties() { + currentError_.SetArray(); + } + void AddMissingProperty(const SValue& name) { + currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator()); + } + bool EndMissingProperties() { + if (currentError_.Empty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetMissingString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorRequired); + return true; + } + void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) { + for (SizeType i = 0; i < count; ++i) + MergeError(static_cast(subvalidators[i])->GetError()); + } + void DisallowedProperty(const Ch* name, SizeType length) { + currentError_.SetObject(); + currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator()); + AddCurrentError(kValidateErrorAdditionalProperties, true); + } + + void StartDependencyErrors() { + currentError_.SetObject(); + } + void StartMissingDependentProperties() { + missingDependents_.SetArray(); + } + void AddMissingDependentProperty(const SValue& targetName) { + missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndMissingDependentProperties(const SValue& sourceName) { + if (!missingDependents_.Empty()) { + // Create equivalent 'required' error + ValueType error(kObjectType); + ValidateErrorCode code = kValidateErrorRequired; + error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator()); + AddErrorCode(error, code); + AddErrorInstanceLocation(error, false); + // When appending to a pointer ensure its allocator is used + PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator()); + AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator())); + ValueType wrapper(kObjectType); + wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator()); + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator()); + } + } + void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) { + currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), + static_cast(subvalidator)->GetError(), GetStateAllocator()); + } + bool EndDependencyErrors() { + if (currentError_.ObjectEmpty()) + return false; + ValueType error(kObjectType); + error.AddMember(GetErrorsString(), currentError_, GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorDependencies); + return true; + } + + void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) { + currentError_.SetObject(); + AddCurrentError(code); + } + void StartDisallowedType() { + currentError_.SetArray(); + } + void AddExpectedType(const typename SchemaType::ValueType& expectedType) { + currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator()); + } + void EndDisallowedType(const typename SchemaType::ValueType& actualType) { + ValueType error(kObjectType); + error.AddMember(GetExpectedString(), currentError_, GetStateAllocator()); + error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator()); + currentError_ = error; + AddCurrentError(kValidateErrorType); + } + void NotAllOf(ISchemaValidator** subvalidators, SizeType count) { + // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf + AddErrorArray(kValidateErrorAllOf, subvalidators, count); + //for (SizeType i = 0; i < count; ++i) { + // MergeError(static_cast(subvalidators[i])->GetError()); + //} + } + void NoneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorAnyOf, subvalidators, count); + } + void NotOneOf(ISchemaValidator** subvalidators, SizeType count) { + AddErrorArray(kValidateErrorOneOf, subvalidators, count); + } + void MultipleOneOf(SizeType index1, SizeType index2) { + ValueType matches(kArrayType); + matches.PushBack(index1, GetStateAllocator()); + matches.PushBack(index2, GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetMatchesString(), matches, GetStateAllocator()); + AddCurrentError(kValidateErrorOneOfMatch); + } + void Disallowed() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorNot); + } + void DisallowedWhenWriting() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorReadOnly); + } + void DisallowedWhenReading() { + currentError_.SetObject(); + AddCurrentError(kValidateErrorWriteOnly); + } + +#define RAPIDJSON_STRING_(name, ...) \ + static const StringRefType& Get##name##String() {\ + static const Ch s[] = { __VA_ARGS__, '\0' };\ + static const StringRefType v(s, static_cast(sizeof(s) / sizeof(Ch) - 1)); \ + return v;\ + } + + RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f') + RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f') + RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd') + RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l') + RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd') + RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g') + RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's') + RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e') + RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e') + RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's') + RAPIDJSON_STRING_(Matches, 'm', 'a', 't', 'c', 'h', 'e', 's') + +#undef RAPIDJSON_STRING_ + +#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\ + if (!valid_) return false; \ + if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\ + *documentStack_.template Push() = '\0';\ + documentStack_.template Pop(1);\ + RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom());\ + valid_ = false;\ + return valid_;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\ + for (Context* context = schemaStack_.template Bottom(); context != schemaStack_.template End(); context++) {\ + if (context->hasher)\ + static_cast(context->hasher)->method arg2;\ + if (context->validators)\ + for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\ + static_cast(context->validators[i_])->method arg2;\ + if (context->patternPropertiesValidators)\ + for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\ + static_cast(context->patternPropertiesValidators[i_])->method arg2;\ + } + +#define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\ + valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\ + return valid_; + +#define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \ + RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\ + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\ + RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2) + + bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); } + bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); } + bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); } + bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); } + bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); } + bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); } + bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); } + bool RawNumber(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + bool String(const Ch* str, SizeType length, bool copy) + { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); } + + bool StartObject() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartObject"); + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ()); + valid_ = !outputHandler_ || outputHandler_->StartObject(); + return valid_; + } + + bool Key(const Ch* str, SizeType len, bool copy) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str); + if (!valid_) return false; + AppendToken(str, len); + if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy)); + valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy); + return valid_; + } + + bool EndObject(SizeType memberCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject"); + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount)); + if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount)); + } + + bool StartArray() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray"); + RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext())); + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ()); + valid_ = !outputHandler_ || outputHandler_->StartArray(); + return valid_; + } + + bool EndArray(SizeType elementCount) { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray"); + if (!valid_) return false; + RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount)); + if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) { + valid_ = false; + return valid_; + } + RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount)); + } + +#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_ +#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_ +#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_ + + // Implementation of ISchemaStateFactory + virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) { + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom(), documentStack_.GetSize(), + depth_ + 1, + &GetStateAllocator()); + sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~static_cast(kValidateContinueOnErrorFlag)); + return sv; + } + + virtual void DestroySchemaValidator(ISchemaValidator* validator) { + GenericSchemaValidator* v = static_cast(validator); + v->~GenericSchemaValidator(); + StateAllocator::Free(v); + } + + virtual void* CreateHasher() { + return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator()); + } + + virtual uint64_t GetHashCode(void* hasher) { + return static_cast(hasher)->GetHashCode(); + } + + virtual void DestroryHasher(void* hasher) { + HasherType* h = static_cast(hasher); + h->~HasherType(); + StateAllocator::Free(h); + } + + virtual void* MallocState(size_t size) { + return GetStateAllocator().Malloc(size); + } + + virtual void FreeState(void* p) { + StateAllocator::Free(p); + } + // End of implementation of ISchemaStateFactory + +private: + typedef typename SchemaType::Context Context; + typedef GenericValue, StateAllocator> HashCodeArray; + typedef internal::Hasher HasherType; + + GenericSchemaValidator( + const SchemaDocumentType& schemaDocument, + const SchemaType& root, + const char* basePath, size_t basePathSize, + unsigned depth, + StateAllocator* allocator = 0, + size_t schemaStackCapacity = kDefaultSchemaStackCapacity, + size_t documentStackCapacity = kDefaultDocumentStackCapacity) + : + schemaDocument_(&schemaDocument), + root_(root), + stateAllocator_(allocator), + ownStateAllocator_(0), + schemaStack_(allocator, schemaStackCapacity), + documentStack_(allocator, documentStackCapacity), + outputHandler_(0), + error_(kObjectType), + currentError_(), + missingDependents_(), + valid_(true), + flags_(kValidateDefaultFlags), + depth_(depth) + { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : ""); + if (basePath && basePathSize) + memcpy(documentStack_.template Push(basePathSize), basePath, basePathSize); + } + + StateAllocator& GetStateAllocator() { + if (!stateAllocator_) + stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)(); + return *stateAllocator_; + } + + bool GetContinueOnErrors() const { + return flags_ & kValidateContinueOnErrorFlag; + } + + bool BeginValue() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue"); + if (schemaStack_.Empty()) + PushSchema(root_); + else { + if (CurrentContext().inArray) + internal::TokenHelper, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex); + + if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + + SizeType count = CurrentContext().patternPropertiesSchemaCount; + const SchemaType** sa = CurrentContext().patternPropertiesSchemas; + typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType; + bool valueUniqueness = CurrentContext().valueUniqueness; + RAPIDJSON_ASSERT(CurrentContext().valueSchema); + PushSchema(*CurrentContext().valueSchema); + + if (count > 0) { + CurrentContext().objectPatternValidatorType = patternValidatorType; + ISchemaValidator**& va = CurrentContext().patternPropertiesValidators; + SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount; + va = static_cast(MallocState(sizeof(ISchemaValidator*) * count)); + std::memset(va, 0, sizeof(ISchemaValidator*) * count); + for (SizeType i = 0; i < count; i++) + va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError + } + + CurrentContext().arrayUniqueness = valueUniqueness; + } + return true; + } + + bool EndValue() { + RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue"); + if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors()) + return false; + + GenericStringBuffer sb; + schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb); + *documentStack_.template Push() = '\0'; + documentStack_.template Pop(1); + RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom(), depth_); + void* hasher = CurrentContext().hasher; + uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast(hasher)->GetHashCode() : 0; + + PopSchema(); + + if (!schemaStack_.Empty()) { + Context& context = CurrentContext(); + // Only check uniqueness if there is a hasher + if (hasher && context.valueUniqueness) { + HashCodeArray* a = static_cast(context.arrayElementHashCodes); + if (!a) + CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType); + for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr) + if (itr->GetUint64() == h) { + DuplicateItems(static_cast(itr - a->Begin()), a->Size()); + // Cleanup before returning if continuing + if (GetContinueOnErrors()) { + a->PushBack(h, GetStateAllocator()); + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/'); + } + RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems); + } + a->PushBack(h, GetStateAllocator()); + } + } + + // Remove the last token of document pointer + while (!documentStack_.Empty() && *documentStack_.template Pop(1) != '/') + ; + + return true; + } + + void AppendToken(const Ch* str, SizeType len) { + documentStack_.template Reserve(1 + len * 2); // worst case all characters are escaped as two characters + *documentStack_.template PushUnsafe() = '/'; + for (SizeType i = 0; i < len; i++) { + if (str[i] == '~') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '0'; + } + else if (str[i] == '/') { + *documentStack_.template PushUnsafe() = '~'; + *documentStack_.template PushUnsafe() = '1'; + } + else + *documentStack_.template PushUnsafe() = str[i]; + } + } + + RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push()) Context(*this, *this, &schema, flags_); } + + RAPIDJSON_FORCEINLINE void PopSchema() { + Context* c = schemaStack_.template Pop(1); + if (HashCodeArray* a = static_cast(c->arrayElementHashCodes)) { + a->~HashCodeArray(); + StateAllocator::Free(a); + } + c->~Context(); + } + + void AddErrorInstanceLocation(ValueType& result, bool parent) { + GenericStringBuffer sb; + PointerType instancePointer = GetInvalidDocumentPointer(); + ((parent && instancePointer.GetTokenCount() > 0) + ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1) + : instancePointer).StringifyUriFragment(sb); + ValueType instanceRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator()); + } + + void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) { + GenericStringBuffer sb; + SizeType len = CurrentSchema().GetURI().GetStringLength(); + if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch)); + if (schema.GetTokenCount()) schema.StringifyUriFragment(sb); + else GetInvalidSchemaPointer().StringifyUriFragment(sb); + ValueType schemaRef(sb.GetString(), static_cast(sb.GetSize() / sizeof(Ch)), + GetStateAllocator()); + result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator()); + } + + void AddErrorCode(ValueType& result, const ValidateErrorCode code) { + result.AddMember(GetErrorCodeString(), code, GetStateAllocator()); + } + + void AddError(ValueType& keyword, ValueType& error) { + typename ValueType::MemberIterator member = error_.FindMember(keyword); + if (member == error_.MemberEnd()) + error_.AddMember(keyword, error, GetStateAllocator()); + else { + if (member->value.IsObject()) { + ValueType errors(kArrayType); + errors.PushBack(member->value, GetStateAllocator()); + member->value = errors; + } + member->value.PushBack(error, GetStateAllocator()); + } + } + + void AddCurrentError(const ValidateErrorCode code, bool parent = false) { + AddErrorCode(currentError_, code); + AddErrorInstanceLocation(currentError_, parent); + AddErrorSchemaLocation(currentError_); + AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_); + } + + void MergeError(ValueType& other) { + for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) { + AddError(it->name, it->value); + } + } + + void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected, + const typename SchemaType::ValueType& (*exclusive)() = 0) { + currentError_.SetObject(); + currentError_.AddMember(GetActualString(), actual, GetStateAllocator()); + currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator()); + if (exclusive) + currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator()); + AddCurrentError(code); + } + + void AddErrorArray(const ValidateErrorCode code, + ISchemaValidator** subvalidators, SizeType count) { + ValueType errors(kArrayType); + for (SizeType i = 0; i < count; ++i) + errors.PushBack(static_cast(subvalidators[i])->GetError(), GetStateAllocator()); + currentError_.SetObject(); + currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator()); + AddCurrentError(code); + } + + const SchemaType& CurrentSchema() const { return *schemaStack_.template Top()->schema; } + Context& CurrentContext() { return *schemaStack_.template Top(); } + const Context& CurrentContext() const { return *schemaStack_.template Top(); } + + static const size_t kDefaultSchemaStackCapacity = 1024; + static const size_t kDefaultDocumentStackCapacity = 256; + const SchemaDocumentType* schemaDocument_; + const SchemaType& root_; + StateAllocator* stateAllocator_; + StateAllocator* ownStateAllocator_; + internal::Stack schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *) + internal::Stack documentStack_; //!< stack to store the current path of validating document (Ch) + OutputHandler* outputHandler_; + ValueType error_; + ValueType currentError_; + ValueType missingDependents_; + bool valid_; + unsigned flags_; + unsigned depth_; +}; + +typedef GenericSchemaValidator SchemaValidator; + +/////////////////////////////////////////////////////////////////////////////// +// SchemaValidatingReader + +//! A helper class for parsing with validation. +/*! + This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate(). + + \tparam parseFlags Combination of \ref ParseFlag. + \tparam InputStream Type of input stream, implementing Stream concept. + \tparam SourceEncoding Encoding of the input stream. + \tparam SchemaDocumentType Type of schema document. + \tparam StackAllocator Allocator type for stack. +*/ +template < + unsigned parseFlags, + typename InputStream, + typename SourceEncoding, + typename SchemaDocumentType = SchemaDocument, + typename StackAllocator = CrtAllocator> +class SchemaValidatingReader { +public: + typedef typename SchemaDocumentType::PointerType PointerType; + typedef typename InputStream::Ch Ch; + typedef GenericValue ValueType; + + //! Constructor + /*! + \param is Input stream. + \param sd Schema document. + */ + SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {} + + template + bool operator()(Handler& handler) { + GenericReader reader; + GenericSchemaValidator validator(sd_, handler); + parseResult_ = reader.template Parse(is_, validator); + + isValid_ = validator.IsValid(); + if (isValid_) { + invalidSchemaPointer_ = PointerType(); + invalidSchemaKeyword_ = 0; + invalidDocumentPointer_ = PointerType(); + error_.SetObject(); + } + else { + invalidSchemaPointer_ = validator.GetInvalidSchemaPointer(); + invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword(); + invalidSchemaCode_ = validator.GetInvalidSchemaCode(); + invalidDocumentPointer_ = validator.GetInvalidDocumentPointer(); + error_.CopyFrom(validator.GetError(), allocator_); + } + + return parseResult_; + } + + const ParseResult& GetParseResult() const { return parseResult_; } + bool IsValid() const { return isValid_; } + const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; } + const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; } + const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; } + const ValueType& GetError() const { return error_; } + ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; } + +private: + InputStream& is_; + const SchemaDocumentType& sd_; + + ParseResult parseResult_; + PointerType invalidSchemaPointer_; + const Ch* invalidSchemaKeyword_; + PointerType invalidDocumentPointer_; + ValidateErrorCode invalidSchemaCode_; + StackAllocator allocator_; + ValueType error_; + bool isValid_; +}; + +RAPIDJSON_NAMESPACE_END +RAPIDJSON_DIAG_POP + +#endif // RAPIDJSON_SCHEMA_H_ diff --git a/Minecraft.Server/vendor/rapidjson/stream.h b/Minecraft.Server/vendor/rapidjson/stream.h new file mode 100644 index 00000000..1fd70915 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/stream.h @@ -0,0 +1,223 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#include "rapidjson.h" + +#ifndef RAPIDJSON_STREAM_H_ +#define RAPIDJSON_STREAM_H_ + +#include "encodings.h" + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// Stream + +/*! \class rapidjson::Stream + \brief Concept for reading and writing characters. + + For read-only stream, no need to implement PutBegin(), Put(), Flush() and PutEnd(). + + For write-only stream, only need to implement Put() and Flush(). + +\code +concept Stream { + typename Ch; //!< Character type of the stream. + + //! Read the current character from stream without moving the read cursor. + Ch Peek() const; + + //! Read the current character from stream and moving the read cursor to next character. + Ch Take(); + + //! Get the current read cursor. + //! \return Number of characters read from start. + size_t Tell(); + + //! Begin writing operation at the current read pointer. + //! \return The begin writer pointer. + Ch* PutBegin(); + + //! Write a character. + void Put(Ch c); + + //! Flush the buffer. + void Flush(); + + //! End the writing operation. + //! \param begin The begin write pointer returned by PutBegin(). + //! \return Number of characters written. + size_t PutEnd(Ch* begin); +} +\endcode +*/ + +//! Provides additional information for stream. +/*! + By using traits pattern, this type provides a default configuration for stream. + For custom stream, this type can be specialized for other configuration. + See TEST(Reader, CustomStringStream) in readertest.cpp for example. +*/ +template +struct StreamTraits { + //! Whether to make local copy of stream for optimization during parsing. + /*! + By default, for safety, streams do not use local copy optimization. + Stream that can be copied fast should specialize this, like StreamTraits. + */ + enum { copyOptimization = 0 }; +}; + +//! Reserve n characters for writing to a stream. +template +inline void PutReserve(Stream& stream, size_t count) { + (void)stream; + (void)count; +} + +//! Write character to a stream, presuming buffer is reserved. +template +inline void PutUnsafe(Stream& stream, typename Stream::Ch c) { + stream.Put(c); +} + +//! Put N copies of a character to a stream. +template +inline void PutN(Stream& stream, Ch c, size_t n) { + PutReserve(stream, n); + for (size_t i = 0; i < n; i++) + PutUnsafe(stream, c); +} + +/////////////////////////////////////////////////////////////////////////////// +// GenericStreamWrapper + +//! A Stream Wrapper +/*! \tThis string stream is a wrapper for any stream by just forwarding any + \treceived message to the origin stream. + \note implements Stream concept +*/ + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4702) // unreachable code +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +template > +class GenericStreamWrapper { +public: + typedef typename Encoding::Ch Ch; + GenericStreamWrapper(InputStream& is): is_(is) {} + + Ch Peek() const { return is_.Peek(); } + Ch Take() { return is_.Take(); } + size_t Tell() { return is_.Tell(); } + Ch* PutBegin() { return is_.PutBegin(); } + void Put(Ch ch) { is_.Put(ch); } + void Flush() { is_.Flush(); } + size_t PutEnd(Ch* ch) { return is_.PutEnd(ch); } + + // wrapper for MemoryStream + const Ch* Peek4() const { return is_.Peek4(); } + + // wrapper for AutoUTFInputStream + UTFType GetType() const { return is_.GetType(); } + bool HasBOM() const { return is_.HasBOM(); } + +protected: + InputStream& is_; +}; + +#if defined(_MSC_VER) && _MSC_VER <= 1800 +RAPIDJSON_DIAG_POP +#endif + +/////////////////////////////////////////////////////////////////////////////// +// StringStream + +//! Read-only string stream. +/*! \note implements Stream concept +*/ +template +struct GenericStringStream { + typedef typename Encoding::Ch Ch; + + GenericStringStream(const Ch *src) : src_(src), head_(src) {} + + Ch Peek() const { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() const { return static_cast(src_ - head_); } + + Ch* PutBegin() { RAPIDJSON_ASSERT(false); return 0; } + void Put(Ch) { RAPIDJSON_ASSERT(false); } + void Flush() { RAPIDJSON_ASSERT(false); } + size_t PutEnd(Ch*) { RAPIDJSON_ASSERT(false); return 0; } + + const Ch* src_; //!< Current read position. + const Ch* head_; //!< Original head of the string. +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! String stream with UTF8 encoding. +typedef GenericStringStream > StringStream; + +/////////////////////////////////////////////////////////////////////////////// +// InsituStringStream + +//! A read-write string stream. +/*! This string stream is particularly designed for in-situ parsing. + \note implements Stream concept +*/ +template +struct GenericInsituStringStream { + typedef typename Encoding::Ch Ch; + + GenericInsituStringStream(Ch *src) : src_(src), dst_(0), head_(src) {} + + // Read + Ch Peek() { return *src_; } + Ch Take() { return *src_++; } + size_t Tell() { return static_cast(src_ - head_); } + + // Write + void Put(Ch c) { RAPIDJSON_ASSERT(dst_ != 0); *dst_++ = c; } + + Ch* PutBegin() { return dst_ = src_; } + size_t PutEnd(Ch* begin) { return static_cast(dst_ - begin); } + void Flush() {} + + Ch* Push(size_t count) { Ch* begin = dst_; dst_ += count; return begin; } + void Pop(size_t count) { dst_ -= count; } + + Ch* src_; + Ch* dst_; + Ch* head_; +}; + +template +struct StreamTraits > { + enum { copyOptimization = 1 }; +}; + +//! Insitu string stream with UTF8 encoding. +typedef GenericInsituStringStream > InsituStringStream; + +RAPIDJSON_NAMESPACE_END + +#endif // RAPIDJSON_STREAM_H_ diff --git a/Minecraft.Server/vendor/rapidjson/stringbuffer.h b/Minecraft.Server/vendor/rapidjson/stringbuffer.h new file mode 100644 index 00000000..82ad3ca6 --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/stringbuffer.h @@ -0,0 +1,121 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_STRINGBUFFER_H_ +#define RAPIDJSON_STRINGBUFFER_H_ + +#include "stream.h" +#include "internal/stack.h" + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +#include // std::move +#endif + +#include "internal/stack.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +//! Represents an in-memory output stream. +/*! + \tparam Encoding Encoding of the stream. + \tparam Allocator type for allocating memory buffer. + \note implements Stream concept +*/ +template +class GenericStringBuffer { +public: + typedef typename Encoding::Ch Ch; + + GenericStringBuffer(Allocator* allocator = 0, size_t capacity = kDefaultCapacity) : stack_(allocator, capacity) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericStringBuffer(GenericStringBuffer&& rhs) : stack_(std::move(rhs.stack_)) {} + GenericStringBuffer& operator=(GenericStringBuffer&& rhs) { + if (&rhs != this) + stack_ = std::move(rhs.stack_); + return *this; + } +#endif + + void Put(Ch c) { *stack_.template Push() = c; } + void PutUnsafe(Ch c) { *stack_.template PushUnsafe() = c; } + void Flush() {} + + void Clear() { stack_.Clear(); } + void ShrinkToFit() { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.ShrinkToFit(); + stack_.template Pop(1); + } + + void Reserve(size_t count) { stack_.template Reserve(count); } + Ch* Push(size_t count) { return stack_.template Push(count); } + Ch* PushUnsafe(size_t count) { return stack_.template PushUnsafe(count); } + void Pop(size_t count) { stack_.template Pop(count); } + + const Ch* GetString() const { + // Push and pop a null terminator. This is safe. + *stack_.template Push() = '\0'; + stack_.template Pop(1); + + return stack_.template Bottom(); + } + + //! Get the size of string in bytes in the string buffer. + size_t GetSize() const { return stack_.GetSize(); } + + //! Get the length of string in Ch in the string buffer. + size_t GetLength() const { return stack_.GetSize() / sizeof(Ch); } + + static const size_t kDefaultCapacity = 256; + mutable internal::Stack stack_; + +private: + // Prohibit copy constructor & assignment operator. + GenericStringBuffer(const GenericStringBuffer&); + GenericStringBuffer& operator=(const GenericStringBuffer&); +}; + +//! String buffer with UTF8 encoding +typedef GenericStringBuffer > StringBuffer; + +template +inline void PutReserve(GenericStringBuffer& stream, size_t count) { + stream.Reserve(count); +} + +template +inline void PutUnsafe(GenericStringBuffer& stream, typename Encoding::Ch c) { + stream.PutUnsafe(c); +} + +//! Implement specialized version of PutN() with memset() for better performance. +template<> +inline void PutN(GenericStringBuffer >& stream, char c, size_t n) { + std::memset(stream.stack_.Push(n), c, n * sizeof(c)); +} + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_STRINGBUFFER_H_ diff --git a/Minecraft.Server/vendor/rapidjson/uri.h b/Minecraft.Server/vendor/rapidjson/uri.h new file mode 100644 index 00000000..f93e508a --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/uri.h @@ -0,0 +1,481 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// (C) Copyright IBM Corporation 2021 +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_URI_H_ +#define RAPIDJSON_URI_H_ + +#include "internal/strfunc.h" + +#if defined(__clang__) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// GenericUri + +template +class GenericUri { +public: + typedef typename ValueType::Ch Ch; +#if RAPIDJSON_HAS_STDSTRING + typedef std::basic_string String; +#endif + + //! Constructors + GenericUri(Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + } + + GenericUri(const Ch* uri, SizeType len, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, len); + } + + GenericUri(const Ch* uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri, internal::StrLen(uri)); + } + + // Use with specializations of GenericValue + template GenericUri(const T& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + const Ch* u = uri.template Get(); // TypeHelper from document.h + Parse(u, internal::StrLen(u)); + } + +#if RAPIDJSON_HAS_STDSTRING + GenericUri(const String& uri, Allocator* allocator = 0) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + Parse(uri.c_str(), internal::StrLen(uri.c_str())); + } +#endif + + //! Copy constructor + GenericUri(const GenericUri& rhs) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(), ownAllocator_() { + *this = rhs; + } + + //! Copy constructor + GenericUri(const GenericUri& rhs, Allocator* allocator) : uri_(), base_(), scheme_(), auth_(), path_(), query_(), frag_(), allocator_(allocator), ownAllocator_() { + *this = rhs; + } + + //! Destructor. + ~GenericUri() { + Free(); + RAPIDJSON_DELETE(ownAllocator_); + } + + //! Assignment operator + GenericUri& operator=(const GenericUri& rhs) { + if (this != &rhs) { + // Do not delete ownAllocator + Free(); + Allocate(rhs.GetStringLength()); + auth_ = CopyPart(scheme_, rhs.scheme_, rhs.GetSchemeStringLength()); + path_ = CopyPart(auth_, rhs.auth_, rhs.GetAuthStringLength()); + query_ = CopyPart(path_, rhs.path_, rhs.GetPathStringLength()); + frag_ = CopyPart(query_, rhs.query_, rhs.GetQueryStringLength()); + base_ = CopyPart(frag_, rhs.frag_, rhs.GetFragStringLength()); + uri_ = CopyPart(base_, rhs.base_, rhs.GetBaseStringLength()); + CopyPart(uri_, rhs.uri_, rhs.GetStringLength()); + } + return *this; + } + + //! Getters + // Use with specializations of GenericValue + template void Get(T& uri, Allocator& allocator) { + uri.template Set(this->GetString(), allocator); // TypeHelper from document.h + } + + const Ch* GetString() const { return uri_; } + SizeType GetStringLength() const { return uri_ == 0 ? 0 : internal::StrLen(uri_); } + const Ch* GetBaseString() const { return base_; } + SizeType GetBaseStringLength() const { return base_ == 0 ? 0 : internal::StrLen(base_); } + const Ch* GetSchemeString() const { return scheme_; } + SizeType GetSchemeStringLength() const { return scheme_ == 0 ? 0 : internal::StrLen(scheme_); } + const Ch* GetAuthString() const { return auth_; } + SizeType GetAuthStringLength() const { return auth_ == 0 ? 0 : internal::StrLen(auth_); } + const Ch* GetPathString() const { return path_; } + SizeType GetPathStringLength() const { return path_ == 0 ? 0 : internal::StrLen(path_); } + const Ch* GetQueryString() const { return query_; } + SizeType GetQueryStringLength() const { return query_ == 0 ? 0 : internal::StrLen(query_); } + const Ch* GetFragString() const { return frag_; } + SizeType GetFragStringLength() const { return frag_ == 0 ? 0 : internal::StrLen(frag_); } + +#if RAPIDJSON_HAS_STDSTRING + static String Get(const GenericUri& uri) { return String(uri.GetString(), uri.GetStringLength()); } + static String GetBase(const GenericUri& uri) { return String(uri.GetBaseString(), uri.GetBaseStringLength()); } + static String GetScheme(const GenericUri& uri) { return String(uri.GetSchemeString(), uri.GetSchemeStringLength()); } + static String GetAuth(const GenericUri& uri) { return String(uri.GetAuthString(), uri.GetAuthStringLength()); } + static String GetPath(const GenericUri& uri) { return String(uri.GetPathString(), uri.GetPathStringLength()); } + static String GetQuery(const GenericUri& uri) { return String(uri.GetQueryString(), uri.GetQueryStringLength()); } + static String GetFrag(const GenericUri& uri) { return String(uri.GetFragString(), uri.GetFragStringLength()); } +#endif + + //! Equality operators + bool operator==(const GenericUri& rhs) const { + return Match(rhs, true); + } + + bool operator!=(const GenericUri& rhs) const { + return !Match(rhs, true); + } + + bool Match(const GenericUri& uri, bool full = true) const { + Ch* s1; + Ch* s2; + if (full) { + s1 = uri_; + s2 = uri.uri_; + } else { + s1 = base_; + s2 = uri.base_; + } + if (s1 == s2) return true; + if (s1 == 0 || s2 == 0) return false; + return internal::StrCmp(s1, s2) == 0; + } + + //! Resolve this URI against another (base) URI in accordance with URI resolution rules. + // See https://tools.ietf.org/html/rfc3986 + // Use for resolving an id or $ref with an in-scope id. + // Returns a new GenericUri for the resolved URI. + GenericUri Resolve(const GenericUri& baseuri, Allocator* allocator = 0) { + GenericUri resuri; + resuri.allocator_ = allocator; + // Ensure enough space for combining paths + resuri.Allocate(GetStringLength() + baseuri.GetStringLength() + 1); // + 1 for joining slash + + if (!(GetSchemeStringLength() == 0)) { + // Use all of this URI + resuri.auth_ = CopyPart(resuri.scheme_, scheme_, GetSchemeStringLength()); + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base scheme + resuri.auth_ = CopyPart(resuri.scheme_, baseuri.scheme_, baseuri.GetSchemeStringLength()); + if (!(GetAuthStringLength() == 0)) { + // Use this auth, path, query + resuri.path_ = CopyPart(resuri.auth_, auth_, GetAuthStringLength()); + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + resuri.RemoveDotSegments(); + } else { + // Use the base auth + resuri.path_ = CopyPart(resuri.auth_, baseuri.auth_, baseuri.GetAuthStringLength()); + if (GetPathStringLength() == 0) { + // Use the base path + resuri.query_ = CopyPart(resuri.path_, baseuri.path_, baseuri.GetPathStringLength()); + if (GetQueryStringLength() == 0) { + // Use the base query + resuri.frag_ = CopyPart(resuri.query_, baseuri.query_, baseuri.GetQueryStringLength()); + } else { + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } else { + if (path_[0] == '/') { + // Absolute path - use all of this path + resuri.query_ = CopyPart(resuri.path_, path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } else { + // Relative path - append this path to base path after base path's last slash + size_t pos = 0; + if (!(baseuri.GetAuthStringLength() == 0) && baseuri.GetPathStringLength() == 0) { + resuri.path_[pos] = '/'; + pos++; + } + size_t lastslashpos = baseuri.GetPathStringLength(); + while (lastslashpos > 0) { + if (baseuri.path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + std::memcpy(&resuri.path_[pos], baseuri.path_, lastslashpos * sizeof(Ch)); + pos += lastslashpos; + resuri.query_ = CopyPart(&resuri.path_[pos], path_, GetPathStringLength()); + resuri.RemoveDotSegments(); + } + // Use this query + resuri.frag_ = CopyPart(resuri.query_, query_, GetQueryStringLength()); + } + } + } + // Always use this frag + resuri.base_ = CopyPart(resuri.frag_, frag_, GetFragStringLength()); + + // Re-constitute base_ and uri_ + resuri.SetBase(); + resuri.uri_ = resuri.base_ + resuri.GetBaseStringLength() + 1; + resuri.SetUri(); + return resuri; + } + + //! Get the allocator of this GenericUri. + Allocator& GetAllocator() { return *allocator_; } + +private: + // Allocate memory for a URI + // Returns total amount allocated + std::size_t Allocate(std::size_t len) { + // Create own allocator if user did not supply. + if (!allocator_) + ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)(); + + // Allocate one block containing each part of the URI (5) plus base plus full URI, all null terminated. + // Order: scheme, auth, path, query, frag, base, uri + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + size_t total = (3 * len + 7) * sizeof(Ch); + scheme_ = static_cast(allocator_->Malloc(total)); + *scheme_ = '\0'; + auth_ = scheme_; + auth_++; + *auth_ = '\0'; + path_ = auth_; + path_++; + *path_ = '\0'; + query_ = path_; + query_++; + *query_ = '\0'; + frag_ = query_; + frag_++; + *frag_ = '\0'; + base_ = frag_; + base_++; + *base_ = '\0'; + uri_ = base_; + uri_++; + *uri_ = '\0'; + return total; + } + + // Free memory for a URI + void Free() { + if (scheme_) { + Allocator::Free(scheme_); + scheme_ = 0; + } + } + + // Parse a URI into constituent scheme, authority, path, query, & fragment parts + // Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per + // https://tools.ietf.org/html/rfc3986 + void Parse(const Ch* uri, std::size_t len) { + std::size_t start = 0, pos1 = 0, pos2 = 0; + Allocate(len); + + // Look for scheme ([^:/?#]+):)? + if (start < len) { + while (pos1 < len) { + if (uri[pos1] == ':') break; + pos1++; + } + if (pos1 != len) { + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + if (pos1 < pos2) { + pos1++; + std::memcpy(scheme_, &uri[start], pos1 * sizeof(Ch)); + scheme_[pos1] = '\0'; + start = pos1; + } + } + } + // Look for auth (//([^/?#]*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + auth_ = scheme_ + GetSchemeStringLength(); + auth_++; + *auth_ = '\0'; + if (start < len - 1 && uri[start] == '/' && uri[start + 1] == '/') { + pos2 = start + 2; + while (pos2 < len) { + if (uri[pos2] == '/') break; + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + std::memcpy(auth_, &uri[start], (pos2 - start) * sizeof(Ch)); + auth_[pos2 - start] = '\0'; + start = pos2; + } + // Look for path ([^?#]*) + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + path_ = auth_ + GetAuthStringLength(); + path_++; + *path_ = '\0'; + if (start < len) { + pos2 = start; + while (pos2 < len) { + if (uri[pos2] == '?') break; + if (uri[pos2] == '#') break; + pos2++; + } + if (start != pos2) { + std::memcpy(path_, &uri[start], (pos2 - start) * sizeof(Ch)); + path_[pos2 - start] = '\0'; + if (path_[0] == '/') + RemoveDotSegments(); // absolute path - normalize + start = pos2; + } + } + // Look for query (\?([^#]*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + query_ = path_ + GetPathStringLength(); + query_++; + *query_ = '\0'; + if (start < len && uri[start] == '?') { + pos2 = start + 1; + while (pos2 < len) { + if (uri[pos2] == '#') break; + pos2++; + } + if (start != pos2) { + std::memcpy(query_, &uri[start], (pos2 - start) * sizeof(Ch)); + query_[pos2 - start] = '\0'; + start = pos2; + } + } + // Look for fragment (#(.*))? + // Note need to set, increment, assign in 3 stages to avoid compiler warning bug. + frag_ = query_ + GetQueryStringLength(); + frag_++; + *frag_ = '\0'; + if (start < len && uri[start] == '#') { + std::memcpy(frag_, &uri[start], (len - start) * sizeof(Ch)); + frag_[len - start] = '\0'; + } + + // Re-constitute base_ and uri_ + base_ = frag_ + GetFragStringLength() + 1; + SetBase(); + uri_ = base_ + GetBaseStringLength() + 1; + SetUri(); + } + + // Reconstitute base + void SetBase() { + Ch* next = base_; + std::memcpy(next, scheme_, GetSchemeStringLength() * sizeof(Ch)); + next+= GetSchemeStringLength(); + std::memcpy(next, auth_, GetAuthStringLength() * sizeof(Ch)); + next+= GetAuthStringLength(); + std::memcpy(next, path_, GetPathStringLength() * sizeof(Ch)); + next+= GetPathStringLength(); + std::memcpy(next, query_, GetQueryStringLength() * sizeof(Ch)); + next+= GetQueryStringLength(); + *next = '\0'; + } + + // Reconstitute uri + void SetUri() { + Ch* next = uri_; + std::memcpy(next, base_, GetBaseStringLength() * sizeof(Ch)); + next+= GetBaseStringLength(); + std::memcpy(next, frag_, GetFragStringLength() * sizeof(Ch)); + next+= GetFragStringLength(); + *next = '\0'; + } + + // Copy a part from one GenericUri to another + // Return the pointer to the next part to be copied to + Ch* CopyPart(Ch* to, Ch* from, std::size_t len) { + RAPIDJSON_ASSERT(to != 0); + RAPIDJSON_ASSERT(from != 0); + std::memcpy(to, from, len * sizeof(Ch)); + to[len] = '\0'; + Ch* next = to + len + 1; + return next; + } + + // Remove . and .. segments from the path_ member. + // https://tools.ietf.org/html/rfc3986 + // This is done in place as we are only removing segments. + void RemoveDotSegments() { + std::size_t pathlen = GetPathStringLength(); + std::size_t pathpos = 0; // Position in path_ + std::size_t newpos = 0; // Position in new path_ + + // Loop through each segment in original path_ + while (pathpos < pathlen) { + // Get next segment, bounded by '/' or end + size_t slashpos = 0; + while ((pathpos + slashpos) < pathlen) { + if (path_[pathpos + slashpos] == '/') break; + slashpos++; + } + // Check for .. and . segments + if (slashpos == 2 && path_[pathpos] == '.' && path_[pathpos + 1] == '.') { + // Backup a .. segment in the new path_ + // We expect to find a previously added slash at the end or nothing + RAPIDJSON_ASSERT(newpos == 0 || path_[newpos - 1] == '/'); + size_t lastslashpos = newpos; + // Make sure we don't go beyond the start segment + if (lastslashpos > 1) { + // Find the next to last slash and back up to it + lastslashpos--; + while (lastslashpos > 0) { + if (path_[lastslashpos - 1] == '/') break; + lastslashpos--; + } + // Set the new path_ position + newpos = lastslashpos; + } + } else if (slashpos == 1 && path_[pathpos] == '.') { + // Discard . segment, leaves new path_ unchanged + } else { + // Move any other kind of segment to the new path_ + RAPIDJSON_ASSERT(newpos <= pathpos); + std::memmove(&path_[newpos], &path_[pathpos], slashpos * sizeof(Ch)); + newpos += slashpos; + // Add slash if not at end + if ((pathpos + slashpos) < pathlen) { + path_[newpos] = '/'; + newpos++; + } + } + // Move to next segment + pathpos += slashpos + 1; + } + path_[newpos] = '\0'; + } + + Ch* uri_; // Everything + Ch* base_; // Everything except fragment + Ch* scheme_; // Includes the : + Ch* auth_; // Includes the // + Ch* path_; // Absolute if starts with / + Ch* query_; // Includes the ? + Ch* frag_; // Includes the # + + Allocator* allocator_; //!< The current allocator. It is either user-supplied or equal to ownAllocator_. + Allocator* ownAllocator_; //!< Allocator owned by this Uri. +}; + +//! GenericUri for Value (UTF-8, default allocator). +typedef GenericUri Uri; + +RAPIDJSON_NAMESPACE_END + +#if defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_URI_H_ diff --git a/Minecraft.Server/vendor/rapidjson/writer.h b/Minecraft.Server/vendor/rapidjson/writer.h new file mode 100644 index 00000000..632e02ce --- /dev/null +++ b/Minecraft.Server/vendor/rapidjson/writer.h @@ -0,0 +1,721 @@ +// Tencent is pleased to support the open source community by making RapidJSON available. +// +// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. +// +// Licensed under the MIT License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// http://opensource.org/licenses/MIT +// +// Unless required by applicable law or agreed to in writing, software distributed +// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. + +#ifndef RAPIDJSON_WRITER_H_ +#define RAPIDJSON_WRITER_H_ + +#include "stream.h" +#include "internal/clzll.h" +#include "internal/meta.h" +#include "internal/stack.h" +#include "internal/strfunc.h" +#include "internal/dtoa.h" +#include "internal/itoa.h" +#include "stringbuffer.h" +#include // placement new + +#if defined(RAPIDJSON_SIMD) && defined(_MSC_VER) +#include +#pragma intrinsic(_BitScanForward) +#endif +#ifdef RAPIDJSON_SSE42 +#include +#elif defined(RAPIDJSON_SSE2) +#include +#elif defined(RAPIDJSON_NEON) +#include +#endif + +#ifdef __clang__ +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(padded) +RAPIDJSON_DIAG_OFF(unreachable-code) +RAPIDJSON_DIAG_OFF(c++98-compat) +#elif defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(4127) // conditional expression is constant +#endif + +RAPIDJSON_NAMESPACE_BEGIN + +/////////////////////////////////////////////////////////////////////////////// +// WriteFlag + +/*! \def RAPIDJSON_WRITE_DEFAULT_FLAGS + \ingroup RAPIDJSON_CONFIG + \brief User-defined kWriteDefaultFlags definition. + + User can define this as any \c WriteFlag combinations. +*/ +#ifndef RAPIDJSON_WRITE_DEFAULT_FLAGS +#define RAPIDJSON_WRITE_DEFAULT_FLAGS kWriteNoFlags +#endif + +//! Combination of writeFlags +enum WriteFlag { + kWriteNoFlags = 0, //!< No flags are set. + kWriteValidateEncodingFlag = 1, //!< Validate encoding of JSON strings. + kWriteNanAndInfFlag = 2, //!< Allow writing of Infinity, -Infinity and NaN. + kWriteNanAndInfNullFlag = 4, //!< Allow writing of Infinity, -Infinity and NaN as null. + kWriteDefaultFlags = RAPIDJSON_WRITE_DEFAULT_FLAGS //!< Default write flags. Can be customized by defining RAPIDJSON_WRITE_DEFAULT_FLAGS +}; + +//! JSON writer +/*! Writer implements the concept Handler. + It generates JSON text by events to an output os. + + User may programmatically calls the functions of a writer to generate JSON text. + + On the other side, a writer can also be passed to objects that generates events, + + for example Reader::Parse() and Document::Accept(). + + \tparam OutputStream Type of output stream. + \tparam SourceEncoding Encoding of source string. + \tparam TargetEncoding Encoding of output stream. + \tparam StackAllocator Type of allocator for allocating memory of stack. + \note implements Handler concept +*/ +template, typename TargetEncoding = UTF8<>, typename StackAllocator = CrtAllocator, unsigned writeFlags = kWriteDefaultFlags> +class Writer { +public: + typedef typename SourceEncoding::Ch Ch; + + static const int kDefaultMaxDecimalPlaces = 324; + + //! Constructor + /*! \param os Output stream. + \param stackAllocator User supplied allocator. If it is null, it will create a private one. + \param levelDepth Initial capacity of stack. + */ + explicit + Writer(OutputStream& os, StackAllocator* stackAllocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(&os), level_stack_(stackAllocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + + explicit + Writer(StackAllocator* allocator = 0, size_t levelDepth = kDefaultLevelDepth) : + os_(0), level_stack_(allocator, levelDepth * sizeof(Level)), maxDecimalPlaces_(kDefaultMaxDecimalPlaces), hasRoot_(false) {} + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + Writer(Writer&& rhs) : + os_(rhs.os_), level_stack_(std::move(rhs.level_stack_)), maxDecimalPlaces_(rhs.maxDecimalPlaces_), hasRoot_(rhs.hasRoot_) { + rhs.os_ = 0; + } +#endif + + //! Reset the writer with a new stream. + /*! + This function reset the writer with a new stream and default settings, + in order to make a Writer object reusable for output multiple JSONs. + + \param os New output stream. + \code + Writer writer(os1); + writer.StartObject(); + // ... + writer.EndObject(); + + writer.Reset(os2); + writer.StartObject(); + // ... + writer.EndObject(); + \endcode + */ + void Reset(OutputStream& os) { + os_ = &os; + hasRoot_ = false; + level_stack_.Clear(); + } + + //! Checks whether the output is a complete JSON. + /*! + A complete JSON has a complete root object or array. + */ + bool IsComplete() const { + return hasRoot_ && level_stack_.Empty(); + } + + int GetMaxDecimalPlaces() const { + return maxDecimalPlaces_; + } + + //! Sets the maximum number of decimal places for double output. + /*! + This setting truncates the output with specified number of decimal places. + + For example, + + \code + writer.SetMaxDecimalPlaces(3); + writer.StartArray(); + writer.Double(0.12345); // "0.123" + writer.Double(0.0001); // "0.0" + writer.Double(1.234567890123456e30); // "1.234567890123456e30" (do not truncate significand for positive exponent) + writer.Double(1.23e-4); // "0.0" (do truncate significand for negative exponent) + writer.EndArray(); + \endcode + + The default setting does not truncate any decimal places. You can restore to this setting by calling + \code + writer.SetMaxDecimalPlaces(Writer::kDefaultMaxDecimalPlaces); + \endcode + */ + void SetMaxDecimalPlaces(int maxDecimalPlaces) { + maxDecimalPlaces_ = maxDecimalPlaces; + } + + /*!@name Implementation of Handler + \see Handler + */ + //@{ + + bool Null() { Prefix(kNullType); return EndValue(WriteNull()); } + bool Bool(bool b) { Prefix(b ? kTrueType : kFalseType); return EndValue(WriteBool(b)); } + bool Int(int i) { Prefix(kNumberType); return EndValue(WriteInt(i)); } + bool Uint(unsigned u) { Prefix(kNumberType); return EndValue(WriteUint(u)); } + bool Int64(int64_t i64) { Prefix(kNumberType); return EndValue(WriteInt64(i64)); } + bool Uint64(uint64_t u64) { Prefix(kNumberType); return EndValue(WriteUint64(u64)); } + + //! Writes the given \c double value to the stream + /*! + \param d The value to be written. + \return Whether it is succeed. + */ + bool Double(double d) { Prefix(kNumberType); return EndValue(WriteDouble(d)); } + + bool RawNumber(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kNumberType); + return EndValue(WriteString(str, length)); + } + + bool String(const Ch* str, SizeType length, bool copy = false) { + RAPIDJSON_ASSERT(str != 0); + (void)copy; + Prefix(kStringType); + return EndValue(WriteString(str, length)); + } + +#if RAPIDJSON_HAS_STDSTRING + bool String(const std::basic_string& str) { + return String(str.data(), SizeType(str.size())); + } +#endif + + bool StartObject() { + Prefix(kObjectType); + new (level_stack_.template Push()) Level(false); + return WriteStartObject(); + } + + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + +#if RAPIDJSON_HAS_STDSTRING + bool Key(const std::basic_string& str) + { + return Key(str.data(), SizeType(str.size())); + } +#endif + + bool EndObject(SizeType memberCount = 0) { + (void)memberCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); // not inside an Object + RAPIDJSON_ASSERT(!level_stack_.template Top()->inArray); // currently inside an Array, not Object + RAPIDJSON_ASSERT(0 == level_stack_.template Top()->valueCount % 2); // Object has a Key without a Value + level_stack_.template Pop(1); + return EndValue(WriteEndObject()); + } + + bool StartArray() { + Prefix(kArrayType); + new (level_stack_.template Push()) Level(true); + return WriteStartArray(); + } + + bool EndArray(SizeType elementCount = 0) { + (void)elementCount; + RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); + RAPIDJSON_ASSERT(level_stack_.template Top()->inArray); + level_stack_.template Pop(1); + return EndValue(WriteEndArray()); + } + //@} + + /*! @name Convenience extensions */ + //@{ + + //! Simpler but slower overload. + bool String(const Ch* const& str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* const& str) { return Key(str, internal::StrLen(str)); } + + //@} + + //! Write a raw JSON value. + /*! + For user to write a stringified JSON as a value. + + \param json A well-formed JSON value. It should not contain null character within [0, length - 1] range. + \param length Length of the json. + \param type Type of the root of json. + */ + bool RawValue(const Ch* json, size_t length, Type type) { + RAPIDJSON_ASSERT(json != 0); + Prefix(type); + return EndValue(WriteRawValue(json, length)); + } + + //! Flush the output stream. + /*! + Allows the user to flush the output stream immediately. + */ + void Flush() { + os_->Flush(); + } + + static const size_t kDefaultLevelDepth = 32; + +protected: + //! Information for each nested level + struct Level { + Level(bool inArray_) : valueCount(0), inArray(inArray_) {} + size_t valueCount; //!< number of values in this level + bool inArray; //!< true if in array, otherwise in object + }; + + bool WriteNull() { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); return true; + } + + bool WriteBool(bool b) { + if (b) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'r'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'e'); + } + else { + PutReserve(*os_, 5); + PutUnsafe(*os_, 'f'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 's'); PutUnsafe(*os_, 'e'); + } + return true; + } + + bool WriteInt(int i) { + char buffer[11]; + const char* end = internal::i32toa(i, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint(unsigned u) { + char buffer[10]; + const char* end = internal::u32toa(u, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteInt64(int64_t i64) { + char buffer[21]; + const char* end = internal::i64toa(i64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (const char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteUint64(uint64_t u64) { + char buffer[20]; + char* end = internal::u64toa(u64, buffer); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + if (!(writeFlags & kWriteNanAndInfFlag) && !(writeFlags & kWriteNanAndInfNullFlag)) + return false; + if (writeFlags & kWriteNanAndInfNullFlag) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); + return true; + } + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char buffer[25]; + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + PutReserve(*os_, static_cast(end - buffer)); + for (char* p = buffer; p != end; ++p) + PutUnsafe(*os_, static_cast(*p)); + return true; + } + + bool WriteString(const Ch* str, SizeType length) { + static const typename OutputStream::Ch hexDigits[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + static const char escape[256] = { +#define Z16 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + //0 1 2 3 4 5 6 7 8 9 A B C D E F + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'b', 't', 'n', 'u', 'f', 'r', 'u', 'u', // 00 + 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', 'u', // 10 + 0, 0, '"', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 + Z16, Z16, // 30~4F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\\', 0, 0, 0, // 50 + Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16, Z16 // 60~FF +#undef Z16 + }; + + if (TargetEncoding::supportUnicode) + PutReserve(*os_, 2 + length * 6); // "\uxxxx..." + else + PutReserve(*os_, 2 + length * 12); // "\uxxxx\uyyyy..." + + PutUnsafe(*os_, '\"'); + GenericStringStream is(str); + while (ScanWriteUnescapedString(is, length)) { + const Ch c = is.Peek(); + if (!TargetEncoding::supportUnicode && static_cast(c) >= 0x80) { + // Unicode escaping + unsigned codepoint; + if (RAPIDJSON_UNLIKELY(!SourceEncoding::Decode(is, &codepoint))) + return false; + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + if (codepoint <= 0xD7FF || (codepoint >= 0xE000 && codepoint <= 0xFFFF)) { + PutUnsafe(*os_, hexDigits[(codepoint >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(codepoint ) & 15]); + } + else { + RAPIDJSON_ASSERT(codepoint >= 0x010000 && codepoint <= 0x10FFFF); + // Surrogate pair + unsigned s = codepoint - 0x010000; + unsigned lead = (s >> 10) + 0xD800; + unsigned trail = (s & 0x3FF) + 0xDC00; + PutUnsafe(*os_, hexDigits[(lead >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(lead >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(lead ) & 15]); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, 'u'); + PutUnsafe(*os_, hexDigits[(trail >> 12) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 8) & 15]); + PutUnsafe(*os_, hexDigits[(trail >> 4) & 15]); + PutUnsafe(*os_, hexDigits[(trail ) & 15]); + } + } + else if ((sizeof(Ch) == 1 || static_cast(c) < 256) && RAPIDJSON_UNLIKELY(escape[static_cast(c)])) { + is.Take(); + PutUnsafe(*os_, '\\'); + PutUnsafe(*os_, static_cast(escape[static_cast(c)])); + if (escape[static_cast(c)] == 'u') { + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, '0'); + PutUnsafe(*os_, hexDigits[static_cast(c) >> 4]); + PutUnsafe(*os_, hexDigits[static_cast(c) & 0xF]); + } + } + else if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + PutUnsafe(*os_, '\"'); + return true; + } + + bool ScanWriteUnescapedString(GenericStringStream& is, size_t length) { + return RAPIDJSON_LIKELY(is.Tell() < length); + } + + bool WriteStartObject() { os_->Put('{'); return true; } + bool WriteEndObject() { os_->Put('}'); return true; } + bool WriteStartArray() { os_->Put('['); return true; } + bool WriteEndArray() { os_->Put(']'); return true; } + + bool WriteRawValue(const Ch* json, size_t length) { + PutReserve(*os_, length); + GenericStringStream is(json); + while (RAPIDJSON_LIKELY(is.Tell() < length)) { + RAPIDJSON_ASSERT(is.Peek() != '\0'); + if (RAPIDJSON_UNLIKELY(!(writeFlags & kWriteValidateEncodingFlag ? + Transcoder::Validate(is, *os_) : + Transcoder::TranscodeUnsafe(is, *os_)))) + return false; + } + return true; + } + + void Prefix(Type type) { + (void)type; + if (RAPIDJSON_LIKELY(level_stack_.GetSize() != 0)) { // this value is not at root + Level* level = level_stack_.template Top(); + if (level->valueCount > 0) { + if (level->inArray) + os_->Put(','); // add comma if it is not the first element in array + else // in object + os_->Put((level->valueCount % 2 == 0) ? ',' : ':'); + } + if (!level->inArray && level->valueCount % 2 == 0) + RAPIDJSON_ASSERT(type == kStringType); // if it's in object, then even number should be a name + level->valueCount++; + } + else { + RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root. + hasRoot_ = true; + } + } + + // Flush the value if it is the top level one. + bool EndValue(bool ret) { + if (RAPIDJSON_UNLIKELY(level_stack_.Empty())) // end of json text + Flush(); + return ret; + } + + OutputStream* os_; + internal::Stack level_stack_; + int maxDecimalPlaces_; + bool hasRoot_; + +private: + // Prohibit copy constructor & assignment operator. + Writer(const Writer&); + Writer& operator=(const Writer&); +}; + +// Full specialization for StringStream to prevent memory copying + +template<> +inline bool Writer::WriteInt(int i) { + char *buffer = os_->Push(11); + const char* end = internal::i32toa(i, buffer); + os_->Pop(static_cast(11 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint(unsigned u) { + char *buffer = os_->Push(10); + const char* end = internal::u32toa(u, buffer); + os_->Pop(static_cast(10 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteInt64(int64_t i64) { + char *buffer = os_->Push(21); + const char* end = internal::i64toa(i64, buffer); + os_->Pop(static_cast(21 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteUint64(uint64_t u) { + char *buffer = os_->Push(20); + const char* end = internal::u64toa(u, buffer); + os_->Pop(static_cast(20 - (end - buffer))); + return true; +} + +template<> +inline bool Writer::WriteDouble(double d) { + if (internal::Double(d).IsNanOrInf()) { + // Note: This code path can only be reached if (RAPIDJSON_WRITE_DEFAULT_FLAGS & kWriteNanAndInfFlag). + if (!(kWriteDefaultFlags & kWriteNanAndInfFlag)) + return false; + if (kWriteDefaultFlags & kWriteNanAndInfNullFlag) { + PutReserve(*os_, 4); + PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'u'); PutUnsafe(*os_, 'l'); PutUnsafe(*os_, 'l'); + return true; + } + if (internal::Double(d).IsNan()) { + PutReserve(*os_, 3); + PutUnsafe(*os_, 'N'); PutUnsafe(*os_, 'a'); PutUnsafe(*os_, 'N'); + return true; + } + if (internal::Double(d).Sign()) { + PutReserve(*os_, 9); + PutUnsafe(*os_, '-'); + } + else + PutReserve(*os_, 8); + PutUnsafe(*os_, 'I'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'f'); + PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 'n'); PutUnsafe(*os_, 'i'); PutUnsafe(*os_, 't'); PutUnsafe(*os_, 'y'); + return true; + } + + char *buffer = os_->Push(25); + char* end = internal::dtoa(d, buffer, maxDecimalPlaces_); + os_->Pop(static_cast(25 - (end - buffer))); + return true; +} + +#if defined(RAPIDJSON_SSE2) || defined(RAPIDJSON_SSE42) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + static const char dquote[16] = { '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"', '\"' }; + static const char bslash[16] = { '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\', '\\' }; + static const char space[16] = { 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F }; + const __m128i dq = _mm_loadu_si128(reinterpret_cast(&dquote[0])); + const __m128i bs = _mm_loadu_si128(reinterpret_cast(&bslash[0])); + const __m128i sp = _mm_loadu_si128(reinterpret_cast(&space[0])); + + for (; p != endAligned; p += 16) { + const __m128i s = _mm_load_si128(reinterpret_cast(p)); + const __m128i t1 = _mm_cmpeq_epi8(s, dq); + const __m128i t2 = _mm_cmpeq_epi8(s, bs); + const __m128i t3 = _mm_cmpeq_epi8(_mm_max_epu8(s, sp), sp); // s < 0x20 <=> max(s, 0x1F) == 0x1F + const __m128i x = _mm_or_si128(_mm_or_si128(t1, t2), t3); + unsigned short r = static_cast(_mm_movemask_epi8(x)); + if (RAPIDJSON_UNLIKELY(r != 0)) { // some of characters is escaped + SizeType len; +#ifdef _MSC_VER // Find the index of first escaped + unsigned long offset; + _BitScanForward(&offset, r); + len = offset; +#else + len = static_cast(__builtin_ffs(r) - 1); +#endif + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + _mm_storeu_si128(reinterpret_cast<__m128i *>(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#elif defined(RAPIDJSON_NEON) +template<> +inline bool Writer::ScanWriteUnescapedString(StringStream& is, size_t length) { + if (length < 16) + return RAPIDJSON_LIKELY(is.Tell() < length); + + if (!RAPIDJSON_LIKELY(is.Tell() < length)) + return false; + + const char* p = is.src_; + const char* end = is.head_ + length; + const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15) & static_cast(~15)); + const char* endAligned = reinterpret_cast(reinterpret_cast(end) & static_cast(~15)); + if (nextAligned > end) + return true; + + while (p != nextAligned) + if (*p < 0x20 || *p == '\"' || *p == '\\') { + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); + } + else + os_->PutUnsafe(*p++); + + // The rest of string using SIMD + const uint8x16_t s0 = vmovq_n_u8('"'); + const uint8x16_t s1 = vmovq_n_u8('\\'); + const uint8x16_t s2 = vmovq_n_u8('\b'); + const uint8x16_t s3 = vmovq_n_u8(32); + + for (; p != endAligned; p += 16) { + const uint8x16_t s = vld1q_u8(reinterpret_cast(p)); + uint8x16_t x = vceqq_u8(s, s0); + x = vorrq_u8(x, vceqq_u8(s, s1)); + x = vorrq_u8(x, vceqq_u8(s, s2)); + x = vorrq_u8(x, vcltq_u8(s, s3)); + + x = vrev64q_u8(x); // Rev in 64 + uint64_t low = vgetq_lane_u64(vreinterpretq_u64_u8(x), 0); // extract + uint64_t high = vgetq_lane_u64(vreinterpretq_u64_u8(x), 1); // extract + + SizeType len = 0; + bool escaped = false; + if (low == 0) { + if (high != 0) { + uint32_t lz = internal::clzll(high); + len = 8 + (lz >> 3); + escaped = true; + } + } else { + uint32_t lz = internal::clzll(low); + len = lz >> 3; + escaped = true; + } + if (RAPIDJSON_UNLIKELY(escaped)) { // some of characters is escaped + char* q = reinterpret_cast(os_->PushUnsafe(len)); + for (size_t i = 0; i < len; i++) + q[i] = p[i]; + + p += len; + break; + } + vst1q_u8(reinterpret_cast(os_->PushUnsafe(16)), s); + } + + is.src_ = p; + return RAPIDJSON_LIKELY(is.Tell() < length); +} +#endif // RAPIDJSON_NEON + +RAPIDJSON_NAMESPACE_END + +#if defined(_MSC_VER) || defined(__clang__) +RAPIDJSON_DIAG_POP +#endif + +#endif // RAPIDJSON_RAPIDJSON_H_ diff --git a/Minecraft.World/MapItemSavedData.cpp b/Minecraft.World/MapItemSavedData.cpp index 852e0004..92baaf85 100644 --- a/Minecraft.World/MapItemSavedData.cpp +++ b/Minecraft.World/MapItemSavedData.cpp @@ -12,6 +12,28 @@ const int MapItemSavedData::END_PORTAL_DECORATION_KEY = -1; +namespace +{ + // Valid player icon slots in mapicons.png/additionalmapicons.png. + + // Keep to these slots so we never sample placeholder entries. + static const char PLAYER_MAP_ICON_SLOTS[] = { 0, 1, 2, 3, 8, 9, 10, 11 }; + + static char getRandomPlayerMapIcon(const shared_ptr& player) + { + // use seed bit shift random + unsigned int seed = static_cast(player->entityId); + seed ^= static_cast(player->getPlayerIndex() * 0x9E3779B9u); + seed ^= (seed >> 16); + seed *= 0x7FEB352Du; + seed ^= (seed >> 15); + seed *= 0x846CA68Bu; + seed ^= (seed >> 16); + + return PLAYER_MAP_ICON_SLOTS[seed % (sizeof(PLAYER_MAP_ICON_SLOTS) / sizeof(PLAYER_MAP_ICON_SLOTS[0]))]; + } +} + // 4J added entityId param MapItemSavedData::MapDecoration::MapDecoration(char img, char x, char y, char rot, int entityId, bool visible) { @@ -427,16 +449,14 @@ void MapItemSavedData::tickCarriedBy(shared_ptr player, shared_ptrgetPlayerIndex(); - if(imgIndex>3) imgIndex += 4; + imgIndex = getRandomPlayerMapIcon(decorationPlayer); } #ifdef _LARGE_WORLDS else //if (abs(xd) < MAP_SIZE * 5 && abs(yd) < MAP_SIZE * 5) { // 4J Stu - As we have added new icons for players on a new row below // other icons used in Java we need to move our index to the next row - imgIndex = (int)decorationPlayer->getPlayerIndex(); - if(imgIndex>3) imgIndex += 4; + imgIndex = getRandomPlayerMapIcon(decorationPlayer); imgIndex += 16; // Add 16 to indicate that it's on the next texture rot = 0; diff --git a/Minecraft.World/Minecraft.World.vcxproj b/Minecraft.World/Minecraft.World.vcxproj index 9680f74e..a6b3c0b8 100644 --- a/Minecraft.World/Minecraft.World.vcxproj +++ b/Minecraft.World/Minecraft.World.vcxproj @@ -1,5 +1,5 @@ - - + + ContentPackage_NO_TU @@ -145,6 +145,30 @@ Release Xbox 360 + + Server + Durango + + + Server + ORBIS + + + Server + PS3 + + + Server + PSVita + + + Server + x64 + + + Server + Xbox 360 + en-US @@ -160,32 +184,38 @@ StaticLibrary MultiByte + v110 StaticLibrary MultiByte + v110 StaticLibrary MultiByte WithExceptsWithRtti + v110 StaticLibrary MultiByte WithExceptsWithRtti + v110 StaticLibrary MultiByte WithExceptsWithRtti NoTocRestore2 + v110 StaticLibrary MultiByte WithExceptsWithRtti NoTocRestore2 + v110 StaticLibrary @@ -211,60 +241,72 @@ StaticLibrary MultiByte + v110 StaticLibrary MultiByte + v110 StaticLibrary MultiByte + v110 StaticLibrary MultiByte + v110 StaticLibrary MultiByte WithExceptsWithRtti NoTocRestore2 + v110 StaticLibrary MultiByte WithExceptsWithRtti NoTocRestore2 + v110 StaticLibrary MultiByte WithExceptsWithRtti NoTocRestore2 + v110 StaticLibrary MultiByte WithExceptsWithRtti NoTocRestore2 + v110 StaticLibrary MultiByte WithExceptsWithRtti + v110 StaticLibrary MultiByte WithExceptsWithRtti + v110 StaticLibrary MultiByte + v110 StaticLibrary MultiByte + v110 StaticLibrary @@ -312,29 +354,44 @@ v110 - Clang + v110 StaticLibrary - Clang + v110 StaticLibrary - Clang + v110 StaticLibrary - Clang + v110 StaticLibrary - Clang + v110 StaticLibrary - Clang + v110 StaticLibrary + + SNC + + + + + + Clang + + + v110 + + + SNC + @@ -1729,6 +1786,7 @@ true true true + true @@ -2455,6 +2513,7 @@ true true true + true @@ -2852,6 +2911,7 @@ true true true + true @@ -2901,6 +2961,7 @@ true true true + true @@ -3330,6 +3391,7 @@ true true true + true true @@ -3369,6 +3431,7 @@ true true true + true true @@ -3408,6 +3471,7 @@ true true true + true @@ -3473,6 +3537,7 @@ true true true + true @@ -3553,6 +3618,7 @@ true true true + true @@ -3652,6 +3718,7 @@ true true true + true @@ -3839,6 +3906,7 @@ true true true + true true @@ -3878,6 +3946,7 @@ true true true + true true @@ -3917,6 +3986,7 @@ true true true + true diff --git a/Minecraft.World/Packet.cpp b/Minecraft.World/Packet.cpp index 0f530ef1..0dbaf793 100644 --- a/Minecraft.World/Packet.cpp +++ b/Minecraft.World/Packet.cpp @@ -269,11 +269,11 @@ shared_ptr Packet::getPacket(int id) { // 4J: Removed try/catch #ifdef __PS3__ - boost::unordered_map::iterator it = idToCreateMap.find(id); -#else - auto it = idToCreateMap.find(id); -#endif - if (it == idToCreateMap.end()) return shared_ptr(); + boost::unordered_map::iterator it = idToCreateMap.find(id); +#else + auto it = idToCreateMap.find(id); +#endif + if (it == idToCreateMap.end()) return shared_ptr(); return it->second(); } @@ -333,6 +333,8 @@ shared_ptr Packet::readPacket(DataInputStream *dis, bool isServer) // th id = dis->read(); if (id == -1) return nullptr; + app.DebugPrintf("GOT PACKET ID: %d", id); + if ((isServer && serverReceivedPackets.find(id) == serverReceivedPackets.end()) || (!isServer && clientReceivedPackets.find(id) == clientReceivedPackets.end())) { //app.DebugPrintf("Bad packet id %d\n", id); diff --git a/Minecraft.World/PreLoginPacket.cpp b/Minecraft.World/PreLoginPacket.cpp index b357e5a0..651c36b2 100644 --- a/Minecraft.World/PreLoginPacket.cpp +++ b/Minecraft.World/PreLoginPacket.cpp @@ -75,7 +75,7 @@ void PreLoginPacket::read(DataInputStream *dis) //throws IOException { m_szUniqueSaveName[i]=dis->readByte(); } - m_szUniqueSaveName[m_iSaveNameLen - 1] = 0; + // m_szUniqueSaveName[m_iSaveNameLen - 1] = 0; // LCEMP does this but I have no idea why, TODO: why? m_serverSettings = dis->readInt(); m_hostIndex = dis->readByte(); diff --git a/MinecraftConsoles.sln b/MinecraftConsoles.sln index 876d806f..36f4924b 100644 --- a/MinecraftConsoles.sln +++ b/MinecraftConsoles.sln @@ -7,47 +7,74 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Minecraft.Client", "Minecra {F046C3CE-9749-4823-B32B-D9CC10B1A2C8} = {F046C3CE-9749-4823-B32B-D9CC10B1A2C8} EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Minecraft.Server", "Minecraft.Server\Minecraft.Server.vcxproj", "{FF2DDBF5-8A27-417A-A01C-B98A25C59760}" + ProjectSection(ProjectDependencies) = postProject + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3} = {1B9A8C38-DD48-448C-AA24-E1A35E0089A3} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution ContentPackage_NO_TU|Durango = ContentPackage_NO_TU|Durango + ContentPackage_NO_TU|Mixed Platforms = ContentPackage_NO_TU|Mixed Platforms ContentPackage_NO_TU|ORBIS = ContentPackage_NO_TU|ORBIS ContentPackage_NO_TU|PS3 = ContentPackage_NO_TU|PS3 ContentPackage_NO_TU|PSVita = ContentPackage_NO_TU|PSVita ContentPackage_NO_TU|Windows64 = ContentPackage_NO_TU|Windows64 + ContentPackage_NO_TU|x64 = ContentPackage_NO_TU|x64 ContentPackage_NO_TU|Xbox 360 = ContentPackage_NO_TU|Xbox 360 CONTENTPACKAGE_SYMBOLS|Durango = CONTENTPACKAGE_SYMBOLS|Durango + CONTENTPACKAGE_SYMBOLS|Mixed Platforms = CONTENTPACKAGE_SYMBOLS|Mixed Platforms CONTENTPACKAGE_SYMBOLS|ORBIS = CONTENTPACKAGE_SYMBOLS|ORBIS CONTENTPACKAGE_SYMBOLS|PS3 = CONTENTPACKAGE_SYMBOLS|PS3 CONTENTPACKAGE_SYMBOLS|PSVita = CONTENTPACKAGE_SYMBOLS|PSVita CONTENTPACKAGE_SYMBOLS|Windows64 = CONTENTPACKAGE_SYMBOLS|Windows64 + CONTENTPACKAGE_SYMBOLS|x64 = CONTENTPACKAGE_SYMBOLS|x64 CONTENTPACKAGE_SYMBOLS|Xbox 360 = CONTENTPACKAGE_SYMBOLS|Xbox 360 ContentPackage|Durango = ContentPackage|Durango + ContentPackage|Mixed Platforms = ContentPackage|Mixed Platforms ContentPackage|ORBIS = ContentPackage|ORBIS ContentPackage|PS3 = ContentPackage|PS3 ContentPackage|PSVita = ContentPackage|PSVita ContentPackage|Windows64 = ContentPackage|Windows64 + ContentPackage|x64 = ContentPackage|x64 ContentPackage|Xbox 360 = ContentPackage|Xbox 360 Debug|Durango = Debug|Durango + Debug|Mixed Platforms = Debug|Mixed Platforms Debug|ORBIS = Debug|ORBIS Debug|PS3 = Debug|PS3 Debug|PSVita = Debug|PSVita Debug|Windows64 = Debug|Windows64 + Debug|x64 = Debug|x64 Debug|Xbox 360 = Debug|Xbox 360 Release|Durango = Release|Durango + Release|Mixed Platforms = Release|Mixed Platforms Release|ORBIS = Release|ORBIS Release|PS3 = Release|PS3 Release|PSVita = Release|PSVita Release|Windows64 = Release|Windows64 + Release|x64 = Release|x64 Release|Xbox 360 = Release|Xbox 360 ReleaseForArt|Durango = ReleaseForArt|Durango + ReleaseForArt|Mixed Platforms = ReleaseForArt|Mixed Platforms ReleaseForArt|ORBIS = ReleaseForArt|ORBIS ReleaseForArt|PS3 = ReleaseForArt|PS3 ReleaseForArt|PSVita = ReleaseForArt|PSVita ReleaseForArt|Windows64 = ReleaseForArt|Windows64 + ReleaseForArt|x64 = ReleaseForArt|x64 ReleaseForArt|Xbox 360 = ReleaseForArt|Xbox 360 + Server|Durango = Server|Durango + Server|Mixed Platforms = Server|Mixed Platforms + Server|ORBIS = Server|ORBIS + Server|PS3 = Server|PS3 + Server|PSVita = Server|PSVita + Server|Windows64 = Server|Windows64 + Server|x64 = Server|x64 + Server|Xbox 360 = Server|Xbox 360 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|Durango.ActiveCfg = ContentPackage_NO_TU|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|Mixed Platforms.ActiveCfg = ContentPackage_NO_TU|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|Mixed Platforms.Build.0 = ContentPackage_NO_TU|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|ORBIS.ActiveCfg = ContentPackage_NO_TU|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|ORBIS.Build.0 = ContentPackage_NO_TU|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|PS3.ActiveCfg = ContentPackage_NO_TU|PS3 @@ -56,9 +83,13 @@ Global {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|PSVita.Build.0 = ContentPackage_NO_TU|PSVita {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|Windows64.ActiveCfg = ContentPackage_NO_TU|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|Windows64.Build.0 = ContentPackage_NO_TU|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|x64.ActiveCfg = ContentPackage_NO_TU|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|x64.Build.0 = ContentPackage_NO_TU|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|Xbox 360.ActiveCfg = ContentPackage_NO_TU|Xbox 360 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage_NO_TU|Xbox 360.Build.0 = ContentPackage_NO_TU|Xbox 360 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|Durango.ActiveCfg = CONTENTPACKAGE_SYMBOLS|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|Mixed Platforms.ActiveCfg = CONTENTPACKAGE_SYMBOLS|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|Mixed Platforms.Build.0 = CONTENTPACKAGE_SYMBOLS|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|ORBIS.ActiveCfg = CONTENTPACKAGE_SYMBOLS|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|ORBIS.Build.0 = CONTENTPACKAGE_SYMBOLS|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|PS3.ActiveCfg = CONTENTPACKAGE_SYMBOLS|PS3 @@ -67,9 +98,13 @@ Global {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|PSVita.Build.0 = CONTENTPACKAGE_SYMBOLS|PSVita {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|Windows64.ActiveCfg = CONTENTPACKAGE_SYMBOLS|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|Windows64.Build.0 = CONTENTPACKAGE_SYMBOLS|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|x64.ActiveCfg = CONTENTPACKAGE_SYMBOLS|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|x64.Build.0 = CONTENTPACKAGE_SYMBOLS|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|Xbox 360.ActiveCfg = CONTENTPACKAGE_SYMBOLS|Xbox 360 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.CONTENTPACKAGE_SYMBOLS|Xbox 360.Build.0 = CONTENTPACKAGE_SYMBOLS|Xbox 360 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|Durango.ActiveCfg = ContentPackage|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|Mixed Platforms.ActiveCfg = ContentPackage|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|Mixed Platforms.Build.0 = ContentPackage|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|ORBIS.ActiveCfg = ContentPackage|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|ORBIS.Build.0 = ContentPackage|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|PS3.ActiveCfg = ContentPackage|PS3 @@ -78,9 +113,14 @@ Global {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|PSVita.Build.0 = ContentPackage|PSVita {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|Windows64.ActiveCfg = ContentPackage|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|Windows64.Build.0 = ContentPackage|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|x64.ActiveCfg = ContentPackage|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|x64.Build.0 = ContentPackage|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|Xbox 360.ActiveCfg = ContentPackage|Xbox 360 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ContentPackage|Xbox 360.Build.0 = ContentPackage|Xbox 360 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|Durango.ActiveCfg = Debug|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|Durango.Build.0 = Debug|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|Mixed Platforms.Build.0 = Debug|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|ORBIS.ActiveCfg = Debug|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|ORBIS.Build.0 = Debug|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|PS3.ActiveCfg = Debug|PS3 @@ -89,9 +129,13 @@ Global {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|PSVita.Build.0 = Debug|PSVita {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|Windows64.ActiveCfg = Debug|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|Windows64.Build.0 = Debug|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|x64.ActiveCfg = Debug|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|x64.Build.0 = Debug|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|Xbox 360.ActiveCfg = Debug|Xbox 360 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Debug|Xbox 360.Build.0 = Debug|Xbox 360 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|Durango.ActiveCfg = Release|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|Mixed Platforms.ActiveCfg = Release|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|Mixed Platforms.Build.0 = Release|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|ORBIS.ActiveCfg = Release|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|ORBIS.Build.0 = Release|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|PS3.ActiveCfg = Release|PS3 @@ -100,9 +144,13 @@ Global {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|PSVita.Build.0 = Release|PSVita {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|Windows64.ActiveCfg = Release|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|Windows64.Build.0 = Release|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|x64.ActiveCfg = Release|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|x64.Build.0 = Release|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|Xbox 360.ActiveCfg = Release|Xbox 360 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Release|Xbox 360.Build.0 = Release|Xbox 360 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|Durango.ActiveCfg = ReleaseForArt|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|Mixed Platforms.ActiveCfg = ReleaseForArt|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|Mixed Platforms.Build.0 = ReleaseForArt|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|ORBIS.ActiveCfg = ReleaseForArt|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|ORBIS.Build.0 = ReleaseForArt|ORBIS {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|PS3.ActiveCfg = ReleaseForArt|x64 @@ -110,9 +158,29 @@ Global {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|PSVita.Build.0 = ReleaseForArt|PSVita {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|Windows64.ActiveCfg = ReleaseForArt|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|Windows64.Build.0 = ReleaseForArt|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|x64.ActiveCfg = ReleaseForArt|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|x64.Build.0 = ReleaseForArt|x64 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|Xbox 360.ActiveCfg = ReleaseForArt|Xbox 360 {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.ReleaseForArt|Xbox 360.Build.0 = ReleaseForArt|Xbox 360 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|Durango.ActiveCfg = Server|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|Durango.Build.0 = Server|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|Mixed Platforms.ActiveCfg = Server|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|Mixed Platforms.Build.0 = Server|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|ORBIS.ActiveCfg = Server|ORBIS + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|ORBIS.Build.0 = Server|ORBIS + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|PS3.ActiveCfg = Server|PS3 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|PS3.Build.0 = Server|PS3 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|PSVita.ActiveCfg = Server|PSVita + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|PSVita.Build.0 = Server|PSVita + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|Windows64.ActiveCfg = Release|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|Windows64.Build.0 = Release|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|x64.ActiveCfg = Server|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|x64.Build.0 = Server|x64 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|Xbox 360.ActiveCfg = Server|Xbox 360 + {F046C3CE-9749-4823-B32B-D9CC10B1A2C8}.Server|Xbox 360.Build.0 = Server|Xbox 360 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|Durango.ActiveCfg = ContentPackage_NO_TU|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|Mixed Platforms.ActiveCfg = ContentPackage_NO_TU|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|Mixed Platforms.Build.0 = ContentPackage_NO_TU|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|ORBIS.ActiveCfg = ContentPackage_NO_TU|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|ORBIS.Build.0 = ContentPackage_NO_TU|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|PS3.ActiveCfg = ContentPackage_NO_TU|PS3 @@ -121,9 +189,13 @@ Global {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|PSVita.Build.0 = ContentPackage_NO_TU|PSVita {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|Windows64.ActiveCfg = ContentPackage_NO_TU|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|Windows64.Build.0 = ContentPackage_NO_TU|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|x64.ActiveCfg = ContentPackage_NO_TU|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|x64.Build.0 = ContentPackage_NO_TU|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|Xbox 360.ActiveCfg = ContentPackage_NO_TU|Xbox 360 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage_NO_TU|Xbox 360.Build.0 = ContentPackage_NO_TU|Xbox 360 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|Durango.ActiveCfg = CONTENTPACKAGE_SYMBOLS|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|Mixed Platforms.ActiveCfg = CONTENTPACKAGE_SYMBOLS|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|Mixed Platforms.Build.0 = CONTENTPACKAGE_SYMBOLS|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|ORBIS.ActiveCfg = CONTENTPACKAGE_SYMBOLS|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|ORBIS.Build.0 = CONTENTPACKAGE_SYMBOLS|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|PS3.ActiveCfg = CONTENTPACKAGE_SYMBOLS|PS3 @@ -132,9 +204,13 @@ Global {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|PSVita.Build.0 = CONTENTPACKAGE_SYMBOLS|PSVita {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|Windows64.ActiveCfg = CONTENTPACKAGE_SYMBOLS|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|Windows64.Build.0 = CONTENTPACKAGE_SYMBOLS|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|x64.ActiveCfg = CONTENTPACKAGE_SYMBOLS|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|x64.Build.0 = CONTENTPACKAGE_SYMBOLS|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|Xbox 360.ActiveCfg = CONTENTPACKAGE_SYMBOLS|Xbox 360 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.CONTENTPACKAGE_SYMBOLS|Xbox 360.Build.0 = CONTENTPACKAGE_SYMBOLS|Xbox 360 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|Durango.ActiveCfg = ContentPackage|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|Mixed Platforms.ActiveCfg = ContentPackage|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|Mixed Platforms.Build.0 = ContentPackage|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|ORBIS.ActiveCfg = ContentPackage|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|ORBIS.Build.0 = ContentPackage|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|PS3.ActiveCfg = ContentPackage|PS3 @@ -143,9 +219,14 @@ Global {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|PSVita.Build.0 = ContentPackage|PSVita {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|Windows64.ActiveCfg = ContentPackage|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|Windows64.Build.0 = ContentPackage|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|x64.ActiveCfg = ContentPackage|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|x64.Build.0 = ContentPackage|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|Xbox 360.ActiveCfg = ContentPackage|Xbox 360 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ContentPackage|Xbox 360.Build.0 = ContentPackage|Xbox 360 - {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|Durango.ActiveCfg = Debug|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|Durango.ActiveCfg = Server|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|Durango.Build.0 = Server|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|Mixed Platforms.Build.0 = Debug|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|ORBIS.ActiveCfg = Debug|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|ORBIS.Build.0 = Debug|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|PS3.ActiveCfg = Debug|PS3 @@ -154,9 +235,13 @@ Global {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|PSVita.Build.0 = Debug|PSVita {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|Windows64.ActiveCfg = Debug|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|Windows64.Build.0 = Debug|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|x64.ActiveCfg = Debug|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|x64.Build.0 = Debug|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|Xbox 360.ActiveCfg = Debug|Xbox 360 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Debug|Xbox 360.Build.0 = Debug|Xbox 360 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|Durango.ActiveCfg = Release|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|Mixed Platforms.ActiveCfg = Release|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|Mixed Platforms.Build.0 = Release|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|ORBIS.ActiveCfg = Release|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|ORBIS.Build.0 = Release|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|PS3.ActiveCfg = Release|PS3 @@ -166,9 +251,13 @@ Global {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|Windows64.ActiveCfg = Release|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|Windows64.Build.0 = Release|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|Windows64.Deploy.0 = Release|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|x64.ActiveCfg = Release|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|x64.Build.0 = Release|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|Xbox 360.ActiveCfg = Release|Xbox 360 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Release|Xbox 360.Build.0 = Release|Xbox 360 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|Durango.ActiveCfg = ReleaseForArt|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|Mixed Platforms.ActiveCfg = ReleaseForArt|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|Mixed Platforms.Build.0 = ReleaseForArt|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|ORBIS.ActiveCfg = ReleaseForArt|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|ORBIS.Build.0 = ReleaseForArt|ORBIS {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|PS3.ActiveCfg = ReleaseForArt|x64 @@ -176,8 +265,98 @@ Global {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|PSVita.Build.0 = ReleaseForArt|PSVita {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|Windows64.ActiveCfg = ReleaseForArt|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|Windows64.Build.0 = ReleaseForArt|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|x64.ActiveCfg = ReleaseForArt|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|x64.Build.0 = ReleaseForArt|x64 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|Xbox 360.ActiveCfg = ReleaseForArt|Xbox 360 {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.ReleaseForArt|Xbox 360.Build.0 = ReleaseForArt|Xbox 360 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|Durango.ActiveCfg = Server|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|Durango.Build.0 = Server|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|Mixed Platforms.ActiveCfg = Server|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|Mixed Platforms.Build.0 = Server|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|ORBIS.ActiveCfg = Server|ORBIS + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|ORBIS.Build.0 = Server|ORBIS + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|PS3.ActiveCfg = Server|PS3 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|PS3.Build.0 = Server|PS3 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|PSVita.ActiveCfg = Server|PSVita + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|PSVita.Build.0 = Server|PSVita + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|Windows64.ActiveCfg = Server|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|Windows64.Build.0 = Server|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|x64.ActiveCfg = Server|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|x64.Build.0 = Server|x64 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|Xbox 360.ActiveCfg = Server|Xbox 360 + {1B9A8C38-DD48-448C-AA24-E1A35E0089A3}.Server|Xbox 360.Build.0 = Server|Xbox 360 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage_NO_TU|Durango.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage_NO_TU|Mixed Platforms.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage_NO_TU|Mixed Platforms.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage_NO_TU|ORBIS.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage_NO_TU|PS3.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage_NO_TU|PSVita.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage_NO_TU|Windows64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage_NO_TU|x64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage_NO_TU|x64.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage_NO_TU|Xbox 360.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.CONTENTPACKAGE_SYMBOLS|Durango.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.CONTENTPACKAGE_SYMBOLS|Mixed Platforms.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.CONTENTPACKAGE_SYMBOLS|Mixed Platforms.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.CONTENTPACKAGE_SYMBOLS|ORBIS.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.CONTENTPACKAGE_SYMBOLS|PS3.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.CONTENTPACKAGE_SYMBOLS|PSVita.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.CONTENTPACKAGE_SYMBOLS|Windows64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.CONTENTPACKAGE_SYMBOLS|x64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.CONTENTPACKAGE_SYMBOLS|x64.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.CONTENTPACKAGE_SYMBOLS|Xbox 360.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage|Durango.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage|Mixed Platforms.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage|Mixed Platforms.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage|ORBIS.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage|PS3.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage|PSVita.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage|Windows64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage|x64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage|x64.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ContentPackage|Xbox 360.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Debug|Durango.ActiveCfg = Debug|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Debug|Durango.Build.0 = Debug|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Debug|Mixed Platforms.ActiveCfg = Debug|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Debug|Mixed Platforms.Build.0 = Debug|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Debug|ORBIS.ActiveCfg = Debug|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Debug|PS3.ActiveCfg = Debug|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Debug|PSVita.ActiveCfg = Debug|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Debug|Windows64.ActiveCfg = Debug|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Debug|x64.ActiveCfg = Debug|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Debug|x64.Build.0 = Debug|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Debug|Xbox 360.ActiveCfg = Debug|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Release|Durango.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Release|Mixed Platforms.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Release|Mixed Platforms.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Release|ORBIS.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Release|PS3.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Release|PSVita.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Release|Windows64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Release|x64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Release|x64.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Release|Xbox 360.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ReleaseForArt|Durango.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ReleaseForArt|Mixed Platforms.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ReleaseForArt|Mixed Platforms.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ReleaseForArt|ORBIS.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ReleaseForArt|PS3.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ReleaseForArt|PSVita.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ReleaseForArt|Windows64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ReleaseForArt|x64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ReleaseForArt|x64.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.ReleaseForArt|Xbox 360.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Server|Durango.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Server|Mixed Platforms.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Server|Mixed Platforms.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Server|ORBIS.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Server|PS3.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Server|PSVita.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Server|Windows64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Server|Windows64.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Server|x64.ActiveCfg = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Server|x64.Build.0 = Release|x64 + {FF2DDBF5-8A27-417A-A01C-B98A25C59760}.Server|Xbox 360.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE