diff options
author | Tiger Wang <ziwei.tiger@hotmail.co.uk> | 2014-05-04 15:15:10 +0200 |
---|---|---|
committer | Tiger Wang <ziwei.tiger@hotmail.co.uk> | 2014-05-04 15:15:10 +0200 |
commit | 0d1804e439ec0028be4d5380e66c13481781fc57 (patch) | |
tree | 95d4c3ab397568f36832cf27cf98c351e9d22e4d | |
parent | Fixed lever and button powering direction (diff) | |
parent | Fixed pressure plate oversights (diff) | |
download | cuberite-0d1804e439ec0028be4d5380e66c13481781fc57.tar cuberite-0d1804e439ec0028be4d5380e66c13481781fc57.tar.gz cuberite-0d1804e439ec0028be4d5380e66c13481781fc57.tar.bz2 cuberite-0d1804e439ec0028be4d5380e66c13481781fc57.tar.lz cuberite-0d1804e439ec0028be4d5380e66c13481781fc57.tar.xz cuberite-0d1804e439ec0028be4d5380e66c13481781fc57.tar.zst cuberite-0d1804e439ec0028be4d5380e66c13481781fc57.zip |
145 files changed, 4081 insertions, 2323 deletions
diff --git a/.gitmodules b/.gitmodules index 2c7ec209b..2aaee3624 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,4 +9,4 @@ url = https://github.com/bearbin/transapi.git [submodule "lib/polarssl"] path = lib/polarssl - url = https://github.com/polarssl/polarssl + url = https://github.com/mc-server/polarssl diff --git a/COMPILING.md b/COMPILING.md index 139f1a0ee..ea6b580d5 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -45,7 +45,30 @@ It is possible to use an external profiler to learn more about how the code perf There's a script file, `MCServer/profile_run.cmd` that encapsulates most of the profiling work, have a look at the comments at the top of that script for details on how to get it to work. You'll need to change to a profiled configuration (both debug and release can be profiled). -## Linux, MacOS, FreeBSD etc. ## +## OSX ## +Install git from its [website](http://git-scm.com) or homebrew: `brew install git`. + +Install Xcode (commandline tools are recommended) from the App Store or https://developer.apple.com/downloads. + +Install CMake from its [website](http://cmake.org) or homebrew: `brew install cmake`. + +### Getting the sources ### +``` +mkdir MCServer +cd MCServer +git clone https://github.com/mc-server/MCServer.git . +git submodule init +git submodule update +``` + +### Building ### + +Follow the instructions at [CMake on Unix-based platforms](#cmake-on-unix-based-platforms), using Xcode as cmake's generator. If no generator is specified, CMake will use the Makefile generator, in which case you must build with the `make` command. + +After doing so, run the command `xcodebuild lib/polarssl/POLARSSL.xcodeproj` in the build directory, in order to build polarssl, a library that is required by MCServer. Lastly, run the command `xcodebuild` to build MCServer. Optionally, you may open the project files for polarssl and then MCServer in Xcode and build there. + + +## Linux, FreeBSD etc. ## Install git, cmake and gcc or clang, using your platform's package manager: ``` @@ -61,6 +84,14 @@ git submodule init git submodule update ``` +### Building ### + +Follow the instructions at [CMake on Unix-based platforms](#cmake-on-unix-based-platforms). + +After doing so, run the command `make` in the build directory, and MCServer will build. + +## CMake on Unix-based platforms ### + ### Release Mode ### Release mode is preferred for almost all cases, it has much better speed and less console spam. However, if you are developing MCServer actively, debug mode might be better. @@ -69,8 +100,10 @@ Assuming you are in the MCServer folder created in the initial setup step, you n ``` mkdir Release cd Release -cmake -DCMAKE_BUILD_TYPE=RELEASE .. && make +cmake -DCMAKE_BUILD_TYPE=RELEASE .. ``` +NOTE: CMake can generate project files for many different programs, such as Xcode, eclipse, and ninja. To use a different generator, first type `cmake --help`, and at the end, cmake will output the different generators that are available. To specify one, add `-G` followed by the name of the generator, in the `cmake` command. Note that the name is case-sensitive. + The executable will be built in the `MCServer/MCServer` folder and will be named `MCServer`. ### Debug Mode ### @@ -81,8 +114,10 @@ Assuming you are in the MCServer folder created in the Getting the sources step, ``` mkdir Debug cd Debug -cmake -DCMAKE_BUILD_TYPE=DEBUG .. && make +cmake -DCMAKE_BUILD_TYPE=DEBUG .. ``` +NOTE: CMake can generate project files for many different programs, such as Xcode, eclipse, and ninja. To use a different generator, first type `cmake --help`, and at the end, cmake will output the different generators that are available. To specify one, add `-G` followed by the name of the generator, in the `cmake` command. Note that the name is case-sensitive. + The executable will be built in the `MCServer/MCServer` folder and will be named `MCServer_debug`. ### 32 Bit Mode switch ### diff --git a/CoverityModel.cpp b/CoverityModel.cpp new file mode 100644 index 000000000..3ee6447ee --- /dev/null +++ b/CoverityModel.cpp @@ -0,0 +1,17 @@ + + +extern "C" { + struct lua_State; + struct tolua_Error + { + int index; + int array; + const char* type; + }; + + void tolua_error (lua_State* L, const char* msg, tolua_Error* err) + { + __coverity_panic__(); + } + +} diff --git a/MCServer/Plugins/APIDump/Hooks/OnDisconnect.lua b/MCServer/Plugins/APIDump/Hooks/OnDisconnect.lua index a3301a8c6..204cb63d2 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnDisconnect.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnDisconnect.lua @@ -2,23 +2,33 @@ return { HOOK_DISCONNECT = { - CalledWhen = "A player has explicitly disconnected.", + CalledWhen = [[ + A client has disconnected, either by explicitly sending the disconnect packet (in older protocols) or + their connection was terminated + ]], DefaultFnName = "OnDisconnect", -- also used as pagename Desc = [[ - This hook is called when a client is about to be disconnected from the server, for whatever reason. - - <p><b>Note that this hook will be removed after <1.7 protocol support is removed, as it was originally a hook for - the client sending the server a disconnect packet, which no longer happens.</b></p> + This hook is called when a client has disconnected from the server, for whatever reason. It is also + called when the client sends the Disconnect packet (only in pre-1.7 protocols). This hook is not called + for server ping connections.</p> + <p> + Note that the hook is called even for connections to players who failed to auth. In such a case there's + no {{cPlayer}} object associated with the client.</p> + <p> + See also the {{OnHandshake|HOOK_HANDSHAKE}} hook which is called when the client connects (and presents + a handshake message, so that they are not just status-pinging). If you need to store a per-player + object, use the {{OnPlayerJoined|HOOK_PLAYER_JOINED}} and {{OnPlayerDestroyed|HOOK_PLAYER_DESTROYED}} + hooks instead, those are guaranteed to have the {{cPlayer}} object associated. ]], Params = { - { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who has disconnected" }, + { Name = "Client", Type = "{{cClientHandle}}", Notes = "The client who has disconnected" }, { Name = "Reason", Type = "string", Notes = "The reason that the client has sent in the disconnect packet" }, }, Returns = [[ If the function returns false or no value, MCServer calls other plugins' callbacks for this event. If the function returns true, no other plugins are called for this event. In either case, - the player is disconnected. + the client is disconnected. ]], }, -- HOOK_DISCONNECT } diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua index 52199740b..a25bab9cf 100644 --- a/MCServer/Plugins/APIDump/main_APIDump.lua +++ b/MCServer/Plugins/APIDump/main_APIDump.lua @@ -27,10 +27,14 @@ local function LoadAPIFiles(a_Folder, a_DstTable) -- We only want .lua files from the folder: if (cFile:IsFile(FileName) and fnam:match(".*%.lua$")) then local TablesFn, Err = loadfile(FileName); - if (TablesFn == nil) then + if (type(TablesFn) ~= "function") then LOGWARNING("Cannot load API descriptions from " .. FileName .. ", Lua error '" .. Err .. "'."); else local Tables = TablesFn(); + if (type(Tables) ~= "table") then + LOGWARNING("Cannot load API descriptions from " .. FileName .. ", returned object is not a table (" .. type(Tables) .. ")."); + break + end for k, cls in pairs(Tables) do a_DstTable[k] = cls; end diff --git a/Tools/ProtoProxy/CMakeLists.txt b/Tools/ProtoProxy/CMakeLists.txt index 01f1e88ad..a136c95df 100644 --- a/Tools/ProtoProxy/CMakeLists.txt +++ b/Tools/ProtoProxy/CMakeLists.txt @@ -36,14 +36,24 @@ set(SHARED_SRC ../../src/StringUtils.cpp ../../src/Log.cpp ../../src/MCLogger.cpp - ../../src/Crypto.cpp + ../../src/PolarSSL++/AesCfb128Decryptor.cpp + ../../src/PolarSSL++/AesCfb128Encryptor.cpp + ../../src/PolarSSL++/CtrDrbgContext.cpp + ../../src/PolarSSL++/EntropyContext.cpp + ../../src/PolarSSL++/PublicKey.cpp + ../../src/PolarSSL++/RsaPrivateKey.cpp ) set(SHARED_HDR ../../src/ByteBuffer.h ../../src/StringUtils.h ../../src/Log.h ../../src/MCLogger.h - ../../src/Crypto.h + ../../src/PolarSSL++/AesCfb128Decryptor.h + ../../src/PolarSSL++/AesCfb128Encryptor.h + ../../src/PolarSSL++/CtrDrbgContext.h + ../../src/PolarSSL++/EntropyContext.h + ../../src/PolarSSL++/PublicKey.h + ../../src/PolarSSL++/RsaPrivateKey.h ) set(SHARED_OSS_SRC ../../src/OSSupport/CriticalSection.cpp diff --git a/Tools/ProtoProxy/Connection.cpp b/Tools/ProtoProxy/Connection.cpp index b21d2ae59..26aed2206 100644 --- a/Tools/ProtoProxy/Connection.cpp +++ b/Tools/ProtoProxy/Connection.cpp @@ -7,6 +7,7 @@ #include "Connection.h" #include "Server.h" #include <iostream> +#include "PolarSSL++/PublicKey.h" #ifdef _WIN32 #include <direct.h> // For _mkdir() @@ -471,7 +472,7 @@ bool cConnection::SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a -bool cConnection::SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer) +bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer) { DataLog(a_Data, a_Size, "Encrypting %d bytes to %s", a_Size, a_Peer); const Byte * Data = (const Byte *)a_Data; @@ -495,7 +496,7 @@ bool cConnection::SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryp -bool cConnection::SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer) +bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer) { AString All; a_Data.ReadAll(All); diff --git a/Tools/ProtoProxy/Connection.h b/Tools/ProtoProxy/Connection.h index 9e04994b7..1fc9536de 100644 --- a/Tools/ProtoProxy/Connection.h +++ b/Tools/ProtoProxy/Connection.h @@ -11,6 +11,8 @@ #include "ByteBuffer.h" #include "OSSupport/Timer.h" +#include "PolarSSL++/AesCfb128Decryptor.h" +#include "PolarSSL++/AesCfb128Encryptor.h" @@ -66,8 +68,8 @@ protected: cByteBuffer m_ClientBuffer; cByteBuffer m_ServerBuffer; - cAESCFBDecryptor m_ServerDecryptor; - cAESCFBEncryptor m_ServerEncryptor; + cAesCfb128Decryptor m_ServerDecryptor; + cAesCfb128Encryptor m_ServerEncryptor; AString m_ServerEncryptionBuffer; // Buffer for the data to be sent to the server once encryption is established @@ -109,10 +111,10 @@ protected: bool SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer); /// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false - bool SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer); + bool SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer); /// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false - bool SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer); + bool SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer); /// Decodes packets coming from the client, sends appropriate counterparts to the server; returns false if the connection is to be dropped bool DecodeClientsPackets(const char * a_Data, int a_Size); diff --git a/Tools/ProtoProxy/Globals.h b/Tools/ProtoProxy/Globals.h index e2f5aa860..54e7e9251 100644 --- a/Tools/ProtoProxy/Globals.h +++ b/Tools/ProtoProxy/Globals.h @@ -216,6 +216,20 @@ typedef unsigned char Byte; // Pretty much the same as ASSERT() but stays in Release builds #define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) ) +// Allow both Older versions of MSVC and newer versions of everything use a shared_ptr: +// Note that we cannot typedef, because C++ doesn't allow (partial) templates to be typedeffed. +#if (defined(_MSC_VER) && (_MSC_VER < 1600)) + // MSVC before 2010 doesn't have std::shared_ptr, but has std::tr1::shared_ptr, defined in <memory> included earlier + #define SharedPtr std::tr1::shared_ptr +#elif (__cplusplus >= 201103L) + // C++11 has std::shared_ptr in <memory>, included earlier + #define SharedPtr std::shared_ptr +#else + // C++03 has std::tr1::shared_ptr in <tr1/memory> + #include <tr1/memory> + #define SharedPtr std::tr1::shared_ptr +#endif + @@ -232,12 +246,6 @@ public: -#include "../../src/Crypto.h" - - - - - #define LOGERROR printf #define LOGINFO printf #define LOGWARNING printf diff --git a/Tools/ProtoProxy/ProtoProxy.txt b/Tools/ProtoProxy/ProtoProxy.txt index e25d513f3..ee52f393e 100644 --- a/Tools/ProtoProxy/ProtoProxy.txt +++ b/Tools/ProtoProxy/ProtoProxy.txt @@ -20,7 +20,7 @@ You need to set the server *not* to verify usernames ("online-mode=false" in ser ProtoProxy is not much dependent on the protocol - it will work with unknown packets, it just won't parse them into human-readable format. -The latest protocol which has been tested is 1.6.1 (#73). +The latest protocol which has been tested is 1.7.9 (#5). */ diff --git a/Tools/ProtoProxy/Server.h b/Tools/ProtoProxy/Server.h index 85f817a4d..8adc7093d 100644 --- a/Tools/ProtoProxy/Server.h +++ b/Tools/ProtoProxy/Server.h @@ -9,6 +9,8 @@ #pragma once +#include "PolarSSL++/RsaPrivateKey.h" + @@ -17,7 +19,7 @@ class cServer { SOCKET m_ListenSocket; - cRSAPrivateKey m_PrivateKey; + cRsaPrivateKey m_PrivateKey; AString m_PublicKeyDER; short m_ConnectPort; @@ -27,7 +29,7 @@ public: int Init(short a_ListenPort, short a_ConnectPort); void Run(void); - cRSAPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } + cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } const AString & GetPublicKeyDER (void) { return m_PublicKeyDER; } short GetConnectPort(void) const { return m_ConnectPort; } diff --git a/lib/polarssl b/lib/polarssl -Subproject 2cb1a0c4009ecf368ecc74eb428394e10f9e6d0 +Subproject 1ed82759c68f92c4acc7e3f33b850cf9f01c8ab diff --git a/src/Bindings/LuaChunkStay.cpp b/src/Bindings/LuaChunkStay.cpp index db865cfa4..985a18a95 100644 --- a/src/Bindings/LuaChunkStay.cpp +++ b/src/Bindings/LuaChunkStay.cpp @@ -42,7 +42,7 @@ bool cLuaChunkStay::AddChunks(int a_ChunkCoordTableStackPos) // Add each set of coords: int NumChunks = luaL_getn(L, a_ChunkCoordTableStackPos); - m_Chunks.reserve(NumChunks); + m_Chunks.reserve((size_t)NumChunks); for (int idx = 1; idx <= NumChunks; idx++) { // Push the idx-th element of the array onto stack top, check that it's a table: diff --git a/src/Bindings/LuaFunctions.h b/src/Bindings/LuaFunctions.h index 4f9eab86d..629e2d77d 100644 --- a/src/Bindings/LuaFunctions.h +++ b/src/Bindings/LuaFunctions.h @@ -4,12 +4,12 @@ #include <time.h> // tolua_begin -unsigned int GetTime() +inline unsigned int GetTime() { return (unsigned int)time(0); } -std::string GetChar( std::string & a_Str, unsigned int a_Idx ) +inline std::string GetChar( std::string & a_Str, unsigned int a_Idx ) { return std::string(1, a_Str[ a_Idx ]); } diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 92b410481..10e560ac0 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -37,7 +37,7 @@ /**************************** * Better error reporting for Lua **/ -int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError) +static int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError) { // Retrieve current function name lua_Debug entry; @@ -57,7 +57,7 @@ int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaErro -int lua_do_error(lua_State* L, const char * a_pFormat, ...) +static int lua_do_error(lua_State* L, const char * a_pFormat, ...) { // Retrieve current function name lua_Debug entry; @@ -235,7 +235,7 @@ static int tolua_Base64Decode(lua_State * tolua_S) -cPluginLua * GetLuaPlugin(lua_State * L) +static cPluginLua * GetLuaPlugin(lua_State * L) { // Get the plugin identification out of LuaState: lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME); @@ -1750,7 +1750,6 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S) { return 0; } - cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin); // Read the params: cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, NULL); @@ -1760,8 +1759,12 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S) L.LogStackTrace(); return 0; } + + cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin); + if (!ChunkStay->AddChunks(2)) { + delete ChunkStay; return 0; } @@ -1773,20 +1776,20 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S) -static int tolua_cPlayer_GetGroups(lua_State* tolua_S) +static int tolua_cPlayer_GetGroups(lua_State * tolua_S) { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL); + cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL); const cPlayer::GroupList & AllGroups = self->GetGroups(); - lua_createtable(tolua_S, AllGroups.size(), 0); + lua_createtable(tolua_S, (int)AllGroups.size(), 0); int newTable = lua_gettop(tolua_S); int index = 1; cPlayer::GroupList::const_iterator iter = AllGroups.begin(); - while(iter != AllGroups.end()) + while (iter != AllGroups.end()) { - const cGroup* Group = *iter; - tolua_pushusertype( tolua_S, (void*)Group, "const cGroup" ); + const cGroup * Group = *iter; + tolua_pushusertype(tolua_S, (void *)Group, "const cGroup"); lua_rawseti(tolua_S, newTable, index); ++iter; ++index; @@ -1798,20 +1801,20 @@ static int tolua_cPlayer_GetGroups(lua_State* tolua_S) -static int tolua_cPlayer_GetResolvedPermissions(lua_State* tolua_S) +static int tolua_cPlayer_GetResolvedPermissions(lua_State * tolua_S) { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL); + cPlayer * self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL); cPlayer::StringList AllPermissions = self->GetResolvedPermissions(); - lua_createtable(tolua_S, AllPermissions.size(), 0); + lua_createtable(tolua_S, (int)AllPermissions.size(), 0); int newTable = lua_gettop(tolua_S); int index = 1; cPlayer::StringList::iterator iter = AllPermissions.begin(); - while(iter != AllPermissions.end()) + while (iter != AllPermissions.end()) { - std::string& Permission = *iter; - tolua_pushstring( tolua_S, Permission.c_str() ); + std::string & Permission = *iter; + lua_pushlstring(tolua_S, Permission.c_str(), Permission.length()); lua_rawseti(tolua_S, newTable, index); ++iter; ++index; @@ -2073,18 +2076,18 @@ static int tolua_get_HTTPRequest_FormData(lua_State* tolua_S) static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) { - cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S, 1, NULL); + cWebAdmin * self = (cWebAdmin *)tolua_tousertype(tolua_S, 1, NULL); const cWebAdmin::PluginList & AllPlugins = self->GetPlugins(); - lua_createtable(tolua_S, AllPlugins.size(), 0); + lua_createtable(tolua_S, (int)AllPlugins.size(), 0); int newTable = lua_gettop(tolua_S); int index = 1; cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin(); - while(iter != AllPlugins.end()) + while (iter != AllPlugins.end()) { - const cWebPlugin* Plugin = *iter; - tolua_pushusertype( tolua_S, (void*)Plugin, "const cWebPlugin" ); + const cWebPlugin * Plugin = *iter; + tolua_pushusertype(tolua_S, (void *)Plugin, "const cWebPlugin"); lua_rawseti(tolua_S, newTable, index); ++iter; ++index; diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index df0bd4dcc..0bd9270c4 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -56,7 +56,7 @@ public: virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0; virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) = 0; virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0; - virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) = 0; + virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index dcc816839..59708bf59 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -400,14 +400,14 @@ bool cPluginLua::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGri -bool cPluginLua::OnDisconnect(cPlayer * a_Player, const AString & a_Reason) +bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason) { cCSLock Lock(m_CriticalSection); bool res = false; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - m_LuaState.Call((int)(**itr), a_Player, a_Reason, cLuaState::Return, res); + m_LuaState.Call((int)(**itr), &a_Client, a_Reason, cLuaState::Return, res); if (res) { return true; @@ -1042,7 +1042,7 @@ bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Cha cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGIN_MESSAGE]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - m_LuaState.Call((int)(**itr), &a_Client, a_Channel, a_Message); + m_LuaState.Call((int)(**itr), &a_Client, a_Channel, a_Message, cLuaState::Return, res); if (res) { return true; diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index 59542d23a..3357dd87b 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -79,7 +79,7 @@ public: virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override; virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) override; virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override; - virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override; + virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 6a5356c0b..aaccc606d 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -442,7 +442,7 @@ bool cPluginManager::CallHookCraftingNoRecipe(const cPlayer * a_Player, const cC -bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Reason) +bool cPluginManager::CallHookDisconnect(cClientHandle & a_Client, const AString & a_Reason) { HookMap::iterator Plugins = m_Hooks.find(HOOK_DISCONNECT); if (Plugins == m_Hooks.end()) @@ -451,7 +451,7 @@ bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Re } for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) { - if ((*itr)->OnDisconnect(a_Player, a_Reason)) + if ((*itr)->OnDisconnect(a_Client, a_Reason)) { return true; } diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 512bc1351..58c1cebb4 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -172,7 +172,7 @@ public: // tolua_export bool CallHookChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ); bool CallHookCollectingPickup (cPlayer * a_Player, cPickup & a_Pickup); bool CallHookCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe); - bool CallHookDisconnect (cPlayer * a_Player, const AString & a_Reason); + bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason); bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == NULL, it is a console cmd bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); diff --git a/src/BiomeDef.cpp b/src/BiomeDef.cpp index 3fba93e8a..9852b3dd9 100644 --- a/src/BiomeDef.cpp +++ b/src/BiomeDef.cpp @@ -7,6 +7,88 @@ #include "BiomeDef.h" + + +// The "map" used for biome <-> string conversions: +static struct { + EMCSBiome m_Biome; + const char * m_String; +} g_BiomeMap[] = +{ + {biOcean, "Ocean"} , + {biPlains, "Plains"}, + {biDesert, "Desert"}, + {biExtremeHills, "ExtremeHills"}, + {biForest, "Forest"}, + {biTaiga, "Taiga"}, + {biSwampland, "Swampland"}, + {biRiver, "River"}, + {biNether, "Hell"}, + {biNether, "Nether"}, + {biEnd, "Sky"}, + {biEnd, "End"}, + {biFrozenOcean, "FrozenOcean"}, + {biFrozenRiver, "FrozenRiver"}, + {biIcePlains, "IcePlains"}, + {biIcePlains, "Tundra"}, + {biIceMountains, "IceMountains"}, + {biMushroomIsland, "MushroomIsland"}, + {biMushroomShore, "MushroomShore"}, + {biBeach, "Beach"}, + {biDesertHills, "DesertHills"}, + {biForestHills, "ForestHills"}, + {biTaigaHills, "TaigaHills"}, + {biExtremeHillsEdge, "ExtremeHillsEdge"}, + {biJungle, "Jungle"}, + {biJungleHills, "JungleHills"}, + + // Release 1.7 biomes: + {biJungleEdge, "JungleEdge"}, + {biDeepOcean, "DeepOcean"}, + {biStoneBeach, "StoneBeach"}, + {biColdBeach, "ColdBeach"}, + {biBirchForest, "BirchForest"}, + {biBirchForestHills, "BirchForestHills"}, + {biRoofedForest, "RoofedForest"}, + {biColdTaiga, "ColdTaiga"}, + {biColdTaigaHills, "ColdTaigaHills"}, + {biMegaTaiga, "MegaTaiga"}, + {biMegaTaigaHills, "MegaTaigaHills"}, + {biExtremeHillsPlus, "ExtremeHillsPlus"}, + {biSavanna, "Savanna"}, + {biSavannaPlateau, "SavannaPlateau"}, + {biMesa, "Mesa"}, + {biMesaPlateauF, "MesaPlateauF"}, + {biMesaPlateau, "MesaPlateau"}, + + // Release 1.7 variants: + {biSunflowerPlains, "SunflowerPlains"}, + {biDesertM, "DesertM"}, + {biExtremeHillsM, "ExtremeHillsM"}, + {biFlowerForest, "FlowerForest"}, + {biTaigaM, "TaigaM"}, + {biSwamplandM, "SwamplandM"}, + {biIcePlainsSpikes, "IcePlainsSpikes"}, + {biJungleM, "JungleM"}, + {biJungleEdgeM, "JungleEdgeM"}, + {biBirchForestM, "BirchForestM"}, + {biBirchForestHillsM, "BirchForestHillsM"}, + {biRoofedForestM, "RoofedForestM"}, + {biColdTaigaM, "ColdTaigaM"}, + {biMegaSpruceTaiga, "MegaSpruceTaiga"}, + {biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"}, + {biExtremeHillsPlusM, "ExtremeHillsPlusM"}, + {biSavannaM, "SavannaM"}, + {biSavannaPlateauM, "SavannaPlateauM"}, + {biMesaBryce, "MesaBryce"}, + {biMesaPlateauFM, "MesaPlateauFM"}, + {biMesaPlateauM, "MesaPlateauM"}, +} ; + + + + + EMCSBiome StringToBiome(const AString & a_BiomeString) { // If it is a number, return it: @@ -25,87 +107,11 @@ EMCSBiome StringToBiome(const AString & a_BiomeString) return biInvalidBiome; } - // Convert using the built-in map: - static struct { - EMCSBiome m_Biome; - const char * m_String; - } BiomeMap[] = - { - {biOcean, "Ocean"} , - {biPlains, "Plains"}, - {biDesert, "Desert"}, - {biExtremeHills, "ExtremeHills"}, - {biForest, "Forest"}, - {biTaiga, "Taiga"}, - {biSwampland, "Swampland"}, - {biRiver, "River"}, - {biNether, "Hell"}, - {biNether, "Nether"}, - {biEnd, "Sky"}, - {biEnd, "End"}, - {biFrozenOcean, "FrozenOcean"}, - {biFrozenRiver, "FrozenRiver"}, - {biIcePlains, "IcePlains"}, - {biIcePlains, "Tundra"}, - {biIceMountains, "IceMountains"}, - {biMushroomIsland, "MushroomIsland"}, - {biMushroomShore, "MushroomShore"}, - {biBeach, "Beach"}, - {biDesertHills, "DesertHills"}, - {biForestHills, "ForestHills"}, - {biTaigaHills, "TaigaHills"}, - {biExtremeHillsEdge, "ExtremeHillsEdge"}, - {biJungle, "Jungle"}, - {biJungleHills, "JungleHills"}, - - // Release 1.7 biomes: - {biJungleEdge, "JungleEdge"}, - {biDeepOcean, "DeepOcean"}, - {biStoneBeach, "StoneBeach"}, - {biColdBeach, "ColdBeach"}, - {biBirchForest, "BirchForest"}, - {biBirchForestHills, "BirchForestHills"}, - {biRoofedForest, "RoofedForest"}, - {biColdTaiga, "ColdTaiga"}, - {biColdTaigaHills, "ColdTaigaHills"}, - {biMegaTaiga, "MegaTaiga"}, - {biMegaTaigaHills, "MegaTaigaHills"}, - {biExtremeHillsPlus, "ExtremeHillsPlus"}, - {biSavanna, "Savanna"}, - {biSavannaPlateau, "SavannaPlateau"}, - {biMesa, "Mesa"}, - {biMesaPlateauF, "MesaPlateauF"}, - {biMesaPlateau, "MesaPlateau"}, - - // Release 1.7 variants: - {biSunflowerPlains, "SunflowerPlains"}, - {biDesertM, "DesertM"}, - {biExtremeHillsM, "ExtremeHillsM"}, - {biFlowerForest, "FlowerForest"}, - {biTaigaM, "TaigaM"}, - {biSwamplandM, "SwamplandM"}, - {biIcePlainsSpikes, "IcePlainsSpikes"}, - {biJungleM, "JungleM"}, - {biJungleEdgeM, "JungleEdgeM"}, - {biBirchForestM, "BirchForestM"}, - {biBirchForestHillsM, "BirchForestHillsM"}, - {biRoofedForestM, "RoofedForestM"}, - {biColdTaigaM, "ColdTaigaM"}, - {biMegaSpruceTaiga, "MegaSpruceTaiga"}, - {biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"}, - {biExtremeHillsPlusM, "ExtremeHillsPlusM"}, - {biSavannaM, "SavannaM"}, - {biSavannaPlateauM, "SavannaPlateauM"}, - {biMesaBryce, "MesaBryce"}, - {biMesaPlateauFM, "MesaPlateauFM"}, - {biMesaPlateauM, "MesaPlateauM"}, - } ; - - for (size_t i = 0; i < ARRAYCOUNT(BiomeMap); i++) + for (size_t i = 0; i < ARRAYCOUNT(g_BiomeMap); i++) { - if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0) + if (NoCaseCompare(g_BiomeMap[i].m_String, a_BiomeString) == 0) { - return BiomeMap[i].m_Biome; + return g_BiomeMap[i].m_Biome; } } // for i - BiomeMap[] return biInvalidBiome; @@ -115,6 +121,22 @@ EMCSBiome StringToBiome(const AString & a_BiomeString) +AString BiomeToString(int a_Biome) +{ + for (size_t i = 0; i < ARRAYCOUNT(g_BiomeMap); i++) + { + if (g_BiomeMap[i].m_Biome == a_Biome) + { + return g_BiomeMap[i].m_String; + } + } + return AString(); +} + + + + + bool IsBiomeNoDownfall(EMCSBiome a_Biome) { switch (a_Biome) diff --git a/src/BiomeDef.h b/src/BiomeDef.h index 474d4df76..67916890d 100644 --- a/src/BiomeDef.h +++ b/src/BiomeDef.h @@ -104,10 +104,13 @@ enum EMCSBiome biMaxVariantBiome = biNumVariantBiomes - 1, // The maximum biome value } ; -/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns biInvalidBiome on failure. +/** Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns biInvalidBiome on failure. */ extern EMCSBiome StringToBiome(const AString & a_BiomeString); -/// Returns true if the biome has no downfall - deserts and savannas +/** Translates biome enum into biome string. Returns empty string on failure (unknown biome). */ +extern AString BiomeToString(int a_Biome); + +/** Returns true if the biome has no downfall - deserts and savannas */ extern bool IsBiomeNoDownfall(EMCSBiome a_Biome); diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp index 40cca8882..e0ae2c5b6 100644 --- a/src/BlockArea.cpp +++ b/src/BlockArea.cpp @@ -14,17 +14,29 @@ +// Disable MSVC warnings: "conditional expression is constant" +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) +#endif + + + + + +typedef void (CombinatorFunc)(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta); + // This wild construct allows us to pass a function argument and still have it inlined by the compiler :) /// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function -template<typename Combinator> void InternalMergeBlocks( +template<bool MetasValid, CombinatorFunc Combinator> +void InternalMergeBlocks( BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes, NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas, int a_SizeX, int a_SizeY, int a_SizeZ, int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ, int a_DstOffX, int a_DstOffY, int a_DstOffZ, int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, - int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ, - Combinator a_Combinator + int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ ) { UNUSED(a_SrcSizeY); @@ -41,7 +53,15 @@ template<typename Combinator> void InternalMergeBlocks( int DstIdx = DstBaseZ + a_DstOffX; for (int x = 0; x < a_SizeX; x++) { - a_Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]); + if (MetasValid) + { + Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]); + } + else + { + BLOCKTYPE FakeDestMeta = 0; + Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], FakeDestMeta, (NIBBLETYPE)0); + } ++DstIdx; ++SrcIdx; } // for x @@ -54,10 +74,14 @@ template<typename Combinator> void InternalMergeBlocks( /// Combinator used for cBlockArea::msOverwrite merging -static inline void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +template<bool MetaValid> +void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) { a_DstType = a_SrcType; - a_DstMeta = a_SrcMeta; + if (MetaValid) + { + a_DstMeta = a_SrcMeta; + } } @@ -65,12 +89,16 @@ static inline void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_S /// Combinator used for cBlockArea::msFillAir merging -static inline void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +template<bool MetaValid> +void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) { if (a_DstType == E_BLOCK_AIR) { a_DstType = a_SrcType; - a_DstMeta = a_SrcMeta; + if (MetaValid) + { + a_DstMeta = a_SrcMeta; + } } // "else" is the default, already in place } @@ -80,12 +108,16 @@ static inline void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_Src /// Combinator used for cBlockArea::msImprint merging -static inline void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +template<bool MetaValid> +void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) { if (a_SrcType != E_BLOCK_AIR) { a_DstType = a_SrcType; - a_DstMeta = a_SrcMeta; + if (MetaValid) + { + a_DstMeta = a_SrcMeta; + } } // "else" is the default, already in place } @@ -95,7 +127,8 @@ static inline void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_Src /// Combinator used for cBlockArea::msLake merging -static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +template<bool MetaValid> +void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) { // Sponge is the NOP block if (a_SrcType == E_BLOCK_SPONGE) @@ -107,7 +140,10 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp if (a_SrcType == E_BLOCK_AIR) { a_DstType = E_BLOCK_AIR; - a_DstMeta = 0; + if (MetaValid) + { + a_DstMeta = 0; + } return; } @@ -132,7 +168,10 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp case E_BLOCK_STATIONARY_LAVA: { a_DstType = a_SrcType; - a_DstMeta = a_SrcMeta; + if (MetaValid) + { + a_DstMeta = a_SrcMeta; + } return; } } @@ -146,7 +185,10 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp case E_BLOCK_MYCELIUM: { a_DstType = E_BLOCK_STONE; - a_DstMeta = 0; + if (MetaValid) + { + a_DstMeta = 0; + } return; } } @@ -159,13 +201,17 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp /** Combinator used for cBlockArea::msSpongePrint merging */ -static inline void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +template<bool MetaValid> +void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) { // Sponge overwrites nothing, everything else overwrites anything if (a_SrcType != E_BLOCK_SPONGE) { a_DstType = a_SrcType; - a_DstMeta = a_SrcMeta; + if (MetaValid) + { + a_DstMeta = a_SrcMeta; + } } } @@ -174,17 +220,24 @@ static inline void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a /** Combinator used for cBlockArea::msDifference merging */ -static inline void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +template<bool MetaValid> +void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) { - if ((a_DstType == a_SrcType) && (a_DstMeta == a_SrcMeta)) + if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta))) { a_DstType = E_BLOCK_AIR; - a_DstMeta = 0; + if (MetaValid) + { + a_DstMeta = 0; + } } else { a_DstType = a_SrcType; - a_DstMeta = a_SrcMeta; + if (MetaValid) + { + a_DstMeta = a_SrcMeta; + } } } @@ -193,16 +246,25 @@ static inline void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_ /** Combinator used for cBlockArea::msMask merging */ -static inline void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) +template<bool MetaValid> +void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta) { // If the blocks are the same, keep the dest; otherwise replace with air - if ((a_SrcType != a_DstType) || (a_SrcMeta != a_DstMeta)) + if ((a_SrcType != a_DstType) || !MetaValid || (a_SrcMeta != a_DstMeta)) { a_DstType = E_BLOCK_AIR; - a_DstMeta = 0; + if (MetaValid) + { + a_DstMeta = 0; + } } } +// Re-enable previously disabled MSVC warnings +#ifdef _MSC_VER + #pragma warning(pop) +#endif + @@ -484,7 +546,7 @@ void cBlockArea::CopyTo(cBlockArea & a_Into) const a_Into.Clear(); a_Into.SetSize(m_Size.x, m_Size.y, m_Size.z, GetDataTypes()); a_Into.m_Origin = m_Origin; - int BlockCount = GetBlockCount(); + size_t BlockCount = GetBlockCount(); if (HasBlockTypes()) { memcpy(a_Into.m_BlockTypes, m_BlockTypes, BlockCount * sizeof(BLOCKTYPE)); @@ -532,7 +594,7 @@ void cBlockArea::DumpToRawFile(const AString & a_FileName) f.Write(&SizeZ, 4); unsigned char DataTypes = (unsigned char)GetDataTypes(); f.Write(&DataTypes, 1); - int NumBlocks = GetBlockCount(); + size_t NumBlocks = GetBlockCount(); if (HasBlockTypes()) { f.Write(m_BlockTypes, NumBlocks * sizeof(BLOCKTYPE)); @@ -637,155 +699,19 @@ void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMa void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy) { - // Block types are compulsory, block metas are voluntary - if (!HasBlockTypes() || !a_Src.HasBlockTypes()) - { - LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__); - return; - } - - // Dst is *this, Src is a_Src - int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading - int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing - int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy - - int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading - int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing - int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy - - int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading - int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing - int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas(); NIBBLETYPE * DstMetas = m_BlockMetas; + bool IsDummyMetas = ((SrcMetas == NULL) || (DstMetas == NULL)); if (IsDummyMetas) { - SrcMetas = new NIBBLETYPE[a_Src.GetBlockCount()]; - DstMetas = new NIBBLETYPE[GetBlockCount()]; + MergeByStrategy<true>(a_Src, a_RelX, a_RelY, a_RelZ, a_Strategy, SrcMetas, DstMetas); } - - switch (a_Strategy) - { - case msOverwrite: - { - InternalMergeBlocks( - m_BlockTypes, a_Src.GetBlockTypes(), - DstMetas, SrcMetas, - SizeX, SizeY, SizeZ, - SrcOffX, SrcOffY, SrcOffZ, - DstOffX, DstOffY, DstOffZ, - a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), - m_Size.x, m_Size.y, m_Size.z, - MergeCombinatorOverwrite - ); - break; - } // case msOverwrite - - case msFillAir: - { - InternalMergeBlocks( - m_BlockTypes, a_Src.GetBlockTypes(), - DstMetas, SrcMetas, - SizeX, SizeY, SizeZ, - SrcOffX, SrcOffY, SrcOffZ, - DstOffX, DstOffY, DstOffZ, - a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), - m_Size.x, m_Size.y, m_Size.z, - MergeCombinatorFillAir - ); - break; - } // case msFillAir - - case msImprint: - { - InternalMergeBlocks( - m_BlockTypes, a_Src.GetBlockTypes(), - DstMetas, SrcMetas, - SizeX, SizeY, SizeZ, - SrcOffX, SrcOffY, SrcOffZ, - DstOffX, DstOffY, DstOffZ, - a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), - m_Size.x, m_Size.y, m_Size.z, - MergeCombinatorImprint - ); - break; - } // case msImprint - - case msLake: - { - InternalMergeBlocks( - m_BlockTypes, a_Src.GetBlockTypes(), - DstMetas, SrcMetas, - SizeX, SizeY, SizeZ, - SrcOffX, SrcOffY, SrcOffZ, - DstOffX, DstOffY, DstOffZ, - a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), - m_Size.x, m_Size.y, m_Size.z, - MergeCombinatorLake - ); - break; - } // case msLake - - case msSpongePrint: - { - InternalMergeBlocks( - m_BlockTypes, a_Src.GetBlockTypes(), - DstMetas, SrcMetas, - SizeX, SizeY, SizeZ, - SrcOffX, SrcOffY, SrcOffZ, - DstOffX, DstOffY, DstOffZ, - a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), - m_Size.x, m_Size.y, m_Size.z, - MergeCombinatorSpongePrint - ); - break; - } // case msSpongePrint - - case msDifference: - { - InternalMergeBlocks( - m_BlockTypes, a_Src.GetBlockTypes(), - DstMetas, SrcMetas, - SizeX, SizeY, SizeZ, - SrcOffX, SrcOffY, SrcOffZ, - DstOffX, DstOffY, DstOffZ, - a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), - m_Size.x, m_Size.y, m_Size.z, - MergeCombinatorDifference - ); - break; - } // case msDifference - - case msMask: - { - InternalMergeBlocks( - m_BlockTypes, a_Src.GetBlockTypes(), - DstMetas, SrcMetas, - SizeX, SizeY, SizeZ, - SrcOffX, SrcOffY, SrcOffZ, - DstOffX, DstOffY, DstOffZ, - a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), - m_Size.x, m_Size.y, m_Size.z, - MergeCombinatorMask - ); - break; - } // case msMask - - default: - { - LOGWARNING("Unknown block area merge strategy: %d", a_Strategy); - ASSERT(!"Unknown block area merge strategy"); - break; - } - } // switch (a_Strategy) - - if (IsDummyMetas) + else { - delete[] SrcMetas; - delete[] DstMetas; + MergeByStrategy<false>(a_Src, a_RelX, a_RelY, a_RelZ, a_Strategy, SrcMetas, DstMetas); } } @@ -2079,7 +2005,7 @@ void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, i int NewSizeX = m_Size.x + a_SubMinX + a_AddMaxX; int NewSizeY = m_Size.y + a_SubMinY + a_AddMaxY; int NewSizeZ = m_Size.z + a_SubMinZ + a_AddMaxZ; - int BlockCount = NewSizeX * NewSizeY * NewSizeZ; + size_t BlockCount = (size_t)(NewSizeX * NewSizeY * NewSizeZ); BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[BlockCount]; memset(NewBlockTypes, 0, BlockCount * sizeof(BLOCKTYPE)); int OldIndex = 0; @@ -2109,7 +2035,7 @@ void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMa int NewSizeX = m_Size.x + a_SubMinX + a_AddMaxX; int NewSizeY = m_Size.y + a_SubMinY + a_AddMaxY; int NewSizeZ = m_Size.z + a_SubMinZ + a_AddMaxZ; - int BlockCount = NewSizeX * NewSizeY * NewSizeZ; + size_t BlockCount = (size_t)(NewSizeX * NewSizeY * NewSizeZ); NIBBLETYPE * NewNibbles = new NIBBLETYPE[BlockCount]; memset(NewNibbles, 0, BlockCount * sizeof(NIBBLETYPE)); int OldIndex = 0; @@ -2161,4 +2087,137 @@ void cBlockArea::RelSetData( +template<bool MetasValid> +void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas) +{ + // Block types are compulsory, block metas are voluntary + if (!HasBlockTypes() || !a_Src.HasBlockTypes()) + { + LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__); + return; + } + + // Dst is *this, Src is a_Src + int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading + int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing + int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy + + int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading + int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing + int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy + + int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading + int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing + int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy + + switch (a_Strategy) + { + case cBlockArea::msOverwrite: + { + InternalMergeBlocks<MetasValid, MergeCombinatorOverwrite<MetasValid> >( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_Size.x, m_Size.y, m_Size.z + ); + break; + } // case msOverwrite + + case cBlockArea::msFillAir: + { + InternalMergeBlocks<MetasValid, MergeCombinatorFillAir<MetasValid> >( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_Size.x, m_Size.y, m_Size.z + ); + break; + } // case msFillAir + + case cBlockArea::msImprint: + { + InternalMergeBlocks<MetasValid, MergeCombinatorImprint<MetasValid> >( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_Size.x, m_Size.y, m_Size.z + ); + break; + } // case msImprint + + case cBlockArea::msLake: + { + InternalMergeBlocks<MetasValid, MergeCombinatorLake<MetasValid> >( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_Size.x, m_Size.y, m_Size.z + ); + break; + } // case msLake + + case cBlockArea::msSpongePrint: + { + InternalMergeBlocks<MetasValid, MergeCombinatorSpongePrint<MetasValid> >( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_Size.x, m_Size.y, m_Size.z + ); + break; + } // case msSpongePrint + + case cBlockArea::msDifference: + { + InternalMergeBlocks<MetasValid, MergeCombinatorDifference<MetasValid> >( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_Size.x, m_Size.y, m_Size.z + ); + break; + } // case msDifference + + case cBlockArea::msMask: + { + InternalMergeBlocks<MetasValid, MergeCombinatorMask<MetasValid> >( + m_BlockTypes, a_Src.GetBlockTypes(), + DstMetas, SrcMetas, + SizeX, SizeY, SizeZ, + SrcOffX, SrcOffY, SrcOffZ, + DstOffX, DstOffY, DstOffZ, + a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(), + m_Size.x, m_Size.y, m_Size.z + ); + break; + } // case msMask + + default: + { + LOGWARNING("Unknown block area merge strategy: %d", a_Strategy); + ASSERT(!"Unknown block area merge strategy"); + break; + } + } // switch (a_Strategy) +} + + diff --git a/src/BlockArea.h b/src/BlockArea.h index c48175b8c..6dba0f12e 100644 --- a/src/BlockArea.h +++ b/src/BlockArea.h @@ -294,7 +294,7 @@ public: NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block! NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block! NIBBLETYPE * GetBlockSkyLight(void) const { return m_BlockSkyLight; } // NOTE: one byte per block! - int GetBlockCount(void) const { return m_Size.x * m_Size.y * m_Size.z; } + size_t GetBlockCount(void) const { return (size_t)(m_Size.x * m_Size.y * m_Size.z); } int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const; protected: @@ -363,6 +363,9 @@ protected: int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight ); + + template<bool MetasValid> + void MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas); // tolua_begin } ; // tolua_end diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp index 96ca0ac37..146ad915b 100644 --- a/src/BlockEntities/CommandBlockEntity.cpp +++ b/src/BlockEntities/CommandBlockEntity.cpp @@ -21,7 +21,8 @@ cCommandBlockEntity::cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) : super(E_BLOCK_COMMAND_BLOCK, a_X, a_Y, a_Z, a_World), m_ShouldExecute(false), - m_IsPowered(false) + m_IsPowered(false), + m_Result(0) {} diff --git a/src/BlockEntities/MobHeadEntity.cpp b/src/BlockEntities/MobHeadEntity.cpp index c0a1781f6..dc9c18d58 100644 --- a/src/BlockEntities/MobHeadEntity.cpp +++ b/src/BlockEntities/MobHeadEntity.cpp @@ -14,6 +14,8 @@ cMobHeadEntity::cMobHeadEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : super(E_BLOCK_HEAD, a_BlockX, a_BlockY, a_BlockZ, a_World), + m_Type(SKULL_TYPE_SKELETON), + m_Rotation(SKULL_ROTATION_NORTH), m_Owner("") { } diff --git a/src/BlockID.cpp b/src/BlockID.cpp index bf95d0798..bfe826f40 100644 --- a/src/BlockID.cpp +++ b/src/BlockID.cpp @@ -102,7 +102,7 @@ public: return true; } - a_Item.m_ItemDamage = atoi(Split[1].c_str()); + a_Item.m_ItemDamage = (short)atoi(Split[1].c_str()); if ((a_Item.m_ItemDamage == 0) && (Split[1] != "0")) { // Parsing the number failed diff --git a/src/BlockInfo.cpp b/src/BlockInfo.cpp index 6fb5aa5b3..def98fdf5 100644 --- a/src/BlockInfo.cpp +++ b/src/BlockInfo.cpp @@ -129,6 +129,7 @@ void cBlockInfo::Initialize(void) ms_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_Transparent = true; ms_Info[E_BLOCK_ICE ].m_Transparent = true; ms_Info[E_BLOCK_IRON_DOOR ].m_Transparent = true; + ms_Info[E_BLOCK_LADDER ].m_Transparent = true; ms_Info[E_BLOCK_LAVA ].m_Transparent = true; ms_Info[E_BLOCK_LEAVES ].m_Transparent = true; ms_Info[E_BLOCK_LEVER ].m_Transparent = true; diff --git a/src/Blocks/BlockFire.h b/src/Blocks/BlockFire.h index c8f158e7e..f9f32eb50 100644 --- a/src/Blocks/BlockFire.h +++ b/src/Blocks/BlockFire.h @@ -68,7 +68,6 @@ public: { return 0; } - for (int newY = Y + 1; newY < cChunkDef::Height; newY++) { @@ -84,7 +83,7 @@ public: // This is because the frame is a solid obsidian pillar if ((MaxY != 0) && (newY == Y + 1)) { - return EvaluatePortalBorder(X, newY, Z, MaxY, a_ChunkInterface); + return EvaluatePortalBorder(X, newY, Z, MaxY, a_ChunkInterface) ? -1 /* -1 = found a frame */ : 0; } else { @@ -99,18 +98,18 @@ public: } /// Evaluates if coords have a valid border on top, based on MaxY - int EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cChunkInterface & a_ChunkInterface) + bool EvaluatePortalBorder(int X, int FoundObsidianY, int Z, int MaxY, cChunkInterface & a_ChunkInterface) { for (int checkBorder = FoundObsidianY + 1; checkBorder <= MaxY - 1; checkBorder++) // FoundObsidianY + 1: FoundObsidianY has already been checked in FindObsidianCeiling; MaxY - 1: portal doesn't need corners { if (a_ChunkInterface.GetBlock(X, checkBorder, Z) != E_BLOCK_OBSIDIAN) { // Base obsidian, base + 1 obsidian, base + x NOT obsidian -> not complete portal - return 0; + return false; } } // Everything was obsidian, found a border! - return -1; // Return -1 for a frame border + return true; } /// Finds entire frame in any direction with the coordinates of a base block and fills hole with nether portal (START HERE) @@ -169,7 +168,7 @@ public: { return false; // Not valid slice, no portal can be formed } - } XZP = X1 - 1; // Set boundary of frame interior, note that for some reason, the loop of X and the loop of Z go to different numbers, hence -1 here and -2 there + } XZP = X1 - 1; // Set boundary of frame interior for (; ((a_ChunkInterface.GetBlock(X2, Y, Z) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock(X2, Y + 1, Z) == E_BLOCK_OBSIDIAN)); X2--) // Go the other direction (XM) { int Value = FindObsidianCeiling(X2, Y, Z, a_ChunkInterface, MaxY); @@ -199,13 +198,13 @@ public: if ((Value == -1) || (ValueTwo == -1)) { FoundFrameZP = true; - continue; + break; } else if ((Value != MaxY) && (ValueTwo != MaxY)) { return false; } - } XZP = Z1 - 2; + } XZP = Z1 - 1; for (; ((a_ChunkInterface.GetBlock(X, Y, Z2) == E_BLOCK_OBSIDIAN) || (a_ChunkInterface.GetBlock(X, Y + 1, Z2) == E_BLOCK_OBSIDIAN)); Z2--) { int Value = FindObsidianCeiling(X, Y, Z2, a_ChunkInterface, MaxY); @@ -213,13 +212,13 @@ public: if ((Value == -1) || (ValueTwo == -1)) { FoundFrameZM = true; - continue; + break; } else if ((Value != MaxY) && (ValueTwo != MaxY)) { return false; } - } XZM = Z2 + 2; + } XZM = Z2 + 1; return (FoundFrameZP && FoundFrameZM); } }; diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp index c634dc308..4de89f7c1 100644 --- a/src/ByteBuffer.cpp +++ b/src/ByteBuffer.cpp @@ -171,7 +171,7 @@ cByteBuffer::~cByteBuffer() -bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count) +bool cByteBuffer::Write(const void * a_Bytes, size_t a_Count) { CHECK_THREAD; CheckValid(); @@ -187,13 +187,14 @@ bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count) } ASSERT(m_BufferSize >= m_WritePos); size_t TillEnd = m_BufferSize - m_WritePos; + const char * Bytes = (const char *)a_Bytes; if (TillEnd <= a_Count) { // Need to wrap around the ringbuffer end if (TillEnd > 0) { - memcpy(m_Buffer + m_WritePos, a_Bytes, TillEnd); - a_Bytes += TillEnd; + memcpy(m_Buffer + m_WritePos, Bytes, TillEnd); + Bytes += TillEnd; a_Count -= TillEnd; WrittenBytes = TillEnd; } @@ -203,7 +204,7 @@ bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count) // We're guaranteed that we'll fit in a single write op if (a_Count > 0) { - memcpy(m_Buffer + m_WritePos, a_Bytes, a_Count); + memcpy(m_Buffer + m_WritePos, Bytes, a_Count); m_WritePos += a_Count; WrittenBytes += a_Count; } @@ -326,7 +327,7 @@ bool cByteBuffer::ReadBEShort(short & a_Value) CheckValid(); NEEDBYTES(2); ReadBuf(&a_Value, 2); - a_Value = ntohs(a_Value); + a_Value = (short)ntohs((u_short)a_Value); return true; } @@ -340,7 +341,7 @@ bool cByteBuffer::ReadBEInt(int & a_Value) CheckValid(); NEEDBYTES(4); ReadBuf(&a_Value, 4); - a_Value = ntohl(a_Value); + a_Value = (int)ntohl((u_long)a_Value); return true; } @@ -419,7 +420,7 @@ bool cByteBuffer::ReadBEUTF16String16(AString & a_Value) ASSERT(!"Negative string length? Are you sure?"); return true; } - return ReadUTF16String(a_Value, Length); + return ReadUTF16String(a_Value, (size_t)Length); } @@ -437,7 +438,7 @@ bool cByteBuffer::ReadVarInt(UInt32 & a_Value) { NEEDBYTES(1); ReadBuf(&b, 1); - Value = Value | (((Int64)(b & 0x7f)) << Shift); + Value = Value | (((UInt32)(b & 0x7f)) << Shift); Shift += 7; } while ((b & 0x80) != 0); a_Value = Value; @@ -461,7 +462,7 @@ bool cByteBuffer::ReadVarUTF8String(AString & a_Value) { LOGWARNING("%s: String too large: %u (%u KiB)", __FUNCTION__, Size, Size / 1024); } - return ReadString(a_Value, (int)Size); + return ReadString(a_Value, (size_t)Size); } @@ -516,7 +517,7 @@ bool cByteBuffer::WriteBEShort(short a_Value) CHECK_THREAD; CheckValid(); PUTBYTES(2); - short Converted = htons(a_Value); + u_short Converted = htons((u_short)a_Value); return WriteBuf(&Converted, 2); } @@ -529,7 +530,7 @@ bool cByteBuffer::WriteBEInt(int a_Value) CHECK_THREAD; CheckValid(); PUTBYTES(4); - int Converted = HostToNetwork4(&a_Value); + UInt32 Converted = HostToNetwork4(&a_Value); return WriteBuf(&Converted, 4); } @@ -542,7 +543,7 @@ bool cByteBuffer::WriteBEInt64(Int64 a_Value) CHECK_THREAD; CheckValid(); PUTBYTES(8); - Int64 Converted = HostToNetwork8(&a_Value); + UInt64 Converted = HostToNetwork8(&a_Value); return WriteBuf(&Converted, 8); } @@ -555,7 +556,7 @@ bool cByteBuffer::WriteBEFloat(float a_Value) CHECK_THREAD; CheckValid(); PUTBYTES(4); - int Converted = HostToNetwork4(&a_Value); + UInt32 Converted = HostToNetwork4(&a_Value); return WriteBuf(&Converted, 4); } @@ -568,7 +569,7 @@ bool cByteBuffer::WriteBEDouble(double a_Value) CHECK_THREAD; CheckValid(); PUTBYTES(8); - Int64 Converted = HostToNetwork8(&a_Value); + UInt64 Converted = HostToNetwork8(&a_Value); return WriteBuf(&Converted, 8); } @@ -612,7 +613,7 @@ bool cByteBuffer::WriteVarInt(UInt32 a_Value) // A 32-bit integer can be encoded by at most 5 bytes: unsigned char b[5]; - int idx = 0; + size_t idx = 0; do { b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00); @@ -631,7 +632,7 @@ bool cByteBuffer::WriteVarUTF8String(const AString & a_Value) CHECK_THREAD; CheckValid(); PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early. - bool res = WriteVarInt(a_Value.size()); + bool res = WriteVarInt((UInt32)(a_Value.size())); if (!res) { return false; @@ -756,7 +757,7 @@ bool cByteBuffer::ReadString(AString & a_String, size_t a_Count) -bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars) +bool cByteBuffer::ReadUTF16String(AString & a_String, size_t a_NumChars) { // Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String CHECK_THREAD; @@ -887,9 +888,7 @@ void cByteBuffer::AdvanceReadPos(size_t a_Count) void cByteBuffer::CheckValid(void) const { - ASSERT(m_ReadPos >= 0); ASSERT(m_ReadPos < m_BufferSize); - ASSERT(m_WritePos >= 0); ASSERT(m_WritePos < m_BufferSize); } diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h index 44f43e17f..929c93167 100644 --- a/src/ByteBuffer.h +++ b/src/ByteBuffer.h @@ -31,7 +31,7 @@ public: ~cByteBuffer(); /// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not - bool Write(const char * a_Bytes, size_t a_Count); + bool Write(const void * a_Bytes, size_t a_Count); /// Returns the number of bytes that can be successfully written to the ringbuffer size_t GetFreeSpace(void) const; @@ -101,7 +101,7 @@ public: bool ReadString(AString & a_String, size_t a_Count); /// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String - bool ReadUTF16String(AString & a_String, int a_NumChars); + bool ReadUTF16String(AString & a_String, size_t a_NumChars); /// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer bool SkipRead(size_t a_Count); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9cc5fcb1e..900577526 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,7 +5,7 @@ include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/") include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/jsoncpp/include") include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/polarssl/include") -set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating) +set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++) set(FOLDERS ${FOLDERS} WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs) @@ -57,6 +57,14 @@ if (NOT MSVC) Entities/Pickup.h Entities/Player.h Entities/ProjectileEntity.h + Entities/ArrowEntity.h + Entities/ThrownEggEntity.h + Entities/ThrownEnderPearlEntity.h + Entities/ExpBottleEntity.h + Entities/ThrownSnowballEntity.h + Entities/FireChargeEntity.h + Entities/FireworkEntity.h + Entities/GhastFireballEntity.h Entities/TNTEntity.h Entities/ExpOrb.h Entities/HangingEntity.h @@ -234,7 +242,7 @@ endif () if (NOT MSVC) target_link_libraries(${EXECUTABLE} OSSupport HTTPServer Bindings Items Blocks) target_link_libraries(${EXECUTABLE} Protocol Generating Generating_Prefabs WorldStorage) - target_link_libraries(${EXECUTABLE} Mobs Entities Simulator UI BlockEntities) + target_link_libraries(${EXECUTABLE} Mobs Entities Simulator UI BlockEntities PolarSSL++) endif () if (WIN32) target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 053648262..ca536e89a 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -380,12 +380,12 @@ void cChunk::SetLight( { // Compress blocklight m_BlockLight.clear(); - m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[m_BlockTypes.size()]); + m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[m_BlockTypes.size() / 2]); } { // Compress skylight m_BlockSkyLight.clear(); - m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_SkyLight[0], &a_SkyLight[m_BlockTypes.size()]); + m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_SkyLight[0], &a_SkyLight[m_BlockTypes.size() / 2]); } m_IsLightValid = true; diff --git a/src/ChunkDef.h b/src/ChunkDef.h index 054168bdd..83f3c8f5f 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -246,8 +246,8 @@ public: { if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1)) { - int Index = MakeIndexNoCheck(x, y, z); - if ((size_t)(Index / 2) >= a_Buffer.size()) + size_t Index = (size_t)MakeIndexNoCheck(x, y, z); + if ((Index / 2) >= a_Buffer.size()) { return (a_IsSkyLightNibble ? 0xff : 0); } @@ -281,7 +281,7 @@ public: { a_Buffer.resize((size_t)((a_BlockIdx / 2) + 1)); } - a_Buffer[(size_t)(a_BlockIdx / 2)] = PackNibble(a_Buffer, a_BlockIdx, a_Nibble); + a_Buffer[(size_t)(a_BlockIdx / 2)] = PackNibble(a_Buffer, (size_t)a_BlockIdx, a_Nibble); } @@ -297,19 +297,19 @@ public: return; } - int Index = MakeIndexNoCheck(x, y, z); - if ((size_t)(Index / 2) >= a_Buffer.size()) + size_t Index = (size_t)MakeIndexNoCheck(x, y, z); + if ((Index / 2) >= a_Buffer.size()) { - a_Buffer.resize((size_t)((Index / 2) + 1)); + a_Buffer.resize(((Index / 2) + 1)); } - a_Buffer[(size_t)(Index / 2)] = PackNibble(a_Buffer, Index, a_Nibble); + a_Buffer[(Index / 2)] = PackNibble(a_Buffer, Index, a_Nibble); } private: - inline static NIBBLETYPE PackNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int a_Index, NIBBLETYPE a_Nibble) + inline static NIBBLETYPE PackNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, size_t a_Index, NIBBLETYPE a_Nibble) { return static_cast<NIBBLETYPE>( (a_Buffer[a_Index / 2] & (0xf0 >> ((a_Index & 1) * 4))) | // The untouched nibble @@ -318,7 +318,7 @@ private: } - inline static NIBBLETYPE ExpandNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int a_Index) + inline static NIBBLETYPE ExpandNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, size_t a_Index) { return (a_Buffer[a_Index / 2] >> ((a_Index & 1) * 4)) & 0x0f; } diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 193937278..d7164a6a5 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -1652,7 +1652,10 @@ void cChunkMap::AddEntity(cEntity * a_Entity) { cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ()); - if ((Chunk == NULL) || !Chunk->IsValid()) + if ( + (Chunk == NULL) || // Chunk not present at all + (!Chunk->IsValid() && !a_Entity->IsPlayer()) // Chunk present, but no valid data; players need to spawn in such chunks (#953) + ) { LOGWARNING("Entity at %p (%s, ID %d) spawning in a non-existent chunk, the entity is lost.", a_Entity, a_Entity->GetClass(), a_Entity->GetUniqueID() diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 8dddef7f8..d414c3178 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -214,9 +214,13 @@ AString cClientHandle::FormatMessageType(bool ShouldAppendChatPrefixes, eMessage case mtPrivateMessage: { if (ShouldAppendChatPrefixes) + { return Printf("%s[MSG: %s] %s%s", cChatColor::LightBlue.c_str(), a_AdditionalData.c_str(), cChatColor::White.c_str(), cChatColor::Italic.c_str()); + } else + { return Printf("%s: %s", a_AdditionalData.c_str(), cChatColor::LightBlue.c_str()); + } } } ASSERT(!"Unhandled chat prefix type!"); @@ -332,6 +336,11 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID) // Send scoreboard data World->GetScoreBoard().SendTo(*this); + + // Delay the first ping until the client "settles down" + // This should fix #889, "BadCast exception, cannot convert bit to fm" error in client + cTimer t1; + m_LastPingTime = t1.GetNowTime() + 3000; // Send the first KeepAlive packet in 3 seconds cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player); } @@ -707,7 +716,7 @@ void cClientHandle::UnregisterPluginChannels(const AStringVector & a_ChannelList -void cClientHandle::HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length) +void cClientHandle::HandleCommandBlockMessage(const char * a_Data, size_t a_Length) { if (a_Length < 14) { @@ -1501,7 +1510,7 @@ void cClientHandle::HandleDisconnect(const AString & a_Reason) { LOGD("Received d/c packet from %s with reason \"%s\"", m_Username.c_str(), a_Reason.c_str()); - cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, a_Reason); + cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, a_Reason); m_HasSentDC = true; Destroy(); @@ -1649,7 +1658,7 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size) { // There is a queued overflow. Append to it, then send as much from its front as possible m_OutgoingDataOverflow.append(a_Data, a_Size); - int CanFit = m_OutgoingData.GetFreeSpace(); + size_t CanFit = m_OutgoingData.GetFreeSpace(); if (CanFit > 128) { // No point in moving the data over if it's not large enough - too much effort for too little an effect @@ -2680,9 +2689,9 @@ void cClientHandle::SocketClosed(void) LOGD("Player %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str()); - if (m_Username != "") // Ignore client pings + if (!m_Username.empty()) // Ignore client pings { - cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, "Player disconnected"); + cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected"); } Destroy(); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 9f8d44129..9fd17ac00 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -384,7 +384,7 @@ private: void UnregisterPluginChannels(const AStringVector & a_ChannelList); /** Handles the "MC|AdvCdm" plugin message */ - void HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length); + void HandleCommandBlockMessage(const char * a_Data, size_t a_Length); // cSocketThreads::cCallback overrides: virtual void DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client diff --git a/src/Crypto.cpp b/src/Crypto.cpp deleted file mode 100644 index 16be5ec35..000000000 --- a/src/Crypto.cpp +++ /dev/null @@ -1,509 +0,0 @@ - -// Crypto.cpp - -// Implements classes that wrap the cryptographic code library - -#include "Globals.h" -#include "Crypto.h" - -#include "polarssl/pk.h" - - - - - -/* -// Self-test the hash formatting for known values: -// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48 -// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1 -// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6 - -class Test -{ -public: - Test(void) - { - AString DigestNotch, DigestJeb, DigestSimon; - Byte Digest[20]; - cSHA1Checksum Checksum; - Checksum.Update((const Byte *)"Notch", 5); - Checksum.Finalize(Digest); - cSHA1Checksum::DigestToJava(Digest, DigestNotch); - Checksum.Restart(); - Checksum.Update((const Byte *)"jeb_", 4); - Checksum.Finalize(Digest); - cSHA1Checksum::DigestToJava(Digest, DigestJeb); - Checksum.Restart(); - Checksum.Update((const Byte *)"simon", 5); - Checksum.Finalize(Digest); - cSHA1Checksum::DigestToJava(Digest, DigestSimon); - printf("Notch: \"%s\"\n", DigestNotch.c_str()); - printf("jeb_: \"%s\"\n", DigestJeb.c_str()); - printf("simon: \"%s\"\n", DigestSimon.c_str()); - assert(DigestNotch == "4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48"); - assert(DigestJeb == "-7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1"); - assert(DigestSimon == "88e16a1019277b15d58faf0541e11910eb756f6"); - } -} test; -*/ - - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cRSAPrivateKey: - -cRSAPrivateKey::cRSAPrivateKey(void) -{ - rsa_init(&m_Rsa, RSA_PKCS_V15, 0); - InitRnd(); -} - - - - - -cRSAPrivateKey::cRSAPrivateKey(const cRSAPrivateKey & a_Other) -{ - rsa_init(&m_Rsa, RSA_PKCS_V15, 0); - rsa_copy(&m_Rsa, &a_Other.m_Rsa); - InitRnd(); -} - - - - - -cRSAPrivateKey::~cRSAPrivateKey() -{ - entropy_free(&m_Entropy); - rsa_free(&m_Rsa); -} - - - - - -void cRSAPrivateKey::InitRnd(void) -{ - entropy_init(&m_Entropy); - const unsigned char pers[] = "rsa_genkey"; - ctr_drbg_init(&m_Ctr_drbg, entropy_func, &m_Entropy, pers, sizeof(pers) - 1); -} - - - - - -bool cRSAPrivateKey::Generate(unsigned a_KeySizeBits) -{ - if (rsa_gen_key(&m_Rsa, ctr_drbg_random, &m_Ctr_drbg, a_KeySizeBits, 65537) != 0) - { - // Key generation failed - return false; - } - - return true; -} - - - - - -AString cRSAPrivateKey::GetPubKeyDER(void) -{ - class cPubKey - { - public: - cPubKey(rsa_context * a_Rsa) : - m_IsValid(false) - { - pk_init(&m_Key); - if (pk_init_ctx(&m_Key, pk_info_from_type(POLARSSL_PK_RSA)) != 0) - { - ASSERT(!"Cannot init PrivKey context"); - return; - } - if (rsa_copy(pk_rsa(m_Key), a_Rsa) != 0) - { - ASSERT(!"Cannot copy PrivKey to PK context"); - return; - } - m_IsValid = true; - } - - ~cPubKey() - { - if (m_IsValid) - { - pk_free(&m_Key); - } - } - - operator pk_context * (void) { return &m_Key; } - - protected: - bool m_IsValid; - pk_context m_Key; - } PkCtx(&m_Rsa); - - unsigned char buf[3000]; - int res = pk_write_pubkey_der(PkCtx, buf, sizeof(buf)); - if (res < 0) - { - return AString(); - } - return AString((const char *)(buf + sizeof(buf) - res), (size_t)res); -} - - - - - -int cRSAPrivateKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength) -{ - if (a_EncryptedLength < m_Rsa.len) - { - LOGD("%s: Invalid a_EncryptedLength: got %u, exp at least %u", - __FUNCTION__, (unsigned)a_EncryptedLength, (unsigned)(m_Rsa.len) - ); - ASSERT(!"Invalid a_DecryptedMaxLength!"); - return -1; - } - if (a_DecryptedMaxLength < m_Rsa.len) - { - LOGD("%s: Invalid a_DecryptedMaxLength: got %u, exp at least %u", - __FUNCTION__, (unsigned)a_EncryptedLength, (unsigned)(m_Rsa.len) - ); - ASSERT(!"Invalid a_DecryptedMaxLength!"); - return -1; - } - size_t DecryptedLength; - int res = rsa_pkcs1_decrypt( - &m_Rsa, ctr_drbg_random, &m_Ctr_drbg, RSA_PRIVATE, &DecryptedLength, - a_EncryptedData, a_DecryptedData, a_DecryptedMaxLength - ); - if (res != 0) - { - return -1; - } - return (int)DecryptedLength; -} - - - - - -int cRSAPrivateKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength) -{ - if (a_EncryptedMaxLength < m_Rsa.len) - { - LOGD("%s: Invalid a_EncryptedMaxLength: got %u, exp at least %u", - __FUNCTION__, (unsigned)a_EncryptedMaxLength, (unsigned)(m_Rsa.len) - ); - ASSERT(!"Invalid a_DecryptedMaxLength!"); - return -1; - } - if (a_PlainLength < m_Rsa.len) - { - LOGD("%s: Invalid a_PlainLength: got %u, exp at least %u", - __FUNCTION__, (unsigned)a_PlainLength, (unsigned)(m_Rsa.len) - ); - ASSERT(!"Invalid a_PlainLength!"); - return -1; - } - int res = rsa_pkcs1_encrypt( - &m_Rsa, ctr_drbg_random, &m_Ctr_drbg, RSA_PRIVATE, - a_PlainLength, a_PlainData, a_EncryptedData - ); - if (res != 0) - { - return -1; - } - return (int)m_Rsa.len; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cPublicKey: - -cPublicKey::cPublicKey(const AString & a_PublicKeyDER) -{ - pk_init(&m_Pk); - if (pk_parse_public_key(&m_Pk, (const Byte *)a_PublicKeyDER.data(), a_PublicKeyDER.size()) != 0) - { - ASSERT(!"Cannot parse PubKey"); - return; - } - InitRnd(); -} - - - - - -cPublicKey::~cPublicKey() -{ - pk_free(&m_Pk); -} - - - - - -int cPublicKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength) -{ - size_t DecryptedLen = a_DecryptedMaxLength; - int res = pk_decrypt(&m_Pk, - a_EncryptedData, a_EncryptedLength, - a_DecryptedData, &DecryptedLen, a_DecryptedMaxLength, - ctr_drbg_random, &m_Ctr_drbg - ); - if (res != 0) - { - return res; - } - return (int)DecryptedLen; -} - - - - - -int cPublicKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength) -{ - size_t EncryptedLength = a_EncryptedMaxLength; - int res = pk_encrypt(&m_Pk, - a_PlainData, a_PlainLength, a_EncryptedData, &EncryptedLength, a_EncryptedMaxLength, - ctr_drbg_random, &m_Ctr_drbg - ); - if (res != 0) - { - return res; - } - return (int)EncryptedLength; -} - - - - - -void cPublicKey::InitRnd(void) -{ - entropy_init(&m_Entropy); - const unsigned char pers[] = "rsa_genkey"; - ctr_drbg_init(&m_Ctr_drbg, entropy_func, &m_Entropy, pers, sizeof(pers) - 1); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cAESCFBDecryptor: - -cAESCFBDecryptor::cAESCFBDecryptor(void) : - m_IVOffset(0), - m_IsValid(false) -{ -} - - - - - -cAESCFBDecryptor::~cAESCFBDecryptor() -{ - // Clear the leftover in-memory data, so that they can't be accessed by a backdoor - memset(&m_Aes, 0, sizeof(m_Aes)); -} - - - - - -void cAESCFBDecryptor::Init(const Byte a_Key[16], const Byte a_IV[16]) -{ - ASSERT(!IsValid()); // Cannot Init twice - - memcpy(m_IV, a_IV, 16); - aes_setkey_enc(&m_Aes, a_Key, 128); - m_IsValid = true; -} - - - - - -void cAESCFBDecryptor::ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length) -{ - ASSERT(IsValid()); // Must Init() first - - // PolarSSL doesn't support AES-CFB8, need to implement it manually: - for (size_t i = 0; i < a_Length; i++) - { - Byte Buffer[sizeof(m_IV)]; - aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer); - for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++) - { - m_IV[idx] = m_IV[idx + 1]; - } - m_IV[sizeof(m_IV) - 1] = a_EncryptedIn[i]; - a_DecryptedOut[i] = a_EncryptedIn[i] ^ Buffer[0]; - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cAESCFBEncryptor: - -cAESCFBEncryptor::cAESCFBEncryptor(void) : - m_IVOffset(0), - m_IsValid(false) -{ -} - - - - - -cAESCFBEncryptor::~cAESCFBEncryptor() -{ - // Clear the leftover in-memory data, so that they can't be accessed by a backdoor - memset(&m_Aes, 0, sizeof(m_Aes)); -} - - - - - -void cAESCFBEncryptor::Init(const Byte a_Key[16], const Byte a_IV[16]) -{ - ASSERT(!IsValid()); // Cannot Init twice - ASSERT(m_IVOffset == 0); - - memcpy(m_IV, a_IV, 16); - aes_setkey_enc(&m_Aes, a_Key, 128); - m_IsValid = true; -} - - - - - -void cAESCFBEncryptor::ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length) -{ - ASSERT(IsValid()); // Must Init() first - - // PolarSSL doesn't do AES-CFB8, so we need to implement it ourselves: - for (size_t i = 0; i < a_Length; i++) - { - Byte Buffer[sizeof(m_IV)]; - aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer); - for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++) - { - m_IV[idx] = m_IV[idx + 1]; - } - a_EncryptedOut[i] = a_PlainIn[i] ^ Buffer[0]; - m_IV[sizeof(m_IV) - 1] = a_EncryptedOut[i]; - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cSHA1Checksum: - -cSHA1Checksum::cSHA1Checksum(void) : - m_DoesAcceptInput(true) -{ - sha1_starts(&m_Sha1); -} - - - - - -void cSHA1Checksum::Update(const Byte * a_Data, size_t a_Length) -{ - ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed - - sha1_update(&m_Sha1, a_Data, a_Length); -} - - - - - -void cSHA1Checksum::Finalize(cSHA1Checksum::Checksum & a_Output) -{ - ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed - - sha1_finish(&m_Sha1, a_Output); - m_DoesAcceptInput = false; -} - - - - - -void cSHA1Checksum::DigestToJava(const Checksum & a_Digest, AString & a_Out) -{ - Checksum Digest; - memcpy(Digest, a_Digest, sizeof(Digest)); - - bool IsNegative = (Digest[0] >= 0x80); - if (IsNegative) - { - // Two's complement: - bool carry = true; // Add one to the whole number - for (int i = 19; i >= 0; i--) - { - Digest[i] = ~Digest[i]; - if (carry) - { - carry = (Digest[i] == 0xff); - Digest[i]++; - } - } - } - a_Out.clear(); - a_Out.reserve(40); - for (int i = 0; i < 20; i++) - { - AppendPrintf(a_Out, "%02x", Digest[i]); - } - while ((a_Out.length() > 0) && (a_Out[0] == '0')) - { - a_Out.erase(0, 1); - } - if (IsNegative) - { - a_Out.insert(0, "-"); - } -} - - - - - - -void cSHA1Checksum::Restart(void) -{ - sha1_starts(&m_Sha1); - m_DoesAcceptInput = true; -} - - - - diff --git a/src/Crypto.h b/src/Crypto.h deleted file mode 100644 index a9ec2c6d4..000000000 --- a/src/Crypto.h +++ /dev/null @@ -1,198 +0,0 @@ - -// Crypto.h - -// Declares classes that wrap the cryptographic code library - - - - - -#pragma once - -#include "polarssl/rsa.h" -#include "polarssl/aes.h" -#include "polarssl/entropy.h" -#include "polarssl/ctr_drbg.h" -#include "polarssl/sha1.h" -#include "polarssl/pk.h" - - - - - -/** Encapsulates an RSA private key used in PKI cryptography */ -class cRSAPrivateKey -{ -public: - /** Creates a new empty object, the key is not assigned */ - cRSAPrivateKey(void); - - /** Deep-copies the key from a_Other */ - cRSAPrivateKey(const cRSAPrivateKey & a_Other); - - ~cRSAPrivateKey(); - - /** Generates a new key within this object, with the specified size in bits. - Returns true on success, false on failure. */ - bool Generate(unsigned a_KeySizeBits = 1024); - - /** Returns the public key part encoded in ASN1 DER encoding */ - AString GetPubKeyDER(void); - - /** Decrypts the data using RSAES-PKCS#1 algorithm. - Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large. - Returns the number of bytes decrypted, or negative number for error. */ - int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength); - - /** Encrypts the data using RSAES-PKCS#1 algorithm. - Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large. - Returns the number of bytes decrypted, or negative number for error. */ - int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength); - -protected: - rsa_context m_Rsa; - entropy_context m_Entropy; - ctr_drbg_context m_Ctr_drbg; - - /** Initializes the m_Entropy and m_Ctr_drbg contexts - Common part of this object's construction, called from all constructors. */ - void InitRnd(void); -} ; - - - - - -class cPublicKey -{ -public: - cPublicKey(const AString & a_PublicKeyDER); - ~cPublicKey(); - - /** Decrypts the data using the stored public key - Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large. - Returns the number of bytes decrypted, or negative number for error. */ - int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength); - - /** Encrypts the data using the stored public key - Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large. - Returns the number of bytes decrypted, or negative number for error. */ - int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength); - -protected: - pk_context m_Pk; - entropy_context m_Entropy; - ctr_drbg_context m_Ctr_drbg; - - /** Initializes the m_Entropy and m_Ctr_drbg contexts - Common part of this object's construction, called from all constructors. */ - void InitRnd(void); -} ; - - - - - -/** Decrypts data using the AES / CFB (128) algorithm */ -class cAESCFBDecryptor -{ -public: - Byte test; - - cAESCFBDecryptor(void); - ~cAESCFBDecryptor(); - - /** Initializes the decryptor with the specified Key / IV */ - void Init(const Byte a_Key[16], const Byte a_IV[16]); - - /** Decrypts a_Length bytes of the encrypted data; produces a_Length output bytes */ - void ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length); - - /** Returns true if the object has been initialized with the Key / IV */ - bool IsValid(void) const { return m_IsValid; } - -protected: - aes_context m_Aes; - - /** The InitialVector, used by the CFB mode decryption */ - Byte m_IV[16]; - - /** Current offset in the m_IV, used by the CFB mode decryption */ - size_t m_IVOffset; - - /** Indicates whether the object has been initialized with the Key / IV */ - bool m_IsValid; -} ; - - - - - -/** Encrypts data using the AES / CFB (128) algorithm */ -class cAESCFBEncryptor -{ -public: - cAESCFBEncryptor(void); - ~cAESCFBEncryptor(); - - /** Initializes the decryptor with the specified Key / IV */ - void Init(const Byte a_Key[16], const Byte a_IV[16]); - - /** Encrypts a_Length bytes of the plain data; produces a_Length output bytes */ - void ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length); - - /** Returns true if the object has been initialized with the Key / IV */ - bool IsValid(void) const { return m_IsValid; } - -protected: - aes_context m_Aes; - - /** The InitialVector, used by the CFB mode encryption */ - Byte m_IV[16]; - - /** Current offset in the m_IV, used by the CFB mode encryption */ - size_t m_IVOffset; - - /** Indicates whether the object has been initialized with the Key / IV */ - bool m_IsValid; -} ; - - - - - -/** Calculates a SHA1 checksum for data stream */ -class cSHA1Checksum -{ -public: - typedef Byte Checksum[20]; // The type used for storing the checksum - - cSHA1Checksum(void); - - /** Adds the specified data to the checksum */ - void Update(const Byte * a_Data, size_t a_Length); - - /** Calculates and returns the final checksum */ - void Finalize(Checksum & a_Output); - - /** Returns true if the object is accepts more input data, false if Finalize()-d (need to Restart()) */ - bool DoesAcceptInput(void) const { return m_DoesAcceptInput; } - - /** Converts a raw 160-bit SHA1 digest into a Java Hex representation - According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802 - */ - static void DigestToJava(const Checksum & a_Digest, AString & a_JavaOut); - - /** Clears the current context and start a new checksum calculation */ - void Restart(void); - -protected: - /** True if the object is accepts more input data, false if Finalize()-d (need to Restart()) */ - bool m_DoesAcceptInput; - - sha1_context m_Sha1; -} ; - - - - diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp new file mode 100644 index 000000000..847b39bbc --- /dev/null +++ b/src/Entities/ArrowEntity.cpp @@ -0,0 +1,193 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Player.h" +#include "ArrowEntity.h" +#include "../Chunk.h" + + + + + +cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5), + m_PickupState(psNoPickup), + m_DamageCoeff(2), + m_IsCritical(false), + m_Timer(0), + m_HitGroundTimer(0), + m_bIsCollected(false), + m_HitBlockPos(Vector3i(0, 0, 0)) +{ + SetSpeed(a_Speed); + SetMass(0.1); + SetYawFromSpeed(); + SetPitchFromSpeed(); + LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}", + m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(), + GetYaw(), GetPitch() + ); +} + + + + + +cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) : + super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5), + m_PickupState(psInSurvivalOrCreative), + m_DamageCoeff(2), + m_IsCritical((a_Force >= 1)), + m_Timer(0), + m_HitGroundTimer(0), + m_HasTeleported(false), + m_bIsCollected(false), + m_HitBlockPos(0, 0, 0) +{ +} + + + + + +bool cArrowEntity::CanPickup(const cPlayer & a_Player) const +{ + switch (m_PickupState) + { + case psNoPickup: return false; + case psInSurvivalOrCreative: return (a_Player.IsGameModeSurvival() || a_Player.IsGameModeCreative()); + case psInCreative: return a_Player.IsGameModeCreative(); + } + ASSERT(!"Unhandled pickup state"); + return false; +} + + + + + +void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +{ + if (a_HitFace == BLOCK_FACE_NONE) { return; } + + super::OnHitSolidBlock(a_HitPos, a_HitFace); + int a_X = (int)a_HitPos.x, a_Y = (int)a_HitPos.y, a_Z = (int)a_HitPos.z; + + switch (a_HitFace) + { + case BLOCK_FACE_XM: // Strangely, bounding boxes / block tracers return the actual block for these two directions, so AddFace not needed + case BLOCK_FACE_YM: + { + break; + } + default: AddFaceDirection(a_X, a_Y, a_Z, a_HitFace, true); + } + + m_HitBlockPos = Vector3i(a_X, a_Y, a_Z); + + // Broadcast arrow hit sound + m_World->BroadcastSoundEffect("random.bowhit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); +} + + + + + +void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer() && !a_EntityHit.IsBoat()) + { + // Not an entity that interacts with an arrow + return; + } + + int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5); + if (m_IsCritical) + { + Damage += m_World->GetTickRandomNumber(Damage / 2 + 2); + } + a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1); + + // Broadcast successful hit sound + m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + + Destroy(); +} + + + + + +void cArrowEntity::CollectedBy(cPlayer * a_Dest) +{ + if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest))) + { + int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW); + if (NumAdded > 0) // Only play effects if there was space in inventory + { + m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest); + // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) + m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + m_bIsCollected = true; + } + } +} + + + + + +void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + m_Timer += a_Dt; + + if (m_bIsCollected) + { + if (m_Timer > 500.f) // 0.5 seconds + { + Destroy(); + return; + } + } + else if (m_Timer > 1000 * 60 * 5) // 5 minutes + { + Destroy(); + return; + } + + if (m_IsInGround) + { + // When an arrow hits, the client doesn't think its in the ground and keeps on moving, IF BroadcastMovementUpdate() and TeleportEntity was called during flight, AT ALL + // Fix is to simply not sync with the client and send a teleport to confirm pos after arrow has stabilised (around 1 sec after landing) + // We can afford to do this because xoft's algorithm for trajectory is near perfect, so things are pretty close anyway without sync + // Besides, this seems to be what the vanilla server does, note how arrows teleport half a second after they hit to the server position + + if (!m_HasTeleported) // Sent a teleport already, don't do again + { + if (m_HitGroundTimer > 1000.f) // Send after a second, could be less, but just in case + { + m_World->BroadcastTeleportEntity(*this); + m_HasTeleported = true; + } + else + { + m_HitGroundTimer += a_Dt; + } + } + + int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width; + int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width; + cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); + + if (Chunk == NULL) + { + // Inside an unloaded chunk, abort + return; + } + + if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed? + { + m_IsInGround = false; // Yes, begin simulating physics again + } + } +} diff --git a/src/Entities/ArrowEntity.h b/src/Entities/ArrowEntity.h new file mode 100644 index 000000000..1fe3032ee --- /dev/null +++ b/src/Entities/ArrowEntity.h @@ -0,0 +1,96 @@ +// +// ArrowEntity.h +// + +#pragma once + +#include "ProjectileEntity.h" + + + + + +// tolua_begin + +class cArrowEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + /// Determines when the arrow can be picked up (depending on player gamemode). Corresponds to the MCA file "pickup" field + enum ePickupState + { + psNoPickup = 0, + psInSurvivalOrCreative = 1, + psInCreative = 2, + } ; + + // tolua_end + + CLASS_PROTODEF(cArrowEntity); + + /// Creates a new arrow with psNoPickup state and default damage modifier coeff + cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + + /// Creates a new arrow as shot by a player, initializes it from the player object + cArrowEntity(cPlayer & a_Player, double a_Force); + + // tolua_begin + + /// Returns whether the arrow can be picked up by players + ePickupState GetPickupState(void) const { return m_PickupState; } + + /// Sets a new pickup state + void SetPickupState(ePickupState a_PickupState) { m_PickupState = a_PickupState; } + + /// Returns the damage modifier coeff. + double GetDamageCoeff(void) const { return m_DamageCoeff; } + + /// Sets the damage modifier coeff + void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; } + + /// Returns true if the specified player can pick the arrow up + bool CanPickup(const cPlayer & a_Player) const; + + /// Returns true if the arrow is set as critical + bool IsCritical(void) const { return m_IsCritical; } + + /// Sets the IsCritical flag + void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; } + + // tolua_end + +protected: + + /// Determines when the arrow can be picked up by players + ePickupState m_PickupState; + + /// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow + double m_DamageCoeff; + + /// If true, the arrow deals more damage + bool m_IsCritical; + + /// Timer for pickup collection animation or five minute timeout + float m_Timer; + + /// Timer for client arrow position confirmation via TeleportEntity + float m_HitGroundTimer; + + // Whether the arrow has already been teleported into the proper position in the ground. + bool m_HasTeleported; + + /// If true, the arrow is in the process of being collected - don't go to anyone else + bool m_bIsCollected; + + /// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air + Vector3i m_HitBlockPos; + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + virtual void CollectedBy(cPlayer * a_Player) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + +}; // tolua_export diff --git a/src/Entities/Boat.cpp b/src/Entities/Boat.cpp index 921252253..31bfe3dc3 100644 --- a/src/Entities/Boat.cpp +++ b/src/Entities/Boat.cpp @@ -33,9 +33,12 @@ void cBoat::SpawnOn(cClientHandle & a_ClientHandle) -void cBoat::DoTakeDamage(TakeDamageInfo & TDI) +bool cBoat::DoTakeDamage(TakeDamageInfo & TDI) { - super::DoTakeDamage(TDI); + if (!super::DoTakeDamage(TDI)) + { + return false; + } if (GetHealth() == 0) { @@ -50,6 +53,7 @@ void cBoat::DoTakeDamage(TakeDamageInfo & TDI) } Destroy(true); } + return true; } diff --git a/src/Entities/Boat.h b/src/Entities/Boat.h index c4c9afe7a..0fcfbd602 100644 --- a/src/Entities/Boat.h +++ b/src/Entities/Boat.h @@ -26,7 +26,7 @@ public: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void OnRightClicked(cPlayer & a_Player) override; - virtual void DoTakeDamage(TakeDamageInfo & TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override; diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 7a301a5b7..5c675a387 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -53,6 +53,8 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d , m_TicksSinceLastVoidDamage(0) , m_IsSwimming(false) , m_IsSubmerged(false) + , m_AirLevel(0) + , m_AirTickTimer(0) , m_HeadYaw( 0.0 ) , m_Rot(0.0, 0.0, 0.0) , m_Pos(a_X, a_Y, a_Z) @@ -60,6 +62,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d , m_Mass (0.001) // Default 1g , m_Width(a_Width) , m_Height(a_Height) + , m_InvulnerableTicks(0) { cCSLock Lock(m_CSCount); m_EntityCount++; @@ -294,17 +297,23 @@ void cEntity::SetPitchFromSpeed(void) -void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) { if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI)) { - return; + return false; } if (m_Health <= 0) { // Can't take damage if already dead - return; + return false; + } + + if (m_InvulnerableTicks > 0) + { + // Entity is invulnerable + return false; } if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer())) @@ -362,10 +371,13 @@ void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI) m_World->BroadcastEntityStatus(*this, esGenericHurt); + m_InvulnerableTicks = 10; + if (m_Health <= 0) { KilledBy(a_TDI.Attacker); } + return true; } @@ -562,6 +574,11 @@ void cEntity::SetHealth(int a_Health) void cEntity::Tick(float a_Dt, cChunk & a_Chunk) { + if (m_InvulnerableTicks > 0) + { + m_InvulnerableTicks--; + } + if (m_AttachedTo != NULL) { if ((m_Pos - m_AttachedTo->GetPosition()).Length() > 0.5) diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index 9b8011b55..a682701de 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -262,8 +262,10 @@ public: // tolua_end - /// Makes this entity take damage specified in the a_TDI. The TDI is sent through plugins first, then applied - virtual void DoTakeDamage(TakeDamageInfo & a_TDI); + /** Makes this entity take damage specified in the a_TDI. + The TDI is sent through plugins first, then applied. + If it returns false, the entity hasn't receive any damage. */ + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI); // tolua_begin @@ -394,6 +396,12 @@ public: virtual bool IsSubmerged(void) const{ return m_IsSubmerged; } /** Gets remaining air of a monster */ int GetAirLevel(void) const { return m_AirLevel; } + + /** Gets the invulnerable ticks from the entity */ + int GetInvulnerableTicks(void) const { return m_InvulnerableTicks; } + + /** Set the invulnerable ticks from the entity */ + void SetInvulnerableTicks(int a_InvulnerableTicks) { m_InvulnerableTicks = a_InvulnerableTicks; } // tolua_end @@ -478,29 +486,33 @@ protected: int m_AirTickTimer; private: - // Measured in degrees, [-180, +180) + /** Measured in degrees, [-180, +180) */ double m_HeadYaw; - // Measured in meter/second (m/s) + /** Measured in meter/second (m/s) */ Vector3d m_Speed; - // Measured in degrees, [-180, +180) + /** Measured in degrees, [-180, +180) */ Vector3d m_Rot; - /// Position of the entity's XZ center and Y bottom + /** Position of the entity's XZ center and Y bottom */ Vector3d m_Pos; - // Measured in meter / second + /** Measured in meter / second */ Vector3d m_WaterSpeed; - // Measured in Kilograms (Kg) + /** Measured in Kilograms (Kg) */ double m_Mass; - /// Width of the entity, in the XZ plane. Since entities are represented as cylinders, this is more of a diameter. + /** Width of the entity, in the XZ plane. Since entities are represented as cylinders, this is more of a diameter. */ double m_Width; - /// Height of the entity (Y axis) + /** Height of the entity (Y axis) */ double m_Height; + + /** If a player hit a entity, the entity receive a invulnerable of 10 ticks. + While this ticks, a player can't hit this entity. */ + int m_InvulnerableTicks; } ; // tolua_export typedef std::list<cEntity *> cEntityList; diff --git a/src/Entities/ExpBottleEntity.cpp b/src/Entities/ExpBottleEntity.cpp new file mode 100644 index 000000000..202dde942 --- /dev/null +++ b/src/Entities/ExpBottleEntity.cpp @@ -0,0 +1,27 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ExpBottleEntity.h" +#include "../World.h" + + + + + +cExpBottleEntity::cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +{ + // Spawn an experience orb with a reward between 3 and 11. + m_World->BroadcastSoundParticleEffect(2002, POSX_TOINT, POSY_TOINT, POSZ_TOINT, 0); + m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8)); + + Destroy(); +} diff --git a/src/Entities/ExpBottleEntity.h b/src/Entities/ExpBottleEntity.h new file mode 100644 index 000000000..b2043d8f1 --- /dev/null +++ b/src/Entities/ExpBottleEntity.h @@ -0,0 +1,33 @@ +// +// ExpBottleEntity.h +// + +#pragma once + +#include "ProjectileEntity.h" + + + + + +// tolua_begin + +class cExpBottleEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cExpBottleEntity); + + cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; + +}; // tolua_export diff --git a/src/Entities/FallingBlock.cpp b/src/Entities/FallingBlock.cpp index 86720ac94..beb58f207 100644 --- a/src/Entities/FallingBlock.cpp +++ b/src/Entities/FallingBlock.cpp @@ -87,7 +87,7 @@ void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk) AddSpeedY(MilliDt * -9.8f); AddPosition(GetSpeed() * MilliDt); - // If not static (One billionth precision) broadcast movement. + // If not static (one billionth precision) broadcast movement if ((fabs(GetSpeedX()) > std::numeric_limits<double>::epsilon()) || (fabs(GetSpeedZ()) > std::numeric_limits<double>::epsilon())) { BroadcastMovementUpdate(); diff --git a/src/Entities/FireChargeEntity.cpp b/src/Entities/FireChargeEntity.cpp new file mode 100644 index 000000000..aba32602f --- /dev/null +++ b/src/Entities/FireChargeEntity.cpp @@ -0,0 +1,50 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "FireChargeEntity.h" +#include "../World.h" + + + + + +cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkFireCharge, a_Creator, a_X, a_Y, a_Z, 0.3125, 0.3125) +{ + SetSpeed(a_Speed); + SetGravity(0); +} + + + + + +void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + if (m_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) + { + m_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 1); + } +} + + + + + +void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); +} + + + + + +void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); + + // TODO: Some entities are immune to hits + a_EntityHit.StartBurning(5 * 20); // 5 seconds of burning +} diff --git a/src/Entities/FireChargeEntity.h b/src/Entities/FireChargeEntity.h new file mode 100644 index 000000000..3924c337c --- /dev/null +++ b/src/Entities/FireChargeEntity.h @@ -0,0 +1,36 @@ +// +// FireChargeEntity.h +// + +#pragma once + +#include "ProjectileEntity.h" + + + + + +// tolua_begin + +class cFireChargeEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cFireChargeEntity); + + cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + +} ; // tolua_export diff --git a/src/Entities/FireworkEntity.cpp b/src/Entities/FireworkEntity.cpp new file mode 100644 index 000000000..403a53c84 --- /dev/null +++ b/src/Entities/FireworkEntity.cpp @@ -0,0 +1,73 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "FireworkEntity.h" +#include "../World.h" +#include "../Chunk.h" + + + + + +cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item) : + super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25), + m_ExplodeTimer(0), + m_FireworkItem(a_Item) +{ +} + + + + + +void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) +{ + int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width; + int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width; + int PosY = POSY_TOINT; + + if ((PosY < 0) || (PosY >= cChunkDef::Height)) + { + goto setspeed; + } + + if (m_IsInGround) + { + if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) == E_BLOCK_AIR) + { + m_IsInGround = false; + } + else + { + return; + } + } + else + { + if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) != E_BLOCK_AIR) + { + OnHitSolidBlock(GetPosition(), BLOCK_FACE_YM); + return; + } + } + +setspeed: + AddSpeedY(1); + AddPosition(GetSpeed() * (a_Dt / 1000)); +} + + + + + +void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + if (m_ExplodeTimer == m_FireworkItem.m_FireworkItem.m_FlightTimeInTicks) + { + m_World->BroadcastEntityStatus(*this, esFireworkExploding); + Destroy(); + } + + m_ExplodeTimer++; +} diff --git a/src/Entities/FireworkEntity.h b/src/Entities/FireworkEntity.h new file mode 100644 index 000000000..c62ca9402 --- /dev/null +++ b/src/Entities/FireworkEntity.h @@ -0,0 +1,40 @@ +// +// FireworkEntity.h +// + +#pragma once + +#include "ProjectileEntity.h" + + + + + +// tolua_begin + +class cFireworkEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cFireworkEntity); + + cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item); + const cItem & GetItem(void) const { return m_FireworkItem; } + +protected: + + // cProjectileEntity overrides: + virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; + +private: + + int m_ExplodeTimer; + cItem m_FireworkItem; + +}; // tolua_export diff --git a/src/Entities/GhastFireballEntity.cpp b/src/Entities/GhastFireballEntity.cpp new file mode 100644 index 000000000..9e4cb387e --- /dev/null +++ b/src/Entities/GhastFireballEntity.cpp @@ -0,0 +1,44 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "GhastFireballEntity.h" +#include "../World.h" + + + + + +cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkGhastFireball, a_Creator, a_X, a_Y, a_Z, 1, 1) +{ + SetSpeed(a_Speed); + SetGravity(0); +} + + + + + +void cGhastFireballEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + m_World->DoExplosionAt(1, a_BlockX, a_BlockY, a_BlockZ, true, esGhastFireball, this); +} + + + + + +void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); +} + + + + + +void cGhastFireballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + Destroy(); + Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); +} diff --git a/src/Entities/GhastFireballEntity.h b/src/Entities/GhastFireballEntity.h new file mode 100644 index 000000000..9e4572c78 --- /dev/null +++ b/src/Entities/GhastFireballEntity.h @@ -0,0 +1,38 @@ +// +// GhastFireballEntity.h +// + +#pragma once + +#include "ProjectileEntity.h" + + + + + +// tolua_begin + +class cGhastFireballEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cGhastFireballEntity); + + cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; + virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + + // TODO: Deflecting the fireballs by arrow- or sword- hits + +} ; // tolua_export diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index db55eb058..7bd440d6d 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -902,18 +902,21 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta) -void cMinecart::DoTakeDamage(TakeDamageInfo & TDI) +bool cMinecart::DoTakeDamage(TakeDamageInfo & TDI) { if ((TDI.Attacker != NULL) && TDI.Attacker->IsPlayer() && ((cPlayer *)TDI.Attacker)->IsGameModeCreative()) { Destroy(); TDI.FinalDamage = GetMaxHealth(); // Instant hit for creative - super::DoTakeDamage(TDI); - return; // No drops for creative + SetInvulnerableTicks(0); + return super::DoTakeDamage(TDI); // No drops for creative } m_LastDamage = TDI.FinalDamage; - super::DoTakeDamage(TDI); + if (!super::DoTakeDamage(TDI)) + { + return false; + } m_World->BroadcastEntityMetadata(*this); @@ -952,12 +955,13 @@ void cMinecart::DoTakeDamage(TakeDamageInfo & TDI) default: { ASSERT(!"Unhandled minecart type when spawning pickup!"); - return; + return true; } } m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ()); } + return true; } diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h index ebdb576e0..1e60f091c 100644 --- a/src/Entities/Minecart.h +++ b/src/Entities/Minecart.h @@ -36,7 +36,7 @@ public: // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - virtual void DoTakeDamage(TakeDamageInfo & TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; virtual void Destroyed() override; int LastDamage(void) const { return m_LastDamage; } diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index fedb62527..08b7d3984 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -808,14 +808,14 @@ void cPlayer::SetFlying(bool a_IsFlying) -void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) { if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtPlugin)) { if (IsGameModeCreative()) { // No damage / health in creative mode if not void or plugin damage - return; + return false; } } @@ -828,17 +828,19 @@ void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI) if (!m_Team->AllowsFriendlyFire()) { // Friendly fire is disabled - return; + return false; } } } - super::DoTakeDamage(a_TDI); - - // Any kind of damage adds food exhaustion - AddFoodExhaustion(0.3f); - - SendHealth(); + if (super::DoTakeDamage(a_TDI)) + { + // Any kind of damage adds food exhaustion + AddFoodExhaustion(0.3f); + SendHealth(); + return true; + } + return false; } @@ -897,6 +899,7 @@ void cPlayer::KilledBy(cEntity * a_Killer) void cPlayer::Respawn(void) { m_Health = GetMaxHealth(); + SetInvulnerableTicks(20); // Reset food level: m_FoodLevel = MAX_FOOD_LEVEL; diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 78d661015..3029abfe0 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -498,7 +498,7 @@ protected: virtual void Destroyed(void); /** Filters out damage for creative mode/friendly fire */ - virtual void DoTakeDamage(TakeDamageInfo & TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & TDI) override; /** Stops players from burning in creative mode */ virtual void TickBurning(cChunk & a_Chunk) override; diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index fd3e80e5f..3e48d310c 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -4,15 +4,24 @@ // Implements the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types #include "Globals.h" + #include "../Bindings/PluginManager.h" #include "ProjectileEntity.h" #include "../ClientHandle.h" -#include "Player.h" #include "../LineBlockTracer.h" #include "../BoundingBox.h" #include "../ChunkMap.h" #include "../Chunk.h" +#include "ArrowEntity.h" +#include "ThrownEggEntity.h" +#include "ThrownEnderPearlEntity.h" +#include "ExpBottleEntity.h" +#include "ThrownSnowballEntity.h" +#include "FireChargeEntity.h" +#include "FireworkEntity.h" +#include "GhastFireballEntity.h" + @@ -401,546 +410,3 @@ void cProjectileEntity::CollectedBy(cPlayer * a_Dest) // Overriden in arrow UNUSED(a_Dest); } - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cArrowEntity: - -cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5), - m_PickupState(psNoPickup), - m_DamageCoeff(2), - m_IsCritical(false), - m_Timer(0), - m_HitGroundTimer(0), - m_bIsCollected(false), - m_HitBlockPos(Vector3i(0, 0, 0)) -{ - SetSpeed(a_Speed); - SetMass(0.1); - SetYawFromSpeed(); - SetPitchFromSpeed(); - LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}", - m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(), - GetYaw(), GetPitch() - ); -} - - - - - -cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) : - super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5), - m_PickupState(psInSurvivalOrCreative), - m_DamageCoeff(2), - m_IsCritical((a_Force >= 1)), - m_Timer(0), - m_HitGroundTimer(0), - m_HasTeleported(false), - m_bIsCollected(false), - m_HitBlockPos(0, 0, 0) -{ -} - - - - - -bool cArrowEntity::CanPickup(const cPlayer & a_Player) const -{ - switch (m_PickupState) - { - case psNoPickup: return false; - case psInSurvivalOrCreative: return (a_Player.IsGameModeSurvival() || a_Player.IsGameModeCreative()); - case psInCreative: return a_Player.IsGameModeCreative(); - } - ASSERT(!"Unhandled pickup state"); - return false; -} - - - - - -void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) -{ - if (a_HitFace == BLOCK_FACE_NONE) { return; } - - super::OnHitSolidBlock(a_HitPos, a_HitFace); - int a_X = (int)a_HitPos.x, a_Y = (int)a_HitPos.y, a_Z = (int)a_HitPos.z; - - switch (a_HitFace) - { - case BLOCK_FACE_XM: // Strangely, bounding boxes / block tracers return the actual block for these two directions, so AddFace not needed - case BLOCK_FACE_YM: - { - break; - } - default: AddFaceDirection(a_X, a_Y, a_Z, a_HitFace, true); - } - - m_HitBlockPos = Vector3i(a_X, a_Y, a_Z); - - // Broadcast arrow hit sound - m_World->BroadcastSoundEffect("random.bowhit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); -} - - - - - -void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer() && !a_EntityHit.IsBoat()) - { - // Not an entity that interacts with an arrow - return; - } - - int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5); - if (m_IsCritical) - { - Damage += m_World->GetTickRandomNumber(Damage / 2 + 2); - } - a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1); - - // Broadcast successful hit sound - m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); - - Destroy(); -} - - - - - -void cArrowEntity::CollectedBy(cPlayer * a_Dest) -{ - if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest))) - { - int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW); - if (NumAdded > 0) // Only play effects if there was space in inventory - { - m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest); - // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) - m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); - m_bIsCollected = true; - } - } -} - - - - - -void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - m_Timer += a_Dt; - - if (m_bIsCollected) - { - if (m_Timer > 500.f) // 0.5 seconds - { - Destroy(); - return; - } - } - else if (m_Timer > 1000 * 60 * 5) // 5 minutes - { - Destroy(); - return; - } - - if (m_IsInGround) - { - // When an arrow hits, the client doesn't think its in the ground and keeps on moving, IF BroadcastMovementUpdate() and TeleportEntity was called during flight, AT ALL - // Fix is to simply not sync with the client and send a teleport to confirm pos after arrow has stabilised (around 1 sec after landing) - // We can afford to do this because xoft's algorithm for trajectory is near perfect, so things are pretty close anyway without sync - // Besides, this seems to be what the vanilla server does, note how arrows teleport half a second after they hit to the server position - - if (!m_HasTeleported) // Sent a teleport already, don't do again - { - if (m_HitGroundTimer > 1000.f) // Send after a second, could be less, but just in case - { - m_World->BroadcastTeleportEntity(*this); - m_HasTeleported = true; - } - else - { - m_HitGroundTimer += a_Dt; - } - } - - int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width; - int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width; - cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ); - - if (Chunk == NULL) - { - // Inside an unloaded chunk, abort - return; - } - - if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed? - { - m_IsInGround = false; // Yes, begin simulating physics again - } - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cThrownEggEntity: - -cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ - SetSpeed(a_Speed); -} - - - - - -void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) -{ - TrySpawnChicken(a_HitPos); - - Destroy(); -} - - - - - -void cThrownEggEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - int TotalDamage = 0; - // TODO: If entity is Ender Crystal, destroy it - - TrySpawnChicken(a_HitPos); - a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1); - - Destroy(true); -} - - - - - -void cThrownEggEntity::TrySpawnChicken(const Vector3d & a_HitPos) -{ - if (m_World->GetTickRandomNumber(7) == 1) - { - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - } - else if (m_World->GetTickRandomNumber(32) == 1) - { - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cThrownEnderPearlEntity : - -cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ - SetSpeed(a_Speed); -} - - - - - -void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) -{ - // TODO: Tweak a_HitPos based on block face. - TeleportCreator(a_HitPos); - - Destroy(); -} - - - - - -void cThrownEnderPearlEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - int TotalDamage = 0; - // TODO: If entity is Ender Crystal, destroy it - - TeleportCreator(a_HitPos); - a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1); - - Destroy(true); -} - - - - - -void cThrownEnderPearlEntity::TeleportCreator(const Vector3d & a_HitPos) -{ - // Teleport the creator here, make them take 5 damage: - if (m_Creator != NULL) - { - m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5); - m_Creator->TakeDamage(dtEnderPearl, this, 5, 0); - } -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cThrownSnowballEntity : - -cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ - SetSpeed(a_Speed); -} - - - - - -void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) -{ - Destroy(); -} - - - - - -void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - int TotalDamage = 0; - if (a_EntityHit.IsMob()) - { - cMonster::eType MobType = ((cMonster &) a_EntityHit).GetMobType(); - if (MobType == cMonster::mtBlaze) - { - TotalDamage = 3; - } - else if (MobType == cMonster::mtEnderDragon) - { - TotalDamage = 1; - } - } - // TODO: If entity is Ender Crystal, destroy it - a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1); - - Destroy(true); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cBottleOEnchantingEntity : - -cExpBottleEntity::cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : -super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) -{ - SetSpeed(a_Speed); -} - - - - - -void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) -{ - // Spawn an experience orb with a reward between 3 and 11. - m_World->BroadcastSoundParticleEffect(2002, POSX_TOINT, POSY_TOINT, POSZ_TOINT, 0); - m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8)); - - Destroy(); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFireworkEntity : - -cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item) : -super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25), - m_ExplodeTimer(0), - m_FireworkItem(a_Item) -{ -} - - - - - -void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) -{ - int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width; - int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width; - int PosY = POSY_TOINT; - - if ((PosY < 0) || (PosY >= cChunkDef::Height)) - { - goto setspeed; - } - - if (m_IsInGround) - { - if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) == E_BLOCK_AIR) - { - m_IsInGround = false; - } - else - { - return; - } - } - else - { - if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) != E_BLOCK_AIR) - { - OnHitSolidBlock(GetPosition(), BLOCK_FACE_YM); - return; - } - } - -setspeed: - AddSpeedY(1); - AddPosition(GetSpeed() * (a_Dt / 1000)); -} - - - - - -void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk) -{ - super::Tick(a_Dt, a_Chunk); - - if (m_ExplodeTimer == m_FireworkItem.m_FireworkItem.m_FlightTimeInTicks) - { - m_World->BroadcastEntityStatus(*this, esFireworkExploding); - Destroy(); - } - - m_ExplodeTimer++; -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cGhastFireballEntity : - -cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkGhastFireball, a_Creator, a_X, a_Y, a_Z, 1, 1) -{ - SetSpeed(a_Speed); - SetGravity(0); -} - - - - - -void cGhastFireballEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - m_World->DoExplosionAt(1, a_BlockX, a_BlockY, a_BlockZ, true, esGhastFireball, this); -} - - - - - -void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); -} - - - - - -void cGhastFireballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); -} - - - - - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cFireChargeEntity : - -cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : - super(pkFireCharge, a_Creator, a_X, a_Y, a_Z, 0.3125, 0.3125) -{ - SetSpeed(a_Speed); - SetGravity(0); -} - - - - - -void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ) -{ - if (m_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) - { - m_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 1); - } -} - - - - - -void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); -} - - - - - -void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) -{ - Destroy(); - Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z)); - - // TODO: Some entities are immune to hits - a_EntityHit.StartBurning(5 * 20); // 5 seconds of burning -} - - - - diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h index 731dd060e..ae06b072f 100644 --- a/src/Entities/ProjectileEntity.h +++ b/src/Entities/ProjectileEntity.h @@ -94,311 +94,4 @@ protected: virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; virtual void SpawnOn(cClientHandle & a_Client) override; - // tolua_begin -} ; - - - - - -class cArrowEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - /// Determines when the arrow can be picked up (depending on player gamemode). Corresponds to the MCA file "pickup" field - enum ePickupState - { - psNoPickup = 0, - psInSurvivalOrCreative = 1, - psInCreative = 2, - } ; - - // tolua_end - - CLASS_PROTODEF(cArrowEntity); - - /// Creates a new arrow with psNoPickup state and default damage modifier coeff - cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - - /// Creates a new arrow as shot by a player, initializes it from the player object - cArrowEntity(cPlayer & a_Player, double a_Force); - - // tolua_begin - - /// Returns whether the arrow can be picked up by players - ePickupState GetPickupState(void) const { return m_PickupState; } - - /// Sets a new pickup state - void SetPickupState(ePickupState a_PickupState) { m_PickupState = a_PickupState; } - - /// Returns the damage modifier coeff. - double GetDamageCoeff(void) const { return m_DamageCoeff; } - - /// Sets the damage modifier coeff - void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; } - - /// Returns true if the specified player can pick the arrow up - bool CanPickup(const cPlayer & a_Player) const; - - /// Returns true if the arrow is set as critical - bool IsCritical(void) const { return m_IsCritical; } - - /// Sets the IsCritical flag - void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; } - - // tolua_end - -protected: - - /// Determines when the arrow can be picked up by players - ePickupState m_PickupState; - - /// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow - double m_DamageCoeff; - - /// If true, the arrow deals more damage - bool m_IsCritical; - - /// Timer for pickup collection animation or five minute timeout - float m_Timer; - - /// Timer for client arrow position confirmation via TeleportEntity - float m_HitGroundTimer; - - // Whether the arrow has already been teleported into the proper position in the ground. - bool m_HasTeleported; - - /// If true, the arrow is in the process of being collected - don't go to anyone else - bool m_bIsCollected; - - /// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air - Vector3i m_HitBlockPos; - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - virtual void CollectedBy(cPlayer * a_Player) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - - // tolua_begin -} ; - - - - - -class cThrownEggEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cThrownEggEntity); - - cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - // tolua_end - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; - virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - - // Randomly decides whether to spawn a chicken where the egg lands. - void TrySpawnChicken(const Vector3d & a_HitPos); - - // tolua_begin - -} ; - - - - - -class cThrownEnderPearlEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cThrownEnderPearlEntity); - - cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - // tolua_end - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; - virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - - // Teleports the creator where the ender pearl lands. - void TeleportCreator(const Vector3d & a_HitPos); - - // tolua_begin - -} ; - - - - - -class cThrownSnowballEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cThrownSnowballEntity); - - cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; - virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - - // tolua_begin - -} ; - - - - - -class cExpBottleEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cExpBottleEntity); - - cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; - - // tolua_begin - -}; - - - - - -class cFireworkEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cFireworkEntity); - - cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item); - const cItem & GetItem(void) const { return m_FireworkItem; } - -protected: - - // cProjectileEntity overrides: - virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override; - virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - -private: - - int m_ExplodeTimer; - cItem m_FireworkItem; - - // tolua_begin - -}; - - - - - -class cGhastFireballEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cGhastFireballEntity); - - cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - - // TODO: Deflecting the fireballs by arrow- or sword- hits - - // tolua_begin - -} ; - - - - - -class cFireChargeEntity : - public cProjectileEntity -{ - typedef cProjectileEntity super; - -public: - - // tolua_end - - CLASS_PROTODEF(cFireChargeEntity); - - cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); - -protected: - - void Explode(int a_BlockX, int a_BlockY, int a_BlockZ); - - // cProjectileEntity overrides: - virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; - virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override; - - // tolua_begin - -} ; - - - - -// tolua_end - - - +} ; // tolua_export diff --git a/src/Entities/ThrownEggEntity.cpp b/src/Entities/ThrownEggEntity.cpp new file mode 100644 index 000000000..224019091 --- /dev/null +++ b/src/Entities/ThrownEggEntity.cpp @@ -0,0 +1,59 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ThrownEggEntity.h" +#include "../World.h" + + + + + +cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +{ + TrySpawnChicken(a_HitPos); + + Destroy(); +} + + + + + +void cThrownEggEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + int TotalDamage = 0; + // TODO: If entity is Ender Crystal, destroy it + + TrySpawnChicken(a_HitPos); + a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1); + + Destroy(true); +} + + + + + +void cThrownEggEntity::TrySpawnChicken(const Vector3d & a_HitPos) +{ + if (m_World->GetTickRandomNumber(7) == 1) + { + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + } + else if (m_World->GetTickRandomNumber(32) == 1) + { + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken); + } +} diff --git a/src/Entities/ThrownEggEntity.h b/src/Entities/ThrownEggEntity.h new file mode 100644 index 000000000..5ba8f051b --- /dev/null +++ b/src/Entities/ThrownEggEntity.h @@ -0,0 +1,37 @@ +// +// ThrownEggEntity.h +// + +#pragma once + +#include "ProjectileEntity.h" + + + + + +// tolua_begin + +class cThrownEggEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cThrownEggEntity); + + cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; + virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + + // Randomly decides whether to spawn a chicken where the egg lands. + void TrySpawnChicken(const Vector3d & a_HitPos); + +} ; // tolua_export diff --git a/src/Entities/ThrownEnderPearlEntity.cpp b/src/Entities/ThrownEnderPearlEntity.cpp new file mode 100644 index 000000000..c37161145 --- /dev/null +++ b/src/Entities/ThrownEnderPearlEntity.cpp @@ -0,0 +1,54 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ThrownEnderPearlEntity.h" + + + + + +cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +{ + // TODO: Tweak a_HitPos based on block face. + TeleportCreator(a_HitPos); + + Destroy(); +} + + + + + +void cThrownEnderPearlEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + int TotalDamage = 0; + // TODO: If entity is Ender Crystal, destroy it + + TeleportCreator(a_HitPos); + a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1); + + Destroy(true); +} + + + + + +void cThrownEnderPearlEntity::TeleportCreator(const Vector3d & a_HitPos) +{ + // Teleport the creator here, make them take 5 damage: + if (m_Creator != NULL) + { + m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5); + m_Creator->TakeDamage(dtEnderPearl, this, 5, 0); + } +} diff --git a/src/Entities/ThrownEnderPearlEntity.h b/src/Entities/ThrownEnderPearlEntity.h new file mode 100644 index 000000000..ddee5babe --- /dev/null +++ b/src/Entities/ThrownEnderPearlEntity.h @@ -0,0 +1,37 @@ +// +// ThrownEnderPearlEntity.h +// + +#pragma once + +#include "ProjectileEntity.h" + + + + + +// tolua_begin + +class cThrownEnderPearlEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cThrownEnderPearlEntity); + + cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; + virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + + // Teleports the creator where the ender pearl lands. + void TeleportCreator(const Vector3d & a_HitPos); + +} ; // tolua_export diff --git a/src/Entities/ThrownSnowballEntity.cpp b/src/Entities/ThrownSnowballEntity.cpp new file mode 100644 index 000000000..427f630f7 --- /dev/null +++ b/src/Entities/ThrownSnowballEntity.cpp @@ -0,0 +1,48 @@ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ThrownSnowballEntity.h" +#include "../World.h" + + + + + +cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) : + super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) +{ + SetSpeed(a_Speed); +} + + + + + +void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) +{ + Destroy(); +} + + + + + +void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) +{ + int TotalDamage = 0; + if (a_EntityHit.IsMob()) + { + cMonster::eType MobType = ((cMonster &) a_EntityHit).GetMobType(); + if (MobType == cMonster::mtBlaze) + { + TotalDamage = 3; + } + else if (MobType == cMonster::mtEnderDragon) + { + TotalDamage = 1; + } + } + // TODO: If entity is Ender Crystal, destroy it + a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1); + + Destroy(true); +} diff --git a/src/Entities/ThrownSnowballEntity.h b/src/Entities/ThrownSnowballEntity.h new file mode 100644 index 000000000..a09512e37 --- /dev/null +++ b/src/Entities/ThrownSnowballEntity.h @@ -0,0 +1,34 @@ +// +// ThrownSnowballEntity.h +// + +#pragma once + +#include "ProjectileEntity.h" + + + + + +// tolua_begin + +class cThrownSnowballEntity : + public cProjectileEntity +{ + typedef cProjectileEntity super; + +public: + + // tolua_end + + CLASS_PROTODEF(cThrownSnowballEntity); + + cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed); + +protected: + + // cProjectileEntity overrides: + virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override; + virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override; + +} ; // tolua_export diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp index c45261947..42bf5f3f9 100644 --- a/src/FastRandom.cpp +++ b/src/FastRandom.cpp @@ -91,7 +91,8 @@ int cFastRandom::m_SeedCounter = 0; cFastRandom::cFastRandom(void) : - m_Seed(m_SeedCounter++) + m_Seed(m_SeedCounter++), + m_Counter(0) { } diff --git a/src/Generating/Prefab.cpp b/src/Generating/Prefab.cpp index c0c9e8a13..44d5097de 100644 --- a/src/Generating/Prefab.cpp +++ b/src/Generating/Prefab.cpp @@ -283,7 +283,7 @@ void cPrefab::ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef) if ((NumElements >= 3) && !CharDef[2].empty()) { BlockMeta = (NIBBLETYPE)atoi(CharDef[2].c_str()); - ASSERT((BlockMeta >= 0) && (BlockMeta <= 15)); + ASSERT((BlockMeta <= 15)); } a_CharMapOut[Src].m_BlockMeta = BlockMeta; } // for itr - Lines[] diff --git a/src/Globals.h b/src/Globals.h index 26a0d87a9..71e9191e4 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -264,7 +264,21 @@ template class SizeChecker<UInt16, 2>; // Same as assert but in all Self test builds #ifdef SELF_TEST -#define assert_test(x) ( !!(x) || (assert(!#x), exit(1), 0)) + #define assert_test(x) ( !!(x) || (assert(!#x), exit(1), 0)) +#endif + +// Allow both Older versions of MSVC and newer versions of everything use a shared_ptr: +// Note that we cannot typedef, because C++ doesn't allow (partial) templates to be typedeffed. +#if (defined(_MSC_VER) && (_MSC_VER < 1600)) + // MSVC before 2010 doesn't have std::shared_ptr, but has std::tr1::shared_ptr, defined in <memory> included earlier + #define SharedPtr std::tr1::shared_ptr +#elif (defined(_MSC_VER) || (__cplusplus >= 201103L)) + // C++11 has std::shared_ptr in <memory>, included earlier + #define SharedPtr std::shared_ptr +#else + // C++03 has std::tr1::shared_ptr in <tr1/memory> + #include <tr1/memory> + #define SharedPtr std::tr1::shared_ptr #endif @@ -296,7 +310,7 @@ T Clamp(T a_Value, T a_Min, T a_Max) #ifndef TOLUA_TEMPLATE_BIND -#define TOLUA_TEMPLATE_BIND(x) + #define TOLUA_TEMPLATE_BIND(x) #endif diff --git a/src/Item.h b/src/Item.h index 00316c188..8eb0a1f4e 100644 --- a/src/Item.h +++ b/src/Item.h @@ -64,7 +64,7 @@ public: { if (!IsValidItem(m_ItemType)) { - if (m_ItemType != E_BLOCK_AIR) + if ((m_ItemType != E_BLOCK_AIR) && (m_ItemType != E_ITEM_EMPTY)) { LOGWARNING("%s: creating an invalid item type (%d), resetting to empty.", __FUNCTION__, a_ItemType); } @@ -73,6 +73,10 @@ public: } + // The constructor is disabled in code, because the compiler generates it anyway, + // but it needs to stay because ToLua needs to generate the binding for it + #if 0 + /** Creates an exact copy of the item */ cItem(const cItem & a_CopyFrom) : m_ItemType (a_CopyFrom.m_ItemType), @@ -85,6 +89,8 @@ public: { } + #endif + void Empty(void) { diff --git a/src/Items/ItemBow.h b/src/Items/ItemBow.h index 410c5f512..8c0b3a0a3 100644 --- a/src/Items/ItemBow.h +++ b/src/Items/ItemBow.h @@ -9,7 +9,7 @@ #pragma once -#include "../Entities/ProjectileEntity.h" +#include "../Entities/ArrowEntity.h" diff --git a/src/Items/ItemSpawnEgg.h b/src/Items/ItemSpawnEgg.h index 0d6019398..bba97afa1 100644 --- a/src/Items/ItemSpawnEgg.h +++ b/src/Items/ItemSpawnEgg.h @@ -33,7 +33,10 @@ public: a_BlockY--; } - if (a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, (cMonster::eType)(a_Item.m_ItemDamage)) >= 0) + cMonster::eType MonsterType = ItemDamageToMonsterType(a_Item.m_ItemDamage); + if ( + (MonsterType != cMonster::mtInvalidType) && // Valid monster type + (a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, MonsterType) >= 0)) // Spawning succeeded { if (!a_Player->IsGameModeCreative()) { @@ -45,6 +48,41 @@ public: return false; } + + + /** Converts the Spawn egg item damage to the monster type to spawn. + Returns mtInvalidType for invalid damage values. */ + static cMonster::eType ItemDamageToMonsterType(short a_ItemDamage) + { + switch (a_ItemDamage) + { + case E_META_SPAWN_EGG_BAT: return cMonster::mtBat; + case E_META_SPAWN_EGG_BLAZE: return cMonster::mtBlaze; + case E_META_SPAWN_EGG_CAVE_SPIDER: return cMonster::mtCaveSpider; + case E_META_SPAWN_EGG_CHICKEN: return cMonster::mtChicken; + case E_META_SPAWN_EGG_COW: return cMonster::mtCow; + case E_META_SPAWN_EGG_CREEPER: return cMonster::mtCreeper; + case E_META_SPAWN_EGG_ENDERMAN: return cMonster::mtEnderman; + case E_META_SPAWN_EGG_GHAST: return cMonster::mtGhast; + case E_META_SPAWN_EGG_HORSE: return cMonster::mtHorse; + case E_META_SPAWN_EGG_MAGMA_CUBE: return cMonster::mtMagmaCube; + case E_META_SPAWN_EGG_MOOSHROOM: return cMonster::mtMooshroom; + case E_META_SPAWN_EGG_OCELOT: return cMonster::mtOcelot; + case E_META_SPAWN_EGG_PIG: return cMonster::mtPig; + case E_META_SPAWN_EGG_SHEEP: return cMonster::mtSheep; + case E_META_SPAWN_EGG_SILVERFISH: return cMonster::mtSilverfish; + case E_META_SPAWN_EGG_SKELETON: return cMonster::mtSkeleton; + case E_META_SPAWN_EGG_SLIME: return cMonster::mtSlime; + case E_META_SPAWN_EGG_SPIDER: return cMonster::mtSpider; + case E_META_SPAWN_EGG_SQUID: return cMonster::mtSquid; + case E_META_SPAWN_EGG_VILLAGER: return cMonster::mtVillager; + case E_META_SPAWN_EGG_WITCH: return cMonster::mtWitch; + case E_META_SPAWN_EGG_WOLF: return cMonster::mtWolf; + case E_META_SPAWN_EGG_ZOMBIE: return cMonster::mtZombie; + case E_META_SPAWN_EGG_ZOMBIE_PIGMAN: return cMonster::mtZombiePigman; + } + return cMonster::mtInvalidType; + } } ; diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index 84ff8929b..326b42f07 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -3,6 +3,7 @@ #include "Blaze.h" #include "../World.h" +#include "../Entities/FireChargeEntity.h" diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp index 3471b4cf1..9cf539427 100644 --- a/src/Mobs/Creeper.cpp +++ b/src/Mobs/Creeper.cpp @@ -75,9 +75,12 @@ void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer) -void cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } if (a_TDI.DamageType == dtLightning) { @@ -85,6 +88,7 @@ void cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI) } m_World->BroadcastEntityMetadata(*this); + return true; } diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h index 9abca369b..fc7db6716 100644 --- a/src/Mobs/Creeper.h +++ b/src/Mobs/Creeper.h @@ -18,7 +18,7 @@ public: CLASS_PROTODEF(cCreeper); virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void Attack(float a_Dt) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual void OnRightClicked(cPlayer & a_Player) override; diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp index fe18f5e76..d8a7663f8 100644 --- a/src/Mobs/Ghast.cpp +++ b/src/Mobs/Ghast.cpp @@ -3,6 +3,7 @@ #include "Ghast.h" #include "../World.h" +#include "../Entities/GhastFireballEntity.h" diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 14d951393..62670907f 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -457,9 +457,12 @@ int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ) -void cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } if((m_SoundHurt != "") && (m_Health > 0)) m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); @@ -468,6 +471,7 @@ void cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { m_Target = a_TDI.Attacker; } + return true; } @@ -823,6 +827,10 @@ cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) switch (a_MobType) { case mtMagmaCube: + { + toReturn = new cMagmaCube(Random.NextInt(2) + 1); + break; + } case mtSlime: { toReturn = new cSlime(Random.NextInt(2) + 1); diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 6b9c4fab8..7d7e90eb2 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -88,7 +88,7 @@ public: virtual void Tick(float a_Dt, cChunk & a_Chunk) override; - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void KilledBy(cEntity * a_Killer) override; diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp index 4b45f9a2a..24501b1ba 100644 --- a/src/Mobs/PassiveAggressiveMonster.cpp +++ b/src/Mobs/PassiveAggressiveMonster.cpp @@ -19,9 +19,12 @@ cPassiveAggressiveMonster::cPassiveAggressiveMonster(const AString & a_ConfigNam -void cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } if ((m_Target != NULL) && (m_Target->IsPlayer())) { @@ -30,6 +33,7 @@ void cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) m_EMState = CHASING; } } + return true; } diff --git a/src/Mobs/PassiveAggressiveMonster.h b/src/Mobs/PassiveAggressiveMonster.h index 2c5ef30b1..a0da50e8e 100644 --- a/src/Mobs/PassiveAggressiveMonster.h +++ b/src/Mobs/PassiveAggressiveMonster.h @@ -15,7 +15,7 @@ class cPassiveAggressiveMonster : public: cPassiveAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height); - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; } ; diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp index 904cd63cc..2861d7314 100644 --- a/src/Mobs/PassiveMonster.cpp +++ b/src/Mobs/PassiveMonster.cpp @@ -18,13 +18,17 @@ cPassiveMonster::cPassiveMonster(const AString & a_ConfigName, eType a_MobType, -void cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cPassiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } if ((a_TDI.Attacker != this) && (a_TDI.Attacker != NULL)) { m_EMState = ESCAPING; } + return true; } diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h index 0b3c155da..70574585a 100644 --- a/src/Mobs/PassiveMonster.h +++ b/src/Mobs/PassiveMonster.h @@ -18,7 +18,7 @@ public: virtual void Tick(float a_Dt, cChunk & a_Chunk) override; /// When hit by someone, run away - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; /** Returns the item that the animal of this class follows when a player holds it in hand Return an empty item not to follow (default). */ virtual const cItem GetFollowedItem(void) const { return cItem(); } diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index 1685f40c5..1e62d7987 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -3,6 +3,7 @@ #include "Skeleton.h" #include "../World.h" +#include "../Entities/ArrowEntity.h" diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp index d049acc1e..41283acf4 100644 --- a/src/Mobs/Villager.cpp +++ b/src/Mobs/Villager.cpp @@ -23,9 +23,13 @@ cVillager::cVillager(eVillagerType VillagerType) : -void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (!super::DoTakeDamage(a_TDI)) + { + return false; + } + if ((a_TDI.Attacker != NULL) && a_TDI.Attacker->IsPlayer()) { if (m_World->GetTickRandomNumber(5) == 3) @@ -33,6 +37,7 @@ void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI) m_World->BroadcastEntityStatus(*this, esVillagerAngry); } } + return true; } diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h index 5bba4d4ba..abde48407 100644 --- a/src/Mobs/Villager.h +++ b/src/Mobs/Villager.h @@ -30,7 +30,7 @@ public: CLASS_PROTODEF(cVillager); // cEntity overrides - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void Tick (float a_Dt, cChunk & a_Chunk) override; // cVillager functions diff --git a/src/Mobs/Wither.cpp b/src/Mobs/Wither.cpp index 8f5d28b68..5b6e895e1 100644 --- a/src/Mobs/Wither.cpp +++ b/src/Mobs/Wither.cpp @@ -10,7 +10,7 @@ cWither::cWither(void) : super("Wither", mtWither, "mob.wither.hurt", "mob.wither.death", 0.9, 4.0), - m_InvulnerableTicks(220) + m_WitherInvulnerableTicks(220) { SetMaxHealth(300); } @@ -40,24 +40,24 @@ bool cWither::Initialize(cWorld * a_World) -void cWither::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cWither::DoTakeDamage(TakeDamageInfo & a_TDI) { if (a_TDI.DamageType == dtDrowning) { - return; + return false; } - if (m_InvulnerableTicks > 0) + if (m_WitherInvulnerableTicks > 0) { - return; + return false; } if (IsArmored() && (a_TDI.DamageType == dtRangedAttack)) { - return; + return false; } - super::DoTakeDamage(a_TDI); + return super::DoTakeDamage(a_TDI); } @@ -68,16 +68,16 @@ void cWither::Tick(float a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - if (m_InvulnerableTicks > 0) + if (m_WitherInvulnerableTicks > 0) { - unsigned int NewTicks = m_InvulnerableTicks - 1; + unsigned int NewTicks = m_WitherInvulnerableTicks - 1; if (NewTicks == 0) { m_World->DoExplosionAt(7.0, GetPosX(), GetPosY(), GetPosZ(), false, esWitherBirth, this); } - m_InvulnerableTicks = NewTicks; + m_WitherInvulnerableTicks = NewTicks; if ((NewTicks % 10) == 0) { diff --git a/src/Mobs/Wither.h b/src/Mobs/Wither.h index bc78bfaad..08b460009 100644 --- a/src/Mobs/Wither.h +++ b/src/Mobs/Wither.h @@ -17,9 +17,9 @@ public: CLASS_PROTODEF(cWither); - unsigned int GetNumInvulnerableTicks(void) const { return m_InvulnerableTicks; } + unsigned int GetWitherInvulnerableTicks(void) const { return m_WitherInvulnerableTicks; } - void SetNumInvulnerableTicks(unsigned int a_Ticks) { m_InvulnerableTicks = a_Ticks; } + void SetWitherInvulnerableTicks(unsigned int a_Ticks) { m_WitherInvulnerableTicks = a_Ticks; } /** Returns whether the wither is invulnerable to arrows. */ bool IsArmored(void) const; @@ -27,13 +27,13 @@ public: // cEntity overrides virtual bool Initialize(cWorld * a_World) override; virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; private: /** The number of ticks of invulnerability left after being initially created. Zero once invulnerability has expired. */ - unsigned int m_InvulnerableTicks; + unsigned int m_WitherInvulnerableTicks; } ; diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index f02b8a4fc..e6268abc7 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -25,14 +25,19 @@ cWolf::cWolf(void) : -void cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) +bool cWolf::DoTakeDamage(TakeDamageInfo & a_TDI) { - super::DoTakeDamage(a_TDI); + if (super::DoTakeDamage(a_TDI)) + { + return false; + } + if (!m_IsTame) { m_IsAngry = true; } m_World->BroadcastEntityMetadata(*this); // Broadcast health and possibly angry face + return true; } diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h index 5925373e1..fb8a7c995 100644 --- a/src/Mobs/Wolf.h +++ b/src/Mobs/Wolf.h @@ -18,7 +18,7 @@ public: CLASS_PROTODEF(cWolf); - virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override; + virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override; virtual void OnRightClicked(cPlayer & a_Player) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual void TickFollowPlayer(); diff --git a/src/Noise.cpp b/src/Noise.cpp index efbb128c3..13a194938 100644 --- a/src/Noise.cpp +++ b/src/Noise.cpp @@ -608,6 +608,8 @@ void cCubicNoise::Generate2D( NOISE_DATATYPE a_StartY, NOISE_DATATYPE a_EndY ///< Noise-space coords of the array in the Y direction ) const { + ASSERT(a_SizeX > 0); + ASSERT(a_SizeY > 0); ASSERT(a_SizeX < MAX_SIZE); ASSERT(a_SizeY < MAX_SIZE); ASSERT(a_StartX < a_EndX); @@ -744,6 +746,8 @@ void cCubicNoise::CalcFloorFrac( int * a_Same, int & a_NumSame ) const { + ASSERT(a_Size > 0); + NOISE_DATATYPE val = a_Start; NOISE_DATATYPE dif = (a_End - a_Start) / (a_Size - 1); for (int i = 0; i < a_Size; i++) diff --git a/src/Noise.h b/src/Noise.h index 62004503f..e605051b5 100644 --- a/src/Noise.h +++ b/src/Noise.h @@ -280,7 +280,7 @@ NOISE_DATATYPE cNoise::CubicInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE cNoise::CosineInterpolate(NOISE_DATATYPE a_A, NOISE_DATATYPE a_B, NOISE_DATATYPE a_Pct) { const NOISE_DATATYPE ft = a_Pct * (NOISE_DATATYPE)3.1415927; - const NOISE_DATATYPE f = (1 - cos(ft)) * (NOISE_DATATYPE)0.5; + const NOISE_DATATYPE f = (NOISE_DATATYPE)((NOISE_DATATYPE)(1 - cos(ft)) * (NOISE_DATATYPE)0.5); return a_A * (1 - f) + a_B * f; } diff --git a/src/OSSupport/BlockingTCPLink.cpp b/src/OSSupport/BlockingTCPLink.cpp deleted file mode 100644 index 07f48b955..000000000 --- a/src/OSSupport/BlockingTCPLink.cpp +++ /dev/null @@ -1,142 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "BlockingTCPLink.h" -#include "Errors.h" - - - - -cBlockingTCPLink::cBlockingTCPLink(void) -{ -} - - - - - -cBlockingTCPLink::~cBlockingTCPLink() -{ - CloseSocket(); -} - - - - - -void cBlockingTCPLink::CloseSocket() -{ - if (!m_Socket.IsValid()) - { - m_Socket.CloseSocket(); - } -} - - - - - -bool cBlockingTCPLink::Connect(const char * iAddress, unsigned int iPort) -{ - ASSERT(!m_Socket.IsValid()); - if (m_Socket.IsValid()) - { - LOGWARN("WARNING: cTCPLink Connect() called while still connected."); - m_Socket.CloseSocket(); - } - - struct hostent *hp; - unsigned int addr; - struct sockaddr_in server; - - m_Socket = socket(AF_INET, SOCK_STREAM, 0); - if (!m_Socket.IsValid()) - { - LOGERROR("cTCPLink: Cannot create a socket"); - return false; - } - - addr = inet_addr(iAddress); - hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); - if (hp == NULL) - { - //LOGWARN("cTCPLink: gethostbyaddr returned NULL"); - hp = gethostbyname(iAddress); - if (hp == NULL) - { - LOGWARN("cTCPLink: Could not resolve %s", iAddress); - CloseSocket(); - return false; - } - } - - memcpy(&server.sin_addr.s_addr,hp->h_addr, hp->h_length); - server.sin_family = AF_INET; - server.sin_port = htons( (unsigned short)iPort); - if (connect(m_Socket, (struct sockaddr *)&server, sizeof(server))) - { - LOGWARN("cTCPLink: Connection to \"%s:%d\" failed (%s)", iAddress, iPort,GetOSErrorString( cSocket::GetLastError() ).c_str() ); - CloseSocket(); - return false; - } - - return true; -} - - - - - -int cBlockingTCPLink::Send(char * a_Data, unsigned int a_Size, int a_Flags /* = 0 */ ) -{ - UNUSED(a_Flags); - - ASSERT(m_Socket.IsValid()); - if (!m_Socket.IsValid()) - { - LOGERROR("cBlockingTCPLink: Trying to send data without a valid connection!"); - return -1; - } - return m_Socket.Send(a_Data, a_Size); -} - - - - - -int cBlockingTCPLink::SendMessage( const char* a_Message, int a_Flags /* = 0 */ ) -{ - UNUSED(a_Flags); - - ASSERT(m_Socket.IsValid()); - if (!m_Socket.IsValid()) - { - LOGWARN("cBlockingTCPLink: Trying to send message without a valid connection!"); - return -1; - } - return m_Socket.Send(a_Message, strlen(a_Message)); -} - - - - - -void cBlockingTCPLink::ReceiveData(AString & oData) -{ - ASSERT(m_Socket.IsValid()); - if (!m_Socket.IsValid()) - { - return; - } - - int Received = 0; - char Buffer[256]; - while ((Received = recv(m_Socket, Buffer, sizeof(Buffer), 0)) > 0) - { - oData.append(Buffer, Received); - } -} - - - - diff --git a/src/OSSupport/BlockingTCPLink.h b/src/OSSupport/BlockingTCPLink.h deleted file mode 100644 index cb5f9e3f4..000000000 --- a/src/OSSupport/BlockingTCPLink.h +++ /dev/null @@ -1,28 +0,0 @@ - -#pragma once - -#include "Socket.h" - - - - - -class cBlockingTCPLink // tolua_export -{ // tolua_export -public: // tolua_export - cBlockingTCPLink(void); // tolua_export - ~cBlockingTCPLink(); // tolua_export - - bool Connect( const char* a_Address, unsigned int a_Port ); // tolua_export - int Send( char* a_Data, unsigned int a_Size, int a_Flags = 0 ); // tolua_export - int SendMessage( const char* a_Message, int a_Flags = 0 ); // tolua_export - void CloseSocket(); // tolua_export - void ReceiveData(AString & oData); // tolua_export -protected: - - cSocket m_Socket; -}; // tolua_export - - - - diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp index f1b3bcf9e..8c24fa541 100644 --- a/src/OSSupport/File.cpp +++ b/src/OSSupport/File.cpp @@ -75,7 +75,7 @@ bool cFile::Open(const AString & iFileName, eMode iMode) } #ifdef _WIN32 - fopen_s(&m_File, (FILE_IO_PREFIX + iFileName).c_str(), Mode); + m_File = _fsopen((FILE_IO_PREFIX + iFileName).c_str(), Mode, _SH_DENYWR); #else m_File = fopen((FILE_IO_PREFIX + iFileName).c_str(), Mode); #endif // _WIN32 @@ -88,7 +88,7 @@ bool cFile::Open(const AString & iFileName, eMode iMode) // Simply re-open for read-writing, erasing existing contents: #ifdef _WIN32 - fopen_s(&m_File, (FILE_IO_PREFIX + iFileName).c_str(), "wb+"); + m_File = _fsopen((FILE_IO_PREFIX + iFileName).c_str(), "wb+", _SH_DENYWR); #else m_File = fopen((FILE_IO_PREFIX + iFileName).c_str(), "wb+"); #endif // _WIN32 @@ -143,7 +143,7 @@ bool cFile::IsEOF(void) const -int cFile::Read (void * iBuffer, int iNumBytes) +int cFile::Read (void * iBuffer, size_t iNumBytes) { ASSERT(IsOpen()); @@ -159,7 +159,7 @@ int cFile::Read (void * iBuffer, int iNumBytes) -int cFile::Write(const void * iBuffer, int iNumBytes) +int cFile::Write(const void * iBuffer, size_t iNumBytes) { ASSERT(IsOpen()); diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h index b394c5cb9..2a7ecf0ed 100644 --- a/src/OSSupport/File.h +++ b/src/OSSupport/File.h @@ -80,10 +80,10 @@ public: bool IsEOF(void) const; /** Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open */ - int Read (void * iBuffer, int iNumBytes); + int Read (void * iBuffer, size_t iNumBytes); /** Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open */ - int Write(const void * iBuffer, int iNumBytes); + int Write(const void * iBuffer, size_t iNumBytes); /** Seeks to iPosition bytes from file start, returns old position or -1 for failure; asserts if not open */ int Seek (int iPosition); diff --git a/src/OSSupport/GZipFile.cpp b/src/OSSupport/GZipFile.cpp index 7a8433f4f..22d887783 100644 --- a/src/OSSupport/GZipFile.cpp +++ b/src/OSSupport/GZipFile.cpp @@ -11,7 +11,7 @@ cGZipFile::cGZipFile(void) : - m_File(NULL) + m_File(NULL), m_Mode(fmRead) { } diff --git a/src/OSSupport/Socket.cpp b/src/OSSupport/Socket.cpp index c29e495c3..98f694a79 100644 --- a/src/OSSupport/Socket.cpp +++ b/src/OSSupport/Socket.cpp @@ -295,7 +295,7 @@ bool cSocket::ConnectToLocalhostIPv4(unsigned short a_Port) bool cSocket::ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Port) { - // First try IP Address string to hostent conversion, because it's faster + // First try IP Address string to hostent conversion, because it's faster and local: unsigned long addr = inet_addr(a_HostNameOrAddr.c_str()); if (addr == INADDR_NONE) { @@ -307,10 +307,16 @@ bool cSocket::ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Por CloseSocket(); return false; } - // Should be optimised to a single word copy memcpy(&addr, hp->h_addr, hp->h_length); } + // If the socket is not created yet, create one: + if (!IsValid()) + { + m_Socket = socket((int)IPv4, SOCK_STREAM, 0); + } + + // Connect the socket: sockaddr_in server; server.sin_addr.s_addr = addr; server.sin_family = AF_INET; diff --git a/src/PolarSSL++/AesCfb128Decryptor.cpp b/src/PolarSSL++/AesCfb128Decryptor.cpp new file mode 100644 index 000000000..af0d5106e --- /dev/null +++ b/src/PolarSSL++/AesCfb128Decryptor.cpp @@ -0,0 +1,67 @@ + +// AesCfb128Decryptor.cpp + +// Implements the cAesCfb128Decryptor class decrypting data using AES CFB-128 + +#include "Globals.h" +#include "AesCfb128Decryptor.h" + + + + + +cAesCfb128Decryptor::cAesCfb128Decryptor(void) : + m_IVOffset(0), + m_IsValid(false) +{ +} + + + + + +cAesCfb128Decryptor::~cAesCfb128Decryptor() +{ + // Clear the leftover in-memory data, so that they can't be accessed by a backdoor + memset(&m_Aes, 0, sizeof(m_Aes)); +} + + + + + +void cAesCfb128Decryptor::Init(const Byte a_Key[16], const Byte a_IV[16]) +{ + ASSERT(!IsValid()); // Cannot Init twice + + memcpy(m_IV, a_IV, 16); + aes_setkey_enc(&m_Aes, a_Key, 128); + m_IsValid = true; +} + + + + + +void cAesCfb128Decryptor::ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length) +{ + ASSERT(IsValid()); // Must Init() first + + // PolarSSL doesn't support AES-CFB8, need to implement it manually: + for (size_t i = 0; i < a_Length; i++) + { + Byte Buffer[sizeof(m_IV)]; + aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer); + for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++) + { + m_IV[idx] = m_IV[idx + 1]; + } + m_IV[sizeof(m_IV) - 1] = a_EncryptedIn[i]; + a_DecryptedOut[i] = a_EncryptedIn[i] ^ Buffer[0]; + } +} + + + + + diff --git a/src/PolarSSL++/AesCfb128Decryptor.h b/src/PolarSSL++/AesCfb128Decryptor.h new file mode 100644 index 000000000..68c203d70 --- /dev/null +++ b/src/PolarSSL++/AesCfb128Decryptor.h @@ -0,0 +1,52 @@ + +// AesCfb128Decryptor.h + +// Declares the cAesCfb128Decryptor class decrypting data using AES CFB-128 + + + + + +#pragma once + +#include "polarssl/aes.h" + + + + + +/** Decrypts data using the AES / CFB 128 algorithm */ +class cAesCfb128Decryptor +{ +public: + Byte test; + + cAesCfb128Decryptor(void); + ~cAesCfb128Decryptor(); + + /** Initializes the decryptor with the specified Key / IV */ + void Init(const Byte a_Key[16], const Byte a_IV[16]); + + /** Decrypts a_Length bytes of the encrypted data; produces a_Length output bytes */ + void ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length); + + /** Returns true if the object has been initialized with the Key / IV */ + bool IsValid(void) const { return m_IsValid; } + +protected: + aes_context m_Aes; + + /** The InitialVector, used by the CFB mode decryption */ + Byte m_IV[16]; + + /** Current offset in the m_IV, used by the CFB mode decryption */ + size_t m_IVOffset; + + /** Indicates whether the object has been initialized with the Key / IV */ + bool m_IsValid; +} ; + + + + + diff --git a/src/PolarSSL++/AesCfb128Encryptor.cpp b/src/PolarSSL++/AesCfb128Encryptor.cpp new file mode 100644 index 000000000..a641ad48e --- /dev/null +++ b/src/PolarSSL++/AesCfb128Encryptor.cpp @@ -0,0 +1,68 @@ + +// AesCfb128Encryptor.cpp + +// Implements the cAesCfb128Encryptor class encrypting data using AES CFB-128 + +#include "Globals.h" +#include "AesCfb128Encryptor.h" + + + + + +cAesCfb128Encryptor::cAesCfb128Encryptor(void) : + m_IVOffset(0), + m_IsValid(false) +{ +} + + + + + +cAesCfb128Encryptor::~cAesCfb128Encryptor() +{ + // Clear the leftover in-memory data, so that they can't be accessed by a backdoor + memset(&m_Aes, 0, sizeof(m_Aes)); +} + + + + + +void cAesCfb128Encryptor::Init(const Byte a_Key[16], const Byte a_IV[16]) +{ + ASSERT(!IsValid()); // Cannot Init twice + ASSERT(m_IVOffset == 0); + + memcpy(m_IV, a_IV, 16); + aes_setkey_enc(&m_Aes, a_Key, 128); + m_IsValid = true; +} + + + + + +void cAesCfb128Encryptor::ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length) +{ + ASSERT(IsValid()); // Must Init() first + + // PolarSSL doesn't do AES-CFB8, so we need to implement it ourselves: + for (size_t i = 0; i < a_Length; i++) + { + Byte Buffer[sizeof(m_IV)]; + aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer); + for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++) + { + m_IV[idx] = m_IV[idx + 1]; + } + a_EncryptedOut[i] = a_PlainIn[i] ^ Buffer[0]; + m_IV[sizeof(m_IV) - 1] = a_EncryptedOut[i]; + } +} + + + + + diff --git a/src/PolarSSL++/AesCfb128Encryptor.h b/src/PolarSSL++/AesCfb128Encryptor.h new file mode 100644 index 000000000..9dbb5d2c3 --- /dev/null +++ b/src/PolarSSL++/AesCfb128Encryptor.h @@ -0,0 +1,50 @@ + +// AesCfb128Encryptor.h + +// Declares the cAesCfb128Encryptor class encrypting data using AES CFB-128 + + + + + +#pragma once + +#include "polarssl/aes.h" + + + + + +/** Encrypts data using the AES / CFB (128) algorithm */ +class cAesCfb128Encryptor +{ +public: + cAesCfb128Encryptor(void); + ~cAesCfb128Encryptor(); + + /** Initializes the decryptor with the specified Key / IV */ + void Init(const Byte a_Key[16], const Byte a_IV[16]); + + /** Encrypts a_Length bytes of the plain data; produces a_Length output bytes */ + void ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length); + + /** Returns true if the object has been initialized with the Key / IV */ + bool IsValid(void) const { return m_IsValid; } + +protected: + aes_context m_Aes; + + /** The InitialVector, used by the CFB mode encryption */ + Byte m_IV[16]; + + /** Current offset in the m_IV, used by the CFB mode encryption */ + size_t m_IVOffset; + + /** Indicates whether the object has been initialized with the Key / IV */ + bool m_IsValid; +} ; + + + + + diff --git a/src/PolarSSL++/BlockingSslClientSocket.cpp b/src/PolarSSL++/BlockingSslClientSocket.cpp new file mode 100644 index 000000000..699bc57ee --- /dev/null +++ b/src/PolarSSL++/BlockingSslClientSocket.cpp @@ -0,0 +1,195 @@ + +// BlockingSslClientSocket.cpp + +// Implements the cBlockingSslClientSocket class representing a blocking TCP socket with client SSL encryption over it + +#include "Globals.h" +#include "BlockingSslClientSocket.h" + + + + + +cBlockingSslClientSocket::cBlockingSslClientSocket(void) : + m_Ssl(*this), + m_IsConnected(false) +{ + // Nothing needed yet +} + + + + + +bool cBlockingSslClientSocket::Connect(const AString & a_ServerName, UInt16 a_Port) +{ + // If already connected, report an error: + if (m_IsConnected) + { + // TODO: Handle this better - if connected to the same server and port, and the socket is alive, return success + m_LastErrorText = "Already connected"; + return false; + } + + // Connect the underlying socket: + m_Socket.CreateSocket(cSocket::IPv4); + if (!m_Socket.ConnectIPv4(a_ServerName.c_str(), a_Port)) + { + Printf(m_LastErrorText, "Socket connect failed: %s", m_Socket.GetLastErrorString().c_str()); + return false; + } + + // Initialize the SSL: + int ret = m_Ssl.Initialize(true); + if (ret != 0) + { + Printf(m_LastErrorText, "SSL initialization failed: -0x%x", -ret); + return false; + } + + // If we have been assigned a trusted CA root cert store, push it into the SSL context: + if (m_CACerts.get() != NULL) + { + m_Ssl.SetCACerts(m_CACerts, m_ExpectedPeerName); + } + + ret = m_Ssl.Handshake(); + if (ret != 0) + { + Printf(m_LastErrorText, "SSL handshake failed: -0x%x", -ret); + return false; + } + + m_IsConnected = true; + return true; +} + + + + + + +bool cBlockingSslClientSocket::SetTrustedRootCertsFromString(const AString & a_CACerts, const AString & a_ExpectedPeerName) +{ + // Warn if used multiple times, but don't signal an error: + if (m_CACerts.get() != NULL) + { + LOGWARNING( + "SSL: Trying to set multiple trusted CA root cert stores, only the last one will be used. Name: %s", + a_ExpectedPeerName.c_str() + ); + } + + // Parse the cert: + m_CACerts.reset(new cX509Cert); + int ret = m_CACerts->Parse(a_CACerts.data(), a_CACerts.size()); + if (ret < 0) + { + Printf(m_LastErrorText, "CA cert parsing failed: -0x%x", -ret); + return false; + } + m_ExpectedPeerName = a_ExpectedPeerName; + + return true; +} + + + + + +bool cBlockingSslClientSocket::Send(const void * a_Data, size_t a_NumBytes) +{ + ASSERT(m_IsConnected); + + // Keep sending the data until all of it is sent: + const char * Data = (const char *)a_Data; + size_t NumBytes = a_NumBytes; + for (;;) + { + int res = m_Ssl.WritePlain(a_Data, a_NumBytes); + if (res < 0) + { + ASSERT(res != POLARSSL_ERR_NET_WANT_READ); // This should never happen with callback-based SSL + ASSERT(res != POLARSSL_ERR_NET_WANT_WRITE); // This should never happen with callback-based SSL + Printf(m_LastErrorText, "Data cannot be written to SSL context: -0x%x", -res); + return false; + } + else + { + Data += res; + NumBytes -= res; + if (NumBytes == 0) + { + return true; + } + } + } +} + + + + + + +int cBlockingSslClientSocket::Receive(void * a_Data, size_t a_MaxBytes) +{ + ASSERT(m_IsConnected); + + int res = m_Ssl.ReadPlain(a_Data, a_MaxBytes); + if (res < 0) + { + Printf(m_LastErrorText, "Data cannot be read form SSL context: -0x%x", -res); + } + return res; +} + + + + + +void cBlockingSslClientSocket::Disconnect(void) +{ + // Ignore if not connected + if (!m_IsConnected) + { + return; + } + + m_Ssl.NotifyClose(); + m_Socket.CloseSocket(); + m_IsConnected = false; +} + + + + + +int cBlockingSslClientSocket::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) +{ + int res = m_Socket.Receive((char *)a_Buffer, a_NumBytes, 0); + if (res < 0) + { + // PolarSSL's net routines distinguish between connection reset and general failure, we don't need to + return POLARSSL_ERR_NET_RECV_FAILED; + } + return res; +} + + + + + +int cBlockingSslClientSocket::SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) +{ + int res = m_Socket.Send((const char *)a_Buffer, a_NumBytes); + if (res < 0) + { + // PolarSSL's net routines distinguish between connection reset and general failure, we don't need to + return POLARSSL_ERR_NET_SEND_FAILED; + } + return res; +} + + + + diff --git a/src/PolarSSL++/BlockingSslClientSocket.h b/src/PolarSSL++/BlockingSslClientSocket.h new file mode 100644 index 000000000..7af897582 --- /dev/null +++ b/src/PolarSSL++/BlockingSslClientSocket.h @@ -0,0 +1,80 @@ + +// BlockingSslClientSocket.h + +// Declares the cBlockingSslClientSocket class representing a blocking TCP socket with client SSL encryption over it + + + + + +#pragma once + +#include "CallbackSslContext.h" +#include "../OSSupport/Socket.h" + + + + + +class cBlockingSslClientSocket : + protected cCallbackSslContext::cDataCallbacks +{ +public: + cBlockingSslClientSocket(void); + + /** Connects to the specified server and performs SSL handshake. + Returns true if successful, false on failure. Sets internal error text on failure. */ + bool Connect(const AString & a_ServerName, UInt16 a_Port); + + /** Sends the specified data over the connection. + Returns true if successful, false on failure. Sets the internal error text on failure. */ + bool Send(const void * a_Data, size_t a_NumBytes); + + /** Receives data from the connection. + Blocks until there is any data available, then returns as much as possible. + Returns the number of bytes actually received, negative number on failure. + Sets the internal error text on failure. */ + int Receive(void * a_Data, size_t a_MaxBytes); + + /** Disconnects the connection gracefully, if possible. + Note that this also frees the internal SSL context, so all the certificates etc. are lost. */ + void Disconnect(void); + + /** Sets the root certificates that are to be trusted. Forces the connection to use strict cert + verification. Needs to be used before calling Connect(). + a_ExpectedPeerName is the name that we expect to receive in the SSL peer's cert; verification will fail if + the presented name is different (possible MITM). + Returns true on success, false on failure. Sets internal error text on failure. */ + bool SetTrustedRootCertsFromString(const AString & a_CACerts, const AString & a_ExpectedPeerName); + + /** Returns the text of the last error that has occurred in this instance. */ + const AString & GetLastErrorText(void) const { return m_LastErrorText; } + +protected: + /** The SSL context used for the socket */ + cCallbackSslContext m_Ssl; + + /** The underlying socket to the SSL server */ + cSocket m_Socket; + + /** The trusted CA root cert store, if we are to verify the cert strictly. Set by SetTrustedRootCertsFromString(). */ + cX509CertPtr m_CACerts; + + /** The expected SSL peer's name, if we are to verify the cert strictly. Set by SetTrustedRootCertsFromString(). */ + AString m_ExpectedPeerName; + + /** Text of the last error that has occurred. */ + AString m_LastErrorText; + + /** Set to true if the connection established successfully. */ + bool m_IsConnected; + + + // cCallbackSslContext::cDataCallbacks overrides: + virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) override; + virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) override; +} ; + + + + diff --git a/src/PolarSSL++/BufferedSslContext.cpp b/src/PolarSSL++/BufferedSslContext.cpp new file mode 100644 index 000000000..885b30c68 --- /dev/null +++ b/src/PolarSSL++/BufferedSslContext.cpp @@ -0,0 +1,62 @@ + +// BufferedSslContext.cpp + +// Implements the cBufferedSslContext class representing a SSL context with the SSL peer data backed by a cByteBuffer + +#include "Globals.h" +#include "BufferedSslContext.h" + + + + + +cBufferedSslContext::cBufferedSslContext(size_t a_BufferSize): + m_OutgoingData(a_BufferSize), + m_IncomingData(a_BufferSize) +{ +} + + + + + +int cBufferedSslContext::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) +{ + // Called when PolarSSL wants to read encrypted data from the SSL peer + // Read the data from the buffer inside this object, where the owner has stored them using WriteIncoming(): + size_t NumBytes = std::min(a_NumBytes, m_IncomingData.GetReadableSpace()); + if (NumBytes == 0) + { + return POLARSSL_ERR_NET_WANT_READ; + } + if (!m_IncomingData.ReadBuf(a_Buffer, NumBytes)) + { + m_IncomingData.ResetRead(); + return POLARSSL_ERR_NET_RECV_FAILED; + } + m_IncomingData.CommitRead(); + return (int)NumBytes; +} + + + + + +int cBufferedSslContext::SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) +{ + // Called when PolarSSL wants to write encrypted data to the SSL peer + // Write the data into the buffer inside this object, where the owner can later read them using ReadOutgoing(): + if (!m_OutgoingData.CanWriteBytes(a_NumBytes)) + { + return POLARSSL_ERR_NET_WANT_WRITE; + } + if (!m_OutgoingData.Write((const char *)a_Buffer, a_NumBytes)) + { + return POLARSSL_ERR_NET_SEND_FAILED; + } + return (int)a_NumBytes; +} + + + + diff --git a/src/PolarSSL++/BufferedSslContext.h b/src/PolarSSL++/BufferedSslContext.h new file mode 100644 index 000000000..1b7e1af46 --- /dev/null +++ b/src/PolarSSL++/BufferedSslContext.h @@ -0,0 +1,52 @@ + +// BufferedSslContext.h + +// Declares the cBufferedSslContext class representing a SSL context with the SSL peer data backed by a cByteBuffer + + + + + +#pragma once + +#include "SslContext.h" + + + + + +class cBufferedSslContext : + public cSslContext +{ + typedef cSslContext super; + +public: + /** Creates a new context with the buffers of specified size for the encrypted / decrypted data. */ + cBufferedSslContext(size_t a_BufferSize = 64000); + + /** Stores the specified data in the "incoming" buffer, to be process by the SSL decryptor. + This is the data received from the SSL peer. + Returns the number of bytes actually stored. If 0 is returned, owner should check the error state. */ + size_t WriteIncoming(const void * a_Data, size_t a_NumBytes); + + /** Retrieves data from the "outgoing" buffer, after being processed by the SSL encryptor. + This is the data to be sent to the SSL peer. + Returns the number of bytes actually retrieved. */ + size_t ReadOutgoing(void * a_Data, size_t a_DataMaxSize); + +protected: + /** Buffer for the data that has been encrypted into the SSL stream and should be sent out. */ + cByteBuffer m_OutgoingData; + + /** Buffer for the data that has come in and needs to be decrypted from the SSL stream. */ + cByteBuffer m_IncomingData; + + + // cSslContext overrides: + virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) override; + virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) override; +} ; + + + + diff --git a/src/PolarSSL++/CMakeLists.txt b/src/PolarSSL++/CMakeLists.txt new file mode 100644 index 000000000..b0a592760 --- /dev/null +++ b/src/PolarSSL++/CMakeLists.txt @@ -0,0 +1,41 @@ + +cmake_minimum_required (VERSION 2.6) +project (MCServer) + +include_directories ("${PROJECT_SOURCE_DIR}/../") + +set(SOURCES + AesCfb128Decryptor.cpp + AesCfb128Encryptor.cpp + BlockingSslClientSocket.cpp + BufferedSslContext.cpp + CallbackSslContext.cpp + CtrDrbgContext.cpp + EntropyContext.cpp + PublicKey.cpp + RsaPrivateKey.cpp + Sha1Checksum.cpp + SslContext.cpp + X509Cert.cpp +) + +set(HEADERS + AesCfb128Decryptor.h + AesCfb128Encryptor.h + BlockingSslClientSocket.h + BufferedSslContext.h + CallbackSslContext.h + CtrDrbgContext.h + EntropyContext.h + PublicKey.h + RsaPrivateKey.h + SslContext.h + Sha1Checksum.h + X509Cert.h +) + +add_library(PolarSSL++ ${SOURCES} ${HEADERS}) + +if (UNIX) + target_link_libraries(PolarSSL++ polarssl) +endif() diff --git a/src/PolarSSL++/CallbackSslContext.cpp b/src/PolarSSL++/CallbackSslContext.cpp new file mode 100644 index 000000000..0cc88a14a --- /dev/null +++ b/src/PolarSSL++/CallbackSslContext.cpp @@ -0,0 +1,59 @@ + +// CallbackSslContext.cpp + +// Declares the cCallbackSslContext class representing a SSL context wrapper that uses callbacks to read and write SSL peer data + +#include "Globals.h" +#include "CallbackSslContext.h" + + + + + + +cCallbackSslContext::cCallbackSslContext(void) +{ + // Nothing needed, but the constructor needs to exist so +} + + + + + +cCallbackSslContext::cCallbackSslContext(cCallbackSslContext::cDataCallbacks & a_Callbacks) : + m_Callbacks(&a_Callbacks) +{ +} + + + + + +int cCallbackSslContext::ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) +{ + if (m_Callbacks == NULL) + { + LOGWARNING("SSL: Trying to receive data with no callbacks, aborting."); + return POLARSSL_ERR_NET_RECV_FAILED; + } + return m_Callbacks->ReceiveEncrypted(a_Buffer, a_NumBytes); +} + + + + + +int cCallbackSslContext::SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) +{ + if (m_Callbacks == NULL) + { + LOGWARNING("SSL: Trying to send data with no callbacks, aborting."); + return POLARSSL_ERR_NET_SEND_FAILED; + } + return m_Callbacks->SendEncrypted(a_Buffer, a_NumBytes); +} + + + + + diff --git a/src/PolarSSL++/CallbackSslContext.h b/src/PolarSSL++/CallbackSslContext.h new file mode 100644 index 000000000..3e6edc5f4 --- /dev/null +++ b/src/PolarSSL++/CallbackSslContext.h @@ -0,0 +1,64 @@ + +// CallbackSslContext.h + +// Declares the cCallbackSslContext class representing a SSL context wrapper that uses callbacks to read and write SSL peer data + + + + + +#pragma once + +#include "SslContext.h" + + + + + +class cCallbackSslContext : + public cSslContext +{ +public: + /** Interface used as a data sink for the SSL peer data. */ + class cDataCallbacks + { + public: + // Force a virtual destructor in descendants: + virtual ~cDataCallbacks() {} + + /** Called when PolarSSL wants to read encrypted data from the SSL peer. + The returned value is the number of bytes received, or a PolarSSL error on failure. + The implementation can return POLARSSL_ERR_NET_WANT_READ or POLARSSL_ERR_NET_WANT_WRITE to indicate + that there's currently no more data and that there might be more data in the future. In such cases the + SSL operation that invoked this call will terminate with the same return value, so that the owner is + notified of this condition and can potentially restart the operation later on. */ + virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) = 0; + + /** Called when PolarSSL wants to write encrypted data to the SSL peer. + The returned value is the number of bytes sent, or a PolarSSL error on failure. + The implementation can return POLARSSL_ERR_NET_WANT_READ or POLARSSL_ERR_NET_WANT_WRITE to indicate + that there's currently no more data and that there might be more data in the future. In such cases the + SSL operation that invoked this call will terminate with the same return value, so that the owner is + notified of this condition and can potentially restart the operation later on. */ + virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) = 0; + } ; + + + /** Creates a new SSL context with no callbacks assigned */ + cCallbackSslContext(void); + + /** Creates a new SSL context with the specified callbacks */ + cCallbackSslContext(cDataCallbacks & a_Callbacks); + +protected: + /** The callbacks to use to send and receive SSL peer data */ + cDataCallbacks * m_Callbacks; + + // cSslContext overrides: + virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) override; + virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) override; +}; + + + + diff --git a/src/PolarSSL++/CtrDrbgContext.cpp b/src/PolarSSL++/CtrDrbgContext.cpp new file mode 100644 index 000000000..86e6d1ca5 --- /dev/null +++ b/src/PolarSSL++/CtrDrbgContext.cpp @@ -0,0 +1,49 @@ + +// CtrDrbgContext.cpp + +// Implements the cCtrDrbgContext class representing a wrapper over CTR-DRBG implementation in PolarSSL + +#include "Globals.h" +#include "CtrDrbgContext.h" +#include "EntropyContext.h" + + + + + +cCtrDrbgContext::cCtrDrbgContext(void) : + m_EntropyContext(new cEntropyContext), + m_IsValid(false) +{ +} + + + + + +cCtrDrbgContext::cCtrDrbgContext(const SharedPtr<cEntropyContext> & a_EntropyContext) : + m_EntropyContext(a_EntropyContext), + m_IsValid(false) +{ +} + + + + + +int cCtrDrbgContext::Initialize(const void * a_Custom, size_t a_CustomSize) +{ + if (m_IsValid) + { + // Already initialized + return 0; + } + + int res = ctr_drbg_init(&m_CtrDrbg, entropy_func, &(m_EntropyContext->m_Entropy), (const unsigned char *)a_Custom, a_CustomSize); + m_IsValid = (res == 0); + return res; +} + + + + diff --git a/src/PolarSSL++/CtrDrbgContext.h b/src/PolarSSL++/CtrDrbgContext.h new file mode 100644 index 000000000..65e9a2374 --- /dev/null +++ b/src/PolarSSL++/CtrDrbgContext.h @@ -0,0 +1,63 @@ + +// CtrDrbgContext.h + +// Declares the cCtrDrbgContext class representing a wrapper over CTR-DRBG implementation in PolarSSL + + + + + +#pragma once + +#include "polarssl/ctr_drbg.h" + + + + + +// fwd: EntropyContext.h +class cEntropyContext; + + + + + +class cCtrDrbgContext +{ + friend class cSslContext; + friend class cRsaPrivateKey; + friend class cPublicKey; + +public: + /** Constructs the context with a new entropy context. */ + cCtrDrbgContext(void); + + /** Constructs the context with the specified entropy context. */ + cCtrDrbgContext(const SharedPtr<cEntropyContext> & a_EntropyContext); + + /** Initializes the context. + a_Custom is optional additional data to use for entropy, nullptr is accepted. + Returns 0 if successful, PolarSSL error code on failure. */ + int Initialize(const void * a_Custom, size_t a_CustomSize); + + /** Returns true if the object is valid (has been initialized properly) */ + bool IsValid(void) const { return m_IsValid; } + +protected: + /** The entropy source used for generating the random */ + SharedPtr<cEntropyContext> m_EntropyContext; + + /** The random generator context */ + ctr_drbg_context m_CtrDrbg; + + /** Set to true if the object is valid (has been initialized properly) */ + bool m_IsValid; + + + /** Returns the internal context ptr. Only use in PolarSSL API calls. */ + ctr_drbg_context * GetInternal(void) { return &m_CtrDrbg; } +} ; + + + + diff --git a/src/PolarSSL++/EntropyContext.cpp b/src/PolarSSL++/EntropyContext.cpp new file mode 100644 index 000000000..9c59b3f11 --- /dev/null +++ b/src/PolarSSL++/EntropyContext.cpp @@ -0,0 +1,29 @@ + +// EntropyContext.cpp + +// Implements the cEntropyContext class representing a wrapper over entropy contexts in PolarSSL + +#include "Globals.h" +#include "EntropyContext.h" + + + + + +cEntropyContext::cEntropyContext(void) +{ + entropy_init(&m_Entropy); +} + + + + + +cEntropyContext::~cEntropyContext() +{ + entropy_free(&m_Entropy); +} + + + + diff --git a/src/PolarSSL++/EntropyContext.h b/src/PolarSSL++/EntropyContext.h new file mode 100644 index 000000000..bc7fff066 --- /dev/null +++ b/src/PolarSSL++/EntropyContext.h @@ -0,0 +1,31 @@ + +// EntropyContext.h + +// Declares the cEntropyContext class representing a wrapper over entropy contexts in PolarSSL + + + + + +#pragma once + +#include "polarssl/entropy.h" + + + + + +class cEntropyContext +{ + friend class cCtrDrbgContext; +public: + cEntropyContext(void); + ~cEntropyContext(); + +protected: + entropy_context m_Entropy; +} ; + + + + diff --git a/src/PolarSSL++/PublicKey.cpp b/src/PolarSSL++/PublicKey.cpp new file mode 100644 index 000000000..49794a0c8 --- /dev/null +++ b/src/PolarSSL++/PublicKey.cpp @@ -0,0 +1,73 @@ + +// PublicKey.cpp + +// Implements the cPublicKey class representing a RSA public key in PolarSSL + +#include "Globals.h" +#include "PublicKey.h" + + + + + +cPublicKey::cPublicKey(const AString & a_PublicKeyDER) +{ + pk_init(&m_Pk); + if (pk_parse_public_key(&m_Pk, (const Byte *)a_PublicKeyDER.data(), a_PublicKeyDER.size()) != 0) + { + ASSERT(!"Cannot parse PubKey"); + return; + } + m_CtrDrbg.Initialize("rsa_pubkey", 10); +} + + + + + +cPublicKey::~cPublicKey() +{ + pk_free(&m_Pk); +} + + + + + +int cPublicKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength) +{ + size_t DecryptedLen = a_DecryptedMaxLength; + int res = pk_decrypt(&m_Pk, + a_EncryptedData, a_EncryptedLength, + a_DecryptedData, &DecryptedLen, a_DecryptedMaxLength, + ctr_drbg_random, m_CtrDrbg.GetInternal() + ); + if (res != 0) + { + return res; + } + return (int)DecryptedLen; +} + + + + + +int cPublicKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength) +{ + size_t EncryptedLength = a_EncryptedMaxLength; + int res = pk_encrypt(&m_Pk, + a_PlainData, a_PlainLength, a_EncryptedData, &EncryptedLength, a_EncryptedMaxLength, + ctr_drbg_random, m_CtrDrbg.GetInternal() + ); + if (res != 0) + { + return res; + } + return (int)EncryptedLength; +} + + + + + diff --git a/src/PolarSSL++/PublicKey.h b/src/PolarSSL++/PublicKey.h new file mode 100644 index 000000000..5a0a57147 --- /dev/null +++ b/src/PolarSSL++/PublicKey.h @@ -0,0 +1,48 @@ + +// PublicKey.h + +// Declares the cPublicKey class representing a RSA public key in PolarSSL + + + + + +#pragma once + +#include "CtrDrbgContext.h" +#include "polarssl/pk.h" + + + + + +class cPublicKey +{ +public: + /** Constructs the public key out of the DER-encoded pubkey data */ + cPublicKey(const AString & a_PublicKeyDER); + + ~cPublicKey(); + + /** Decrypts the data using the stored public key + Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large. + Returns the number of bytes decrypted, or negative number for error. */ + int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength); + + /** Encrypts the data using the stored public key + Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large. + Returns the number of bytes decrypted, or negative number for error. */ + int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength); + +protected: + /** The public key PolarSSL representation */ + pk_context m_Pk; + + /** The random generator used in encryption and decryption */ + cCtrDrbgContext m_CtrDrbg; +} ; + + + + + diff --git a/src/PolarSSL++/RsaPrivateKey.cpp b/src/PolarSSL++/RsaPrivateKey.cpp new file mode 100644 index 000000000..2d5a2a4b1 --- /dev/null +++ b/src/PolarSSL++/RsaPrivateKey.cpp @@ -0,0 +1,176 @@ + +// RsaPrivateKey.cpp + +#include "Globals.h" +#include "RsaPrivateKey.h" +#include "CtrDrbgContext.h" +#include "polarssl/pk.h" + + + + + + +cRsaPrivateKey::cRsaPrivateKey(void) +{ + rsa_init(&m_Rsa, RSA_PKCS_V15, 0); + m_CtrDrbg.Initialize("RSA", 3); +} + + + + + +cRsaPrivateKey::cRsaPrivateKey(const cRsaPrivateKey & a_Other) +{ + rsa_init(&m_Rsa, RSA_PKCS_V15, 0); + rsa_copy(&m_Rsa, &a_Other.m_Rsa); + m_CtrDrbg.Initialize("RSA", 3); +} + + + + + +cRsaPrivateKey::~cRsaPrivateKey() +{ + rsa_free(&m_Rsa); +} + + + + + +bool cRsaPrivateKey::Generate(unsigned a_KeySizeBits) +{ + int res = rsa_gen_key(&m_Rsa, ctr_drbg_random, m_CtrDrbg.GetInternal(), a_KeySizeBits, 65537); + if (res != 0) + { + LOG("RSA key generation failed: -0x%x", -res); + return false; + } + + return true; +} + + + + + +AString cRsaPrivateKey::GetPubKeyDER(void) +{ + class cPubKey + { + public: + cPubKey(rsa_context * a_Rsa) : + m_IsValid(false) + { + pk_init(&m_Key); + if (pk_init_ctx(&m_Key, pk_info_from_type(POLARSSL_PK_RSA)) != 0) + { + ASSERT(!"Cannot init PrivKey context"); + return; + } + if (rsa_copy(pk_rsa(m_Key), a_Rsa) != 0) + { + ASSERT(!"Cannot copy PrivKey to PK context"); + return; + } + m_IsValid = true; + } + + ~cPubKey() + { + if (m_IsValid) + { + pk_free(&m_Key); + } + } + + operator pk_context * (void) { return &m_Key; } + + protected: + bool m_IsValid; + pk_context m_Key; + } PkCtx(&m_Rsa); + + unsigned char buf[3000]; + int res = pk_write_pubkey_der(PkCtx, buf, sizeof(buf)); + if (res < 0) + { + return AString(); + } + return AString((const char *)(buf + sizeof(buf) - res), (size_t)res); +} + + + + + +int cRsaPrivateKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength) +{ + if (a_EncryptedLength < m_Rsa.len) + { + LOGD("%s: Invalid a_EncryptedLength: got %u, exp at least %u", + __FUNCTION__, (unsigned)a_EncryptedLength, (unsigned)(m_Rsa.len) + ); + ASSERT(!"Invalid a_DecryptedMaxLength!"); + return -1; + } + if (a_DecryptedMaxLength < m_Rsa.len) + { + LOGD("%s: Invalid a_DecryptedMaxLength: got %u, exp at least %u", + __FUNCTION__, (unsigned)a_EncryptedLength, (unsigned)(m_Rsa.len) + ); + ASSERT(!"Invalid a_DecryptedMaxLength!"); + return -1; + } + size_t DecryptedLength; + int res = rsa_pkcs1_decrypt( + &m_Rsa, ctr_drbg_random, m_CtrDrbg.GetInternal(), RSA_PRIVATE, &DecryptedLength, + a_EncryptedData, a_DecryptedData, a_DecryptedMaxLength + ); + if (res != 0) + { + return -1; + } + return (int)DecryptedLength; +} + + + + + +int cRsaPrivateKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength) +{ + if (a_EncryptedMaxLength < m_Rsa.len) + { + LOGD("%s: Invalid a_EncryptedMaxLength: got %u, exp at least %u", + __FUNCTION__, (unsigned)a_EncryptedMaxLength, (unsigned)(m_Rsa.len) + ); + ASSERT(!"Invalid a_DecryptedMaxLength!"); + return -1; + } + if (a_PlainLength < m_Rsa.len) + { + LOGD("%s: Invalid a_PlainLength: got %u, exp at least %u", + __FUNCTION__, (unsigned)a_PlainLength, (unsigned)(m_Rsa.len) + ); + ASSERT(!"Invalid a_PlainLength!"); + return -1; + } + int res = rsa_pkcs1_encrypt( + &m_Rsa, ctr_drbg_random, m_CtrDrbg.GetInternal(), RSA_PRIVATE, + a_PlainLength, a_PlainData, a_EncryptedData + ); + if (res != 0) + { + return -1; + } + return (int)m_Rsa.len; +} + + + + + diff --git a/src/PolarSSL++/RsaPrivateKey.h b/src/PolarSSL++/RsaPrivateKey.h new file mode 100644 index 000000000..ffacde11b --- /dev/null +++ b/src/PolarSSL++/RsaPrivateKey.h @@ -0,0 +1,59 @@ + +// RsaPrivateKey.h + +// Declares the cRsaPrivateKey class representing a private key for RSA operations. + + + + + +#pragma once + +#include "CtrDrbgContext.h" +#include "polarssl/rsa.h" + + + + + +/** Encapsulates an RSA private key used in PKI cryptography */ +class cRsaPrivateKey +{ +public: + /** Creates a new empty object, the key is not assigned */ + cRsaPrivateKey(void); + + /** Deep-copies the key from a_Other */ + cRsaPrivateKey(const cRsaPrivateKey & a_Other); + + ~cRsaPrivateKey(); + + /** Generates a new key within this object, with the specified size in bits. + Returns true on success, false on failure. */ + bool Generate(unsigned a_KeySizeBits = 1024); + + /** Returns the public key part encoded in ASN1 DER encoding */ + AString GetPubKeyDER(void); + + /** Decrypts the data using RSAES-PKCS#1 algorithm. + Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large. + Returns the number of bytes decrypted, or negative number for error. */ + int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength); + + /** Encrypts the data using RSAES-PKCS#1 algorithm. + Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large. + Returns the number of bytes decrypted, or negative number for error. */ + int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength); + +protected: + /** The PolarSSL key context */ + rsa_context m_Rsa; + + /** The random generator used for generating the key and encryption / decryption */ + cCtrDrbgContext m_CtrDrbg; +} ; + + + + + diff --git a/src/PolarSSL++/Sha1Checksum.cpp b/src/PolarSSL++/Sha1Checksum.cpp new file mode 100644 index 000000000..a1ee9d7b9 --- /dev/null +++ b/src/PolarSSL++/Sha1Checksum.cpp @@ -0,0 +1,138 @@ + +// Sha1Checksum.cpp + +// Declares the cSha1Checksum class representing the SHA-1 checksum calculator + +#include "Globals.h" +#include "Sha1Checksum.h" + + + + + +/* +// Self-test the hash formatting for known values: +// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48 +// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1 +// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6 + +static class Test +{ +public: + Test(void) + { + AString DigestNotch, DigestJeb, DigestSimon; + Byte Digest[20]; + cSha1Checksum Checksum; + Checksum.Update((const Byte *)"Notch", 5); + Checksum.Finalize(Digest); + cSha1Checksum::DigestToJava(Digest, DigestNotch); + Checksum.Restart(); + Checksum.Update((const Byte *)"jeb_", 4); + Checksum.Finalize(Digest); + cSha1Checksum::DigestToJava(Digest, DigestJeb); + Checksum.Restart(); + Checksum.Update((const Byte *)"simon", 5); + Checksum.Finalize(Digest); + cSha1Checksum::DigestToJava(Digest, DigestSimon); + printf("Notch: \"%s\"\n", DigestNotch.c_str()); + printf("jeb_: \"%s\"\n", DigestJeb.c_str()); + printf("simon: \"%s\"\n", DigestSimon.c_str()); + assert(DigestNotch == "4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48"); + assert(DigestJeb == "-7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1"); + assert(DigestSimon == "88e16a1019277b15d58faf0541e11910eb756f6"); + } +} test; +*/ + + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cSha1Checksum: + +cSha1Checksum::cSha1Checksum(void) : + m_DoesAcceptInput(true) +{ + sha1_starts(&m_Sha1); +} + + + + + +void cSha1Checksum::Update(const Byte * a_Data, size_t a_Length) +{ + ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed + + sha1_update(&m_Sha1, a_Data, a_Length); +} + + + + + +void cSha1Checksum::Finalize(cSha1Checksum::Checksum & a_Output) +{ + ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed + + sha1_finish(&m_Sha1, a_Output); + m_DoesAcceptInput = false; +} + + + + + +void cSha1Checksum::DigestToJava(const Checksum & a_Digest, AString & a_Out) +{ + Checksum Digest; + memcpy(Digest, a_Digest, sizeof(Digest)); + + bool IsNegative = (Digest[0] >= 0x80); + if (IsNegative) + { + // Two's complement: + bool carry = true; // Add one to the whole number + for (int i = 19; i >= 0; i--) + { + Digest[i] = ~Digest[i]; + if (carry) + { + carry = (Digest[i] == 0xff); + Digest[i]++; + } + } + } + a_Out.clear(); + a_Out.reserve(40); + for (int i = 0; i < 20; i++) + { + AppendPrintf(a_Out, "%02x", Digest[i]); + } + while ((a_Out.length() > 0) && (a_Out[0] == '0')) + { + a_Out.erase(0, 1); + } + if (IsNegative) + { + a_Out.insert(0, "-"); + } +} + + + + + + +void cSha1Checksum::Restart(void) +{ + sha1_starts(&m_Sha1); + m_DoesAcceptInput = true; +} + + + + diff --git a/src/PolarSSL++/Sha1Checksum.h b/src/PolarSSL++/Sha1Checksum.h new file mode 100644 index 000000000..68fdbcf1b --- /dev/null +++ b/src/PolarSSL++/Sha1Checksum.h @@ -0,0 +1,52 @@ + +// Sha1Checksum.h + +// Declares the cSha1Checksum class representing the SHA-1 checksum calculator + + + + + +#pragma once + +#include "polarssl/sha1.h" + + + + + +/** Calculates a SHA1 checksum for data stream */ +class cSha1Checksum +{ +public: + typedef Byte Checksum[20]; // The type used for storing the checksum + + cSha1Checksum(void); + + /** Adds the specified data to the checksum */ + void Update(const Byte * a_Data, size_t a_Length); + + /** Calculates and returns the final checksum */ + void Finalize(Checksum & a_Output); + + /** Returns true if the object is accepts more input data, false if Finalize()-d (need to Restart()) */ + bool DoesAcceptInput(void) const { return m_DoesAcceptInput; } + + /** Converts a raw 160-bit SHA1 digest into a Java Hex representation + According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802 + */ + static void DigestToJava(const Checksum & a_Digest, AString & a_JavaOut); + + /** Clears the current context and start a new checksum calculation */ + void Restart(void); + +protected: + /** True if the object is accepts more input data, false if Finalize()-d (need to Restart()) */ + bool m_DoesAcceptInput; + + sha1_context m_Sha1; +} ; + + + + diff --git a/src/PolarSSL++/SslContext.cpp b/src/PolarSSL++/SslContext.cpp new file mode 100644 index 000000000..1994cf844 --- /dev/null +++ b/src/PolarSSL++/SslContext.cpp @@ -0,0 +1,243 @@ + +// SslContext.cpp + +// Implements the cSslContext class that holds everything a single SSL context needs to function + +#include "Globals.h" +#include "SslContext.h" +#include "EntropyContext.h" +#include "CtrDrbgContext.h" + + + + + +cSslContext::cSslContext(void) : + m_IsValid(false), + m_HasHandshaken(false) +{ +} + + + + + +cSslContext::~cSslContext() +{ + if (m_IsValid) + { + ssl_free(&m_Ssl); + } +} + + + + + +int cSslContext::Initialize(bool a_IsClient, const SharedPtr<cCtrDrbgContext> & a_CtrDrbg) +{ + // Check double-initialization: + if (m_IsValid) + { + LOGWARNING("SSL: Double initialization is not supported."); + return POLARSSL_ERR_SSL_MALLOC_FAILED; // There is no return value well-suited for this, reuse this one. + } + + // Set the CtrDrbg context, create a new one if needed: + m_CtrDrbg = a_CtrDrbg; + if (m_CtrDrbg.get() == NULL) + { + m_CtrDrbg.reset(new cCtrDrbgContext); + m_CtrDrbg->Initialize("MCServer", 8); + } + + // Initialize PolarSSL's structures: + memset(&m_Ssl, 0, sizeof(m_Ssl)); + int res = ssl_init(&m_Ssl); + if (res != 0) + { + return res; + } + ssl_set_endpoint(&m_Ssl, a_IsClient ? SSL_IS_CLIENT : SSL_IS_SERVER); + ssl_set_authmode(&m_Ssl, SSL_VERIFY_OPTIONAL); + ssl_set_rng(&m_Ssl, ctr_drbg_random, &m_CtrDrbg->m_CtrDrbg); + ssl_set_bio(&m_Ssl, ReceiveEncrypted, this, SendEncrypted, this); + + #ifdef _DEBUG + /* + // These functions allow us to debug SSL and certificate problems, but produce way too much output, + // so they're disabled until someone needs them + ssl_set_dbg(&m_Ssl, &SSLDebugMessage, this); + ssl_set_verify(&m_Ssl, &SSLVerifyCert, this); + */ + #endif + + m_IsValid = true; + return 0; +} + + + + + +void cSslContext::SetCACerts(const cX509CertPtr & a_CACert, const AString & a_ExpectedPeerName) +{ + // Store the data in our internal buffers, to avoid losing the pointers later on + // PolarSSL will need these after this call returns, and the caller may move / delete the data before that: + m_ExpectedPeerName = a_ExpectedPeerName; + m_CACerts = a_CACert; + + // Set the trusted CA root cert store: + ssl_set_authmode(&m_Ssl, SSL_VERIFY_REQUIRED); + ssl_set_ca_chain(&m_Ssl, m_CACerts->GetInternal(), NULL, m_ExpectedPeerName.empty() ? NULL : m_ExpectedPeerName.c_str()); +} + + + + + +int cSslContext::WritePlain(const void * a_Data, size_t a_NumBytes) +{ + ASSERT(m_IsValid); // Need to call Initialize() first + if (!m_HasHandshaken) + { + int res = Handshake(); + if (res != 0) + { + return res; + } + } + + return ssl_write(&m_Ssl, (const unsigned char *)a_Data, a_NumBytes); +} + + + + + +int cSslContext::ReadPlain(void * a_Data, size_t a_MaxBytes) +{ + ASSERT(m_IsValid); // Need to call Initialize() first + if (!m_HasHandshaken) + { + int res = Handshake(); + if (res != 0) + { + return res; + } + } + + return ssl_read(&m_Ssl, (unsigned char *)a_Data, a_MaxBytes); +} + + + + + +int cSslContext::Handshake(void) +{ + ASSERT(m_IsValid); // Need to call Initialize() first + ASSERT(!m_HasHandshaken); // Must not call twice + + int res = ssl_handshake(&m_Ssl); + if (res == 0) + { + m_HasHandshaken = true; + } + return res; +} + + + + + +int cSslContext::NotifyClose(void) +{ + return ssl_close_notify(&m_Ssl); +} + + + + + +#ifdef _DEBUG + void cSslContext::SSLDebugMessage(void * a_UserParam, int a_Level, const char * a_Text) + { + if (a_Level > 3) + { + // Don't want the trace messages + return; + } + + // Remove the terminating LF: + size_t len = strlen(a_Text) - 1; + while ((len > 0) && (a_Text[len] <= 32)) + { + len--; + } + AString Text(a_Text, len + 1); + + LOGD("SSL (%d): %s", a_Level, Text.c_str()); + } + + + + + + int cSslContext::SSLVerifyCert(void * a_This, x509_crt * a_Crt, int a_Depth, int * a_Flags) + { + char buf[1024]; + UNUSED(a_This); + + LOG("Verify requested for (Depth %d):", a_Depth); + x509_crt_info(buf, sizeof(buf) - 1, "", a_Crt); + LOG("%s", buf); + + int Flags = *a_Flags; + if ((Flags & BADCERT_EXPIRED) != 0) + { + LOG(" ! server certificate has expired"); + } + + if ((Flags & BADCERT_REVOKED) != 0) + { + LOG(" ! server certificate has been revoked"); + } + + if ((Flags & BADCERT_CN_MISMATCH) != 0) + { + LOG(" ! CN mismatch"); + } + + if ((Flags & BADCERT_NOT_TRUSTED) != 0) + { + LOG(" ! self-signed or not signed by a trusted CA"); + } + + if ((Flags & BADCRL_NOT_TRUSTED) != 0) + { + LOG(" ! CRL not trusted"); + } + + if ((Flags & BADCRL_EXPIRED) != 0) + { + LOG(" ! CRL expired"); + } + + if ((Flags & BADCERT_OTHER) != 0) + { + LOG(" ! other (unknown) flag"); + } + + if (Flags == 0) + { + LOG(" This certificate has no flags"); + } + + return 0; + } +#endif // _DEBUG + + + + diff --git a/src/PolarSSL++/SslContext.h b/src/PolarSSL++/SslContext.h new file mode 100644 index 000000000..85add5f8b --- /dev/null +++ b/src/PolarSSL++/SslContext.h @@ -0,0 +1,137 @@ + +// SslContext.h + +// Declares the cSslContext class that holds everything a single SSL context needs to function + + + + + +#pragma once + +#include "polarssl/ssl.h" +#include "../ByteBuffer.h" +#include "X509Cert.h" + + + + + +// fwd: +class cCtrDrbgContext; + + + + + +/** +Acts as a generic SSL encryptor / decryptor between the two endpoints. The "owner" of this class is expected +to create it, initialize it and then provide the means of reading and writing data through the SSL link. +This is an abstract base class, there are descendants that handle the specific aspects of how the SSL peer +data comes into the system: + - cBufferedSslContext uses a cByteBuffer to read and write the data + - cCallbackSslContext uses callbacks to provide the data +*/ +class cSslContext abstract +{ +public: + /** Creates a new uninitialized context */ + cSslContext(void); + + ~cSslContext(); + + /** Initializes the context for use as a server or client. + Returns 0 on success, PolarSSL error on failure. */ + int Initialize(bool a_IsClient, const SharedPtr<cCtrDrbgContext> & a_CtrDrbg = SharedPtr<cCtrDrbgContext>()); + + /** Returns true if the object has been initialized properly. */ + bool IsValid(void) const { return m_IsValid; } + + /** Sets a cert chain as the trusted cert store for this context. + Calling this will switch the context into strict cert verification mode. + a_ExpectedPeerName is the CommonName that we expect the SSL peer to have in its cert, + if it is different, the verification will fail. An empty string will disable the CN check. */ + void SetCACerts(const cX509CertPtr & a_CACert, const AString & a_ExpectedPeerName); + + /** Writes data to be encrypted and sent to the SSL peer. Will perform SSL handshake, if needed. + Returns the number of bytes actually written, or PolarSSL error code. + If the return value is POLARSSL_ERR_NET_WANT_READ or POLARSSL_ERR_NET_WANT_WRITE, the owner should send any + cached outgoing data to the SSL peer and write any incoming data received from the SSL peer and then call + this function again with the same parameters. Note that this may repeat a few times before the data is + actually written, mainly due to initial handshake. */ + int WritePlain(const void * a_Data, size_t a_NumBytes); + + /** Reads data decrypted from the SSL stream. Will perform SSL handshake, if needed. + Returns the number of bytes actually read, or PolarSSL error code. + If the return value is POLARSSL_ERR_NET_WANT_READ or POLARSSL_ERR_NET_WANT_WRITE, the owner should send any + cached outgoing data to the SSL peer and write any incoming data received from the SSL peer and then call + this function again with the same parameters. Note that this may repeat a few times before the data is + actually read, mainly due to initial handshake. */ + int ReadPlain(void * a_Data, size_t a_MaxBytes); + + /** Performs the SSL handshake. + Returns zero on success, PoladSSL error code on failure. + If the return value is POLARSSL_ERR_NET_WANT_READ or POLARSSL_ERR_NET_WANT_WRITE, the owner should send any + cached outgoing data to the SSL peer and write any incoming data received from the SSL peer and then call + this function again. Note that this may repeat a few times before the handshake is completed. */ + int Handshake(void); + + /** Returns true if the SSL handshake has been completed. */ + bool HasHandshaken(void) const { return m_HasHandshaken; } + + /** Notifies the SSL peer that the connection is being closed. + Returns 0 on success, PolarSSL error code on failure. */ + int NotifyClose(void); + +protected: + /** True if the object has been initialized properly. */ + bool m_IsValid; + + /** The random generator to use */ + SharedPtr<cCtrDrbgContext> m_CtrDrbg; + + /** The SSL context that PolarSSL uses. */ + ssl_context m_Ssl; + + /** True if the SSL handshake has been completed. */ + bool m_HasHandshaken; + + /** A copy of the trusted CA root cert store that is passed to us in SetCACerts(), so that the pointer + stays valid even after the call, when PolarSSL finally uses it. */ + cX509CertPtr m_CACerts; + + /** Buffer for the expected peer name. We need to buffer it because the caller may free the string they + give us before PolarSSL consumes the raw pointer it gets to the CN. */ + AString m_ExpectedPeerName; + + + /** The callback used by PolarSSL when it wants to read encrypted data. */ + static int ReceiveEncrypted(void * a_This, unsigned char * a_Buffer, size_t a_NumBytes) + { + return ((cSslContext *)a_This)->ReceiveEncrypted(a_Buffer, a_NumBytes); + } + + /** The callback used by PolarSSL when it wants to write encrypted data. */ + static int SendEncrypted(void * a_This, const unsigned char * a_Buffer, size_t a_NumBytes) + { + return ((cSslContext *)a_This)->SendEncrypted(a_Buffer, a_NumBytes); + } + + #ifdef _DEBUG + /** The callback used by PolarSSL to output debug messages */ + static void SSLDebugMessage(void * a_UserParam, int a_Level, const char * a_Text); + + /** The callback used by PolarSSL to log information on the cert chain */ + static int SSLVerifyCert(void * a_This, x509_crt * a_Crt, int a_Depth, int * a_Flags); + #endif // _DEBUG + + /** Called when PolarSSL wants to read encrypted data. */ + virtual int ReceiveEncrypted(unsigned char * a_Buffer, size_t a_NumBytes) = 0; + + /** Called when PolarSSL wants to write encrypted data. */ + virtual int SendEncrypted(const unsigned char * a_Buffer, size_t a_NumBytes) = 0; +} ; + + + + diff --git a/src/PolarSSL++/X509Cert.cpp b/src/PolarSSL++/X509Cert.cpp new file mode 100644 index 000000000..ecf664855 --- /dev/null +++ b/src/PolarSSL++/X509Cert.cpp @@ -0,0 +1,38 @@ + +// X509Cert.cpp + +// Implements the cX509Cert class representing a wrapper over X509 certs in PolarSSL + +#include "Globals.h" +#include "X509Cert.h" + + + + + +cX509Cert::cX509Cert(void) +{ + x509_crt_init(&m_Cert); +} + + + + + +cX509Cert::~cX509Cert() +{ + x509_crt_free(&m_Cert); +} + + + + + +int cX509Cert::Parse(const void * a_CertContents, size_t a_Size) +{ + return x509_crt_parse(&m_Cert, (const unsigned char *)a_CertContents, a_Size); +} + + + + diff --git a/src/PolarSSL++/X509Cert.h b/src/PolarSSL++/X509Cert.h new file mode 100644 index 000000000..991617d24 --- /dev/null +++ b/src/PolarSSL++/X509Cert.h @@ -0,0 +1,41 @@ + +// X509Cert.h + +// Declares the cX509Cert class representing a wrapper over X509 certs in PolarSSL + + + + + +#pragma once + +#include "polarssl/x509_crt.h" + + + + + +class cX509Cert +{ + friend class cSslContext; + +public: + cX509Cert(void); + ~cX509Cert(void); + + /** Parses the certificate chain data into the context. + Returns 0 on succes, or PolarSSL error code on failure. */ + int Parse(const void * a_CertContents, size_t a_Size); + +protected: + x509_crt m_Cert; + + /** Returns the internal cert ptr. Only use in PolarSSL API calls. */ + x509_crt * GetInternal(void) { return &m_Cert; } +} ; + +typedef SharedPtr<cX509Cert> cX509CertPtr; + + + + diff --git a/src/Protocol/Authenticator.cpp b/src/Protocol/Authenticator.cpp index e0fcc0007..2050393c2 100644 --- a/src/Protocol/Authenticator.cpp +++ b/src/Protocol/Authenticator.cpp @@ -2,7 +2,6 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Authenticator.h" -#include "../OSSupport/BlockingTCPLink.h" #include "../Root.h" #include "../Server.h" #include "../ClientHandle.h" @@ -10,13 +9,7 @@ #include "inifile/iniFile.h" #include "json/json.h" -#include "polarssl/config.h" -#include "polarssl/net.h" -#include "polarssl/ssl.h" -#include "polarssl/entropy.h" -#include "polarssl/ctr_drbg.h" -#include "polarssl/error.h" -#include "polarssl/certs.h" +#include "PolarSSL++/BlockingSslClientSocket.h" #include <sstream> #include <iomanip> @@ -148,94 +141,102 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S { LOGD("Trying to auth user %s", a_UserName.c_str()); - int ret, server_fd = -1; + int ret; unsigned char buf[1024]; - const char *pers = "cAuthenticator"; - - entropy_context entropy; - ctr_drbg_context ctr_drbg; - ssl_context ssl; - x509_crt cacert; - - /* Initialize the RNG and the session data */ - memset(&ssl, 0, sizeof(ssl_context)); - x509_crt_init(&cacert); - - entropy_init(&entropy); - if ((ret = ctr_drbg_init(&ctr_drbg, entropy_func, &entropy, (const unsigned char *)pers, strlen(pers))) != 0) - { - LOGWARNING("cAuthenticator: ctr_drbg_init returned %d", ret); - return false; - } /* Initialize certificates */ - // TODO: Grab the sessionserver's root CA and any intermediates and hard-code them here, instead of test_ca_list - ret = x509_crt_parse(&cacert, (const unsigned char *)test_ca_list, strlen(test_ca_list)); - - if (ret < 0) - { - LOGWARNING("cAuthenticator: x509_crt_parse returned -0x%x", -ret); - return false; - } - - /* Connect */ - if ((ret = net_connect(&server_fd, m_Server.c_str(), 443)) != 0) - { - LOGWARNING("cAuthenticator: Can't connect to %s: %d", m_Server.c_str(), ret); - return false; - } - - /* Setup */ - if ((ret = ssl_init(&ssl)) != 0) + // This is the data of the root certs for Starfield Technologies, the CA that signed sessionserver.mojang.com's cert: + // Downloaded from http://certs.starfieldtech.com/repository/ + static const AString StarfieldCACert( + // G2 cert + "-----BEGIN CERTIFICATE-----\n" + "MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx\n" + "EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT\n" + "HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs\n" + "ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw\n" + "MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6\n" + "b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj\n" + "aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp\n" + "Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\n" + "ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg\n" + "nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1\n" + "HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N\n" + "Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN\n" + "dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0\n" + "HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO\n" + "BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G\n" + "CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU\n" + "sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3\n" + "4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg\n" + "8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K\n" + "pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1\n" + "mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0\n" + "-----END CERTIFICATE-----\n\n" + // Original (G1) cert: + "-----BEGIN CERTIFICATE-----\n" + "MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl\n" + "MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp\n" + "U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw\n" + "NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE\n" + "ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp\n" + "ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3\n" + "DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf\n" + "8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN\n" + "+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0\n" + "X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa\n" + "K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA\n" + "1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G\n" + "A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR\n" + "zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0\n" + "YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD\n" + "bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w\n" + "DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3\n" + "L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D\n" + "eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl\n" + "xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp\n" + "VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY\n" + "WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q=\n" + "-----END CERTIFICATE-----\n" + ); + + // Connect the socket: + cBlockingSslClientSocket Socket; + Socket.SetTrustedRootCertsFromString(StarfieldCACert, m_Server); + if (!Socket.Connect(m_Server, 443)) { - LOGWARNING("cAuthenticator: ssl_init returned %d", ret); + LOGWARNING("cAuthenticator: Can't connect to %s: %s", m_Server.c_str(), Socket.GetLastErrorText().c_str()); return false; } - ssl_set_endpoint(&ssl, SSL_IS_CLIENT); - ssl_set_authmode(&ssl, SSL_VERIFY_OPTIONAL); - ssl_set_ca_chain(&ssl, &cacert, NULL, "PolarSSL Server 1"); - ssl_set_rng(&ssl, ctr_drbg_random, &ctr_drbg); - ssl_set_bio(&ssl, net_recv, &server_fd, net_send, &server_fd); - - /* Handshake */ - while ((ret = ssl_handshake(&ssl)) != 0) - { - if ((ret != POLARSSL_ERR_NET_WANT_READ) && (ret != POLARSSL_ERR_NET_WANT_WRITE)) - { - LOGWARNING("cAuthenticator: ssl_handshake returned -0x%x", -ret); - return false; - } - } - /* Write the GET request */ + // Create the GET request: AString ActualAddress = m_Address; ReplaceString(ActualAddress, "%USERNAME%", a_UserName); ReplaceString(ActualAddress, "%SERVERID%", a_ServerId); AString Request; - Request += "GET " + ActualAddress + " HTTP/1.1\r\n"; + Request += "GET " + ActualAddress + " HTTP/1.0\r\n"; Request += "Host: " + m_Server + "\r\n"; Request += "User-Agent: MCServer\r\n"; Request += "Connection: close\r\n"; Request += "\r\n"; - ret = ssl_write(&ssl, (const unsigned char *)Request.c_str(), Request.size()); - if (ret <= 0) + if (!Socket.Send(Request.c_str(), Request.size())) { - LOGWARNING("cAuthenticator: ssl_write returned %d", ret); + LOGWARNING("cAuthenticator: Writing SSL data failed: %s", Socket.GetLastErrorText().c_str()); return false; } - /* Read the HTTP response */ + // Read the HTTP response: std::string Response; for (;;) { - memset(buf, 0, sizeof(buf)); - ret = ssl_read(&ssl, buf, sizeof(buf)); + ret = Socket.Receive(buf, sizeof(buf)); if ((ret == POLARSSL_ERR_NET_WANT_READ) || (ret == POLARSSL_ERR_NET_WANT_WRITE)) { - continue; + // This value should never be returned, it is handled internally by cBlockingSslClientSocket + LOGWARNING("cAuthenticator: SSL reading failed internally."); + return false; } if (ret == POLARSSL_ERR_SSL_PEER_CLOSE_NOTIFY) { @@ -243,24 +244,18 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S } if (ret < 0) { - LOGWARNING("cAuthenticator: ssl_read returned %d", ret); - break; + LOGWARNING("cAuthenticator: SSL reading failed: -0x%x", -ret); + return false; } if (ret == 0) { - LOGWARNING("cAuthenticator: EOF"); break; } - Response.append((const char *)buf, ret); - } + Response.append((const char *)buf, (size_t)ret); + } - ssl_close_notify(&ssl); - x509_crt_free(&cacert); - net_close(server_fd); - ssl_free(&ssl); - entropy_free(&entropy); - memset(&ssl, 0, sizeof(ssl)); + Socket.Disconnect(); // Check the HTTP status line: AString prefix("HTTP/1.1 200 OK"); diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 2fbeef0fa..8f152ad37 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -123,7 +123,7 @@ public: virtual void SendWholeInventory (const cWindow & a_Window) = 0; virtual void SendWindowClose (const cWindow & a_Window) = 0; virtual void SendWindowOpen (const cWindow & a_Window) = 0; - virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) = 0; + virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) = 0; /// Returns the ServerID used for authentication through session.minecraft.net virtual AString GetAuthServerID(void) = 0; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index 3282a827f..e7873cf7a 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -26,7 +26,7 @@ Documentation: #include "../Root.h" #include "../Server.h" -#include "../Entities/ProjectileEntity.h" +#include "../Entities/ArrowEntity.h" #include "../Entities/Minecart.h" #include "../Entities/FallingBlock.h" @@ -1175,7 +1175,7 @@ void cProtocol125::SendWindowOpen(const cWindow & a_Window) -void cProtocol125::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) +void cProtocol125::SendWindowProperty(const cWindow & a_Window, int a_Property, int a_Value) { cCSLock Lock(m_CSPacket); WriteByte (PACKET_WINDOW_PROPERTY); @@ -2013,7 +2013,7 @@ void cProtocol125::WriteMobMetadata(const cMonster & a_Mob) case cMonster::mtWither: { WriteByte(0x54); // Int at index 20 - WriteInt((Int32)((const cWither &)a_Mob).GetNumInvulnerableTicks()); + WriteInt((Int32)((const cWither &)a_Mob).GetWitherInvulnerableTicks()); WriteByte(0x66); // Float at index 6 WriteFloat((float)(a_Mob.GetHealth())); break; diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index 89e64f386..423e58d67 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -96,7 +96,7 @@ public: virtual void SendWholeInventory (const cWindow & a_Window) override; virtual void SendWindowClose (const cWindow & a_Window) override; virtual void SendWindowOpen (const cWindow & a_Window) override; - virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; + virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) override; virtual AString GetAuthServerID(void) override; diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index 53d8c1561..f4717f592 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -18,19 +18,7 @@ #include "../WorldStorage/FastNBT.h" #include "../WorldStorage/EnchantmentSerializer.h" #include "../StringCompression.h" - -#ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable:4127) - #pragma warning(disable:4244) - #pragma warning(disable:4231) - #pragma warning(disable:4189) - #pragma warning(disable:4702) -#endif - -#ifdef _MSC_VER - #pragma warning(pop) -#endif +#include "PolarSSL++/Sha1Checksum.h" @@ -819,7 +807,7 @@ void cProtocol132::SendEncryptionKeyRequest(void) void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce) { // Decrypt EncNonce using privkey - cRSAPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey(); + cRsaPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey(); Int32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)]; int res = rsaDecryptor.Decrypt((const Byte *)a_EncNonce.data(), a_EncNonce.size(), (Byte *)DecryptedNonce, sizeof(DecryptedNonce)); @@ -876,7 +864,7 @@ void cProtocol132::StartEncryption(const Byte * a_Key) m_IsEncrypted = true; // Prepare the m_AuthServerID: - cSHA1Checksum Checksum; + cSha1Checksum Checksum; cServer * Server = cRoot::Get()->GetServer(); AString ServerID = Server->GetServerID(); Checksum.Update((const Byte *)ServerID.c_str(), ServerID.length()); @@ -884,7 +872,7 @@ void cProtocol132::StartEncryption(const Byte * a_Key) Checksum.Update((const Byte *)Server->GetPublicKeyDER().data(), Server->GetPublicKeyDER().size()); Byte Digest[20]; Checksum.Finalize(Digest); - cSHA1Checksum::DigestToJava(Digest, m_AuthServerID); + cSha1Checksum::DigestToJava(Digest, m_AuthServerID); } diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h index b280c8a41..32bc7d581 100644 --- a/src/Protocol/Protocol132.h +++ b/src/Protocol/Protocol132.h @@ -24,7 +24,8 @@ #pragma warning(pop) #endif -#include "../Crypto.h" +#include "PolarSSL++/AesCfb128Decryptor.h" +#include "PolarSSL++/AesCfb128Encryptor.h" @@ -79,8 +80,8 @@ public: protected: bool m_IsEncrypted; - cAESCFBDecryptor m_Decryptor; - cAESCFBEncryptor m_Encryptor; + cAesCfb128Decryptor m_Decryptor; + cAesCfb128Encryptor m_Encryptor; AString m_DataToSend; diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index e57b551cb..bc9aff0c0 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -31,6 +31,9 @@ Implements the 1.7.x protocol classes: #include "../BlockEntities/MobHeadEntity.h" #include "../BlockEntities/FlowerPotEntity.h" #include "../CompositeChat.h" +#include "../Entities/ArrowEntity.h" +#include "../Entities/FireworkEntity.h" +#include "PolarSSL++/Sha1Checksum.h" @@ -637,9 +640,11 @@ void cProtocol172::SendLoginSuccess(void) { ASSERT(m_State == 2); // State: login? - cPacketizer Pkt(*this, 0x02); // Login success packet - Pkt.WriteString(m_Client->GetUUID()); - Pkt.WriteString(m_Client->GetUsername()); + { + cPacketizer Pkt(*this, 0x02); // Login success packet + Pkt.WriteString(m_Client->GetUUID()); + Pkt.WriteString(m_Client->GetUsername()); + } m_State = 3; // State = Game } @@ -1378,7 +1383,7 @@ void cProtocol172::SendWindowOpen(const cWindow & a_Window) -void cProtocol172::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) +void cProtocol172::SendWindowProperty(const cWindow & a_Window, int a_Property, int a_Value) { ASSERT(m_State == 3); // In game mode? @@ -1686,7 +1691,7 @@ void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffe } // Decrypt EncNonce using privkey - cRSAPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey(); + cRsaPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey(); Int32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)]; int res = rsaDecryptor.Decrypt((const Byte *)EncNonce.data(), EncNonce.size(), (Byte *)DecryptedNonce, sizeof(DecryptedNonce)); if (res != 4) @@ -2289,7 +2294,7 @@ void cProtocol172::StartEncryption(const Byte * a_Key) m_IsEncrypted = true; // Prepare the m_AuthServerID: - cSHA1Checksum Checksum; + cSha1Checksum Checksum; cServer * Server = cRoot::Get()->GetServer(); const AString & ServerID = Server->GetServerID(); Checksum.Update((const Byte *)ServerID.c_str(), ServerID.length()); @@ -2297,7 +2302,7 @@ void cProtocol172::StartEncryption(const Byte * a_Key) Checksum.Update((const Byte *)Server->GetPublicKeyDER().data(), Server->GetPublicKeyDER().size()); Byte Digest[20]; Checksum.Finalize(Digest); - cSHA1Checksum::DigestToJava(Digest, m_AuthServerID); + cSha1Checksum::DigestToJava(Digest, m_AuthServerID); } @@ -2820,7 +2825,7 @@ void cProtocol172::cPacketizer::WriteMobMetadata(const cMonster & a_Mob) case cMonster::mtWither: { WriteByte(0x54); // Int at index 20 - WriteInt(((const cWither &)a_Mob).GetNumInvulnerableTicks()); + WriteInt(((const cWither &)a_Mob).GetWitherInvulnerableTicks()); WriteByte(0x66); // Float at index 6 WriteFloat((float)(a_Mob.GetHealth())); break; diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 6a3e81eff..dc111e737 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -30,7 +30,8 @@ Declares the 1.7.x protocol classes: #pragma warning(pop) #endif -#include "../Crypto.h" +#include "PolarSSL++/AesCfb128Decryptor.h" +#include "PolarSSL++/AesCfb128Encryptor.h" @@ -127,7 +128,7 @@ public: virtual void SendWholeInventory (const cWindow & a_Window) override; virtual void SendWindowClose (const cWindow & a_Window) override; virtual void SendWindowOpen (const cWindow & a_Window) override; - virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; + virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) override; virtual AString GetAuthServerID(void) override { return m_AuthServerID; } @@ -236,8 +237,8 @@ protected: bool m_IsEncrypted; - cAESCFBDecryptor m_Decryptor; - cAESCFBEncryptor m_Encryptor; + cAesCfb128Decryptor m_Decryptor; + cAesCfb128Encryptor m_Encryptor; /** The logfile where the comm is logged, when g_ShouldLogComm is true */ cFile m_CommLogFile; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 2ccb9f197..22dfe7c88 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -357,7 +357,7 @@ void cProtocolRecognizer::SendHealth(void) -void cProtocolRecognizer::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value) +void cProtocolRecognizer::SendWindowProperty(const cWindow & a_Window, int a_Property, int a_Value) { ASSERT(m_Protocol != NULL); m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value); diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index 408109ef4..37f47379d 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -131,7 +131,7 @@ public: virtual void SendWholeInventory (const cWindow & a_Window) override; virtual void SendWindowClose (const cWindow & a_Window) override; virtual void SendWindowOpen (const cWindow & a_Window) override; - virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override; + virtual void SendWindowProperty (const cWindow & a_Window, int a_Property, int a_Value) override; virtual AString GetAuthServerID(void) override; diff --git a/src/Server.h b/src/Server.h index 51c450ebd..3d76c8ccf 100644 --- a/src/Server.h +++ b/src/Server.h @@ -23,7 +23,7 @@ #pragma warning(disable:4702) #endif -#include "Crypto.h" +#include "PolarSSL++/RsaPrivateKey.h" #ifdef _MSC_VER #pragma warning(pop) @@ -109,7 +109,7 @@ public: // tolua_export /** Returns base64 encoded favicon data (obtained from favicon.png) */ const AString & GetFaviconData(void) const { return m_FaviconData; } - cRSAPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } + cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; } bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; } @@ -182,7 +182,7 @@ private: bool m_bRestarting; /** The private key used for the assymetric encryption start in the protocols */ - cRSAPrivateKey m_PrivateKey; + cRsaPrivateKey m_PrivateKey; /** Public key for m_PrivateKey, ASN1-DER-encoded */ AString m_PublicKeyDER; diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp index 03e94e791..e95af3a1c 100644 --- a/src/Simulator/FloodyFluidSimulator.cpp +++ b/src/Simulator/FloodyFluidSimulator.cpp @@ -119,7 +119,7 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re if (SpreadFurther && (NewMeta < 8)) { // Spread to the neighbors: - Spread(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta); + SpreadXZ(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta); } // Mark as processed: @@ -130,7 +130,7 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re -void cFloodyFluidSimulator::Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) +void cFloodyFluidSimulator::SpreadXZ(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) { SpreadToNeighbor(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, a_NewMeta); SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, a_NewMeta); diff --git a/src/Simulator/FloodyFluidSimulator.h b/src/Simulator/FloodyFluidSimulator.h index 632de3bb2..8e1be5e6b 100644 --- a/src/Simulator/FloodyFluidSimulator.h +++ b/src/Simulator/FloodyFluidSimulator.h @@ -48,16 +48,13 @@ protected: bool CheckNeighborsForSource(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ); /** Checks if the specified block should harden (Water/Lava interaction) and if so, converts it to a suitable block. - * - * Returns whether the block was changed or not. - */ + Returns whether the block was changed or not. */ bool HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_Meta); - /** Spread water to neighbors. - * - * May be overridden to provide more sophisticated algorithms. - */ - virtual void Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta); + /** Spread fluid to XZ neighbors. + The coords are of the block currently being processed; a_NewMeta is the new meta for the new fluid block. + Descendants may overridde to provide more sophisticated algorithms. */ + virtual void SpreadXZ(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta); } ; diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index c8709a9af..6e722d0fa 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -118,7 +118,9 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, // Things that can send power through a block but which depends on meta ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || - (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) + (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) || + (((Block == E_BLOCK_STONE_PRESSURE_PLATE) || (Block == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (Meta == 0)) || + (((Block == E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE) || (Block == E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE)) && (Meta == 0)) ) { LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); @@ -1047,12 +1049,13 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R case E_BLOCK_STONE_PRESSURE_PLATE: { // MCS feature - stone pressure plates can only be triggered by players :D - cPlayer * a_Player = m_World.FindClosestPlayer(Vector3f(BlockX + 0.5f, (float)a_RelBlockY, BlockZ + 0.5f), 0.5f, false); + cPlayer * a_Player = m_World.FindClosestPlayer(Vector3f(BlockX + 0.5f, (float)a_RelBlockY, BlockZ + 0.5f), 0.7f, false); if (a_Player != NULL) { m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x1); SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); } else { @@ -1115,6 +1118,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R } m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Power); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); } else { @@ -1182,6 +1186,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R } m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Power); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); } else { @@ -1247,6 +1252,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R } m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + SetDirectionLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, BLOCK_FACE_YM, a_MyType); } else { diff --git a/src/Simulator/VanillaFluidSimulator.cpp b/src/Simulator/VanillaFluidSimulator.cpp index 78aff9d68..18d9b07e1 100644 --- a/src/Simulator/VanillaFluidSimulator.cpp +++ b/src/Simulator/VanillaFluidSimulator.cpp @@ -35,14 +35,16 @@ cVanillaFluidSimulator::cVanillaFluidSimulator( -void cVanillaFluidSimulator::Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) +void cVanillaFluidSimulator::SpreadXZ(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) { + // Calculate the distance to the nearest "hole" in each direction: int Cost[4]; Cost[0] = CalculateFlowCost(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, X_PLUS); Cost[1] = CalculateFlowCost(a_Chunk, a_RelX - 1, a_RelY, a_RelZ, X_MINUS); Cost[2] = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ + 1, Z_PLUS); Cost[3] = CalculateFlowCost(a_Chunk, a_RelX, a_RelY, a_RelZ - 1, Z_MINUS); + // Find the minimum distance: int MinCost = InfiniteCost; for (unsigned int i = 0; i < ARRAYCOUNT(Cost); ++i) { @@ -52,6 +54,7 @@ void cVanillaFluidSimulator::Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, in } } + // Spread in all directions where the distance matches the minimum: if (Cost[0] == MinCost) { SpreadToNeighbor(a_Chunk, a_RelX + 1, a_RelY, a_RelZ, a_NewMeta); @@ -86,7 +89,10 @@ int cVanillaFluidSimulator::CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int { return Cost; } - if (!IsPassableForFluid(BlockType) && !IsBlockLiquid(BlockType)) + if ( + !IsPassableForFluid(BlockType) || // The block cannot be passed by the liquid ... + (IsAllowedBlock(BlockType) && (BlockMeta == 0)) // ... or if it is liquid, it is a source block + ) { return Cost; } diff --git a/src/Simulator/VanillaFluidSimulator.h b/src/Simulator/VanillaFluidSimulator.h index a9ea98b5a..89a56ca14 100644 --- a/src/Simulator/VanillaFluidSimulator.h +++ b/src/Simulator/VanillaFluidSimulator.h @@ -30,7 +30,7 @@ public: protected: // cFloodyFluidSimulator overrides: - virtual void Spread(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) override; + virtual void SpreadXZ(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_NewMeta) override; /** Recursively calculates the minimum number of blocks needed to descend a level. */ int CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ, Direction a_Dir, unsigned a_Iteration = 0); diff --git a/src/Statistics.cpp b/src/Statistics.cpp new file mode 100644 index 000000000..2c980d98e --- /dev/null +++ b/src/Statistics.cpp @@ -0,0 +1,139 @@ + +// Statistics.cpp + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Statistics.h" + + + +cStatInfo cStatInfo::ms_Info[statCount] = { + // The order must match the order of enum eStatistic + + // http://minecraft.gamepedia.com/Achievements + + /* Type | Name | Prerequisite */ + cStatInfo(achOpenInv, "openInventory"), + cStatInfo(achMineWood, "mineWood", achOpenInv), + cStatInfo(achCraftWorkbench, "buildWorkBench", achMineWood), + cStatInfo(achCraftPickaxe, "buildPickaxe", achCraftWorkbench), + cStatInfo(achCraftFurnace, "buildFurnace", achCraftPickaxe), + cStatInfo(achAcquireIron, "acquireIron", achCraftFurnace), + cStatInfo(achCraftHoe, "buildHoe", achCraftWorkbench), + cStatInfo(achMakeBread, "makeBread", achCraftHoe), + cStatInfo(achBakeCake, "bakeCake", achCraftHoe), + cStatInfo(achCraftBetterPick, "buildBetterPickaxe", achCraftPickaxe), + cStatInfo(achCookFish, "cookFish", achAcquireIron), + cStatInfo(achOnARail, "onARail", achAcquireIron), + cStatInfo(achCraftSword, "buildSword", achCraftWorkbench), + cStatInfo(achKillMonster, "killEnemy", achCraftSword), + cStatInfo(achKillCow, "killCow", achCraftSword), + cStatInfo(achFlyPig, "flyPig", achKillCow), + cStatInfo(achSnipeSkeleton, "snipeSkeleton", achKillMonster), + cStatInfo(achDiamonds, "diamonds", achAcquireIron), + cStatInfo(achEnterPortal, "portal", achDiamonds), + cStatInfo(achReturnToSender, "ghast", achEnterPortal), + cStatInfo(achBlazeRod, "blazeRod", achEnterPortal), + cStatInfo(achBrewPotion, "potion", achBlazeRod), + cStatInfo(achEnterTheEnd, "theEnd", achBlazeRod), + cStatInfo(achDefeatDragon, "theEnd2", achEnterTheEnd), + cStatInfo(achCraftEnchantTable, "enchantments", achDiamonds), + cStatInfo(achOverkill, "overkill", achCraftEnchantTable), + cStatInfo(achBookshelf, "bookcase", achCraftEnchantTable), + cStatInfo(achExploreAllBiomes, "exploreAllBiomes", achEnterTheEnd), + cStatInfo(achSpawnWither, "spawnWither", achDefeatDragon), + cStatInfo(achKillWither, "killWither", achSpawnWither), + cStatInfo(achFullBeacon, "fullBeacon", achKillWither), + cStatInfo(achBreedCow, "breedCow", achKillCow), + cStatInfo(achThrowDiamonds, "diamondsToYou", achDiamonds), + + // http://minecraft.gamepedia.com/Statistics + + /* Type | Name */ + cStatInfo(statGamesQuit, "stat.leaveGame"), + cStatInfo(statMinutesPlayed, "stat.playOneMinute"), + cStatInfo(statDistWalked, "stat.walkOnCm"), + cStatInfo(statDistSwum, "stat.swimOneCm"), + cStatInfo(statDistFallen, "stat.fallOneCm"), + cStatInfo(statDistClimbed, "stat.climbOneCm"), + cStatInfo(statDistFlown, "stat.flyOneCm"), + cStatInfo(statDistMinecart, "stat.minecartOneCm"), + cStatInfo(statDistBoat, "stat.boatOneCm"), + cStatInfo(statDistPig, "stat.pigOneCm"), + cStatInfo(statDistHorse, "stat.horseOneCm"), + cStatInfo(statJumps, "stat.jump"), + cStatInfo(statItemsDropped, "stat.drop"), + cStatInfo(statDamageDealt, "stat.damageDealth"), + cStatInfo(statDamageTaken, "stat.damageTaken"), + cStatInfo(statDeaths, "stat.deaths"), + cStatInfo(statMobKills, "stat.mobKills"), + cStatInfo(statAnimalsBred, "stat.animalsBred"), + cStatInfo(statPlayerKills, "stat.playerKills"), + cStatInfo(statFishCaught, "stat.fishCaught"), + cStatInfo(statJunkFished, "stat.junkFished"), + cStatInfo(statTreasureFished, "stat.treasureFished") +}; + + + + + + +cStatInfo::cStatInfo() + : m_Type(statInvalid) + , m_Depends(statInvalid) +{} + + + + + +cStatInfo::cStatInfo(const eStatistic a_Type, const AString & a_Name, const eStatistic a_Depends) + : m_Type(a_Type) + , m_Name(a_Name) + , m_Depends(a_Depends) +{} + + + + + +const AString & cStatInfo::GetName(const eStatistic a_Type) +{ + ASSERT((a_Type > statInvalid) && (a_Type < statCount)); + + return ms_Info[a_Type].m_Name; +} + + + + + +eStatistic cStatInfo::GetType(const AString & a_Name) +{ + for (unsigned int i = 0; i < ARRAYCOUNT(ms_Info); ++i) + { + if (NoCaseCompare(ms_Info[i].m_Name, a_Name)) + { + return ms_Info[i].m_Type; + } + } + + return statInvalid; +} + + + + + +eStatistic cStatInfo::GetPrerequisite(const eStatistic a_Type) +{ + ASSERT((a_Type > statInvalid) && (a_Type < statCount)); + + return ms_Info[a_Type].m_Depends; +} + + + + + diff --git a/src/Statistics.h b/src/Statistics.h new file mode 100644 index 000000000..540df38cc --- /dev/null +++ b/src/Statistics.h @@ -0,0 +1,116 @@ + +// Statistics.h + + + + +#pragma once + + + + +enum eStatistic +{ + // The order must match the order of cStatInfo::ms_Info + + statInvalid = -1, + + /* Achievements */ + achOpenInv, /* Taking Inventory */ + achMineWood, /* Getting Wood */ + achCraftWorkbench, /* Benchmarking */ + achCraftPickaxe, /* Time to Mine! */ + achCraftFurnace, /* Hot Topic */ + achAcquireIron, /* Acquire Hardware */ + achCraftHoe, /* Time to Farm! */ + achMakeBread, /* Bake Bread */ + achBakeCake, /* The Lie */ + achCraftBetterPick, /* Getting an Upgrade */ + achCookFish, /* Delicious Fish */ + achOnARail, /* On A Rail */ + achCraftSword, /* Time to Strike! */ + achKillMonster, /* Monster Hunter */ + achKillCow, /* Cow Tipper */ + achFlyPig, /* When Pigs Fly */ + achSnipeSkeleton, /* Sniper Duel */ + achDiamonds, /* DIAMONDS! */ + achEnterPortal, /* We Need to Go Deeper */ + achReturnToSender, /* Return to Sender */ + achBlazeRod, /* Into Fire */ + achBrewPotion, /* Local Brewery */ + achEnterTheEnd, /* The End? */ + achDefeatDragon, /* The End. */ + achCraftEnchantTable, /* Enchanter */ + achOverkill, /* Overkill */ + achBookshelf, /* Librarian */ + achExploreAllBiomes, /* Adventuring Time */ + achSpawnWither, /* The Beginning? */ + achKillWither, /* The Beginning. */ + achFullBeacon, /* Beaconator */ + achBreedCow, /* Repopulation */ + achThrowDiamonds, /* Diamonds to you! */ + + /* Statistics */ + statGamesQuit, + statMinutesPlayed, + statDistWalked, + statDistSwum, + statDistFallen, + statDistClimbed, + statDistFlown, + statDistDove, + statDistMinecart, + statDistBoat, + statDistPig, + statDistHorse, + statJumps, + statItemsDropped, + statDamageDealt, + statDamageTaken, + statDeaths, + statMobKills, + statAnimalsBred, + statPlayerKills, + statFishCaught, + statJunkFished, + statTreasureFished, + + statCount +}; + + + + + + +/** Class used to store and query statistic-related information. */ +class cStatInfo +{ +public: + + cStatInfo(); + + cStatInfo(const eStatistic a_Type, const AString & a_Name, const eStatistic a_Depends = statInvalid); + + /** Type -> Name */ + static const AString & GetName(const eStatistic a_Type); + + /** Name -> Type */ + static eStatistic GetType(const AString & a_Name); + + /** Returns stat prerequisite. (Used for achievements) */ + static eStatistic GetPrerequisite(const eStatistic a_Type); + +private: + + eStatistic m_Type; + + AString m_Name; + + eStatistic m_Depends; + + static cStatInfo ms_Info[statCount]; +}; + + + diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index a3b3cc5be..08e164d78 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -89,6 +89,7 @@ bool cWebAdmin::Init(void) m_IniFile.AddHeaderComment(" Password format: Password=*password*; for example:"); m_IniFile.AddHeaderComment(" [User:admin]"); m_IniFile.AddHeaderComment(" Password=admin"); + m_IniFile.WriteFile("webadmin.ini"); } if (!m_IniFile.GetValueSetB("WebAdmin", "Enabled", true)) @@ -285,11 +286,6 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque Content = GetDefaultPage(); } - if (ShouldWrapInTemplate && (URL.size() > 1)) - { - Content += "\n<p><a href='" + BaseURL + "'>Go back</a></p>"; - } - int MemUsageKiB = cRoot::GetPhysicalRAMUsage(); if (MemUsageKiB > 0) { diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h index 5e5af3ca3..bcf93228f 100644 --- a/src/WorldStorage/FastNBT.h +++ b/src/WorldStorage/FastNBT.h @@ -237,7 +237,7 @@ public: { ASSERT(m_Tags[(size_t)a_Tag].m_Type == TAG_String); AString res; - res.assign(m_Data + m_Tags[(size_t)a_Tag].m_DataStart, m_Tags[(size_t)a_Tag].m_DataLength); + res.assign(m_Data + m_Tags[(size_t)a_Tag].m_DataStart, (size_t)m_Tags[(size_t)a_Tag].m_DataLength); return res; } @@ -245,7 +245,7 @@ public: inline AString GetName(int a_Tag) const { AString res; - res.assign(m_Data + m_Tags[(size_t)a_Tag].m_NameStart, m_Tags[(size_t)a_Tag].m_NameLength); + res.assign(m_Data + m_Tags[(size_t)a_Tag].m_NameStart, (size_t)m_Tags[(size_t)a_Tag].m_NameLength); return res; } diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 773954f73..fd356c7de 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -28,7 +28,7 @@ #include "../Entities/Boat.h" #include "../Entities/Minecart.h" #include "../Entities/Pickup.h" -#include "../Entities/ProjectileEntity.h" +#include "../Entities/ArrowEntity.h" #include "../Entities/TNTEntity.h" #include "../Entities/ExpOrb.h" #include "../Entities/HangingEntity.h" @@ -516,7 +516,7 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster) } case cMonster::mtWither: { - m_Writer.AddInt("Invul", ((const cWither *)a_Monster)->GetNumInvulnerableTicks()); + m_Writer.AddInt("Invul", ((const cWither *)a_Monster)->GetWitherInvulnerableTicks()); break; } case cMonster::mtWolf: diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 66144dbd5..f33178173 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -27,7 +27,6 @@ #include "../BlockEntities/MobHeadEntity.h" #include "../BlockEntities/FlowerPotEntity.h" - #include "../Mobs/Monster.h" #include "../Mobs/IncludeAllMonsters.h" @@ -36,7 +35,12 @@ #include "../Entities/FallingBlock.h" #include "../Entities/Minecart.h" #include "../Entities/Pickup.h" -#include "../Entities/ProjectileEntity.h" +#include "../Entities/ArrowEntity.h" +#include "../Entities/ThrownEggEntity.h" +#include "../Entities/ThrownEnderPearlEntity.h" +#include "../Entities/ThrownSnowballEntity.h" +#include "../Entities/FireChargeEntity.h" +#include "../Entities/GhastFireballEntity.h" #include "../Entities/TNTEntity.h" #include "../Entities/ExpOrb.h" #include "../Entities/HangingEntity.h" @@ -2272,7 +2276,7 @@ void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a int CurrLine = a_NBT.FindChildByName(a_TagIdx, "Invul"); if (CurrLine > 0) { - Monster->SetNumInvulnerableTicks(a_NBT.GetInt(CurrLine)); + Monster->SetWitherInvulnerableTicks(a_NBT.GetInt(CurrLine)); } a_Entities.push_back(Monster.release()); @@ -2668,6 +2672,11 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri return false; } + // Add padding to 4K boundary: + size_t BytesWritten = a_Data.size() + MCA_CHUNK_HEADER_LENGTH; + static const char Padding[4095] = {0}; + m_File.Write(Padding, 4096 - (BytesWritten % 4096)); + // Store the header: ChunkSize = (a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number ASSERT(ChunkSize < 256); diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp index c07c9e96f..359bac4a8 100644 --- a/src/WorldStorage/WSSCompact.cpp +++ b/src/WorldStorage/WSSCompact.cpp @@ -468,7 +468,15 @@ cWSSCompact::cPAKFile::cPAKFile(const AString & a_FileName, int a_LayerX, int a_ for (int i = 0; i < NumChunks; i++) { sChunkHeader * Header = new sChunkHeader; - READ(*Header); + + // Here we do not use the READ macro, as it does not free the resources + // allocated with new in case of error. + if (f.Read(Header, sizeof(*Header)) != sizeof(*Header)) + { + LOGERROR("ERROR READING %s FROM FILE %s (line %d); file offset %d", "Header", m_FileName.c_str(), __LINE__, f.Tell()); + delete Header; + return; + } m_ChunkHeaders.push_back(Header); } // for i - chunk headers |