diff options
author | andrew <xdotftw@gmail.com> | 2014-02-20 14:37:15 +0100 |
---|---|---|
committer | andrew <xdotftw@gmail.com> | 2014-02-20 14:37:15 +0100 |
commit | 83d3a2eedf88a3a180b9dfc4c706838d7f99382a (patch) | |
tree | d96970727615c681efe3bf76793015cead438394 | |
parent | cMapDecorator: Implemented random rotations (diff) | |
parent | APIDump: Fixed cBlockArea:GetRelBlockType() return types. (diff) | |
download | cuberite-83d3a2eedf88a3a180b9dfc4c706838d7f99382a.tar cuberite-83d3a2eedf88a3a180b9dfc4c706838d7f99382a.tar.gz cuberite-83d3a2eedf88a3a180b9dfc4c706838d7f99382a.tar.bz2 cuberite-83d3a2eedf88a3a180b9dfc4c706838d7f99382a.tar.lz cuberite-83d3a2eedf88a3a180b9dfc4c706838d7f99382a.tar.xz cuberite-83d3a2eedf88a3a180b9dfc4c706838d7f99382a.tar.zst cuberite-83d3a2eedf88a3a180b9dfc4c706838d7f99382a.zip |
106 files changed, 4203 insertions, 684 deletions
diff --git a/.gitignore b/.gitignore index 007b21519..c8ad93a80 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,9 @@ cloc.xsl *.*~ *~ *.orig +## Eclipse +.cproject +.project # world inside source ChunkWorx.ini @@ -57,7 +60,7 @@ install_mainfest.txt src/MCServer lib/tolua++/tolua src/Bindings/Bindings.* -src/Bindings/BindingsDependecies.txt +src/Bindings/BindingDependecies.txt MCServer.dir/ #win32 cmake stuff diff --git a/.travis.yml b/.travis.yml index 38dd2f280..c6537cf47 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ compiler: - gcc - clang # Build MCServer -script: cmake . -DCMAKE_BUILD_TYPE=RELEASE -DSELF_TEST=1 && make -j 2 && cd MCServer/ && (echo stop | ./MCServer) +script: cmake . -DCMAKE_BUILD_TYPE=RELEASE -DBUILD_TOOLS=1 -DSELF_TEST=1 && make -j 2 && cd MCServer/ && (echo stop | ./MCServer) # Notification Settings notifications: diff --git a/CMakeLists.txt b/CMakeLists.txt index 57b200a2a..05b6d879b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,105 +3,20 @@ cmake_minimum_required (VERSION 2.6) # Without this, the MSVC variable isn't defined for MSVC builds ( http://www.cmake.org/pipermail/cmake/2011-November/047130.html ) enable_language(CXX C) -macro (add_flags_lnk FLAGS) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} ${FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${FLAGS}") - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAGS}") - set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS_DEBUG} ${FLAGS}") - set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${FLAGS}") -endmacro() - -macro(add_flags_cxx FLAGS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}") -endmacro() - -# Add the preprocessor macros used for distinguishing between debug and release builds (CMake does this automatically for MSVC): -if (NOT MSVC) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG") -endif() - -if(MSVC) - # Make build use multiple threads under MSVC: - add_flags_cxx("/MP") - - # Make release builds use link-time code generation: - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /GL") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") - set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG") - set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /LTCG") -elseif(APPLE) - #on os x clang adds pthread for us but we need to add it for gcc - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11") - else() - add_flags_cxx("-pthread") - endif() - -else() - # Let gcc / clang know that we're compiling a multi-threaded app: - add_flags_cxx("-pthread") - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11") - endif() - - # We use a signed char (fixes #640 on RasPi) - add_flags_cxx("-fsigned-char") -endif() - - -# Allow for a forced 32-bit build under 64-bit OS: -if (FORCE_32) - add_flags_cxx("-m32") - add_flags_lnk("-m32") -endif() - - -# Have the compiler generate code specifically targeted at the current machine on Linux -if(LINUX AND NOT CROSSCOMPILE) - add_flags_cxx("-march=native") -endif() - - -# Use static CRT in MSVC builds: -if (MSVC) - string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") - string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") - string(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - string(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") +# This has to be done before any flags have been set up. +if(${BUILD_TOOLS}) + add_subdirectory(Tools/MCADefrag/) + add_subdirectory(Tools/ProtoProxy/) endif() - -# Set lower warnings-level for the libraries: -if (MSVC) - # Remove /W3 from command line -- cannot just cancel it later with /w like in unix, MSVC produces a D9025 warning (option1 overriden by option2) - string(REPLACE "/W3" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") - string(REPLACE "/W3" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") - string(REPLACE "/W3" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - string(REPLACE "/W3" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") -else() - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -w") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -w") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -w") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -w") +if(${BUILD_UNSTABLE_TOOLS}) + add_subdirectory(Tools/GeneratorPerformanceTest/) endif() +include(SetFlags.cmake) +set_flags() +set_lib_flags() +enable_profile() # Under Windows, we need Lua as DLL; on *nix we need it linked statically: if (WIN32) @@ -109,18 +24,6 @@ if (WIN32) endif() -# On Unix we use two dynamic loading libraries dl and ltdl. -# Preference is for dl on unknown systems as it is specified in POSIX -# the dynamic loader is used by lua and sqllite. -if (UNIX) - if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") - set(DYNAMIC_LOADER ltdl) - else() - set(DYNAMIC_LOADER dl) - endif() -endif() - - # The Expat library is linked in statically, make the source files aware of that: add_definitions(-DXML_STATIC) @@ -129,64 +32,10 @@ if(${SELF_TEST}) add_definitions(-DSELF_TEST) endif() -# Declare the flags used for profiling builds: -if (MSVC) - set (CXX_PROFILING "") - set (LNK_PROFILING "/PROFILE") -else() - set (CXX_PROFILING "-pg") - set (LNK_PROFILING "-pg") -endif() -# Declare the profiling configurations: -SET(CMAKE_CXX_FLAGS_DEBUGPROFILE - "${CMAKE_CXX_FLAGS_DEBUG} ${PCXX_ROFILING}" - CACHE STRING "Flags used by the C++ compiler during profile builds." - FORCE ) -SET(CMAKE_C_FLAGS_DEBUGPROFILE - "${CMAKE_C_FLAGS_DEBUG} ${CXX_PROFILING}" - CACHE STRING "Flags used by the C compiler during profile builds." - FORCE ) -SET(CMAKE_EXE_LINKER_FLAGS_DEBUGPROFILE - "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${LNK_PROFILING}" - CACHE STRING "Flags used for linking binaries during profile builds." - FORCE ) -SET(CMAKE_SHARED_LINKER_FLAGS_DEBUGPROFILE - "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} ${LNK_PROFILING}" - CACHE STRING "Flags used by the shared libraries linker during profile builds." - FORCE ) -MARK_AS_ADVANCED( - CMAKE_CXX_FLAGS_DEBUGPROFILE - CMAKE_C_FLAGS_DEBUGPROFILE - CMAKE_EXE_LINKER_FLAGS_DEBUGPROFILE - CMAKE_SHARED_LINKER_FLAGS_DEBUGPROFILE ) - -SET(CMAKE_CXX_FLAGS_RELEASEPROFILE - "${CMAKE_CXX_FLAGS_RELEASE} ${CXX_PROFILING}" - CACHE STRING "Flags used by the C++ compiler during profile builds." - FORCE ) -SET(CMAKE_C_FLAGS_RELEASEPROFILE - "${CMAKE_C_FLAGS_RELEASE} ${CXX_PROFILING}" - CACHE STRING "Flags used by the C compiler during profile builds." - FORCE ) -SET(CMAKE_EXE_LINKER_FLAGS_RELEASEPROFILE - "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${LNK_PROFILING}" - CACHE STRING "Flags used for linking binaries during profile builds." - FORCE ) -SET(CMAKE_SHARED_LINKER_FLAGS_RELEASEPROFILE - "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${LNK_PROFILING}" - CACHE STRING "Flags used by the shared libraries linker during profile builds." - FORCE ) -MARK_AS_ADVANCED( - CMAKE_CXX_FLAGS_RELEASEPROFILE - CMAKE_C_FLAGS_RELEASEPROFILE - CMAKE_EXE_LINKER_FLAGS_RELEASEPROFILE - CMAKE_SHARED_LINKER_FLAGS_RELEASEPROFILE ) - - -# The configuration types need to be set after their respective c/cxx/linker flags and before the project directive -set(CMAKE_CONFIGURATION_TYPES "Debug;Release;DebugProfile;ReleaseProfile" CACHE STRING "" FORCE) + + project (MCServer) # Include all the libraries: @@ -203,24 +52,9 @@ add_subdirectory(lib/md5/) # We use EXCLUDE_FROM_ALL so that only the explicit dependencies are used # (PolarSSL also has test and example programs in their CMakeLists.txt, we don't want those) -add_subdirectory(lib/polarssl/ EXCLUDE_FROM_ALL) - - -# Remove disabling the maximum warning level: -# clang does not like a command line that reads -Wall -Wextra -w -Wall -Wextra and does not output any warnings -# We do not do that for MSVC since MSVC produces an awful lot of warnings for its own STL headers; -# the important warnings are turned on using #pragma in Globals.h -if (NOT MSVC) - string(REPLACE "-w" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") - string(REPLACE "-w" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") - string(REPLACE "-w" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") - add_flags_cxx("-Wall") -endif() +include(lib/polarssl.cmake) -if(${BUILD_TOOLS}) -add_subdirectory(Tools/GeneratorPerformanceTest/) -endif() +set_exe_flags() add_subdirectory (src) diff --git a/COMPILING.md b/COMPILING.md index d3c896bdd..139f1a0ee 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -69,7 +69,7 @@ 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 .. && make ``` The executable will be built in the `MCServer/MCServer` folder and will be named `MCServer`. @@ -81,7 +81,7 @@ 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 .. && make ``` The executable will be built in the `MCServer/MCServer` folder and will be named `MCServer_debug`. diff --git a/MCServer/.gitignore b/MCServer/.gitignore index e3aebbf92..0fd04ef59 100644 --- a/MCServer/.gitignore +++ b/MCServer/.gitignore @@ -4,6 +4,7 @@ *.lib *.ini MCServer +MCServer_debug CommLogs/ logs players diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 01554d99c..73bb5c7fb 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -118,7 +118,7 @@ g_APIDesc = GetRelBlockMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the block meta at the specified relative coords" }, GetRelBlockSkyLight = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the skylight at the specified relative coords" }, GetRelBlockType = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "BLOCKTYPE", Notes = "Returns the block type at the specified relative coords" }, - GetRelBlockTypeMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the block type and meta at the specified relative coords" }, + GetRelBlockTypeMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "BLOCKTYPE, NIBBLETYPE", Notes = "Returns the block type and meta at the specified relative coords" }, GetSizeX = { Params = "", Return = "number", Notes = "Returns the size of the held data in the x-axis" }, GetSizeY = { Params = "", Return = "number", Notes = "Returns the size of the held data in the y-axis" }, GetSizeZ = { Params = "", Return = "number", Notes = "Returns the size of the held data in the z-axis" }, @@ -713,6 +713,7 @@ end IsMinecart = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cMinecart|minecart}}" }, IsMob = { Params = "", Return = "bool", Notes = "Returns true if the entity represents any {{cMonster|mob}}." }, IsOnFire = { Params = "", Return = "bool", Notes = "Returns true if the entity is on fire" }, + IsPainting = { Params = "", Return = "bool", Notes = "Returns if this entity is a painting." }, IsPickup = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cPickup|pickup}}." }, IsPlayer = { Params = "", Return = "bool", Notes = "Returns true if the entity represents a {{cPlayer|player}}" }, IsProjectile = { Params = "", Return = "bool", Notes = "Returns true if the entity is a {{cProjectileEntity}} descendant." }, @@ -779,6 +780,7 @@ end etPickup = { Notes = "The entity is a {{cPickup}}" }, etProjectile = { Notes = "The entity is a {{cProjectileEntity}} descendant" }, etTNT = { Notes = "The entity is a {{cTNTEntity}}" }, + etPainting = { Notes = "The entity is a {{cPainting}}" }, }, ConstantGroups = { @@ -1109,6 +1111,9 @@ These ItemGrids are available in the API and can be manipulated by the plugins, IsFullStack = { Params = "", Return = "bool", Notes = "Returns true if the item is stacked up to its maximum stacking" }, IsSameType = { Params = "cItem", Return = "bool", Notes = "Returns true if the item in the parameter is of the same ItemType as the one stored in the object. This is true even if the two items have different enchantments" }, IsStackableWith = { Params = "cItem", Return = "bool", Notes = "Returns true if the item in the parameter is stackable with the one stored in the object. Two items with different enchantments cannot be stacked" }, + IsBothNameAndLoreEmpty = { Params = "", Return = "bool", Notes = "Returns if both the custom name and lore are not set." }, + IsCustomNameEmpty = { Params = "", Return = "bool", Notes = "Returns if the custom name of the cItem is empty." }, + IsLoreEmpty = { Params = "", Return = "", Notes = "Returns if the lore of the cItem is empty." }, }, Variables = { @@ -1116,6 +1121,8 @@ These ItemGrids are available in the API and can be manipulated by the plugins, m_ItemCount = { Type = "number", Notes = "Number of items in this stack" }, m_ItemDamage = { Type = "number", Notes = "The damage of the item. Zero means no damage. Maximum damage can be queried with GetMaxDamage()" }, m_ItemType = { Type = "number", Notes = "The item type. One of E_ITEM_ or E_BLOCK_ constants" }, + m_CustomName = { Type = "string", Notes = "The custom name for an item." }, + m_Lore = { Type = "string", Notes = "The lore for an item. Line breaks are represented by the ` character." }, }, AdditionalInfo = { @@ -1160,6 +1167,17 @@ local Item5 = cItem(E_ITEM_DIAMOND_CHESTPLATE, 1, 0, "thorns=1;unbreaking=3"); }, }, -- cItem + cPainting = + { + Desc = "This class represents a painting in the world. These paintings are special and different from Vanilla in that they can be critical-hit.", + Functions = + { + GetDirection = { Params = "", Return = "number", Notes = "Returns the direction the painting faces. Directions: ZP - 0, ZM - 2, XM - 1, XP - 3. Note that these are not the BLOCK_FACE constants." }, + GetName = { Params = "", Return = "string", Notes = "Returns the name of the painting" }, + }, + + }, -- cPainting + cItemGrid = { Desc = [[This class represents a 2D array of items. It is used as the underlying storage and API for all cases that use a grid of items: @@ -1749,6 +1767,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); GetCurrentPlugin = { Params = "", Return = "{{cPlugin}}", Notes = "Returns the {{cPlugin}} object for the calling plugin. This is the same object that the Initialize function receives as the argument." }, GetNumPlugins = { Params = "", Return = "number", Notes = "Returns the number of plugins, including the disabled ones" }, GetPlugin = { Params = "PluginName", Return = "{{cPlugin}}", Notes = "(<b>DEPRECATED, UNSAFE</b>) Returns a plugin handle of the specified plugin, or nil if such plugin is not loaded. Note thatdue to multithreading the handle is not guaranteed to be safe for use when stored - a single-plugin reload may have been triggered in the mean time for the requested plugin." }, + GetPluginsPath = { Params = "", Return = "string", Notes = "Returns the path where the individual plugin folders are located. Doesn't include the path separator at the end of the returned string." }, IsCommandBound = { Params = "Command", Return = "bool", Notes = "Returns true if in-game Command is already bound (by any plugin)" }, IsConsoleCommandBound = { Params = "Command", Return = "bool", Notes = "Returns true if console Command is already bound (by any plugin)" }, LoadPlugin = { Params = "PluginFolder", Return = "", Notes = "(<b>DEPRECATED</b>) Loads a plugin from the specified folder. NOTE: Loading plugins may be an unsafe operation and may result in a deadlock or a crash. This API is deprecated and might be removed." }, @@ -2026,11 +2045,13 @@ end AreCommandBlocksEnabled = { Params = "", Return = "bool", Notes = "Returns whether command blocks are enabled on the (entire) server" }, BroadcastBlockAction = { Params = "BlockX, BlockY, BlockZ, ActionByte1, ActionByte2, BlockType, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Broadcasts the BlockAction packet to all clients who have the appropriate chunk loaded (except ExcludeClient). The contents of the packet are specified by the parameters for the call, the blocktype needn't match the actual block that is present in the world data at the specified location." }, BroadcastChat = { Params = "Message, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Sends the Message to all players in this world, except the optional ExcludeClient. No formatting is done by the server." }, + BroadcastChatDeath = { Params = "Message, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Prepends Gray [DEATH] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For when a player dies." }, BroadcastChatFailure = { Params = "Message, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Prepends Rose [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For a command that failed to run because of insufficient permissions, etc." }, BroadcastChatFatal = { Params = "Message, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Prepends Red [FATAL] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For a plugin that crashed, or similar." }, BroadcastChatInfo = { Params = "Message, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Prepends Yellow [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For informational messages, such as command usage." }, BroadcastChatSuccess = { Params = "Message, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Prepends Green [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For success messages." }, BroadcastChatWarning = { Params = "Message, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Prepends Rose [WARN] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For concerning events, such as plugin reload etc." }, + BroadcastParticleEffect = { Params = "ParticleName, X, Y, Z, OffSetX, OffSetY, OffSetZ, ParticleData, ParticleAmmount, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Spawns the specified particles to all players in the world exept the optional ExeptClient. A list of available particles by thinkofdeath can be found {{https://gist.github.com/thinkofdeath/5110835|Here}}" }, BroadcastSoundEffect = { Params = "SoundName, X, Y, Z, Volume, Pitch, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Sends the specified sound effect to all players in this world, except the optional ExceptClient" }, BroadcastSoundParticleEffect = { Params = "EffectID, X, Y, Z, EffectData, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Sends the specified effect to all players in this world, except the optional ExceptClient" }, CastThunderbolt = { Params = "X, Y, Z", Return = "", Notes = "Creates a thunderbolt at the specified coords" }, @@ -2041,6 +2062,7 @@ end DoExplosionAt = { Params = "Force, X, Y, Z, CanCauseFire, Source, SourceData", Return = "", Notes = "Creates an explosion of the specified relative force in the specified position. If CanCauseFire is set, the explosion will set blocks on fire, too. The Source parameter specifies the source of the explosion, one of the esXXX constants. The SourceData parameter is specific to each source type, usually it provides more info about the source." }, DoWithBlockEntityAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a block entity at the specified coords, calls the CallbackFunction with the {{cBlockEntity}} parameter representing the block entity. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cBlockEntity|BlockEntity}}, [CallbackData])</pre> The function returns false if there is no block entity, or if there is, it returns the bool value that the callback has returned. Use {{tolua}}.cast() to cast the Callback's BlockEntity parameter to the correct {{cBlockEntity}} descendant." }, DoWithChestAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a chest at the specified coords, calls the CallbackFunction with the {{cChestEntity}} parameter representing the chest. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cChestEntity|ChestEntity}}, [CallbackData])</pre> The function returns false if there is no chest, or if there is, it returns the bool value that the callback has returned." }, + DoWithCommandBlockAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a command block at the specified coords, calls the CallbackFunction with the {{cCommandBlockEntity}} parameter representing the command block. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cCommandBlockEntity|CommandBlockEntity}}, [CallbackData])</pre> The function returns false if there is no command block, or if there is, it returns the bool value that the callback has returned." }, DoWithDispenserAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dispenser at the specified coords, calls the CallbackFunction with the {{cDispenserEntity}} parameter representing the dispenser. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDispenserEntity|DispenserEntity}}, [CallbackData])</pre> The function returns false if there is no dispenser, or if there is, it returns the bool value that the callback has returned." }, DoWithDropSpenserAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dropper or a dispenser at the specified coords, calls the CallbackFunction with the {{cDropSpenserEntity}} parameter representing the dropper or dispenser. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropSpenserEntity|DropSpenserEntity}}, [CallbackData])</pre> Note that this can be used to access both dispensers and droppers in a similar way. The function returns false if there is neither dispenser nor dropper, or if there is, it returns the bool value that the callback has returned." }, DoWithDropperAt = { Params = "X, Y, Z, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If there is a dropper at the specified coords, calls the CallbackFunction with the {{cDropperEntity}} parameter representing the dropper. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cDropperEntity|DropperEntity}}, [CallbackData])</pre> The function returns false if there is no dropper, or if there is, it returns the bool value that the callback has returned." }, @@ -2086,6 +2108,7 @@ end GetMaxSugarcaneHeight = { Params = "", Return = "number", Notes = "Returns the configured maximum height to which sugarcane will grow naturally." }, GetName = { Params = "", Return = "string", Notes = "Returns the name of the world, as specified in the settings.ini file." }, GetNumChunks = { Params = "", Return = "number", Notes = "Returns the number of chunks currently loaded." }, + GetScoreBoard = { Params = "", Return = "{{cScoreBoard}}", Notes = "Returns the {{cScoreBoard|ScoreBoard}} object used by this world. " }, GetSignLines = { Params = "BlockX, BlockY, BlockZ", Return = "IsValid, [Line1, Line2, Line3, Line4]", Notes = "Returns true and the lines of a sign at the specified coords, or false if there is no sign at the coords." }, GetSpawnX = { Params = "", Return = "number", Notes = "Returns the X coord of the default spawn" }, GetSpawnY = { Params = "", Return = "number", Notes = "Returns the Y coord of the default spawn" }, @@ -2117,9 +2140,15 @@ end QueueSaveAllChunks = { Params = "", Return = "", Notes = "Queues all chunks to be saved in the world storage thread" }, QueueSetBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta, TickDelay", Return = "", Notes = "Queues the block to be set to the specified blocktype and meta after the specified amount of game ticks. Uses SetBlock() for the actual setting, so simulators are woken up and block entities are handled correctly." }, QueueTask = { Params = "TaskFunction", Return = "", Notes = "Queues the specified function to be executed in the tick thread. This is the primary means of interaction with a cWorld from the WebAdmin page handlers (see {{WebWorldThreads}}). The function signature is <pre class=\"pretty-print lang-lua\">function()</pre>All return values from the function are ignored. Note that this function is actually called *after* the QueueTask() function returns. Note that it is unsafe to store references to MCServer objects, such as entities, across from the caller to the task handler function; store the EntityID instead." }, + QueueUnloadUnusedChunks = { Params = "", Return = "", Notes = "Queues a cTask that unloads chunks that are no longer needed and are saved." }, RegenerateChunk = { Params = "ChunkX, ChunkZ", Return = "", Notes = "Queues the specified chunk to be re-generated, overwriting the current data. To queue a chunk for generating only if it doesn't exist, use the GenerateChunk() instead." }, ScheduleTask = { Params = "DelayTicks, TaskFunction", Return = "", Notes = "Queues the specified function to be executed in the world's tick thread after a the specified number of ticks. This enables operations to be queued for execution in the future. The function signature is <pre class=\"pretty-print lang-lua\">function({{cWorld|World}})</pre>All return values from the function are ignored. Note that it is unsafe to store references to MCServer objects, such as entities, across from the caller to the task handler function; store the EntityID instead." }, SendBlockTo = { Params = "BlockX, BlockY, BlockZ, {{cPlayer|Player}}", Return = "", Notes = "Sends the block at the specified coords to the specified player's client, as an UpdateBlock packet." }, + SetAreaBiome = { + { Params = "MinX, MaxX, MinZ, MaxZ, EMCSBiome", Return = "bool", Notes = "Sets the biome in the rectangular area specified. Returns true if successful, false if any of the chunks were unloaded." }, + { Params = "{{cCuboid|Cuboid}}, EMCSBiome", Return = "bool", Notes = "Sets the biome in the cuboid specified. Returns true if successful, false if any of the chunks were unloaded. The cuboid needn't be sorted." }, + }, + SetBiomeAt = { Params = "BlockX, BlockZ, EMCSBiome", Return = "bool", Notes = "Sets the biome at the specified block coords. Returns true if successful, false otherwise." }, SetBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "", Notes = "Sets the block at the specified coords, replaces the block entities for the previous block type, creates a new block entity for the new block, if appropriate, and wakes up the simulators. This is the preferred way to set blocks, as opposed to FastSetBlock(), which is only to be used under special circumstances." }, SetBlockMeta = { @@ -2146,7 +2175,6 @@ end SpawnExperienceOrb = { Params = "X, Y, Z, Reward", Return = "EntityID", Notes = "Spawns an {{cExpOrb|experience orb}} at the specified coords, with the given reward" }, SpawnPrimedTNT = { Params = "X, Y, Z, FuseTimeSecs, InitialVelocityCoeff", Return = "", Notes = "Spawns a {{cTNTEntity|primed TNT entity}} at the specified coords, with the given fuse time. The entity gets a random speed multiplied by the InitialVelocityCoeff, 1 being the default value." }, TryGetHeight = { Params = "BlockX, BlockZ", Return = "IsValid, Height", Notes = "Returns true and height of the highest non-air block if the chunk is loaded, or false otherwise." }, - UnloadUnusedChunks = { Params = "", Return = "", Notes = "Unloads chunks that are no longer needed, and are saved. NOTE: This API is deprecated and will be removed soon." }, UpdateSign = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "Sets the sign text at the specified coords. The sign-updating hooks are called for the change. The Player parameter is used to indicate the player from whom the change has come, it may be nil. Same as SetSignLiens()" }, UseBlockEntity = { Params = "{{cPlayer|Player}}, BlockX, BlockY, BlockZ", Return = "", Notes = "Makes the specified Player use the block entity at the specified coords (open chest UI, etc.) If the cords are in an unloaded chunk or there's no block entity, ignores the call." }, WakeUpSimulators = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Wakes up the simulators for the specified block." }, diff --git a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html index 1eec4842a..35c880b00 100644 --- a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html +++ b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html @@ -20,13 +20,7 @@ <p> Let us begin. In order to begin development, we must firstly obtain a compiled copy of MCServer, and make sure that the Core plugin is within the Plugins folder, and activated. - Core handles much of the MCServer end-user experience and is a necessary component of - plugin development, as necessary plugin components depend on sone of its functions. - </p> - <p> - Next, we must obtain a copy of CoreMessaging.lua. This can be found - <a href="https://gist.github.com/bearbin/8715888">here.</a> - This is used to provide messaging support that is compliant with MCServer standards. + Core handles much of the MCServer end-user experience and gameplay will be very bland without it. </p> <h2>Creating the basic template</h2> <p> @@ -41,7 +35,11 @@ function Initialize(Plugin) Plugin:SetName("NewPlugin") Plugin:SetVersion(1) - PLUGIN = Plugin + -- Hooks + + PLUGIN = Plugin -- NOTE: only needed if you want OnDisable() to use GetName() or something like that + + -- Command Bindings LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion()) return true @@ -58,7 +56,8 @@ end <li><b>Plugin:SetName</b> sets the name of the plugin.</li> <li><b>Plugin:SetVersion</b> sets the revision number of the plugin. This must be an integer.</li> <li><b>LOG</b> logs to console a message, in this case, it prints that the plugin was initialised.</li> - <li>The <b>PLUGIN</b> variable just stores this plugin's object, so GetName() can be called in OnDisable (as no Plugin parameter is passed there, contrary to Initialize).</li> + <li>The <b>PLUGIN</b> variable just stores this plugin's object, so GetName() can be called in OnDisable (as no Plugin parameter is passed there, contrary to Initialize). + This global variable is only needed if you want to know the plugin details (name, etc.) when shutting down.</li> <li><b>function OnDisable</b> is called when the plugin is disabled, commonly when the server is shutting down. Perform cleanup and logging here.</li> </ul> Be sure to return true for this function, else MCS thinks you plugin had failed to initialise and prints a stacktrace with an error message. @@ -159,21 +158,23 @@ cPluginManager.BindCommand("/commandname", "permissionnode", FunctionToCall, " ~ a message. Again, see the API documentation for fuller details. But, you ask, how <i>do</i> we send a message to the client? </p> <p> - Remember that copy of CoreMessaging.lua that we downloaded earlier? Make sure that file is in your plugin folder, along with the main.lua file you are typing - your code in. Since MCS brings all the files together on JIT compile, we don't need to worry about requiring any files or such. Simply follow the below examples: + There are dedicated functions used for sending a player formatted messages. By format, I refer to coloured prefixes/coloured text (depending on configuration) + that clearly categorise what type of message a player is being sent. For example, an informational message has a yellow coloured [INFO] prefix, and a warning message + has a rose coloured [WARNING] prefix. A few of the most used functions are listed here, but see the API docs for more details. Look in the cRoot, cWorld, and cPlayer sections + for functions that broadcast to the entire server, the whole world, and a single player, respectively. </p> <pre class="prettyprint lang-lua"> -- Format: §yellow[INFO] §white%text% (yellow [INFO], white text following it) -- Use: Informational message, such as instructions for usage of a command -SendMessage(Player, "Usage: /explode [player]") +Player:SendMessageInfo("Usage: /explode [player]") -- Format: §green[INFO] §white%text% (green [INFO] etc.) -- Use: Success message, like when a command executes successfully -SendMessageSuccess(Player, "Notch was blown up!") +Player:SendMessageSuccess("Notch was blown up!") -- Format: §rose[INFO] §white%text% (rose coloured [INFO] etc.) -- Use: Failure message, like when a command was entered correctly but failed to run, such as when the destination player wasn't found in a /tp command -SendMessageFailure(Player, "Player Salted was not found") +Player:SendMessageFailure("Player Salted was not found") </pre> <p> Those are the basics. If you want to output text to the player for a reason other than the three listed above, and you want to colour the text, simply concatenate diff --git a/MCServer/Plugins/ChunkWorx/chunkworx_main.lua b/MCServer/Plugins/ChunkWorx/chunkworx_main.lua index f74c4ea2d..88ecb3979 100644 --- a/MCServer/Plugins/ChunkWorx/chunkworx_main.lua +++ b/MCServer/Plugins/ChunkWorx/chunkworx_main.lua @@ -122,7 +122,7 @@ function OnTick( DeltaTime ) end end WW_instance:QueueSaveAllChunks() - WW_instance:UnloadUnusedChunks() + WW_instance:QueueUnloadUnusedChunks() end end end
\ No newline at end of file diff --git a/MCServer/Plugins/ChunkWorx/chunkworx_web.lua b/MCServer/Plugins/ChunkWorx/chunkworx_web.lua index 44993f81c..6c5eab676 100644 --- a/MCServer/Plugins/ChunkWorx/chunkworx_web.lua +++ b/MCServer/Plugins/ChunkWorx/chunkworx_web.lua @@ -44,7 +44,7 @@ function HandleRequest_Generation( Request ) if (Request.PostParams["AGHRRRR"] ~= nil) then GENERATION_STATE = 0 WW_instance:SaveAllChunks() - WW_instance:UnloadUnusedChunks() + WW_instance:QueueUnloadUnusedChunks() LOGERROR("" .. PLUGIN:GetName() .. " v" .. PLUGIN:GetVersion() .. ": works ABORTED by admin") end --Content = Content .. "<head><meta http-equiv=\"refresh\" content=\"2;\"></head>" diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core -Subproject d6ed2041469ab959bbf3842db41c0e25fd249dc +Subproject 3b416b07a339b3abcbc127070d56eea05b05373 diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 8345e2169..66894d835 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -19,18 +19,18 @@ function Initialize(Plugin) cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick2); --]] - cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock); - cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem); - cPluginManager:AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage); - cPluginManager:AddHook(cPluginManager.HOOK_TICK, OnTick); - cPluginManager:AddHook(cPluginManager.HOOK_CHAT, OnChat); - cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity); - cPluginManager:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick); - cPluginManager:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); - cPluginManager:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded); - cPluginManager:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); - - PM = cRoot:Get():GetPluginManager(); + local PM = cPluginManager; + PM:AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock); + PM:AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem); + PM:AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage); + PM:AddHook(cPluginManager.HOOK_TICK, OnTick); + PM:AddHook(cPluginManager.HOOK_CHAT, OnChat); + PM:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity); + PM:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick); + PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); + PM:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded); + PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); + PM:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities"); PM:BindCommand("/ke", "debuggers", HandleKillEntitiesCmd, "- Kills all the loaded entities"); PM:BindCommand("/wool", "debuggers", HandleWoolCmd, "- Sets all your armor to blue wool"); @@ -54,8 +54,11 @@ function Initialize(Plugin) PM:BindCommand("/ff", "debuggers", HandleFurnaceFuel, "- Shows how long the currently held item would burn in a furnace"); PM:BindCommand("/sched", "debuggers", HandleSched, "- Schedules a simple countdown using cWorld:ScheduleTask()"); PM:BindCommand("/cs", "debuggers", HandleChunkStay, "- Tests the ChunkStay Lua integration for the specified chunk coords"); + PM:BindCommand("/compo", "debuggers", HandleCompo, "- Tests the cCompositeChat bindings") + PM:BindCommand("/sb", "debuggers", HandleSetBiome, "- Sets the biome around you to the specified one"); - Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers); + Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers) + Plugin:AddWebTab("StressTest", HandleRequest_StressTest) -- Enable the following line for BlockArea / Generator interface testing: -- PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATED); @@ -1038,6 +1041,68 @@ end +local g_Counter = 0 +local g_JavaScript = +[[ +<script> +function createXHR() +{ + var request = false; + try { + request = new ActiveXObject('Msxml2.XMLHTTP'); + } + catch (err2) + { + try + { + request = new ActiveXObject('Microsoft.XMLHTTP'); + } + catch (err3) + { + try + { + request = new XMLHttpRequest(); + } + catch (err1) + { + request = false; + } + } + } + return request; +} + +function RefreshCounter() +{ + var xhr = createXHR(); + xhr.onreadystatechange = function() + { + if (xhr.readyState == 4) + { + document.getElementById("cnt").innerHTML = xhr.responseText; + } + }; + xhr.open("POST", "/~webadmin/Debuggers/StressTest", true); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.send("counter=true"); +} + +setInterval(RefreshCounter, 10) +</script> +]] + +function HandleRequest_StressTest(a_Request) + if (a_Request.PostParams["counter"]) then + g_Counter = g_Counter + 1 + return tostring(g_Counter) + end + return g_JavaScript .. "<p>The counter below should be reloading as fast as possible</p><div id='cnt'>0</div>" +end + + + + + function OnPluginMessage(a_Client, a_Channel, a_Message) LOGINFO("Received a plugin message from client " .. a_Client:GetUsername() .. ": channel '" .. a_Channel .. "', message '" .. a_Message .. "'"); @@ -1132,3 +1197,64 @@ end + +function HandleCompo(a_Split, a_Player) + -- Send one composite message to self: + local msg = cCompositeChat() + msg:AddTextPart("Hello! ", "b@e") -- bold yellow + msg:AddUrlPart("MCServer", "http://mc-server.org") + msg:AddTextPart(" rules! ") + msg:AddRunCommandPart("Set morning", "/time set 0") + a_Player:SendMessage(msg) + + -- Broadcast another one to the world: + local msg2 = cCompositeChat() + msg2:AddSuggestCommandPart(a_Player:GetName(), "/tell " .. a_Player:GetName() .. " ") + msg2:AddTextPart(" knows how to use cCompositeChat!"); + a_Player:GetWorld():BroadcastChat(msg2) + + return true +end + + + + + +function HandleSetBiome(a_Split, a_Player) + local Biome = biJungle + local Size = 20 + local SplitSize = #a_Split + if (SplitSize > 3) then + a_Player:SendMessage("Too many parameters. Usage: " .. a_Split[1] .. " <BiomeType>") + return true + end + + if (SplitSize >= 2) then + Biome = StringToBiome(a_Split[2]) + if (Biome == biInvalidBiome) then + a_Player:SendMessage("Unknown biome: '" .. a_Split[2] .. "'. Command ignored.") + return true + end + end + if (SplitSize >= 3) then + Size = tostring(a_Split[3]) + if (Size == nil) then + a_Player:SendMessage("Unknown size: '" .. a_Split[3] .. "'. Command ignored.") + return true + end + end + + local BlockX = math.floor(a_Player:GetPosX()) + local BlockZ = math.floor(a_Player:GetPosZ()) + a_Player:GetWorld():SetAreaBiome(BlockX - Size, BlockX + Size, BlockZ - Size, BlockZ + Size, Biome) + a_Player:SendMessage( + "Blocks {" .. (BlockX - Size) .. ", " .. (BlockZ - Size) .. + "} - {" .. (BlockX + Size) .. ", " .. (BlockZ + Size) .. + "} set to biome #" .. tostring(Biome) .. "." + ) + return true +end + + + + diff --git a/MCServer/Plugins/InfoReg.lua b/MCServer/Plugins/InfoReg.lua new file mode 100644 index 000000000..3afb57488 --- /dev/null +++ b/MCServer/Plugins/InfoReg.lua @@ -0,0 +1,157 @@ + +-- InfoReg.lua + +-- Implements registration functions that process g_PluginInfo + + + + + +--- Lists all the subcommands that the player has permissions for +local function ListSubcommands(a_Player, a_Subcommands, a_CmdString) + a_Player:SendMessage("The " .. a_CmdString .. " command requires another verb:"); + local Verbs = {}; + for cmd, info in pairs(a_Subcommands) do + if (a_Player:HasPermission(info.Permission or "")) then + table.insert(Verbs, a_CmdString .. " " .. cmd); + end + end + table.sort(Verbs); + for idx, verb in ipairs(Verbs) do + a_Player:SendMessage(verb); + end +end + + + + + +--- This is a generic command callback used for handling multicommands' parent commands +-- For example, if there are "/gal save" and "/gal load" commands, this callback handles the "/gal" command +local function MultiCommandHandler(a_Split, a_Player, a_CmdString, a_CmdInfo, a_Level) + local Verb = a_Split[a_Level + 1]; + if (Verb == nil) then + -- No verb was specified. If there is a handler for the upper level command, call it: + if (a_CmdInfo.Handler ~= nil) then + return a_CmdInfo.Handler(a_Split, a_Player); + end + -- Let the player know they need to give a subcommand: + ListSubcommands(a_Player, a_CmdInfo.Subcommands, a_CmdString); + return true; + end + + -- A verb was specified, look it up in the subcommands table: + local Subcommand = a_CmdInfo.Subcommands[Verb]; + if (Subcommand == nil) then + if (a_Level > 1) then + -- This is a true subcommand, display the message and make MCS think the command was handled + -- Otherwise we get weird behavior: for "/cmd verb" we get "unknown command /cmd" although "/cmd" is valid + a_Player:SendMessage("The " .. a_CmdString .. " command doesn't support verb " .. Verb); + return true; + end + -- This is a top-level command, let MCS handle the unknown message + return false; + end + + -- Check the permission: + if not(a_Player:HasPermission(Subcommand.Permission or "")) then + a_Player:SendMessage("You don't have permission to execute this command"); + return true; + end + + -- Check if the handler is valid: + if (Subcommand.Handler == nil) then + if (Subcommand.Subcommands == nil) then + LOG("Cannot find handler for command " .. a_CmdString .. " " .. Verb); + return false; + end + ListSubcommands(a_Player, Subcommand.Subcommands, a_CmdString .. " " .. Verb); + return true; + end + + -- Execute: + return Subcommand.Handler(a_Split, a_Player); +end + + + + + +--- Registers all commands specified in the g_PluginInfo.Commands +function RegisterPluginInfoCommands() + -- A sub-function that registers all subcommands of a single command, using the command's Subcommands table + -- The a_Prefix param already contains the space after the previous command + -- a_Level is the depth of the subcommands being registered, with 1 being the top level command + local function RegisterSubcommands(a_Prefix, a_Subcommands, a_Level) + assert(a_Subcommands ~= nil); + + for cmd, info in pairs(a_Subcommands) do + local CmdName = a_Prefix .. cmd; + local Handler = info.Handler; + -- Provide a special handler for multicommands: + if (info.Subcommands ~= nil) then + Handler = function(a_Split, a_Player) + return MultiCommandHandler(a_Split, a_Player, CmdName, info, a_Level); + end + end + + if (Handler == nil) then + LOGWARNING(g_PluginInfo.Name .. ": Invalid handler for command " .. CmdName .. ", command will not be registered."); + else + local HelpString; + if (info.HelpString ~= nil) then + HelpString = " - " .. info.HelpString; + else + HelpString = ""; + end + cPluginManager.BindCommand(CmdName, info.Permission or "", Handler, HelpString); + -- Register all aliases for the command: + if (info.Alias ~= nil) then + if (type(info.Alias) == "string") then + info.Alias = {info.Alias}; + end + for idx, alias in ipairs(info.Alias) do + cPluginManager.BindCommand(a_Prefix .. alias, info.Permission or "", Handler, HelpString); + end + end + end + + -- Recursively register any subcommands: + if (info.Subcommands ~= nil) then + RegisterSubcommands(a_Prefix .. cmd .. " ", info.Subcommands, a_Level + 1); + end + end + end + + -- Loop through all commands in the plugin info, register each: + RegisterSubcommands("", g_PluginInfo.Commands, 1); +end + + + + + +--- Registers all console commands specified in the g_PluginInfo.ConsoleCommands +function RegisterPluginInfoConsoleCommands() + -- A sub-function that registers all subcommands of a single command, using the command's Subcommands table + -- The a_Prefix param already contains the space after the previous command + local function RegisterSubcommands(a_Prefix, a_Subcommands) + assert(a_Subcommands ~= nil); + + for cmd, info in pairs(a_Subcommands) do + local CmdName = a_Prefix .. cmd; + cPluginManager.BindConsoleCommand(cmd, info.Handler, info.HelpString or ""); + -- Recursively register any subcommands: + if (info.Subcommands ~= nil) then + RegisterSubcommands(a_Prefix .. cmd .. " ", info.Subcommands); + end + end + end + + -- Loop through all commands in the plugin info, register each: + RegisterSubcommands("", g_PluginInfo.ConsoleCommands); +end + + + + diff --git a/SetFlags.cmake b/SetFlags.cmake new file mode 100644 index 000000000..ded57e6d7 --- /dev/null +++ b/SetFlags.cmake @@ -0,0 +1,189 @@ + + +macro (add_flags_lnk FLAGS) + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} ${FLAGS}") + set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${FLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS_DEBUG} ${FLAGS}") + set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} ${FLAGS}") +endmacro() + +macro(add_flags_cxx FLAGS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}") +endmacro() + + +macro(set_flags) + # Add the preprocessor macros used for distinguishing between debug and release builds (CMake does this automatically for MSVC): + if (NOT MSVC) + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG") + endif() + + if(MSVC) + # Make build use multiple threads under MSVC: + add_flags_cxx("/MP") + + # Make release builds use link-time code generation: + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /GL") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") + set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG") + set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /LTCG") + elseif(APPLE) + #on os x clang adds pthread for us but we need to add it for gcc + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11") + else() + add_flags_cxx("-pthread") + endif() + + else() + # Let gcc / clang know that we're compiling a multi-threaded app: + add_flags_cxx("-pthread") + if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11") + endif() + + # We use a signed char (fixes #640 on RasPi) + add_flags_cxx("-fsigned-char") + endif() + + + # Allow for a forced 32-bit build under 64-bit OS: + if (FORCE_32) + add_flags_cxx("-m32") + add_flags_lnk("-m32") + endif() + + + # Have the compiler generate code specifically targeted at the current machine on Linux + if(LINUX AND NOT CROSSCOMPILE) + add_flags_cxx("-march=native") + endif() + + + # Use static CRT in MSVC builds: + if (MSVC) + string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") + string(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + string(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") + endif() +endmacro() + +macro(set_lib_flags) + # Set lower warnings-level for the libraries: + if (MSVC) + # Remove /W3 from command line -- cannot just cancel it later with /w like in unix, MSVC produces a D9025 warning (option1 overriden by option2) + string(REPLACE "/W3" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + string(REPLACE "/W3" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") + string(REPLACE "/W3" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + string(REPLACE "/W3" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") + else() + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -w") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -w") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -w") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -w") + endif() + + # On Unix we use two dynamic loading libraries dl and ltdl. + # Preference is for dl on unknown systems as it is specified in POSIX + # the dynamic loader is used by lua and sqllite. + if (UNIX) + if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + set(DYNAMIC_LOADER ltdl) + else() + set(DYNAMIC_LOADER dl) + endif() + endif() +endmacro() + +macro(enable_profile) + # Declare the flags used for profiling builds: + if (MSVC) + set (CXX_PROFILING "") + set (LNK_PROFILING "/PROFILE") + else() + set (CXX_PROFILING "-pg") + set (LNK_PROFILING "-pg") + endif() + + + # Declare the profiling configurations: + SET(CMAKE_CXX_FLAGS_DEBUGPROFILE + "${CMAKE_CXX_FLAGS_DEBUG} ${PCXX_ROFILING}" + CACHE STRING "Flags used by the C++ compiler during profile builds." + FORCE ) + SET(CMAKE_C_FLAGS_DEBUGPROFILE + "${CMAKE_C_FLAGS_DEBUG} ${CXX_PROFILING}" + CACHE STRING "Flags used by the C compiler during profile builds." + FORCE ) + SET(CMAKE_EXE_LINKER_FLAGS_DEBUGPROFILE + "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${LNK_PROFILING}" + CACHE STRING "Flags used for linking binaries during profile builds." + FORCE ) + SET(CMAKE_SHARED_LINKER_FLAGS_DEBUGPROFILE + "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} ${LNK_PROFILING}" + CACHE STRING "Flags used by the shared libraries linker during profile builds." + FORCE ) + MARK_AS_ADVANCED( + CMAKE_CXX_FLAGS_DEBUGPROFILE + CMAKE_C_FLAGS_DEBUGPROFILE + CMAKE_EXE_LINKER_FLAGS_DEBUGPROFILE + CMAKE_SHARED_LINKER_FLAGS_DEBUGPROFILE ) + + SET(CMAKE_CXX_FLAGS_RELEASEPROFILE + "${CMAKE_CXX_FLAGS_RELEASE} ${CXX_PROFILING}" + CACHE STRING "Flags used by the C++ compiler during profile builds." + FORCE ) + SET(CMAKE_C_FLAGS_RELEASEPROFILE + "${CMAKE_C_FLAGS_RELEASE} ${CXX_PROFILING}" + CACHE STRING "Flags used by the C compiler during profile builds." + FORCE ) + SET(CMAKE_EXE_LINKER_FLAGS_RELEASEPROFILE + "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${LNK_PROFILING}" + CACHE STRING "Flags used for linking binaries during profile builds." + FORCE ) + SET(CMAKE_SHARED_LINKER_FLAGS_RELEASEPROFILE + "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} ${LNK_PROFILING}" + CACHE STRING "Flags used by the shared libraries linker during profile builds." + FORCE ) + MARK_AS_ADVANCED( + CMAKE_CXX_FLAGS_RELEASEPROFILE + CMAKE_C_FLAGS_RELEASEPROFILE + CMAKE_EXE_LINKER_FLAGS_RELEASEPROFILE + CMAKE_SHARED_LINKER_FLAGS_RELEASEPROFILE ) + # The configuration types need to be set after their respective c/cxx/linker flags and before the project directive + set(CMAKE_CONFIGURATION_TYPES "Debug;Release;DebugProfile;ReleaseProfile" CACHE STRING "" FORCE) +endmacro() + +macro(set_exe_flags) + # Remove disabling the maximum warning level: + # clang does not like a command line that reads -Wall -Wextra -w -Wall -Wextra and does not output any warnings + # We do not do that for MSVC since MSVC produces an awful lot of warnings for its own STL headers; + # the important warnings are turned on using #pragma in Globals.h + if (NOT MSVC) + string(REPLACE "-w" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + string(REPLACE "-w" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") + string(REPLACE "-w" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") + add_flags_cxx("-Wall -Wextra") + endif() + +endmacro() diff --git a/Tools/MCADefrag/.gitignore b/Tools/MCADefrag/.gitignore new file mode 100644 index 000000000..44a3e1f48 --- /dev/null +++ b/Tools/MCADefrag/.gitignore @@ -0,0 +1 @@ +*.mca diff --git a/Tools/MCADefrag/CMakeLists.txt b/Tools/MCADefrag/CMakeLists.txt new file mode 100644 index 000000000..2a021049f --- /dev/null +++ b/Tools/MCADefrag/CMakeLists.txt @@ -0,0 +1,97 @@ + +cmake_minimum_required (VERSION 2.6) + +project (MCADefrag) + +# Without this, the MSVC variable isn't defined for MSVC builds ( http://www.cmake.org/pipermail/cmake/2011-November/047130.html ) +enable_language(CXX C) + +include(../../SetFlags.cmake) +set_flags() +set_lib_flags() +enable_profile() + + + + +# Set include paths to the used libraries: +include_directories("../../lib") +include_directories("../../src") + + +function(flatten_files arg1) + set(res "") + foreach(f ${${arg1}}) + get_filename_component(f ${f} ABSOLUTE) + list(APPEND res ${f}) + endforeach() + set(${arg1} "${res}" PARENT_SCOPE) +endfunction() + + +# Include the libraries: + +add_subdirectory(../../lib/zlib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/lib/zlib) + +set_exe_flags() + +# Include the shared files: +set(SHARED_SRC + ../../src/StringCompression.cpp + ../../src/StringUtils.cpp + ../../src/Log.cpp + ../../src/MCLogger.cpp +) +set(SHARED_HDR + ../../src/ByteBuffer.h + ../../src/StringUtils.h + ../../src/Log.h + ../../src/MCLogger.h +) +flatten_files(SHARED_SRC) +flatten_files(SHARED_HDR) +source_group("Shared" FILES ${SHARED_SRC} ${SHARED_HDR}) + +set(SHARED_OSS_SRC + ../../src/OSSupport/CriticalSection.cpp + ../../src/OSSupport/File.cpp + ../../src/OSSupport/IsThread.cpp + ../../src/OSSupport/Timer.cpp +) +set(SHARED_OSS_HDR + ../../src/OSSupport/CriticalSection.h + ../../src/OSSupport/File.h + ../../src/OSSupport/IsThread.h + ../../src/OSSupport/Timer.h +) + +flatten_files(SHARED_OSS_SRC) +flatten_files(SHARED_OSS_HDR) + +source_group("Shared\\OSSupport" FILES ${SHARED_OSS_SRC} ${SHARED_OSS_HDR}) + + + +# Include the main source files: +set(SOURCES + MCADefrag.cpp + Globals.cpp +) +set(HEADERS + MCADefrag.h + Globals.h +) + +source_group("" FILES ${SOURCES} ${HEADERS}) + +add_executable(MCADefrag + ${SOURCES} + ${HEADERS} + ${SHARED_SRC} + ${SHARED_HDR} + ${SHARED_OSS_SRC} + ${SHARED_OSS_HDR} +) + +target_link_libraries(MCADefrag zlib) + diff --git a/Tools/MCADefrag/Globals.cpp b/Tools/MCADefrag/Globals.cpp new file mode 100644 index 000000000..13c6ae709 --- /dev/null +++ b/Tools/MCADefrag/Globals.cpp @@ -0,0 +1,10 @@ + +// Globals.cpp + +// This file is used for precompiled header generation in MSVC environments + +#include "Globals.h" + + + + diff --git a/Tools/MCADefrag/Globals.h b/Tools/MCADefrag/Globals.h new file mode 100644 index 000000000..6f4bbdc76 --- /dev/null +++ b/Tools/MCADefrag/Globals.h @@ -0,0 +1,229 @@ + +// Globals.h + +// This file gets included from every module in the project, so that global symbols may be introduced easily +// Also used for precompiled header generation in MSVC environments + + + + + +// Compiler-dependent stuff: +#if defined(_MSC_VER) + // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether + #pragma warning(disable:4481) + + // Disable some warnings that we don't care about: + #pragma warning(disable:4100) + + #define OBSOLETE __declspec(deprecated) + + // No alignment needed in MSVC + #define ALIGN_8 + #define ALIGN_16 + +#elif defined(__GNUC__) + + // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)? + #define abstract + + // TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) + #define override + + #define OBSOLETE __attribute__((deprecated)) + + #define ALIGN_8 __attribute__((aligned(8))) + #define ALIGN_16 __attribute__((aligned(16))) + + // Some portability macros :) + #define stricmp strcasecmp + +#else + + #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler" + + /* + // Copy and uncomment this into another #elif section based on your compiler identification + + // Explicitly mark classes as abstract (no instances can be created) + #define abstract + + // Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class) + #define override + + // Mark functions as obsolete, so that their usage results in a compile-time warning + #define OBSOLETE + + // Mark types / variables for alignment. Do the platforms need it? + #define ALIGN_8 + #define ALIGN_16 + */ + +#endif + + + + + +// Integral types with predefined sizes: +typedef long long Int64; +typedef int Int32; +typedef short Int16; + +typedef unsigned long long UInt64; +typedef unsigned int UInt32; +typedef unsigned short UInt16; + +typedef unsigned char Byte; + + + + + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for any class that shouldn't allow copying itself +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName &); \ + void operator=(const TypeName &) + +// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc +#define UNUSED(X) (void)(X) + + + + +// OS-dependent stuff: +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #include <Windows.h> + #include <winsock2.h> + #include <ws2tcpip.h> + + // Windows SDK defines min and max macros, messing up with our std::min and std::max usage + #undef min + #undef max + + // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant + #ifdef GetFreeSpace + #undef GetFreeSpace + #endif // GetFreeSpace + + #define SocketError WSAGetLastError() +#else + #include <sys/types.h> + #include <sys/stat.h> // for mkdir + #include <sys/time.h> + #include <sys/socket.h> + #include <netinet/in.h> + #include <arpa/inet.h> + #include <netdb.h> + #include <time.h> + #include <dirent.h> + #include <errno.h> + #include <iostream> + #include <unistd.h> + + #include <cstdio> + #include <cstring> + #include <pthread.h> + #include <semaphore.h> + #include <errno.h> + #include <fcntl.h> + + typedef int SOCKET; + enum + { + INVALID_SOCKET = -1, + }; + #define closesocket close + #define SocketError errno +#if !defined(ANDROID_NDK) + #include <tr1/memory> +#endif +#endif + +#if !defined(ANDROID_NDK) + #define USE_SQUIRREL +#endif + +#if defined(ANDROID_NDK) + #define FILE_IO_PREFIX "/sdcard/mcserver/" +#else + #define FILE_IO_PREFIX "" +#endif + + + + + +// CRT stuff: +#include <assert.h> +#include <stdio.h> +#include <math.h> +#include <stdarg.h> +#include <time.h> + + + + + +// STL stuff: +#include <vector> +#include <list> +#include <deque> +#include <string> +#include <map> +#include <algorithm> +#include <memory> + + + + + +// Common headers (without macros): +#include "StringUtils.h" +#include "OSSupport/CriticalSection.h" +#include "OSSupport/IsThread.h" +#include "OSSupport/File.h" + + + + + +// Common definitions: + +/// Evaluates to the number of elements in an array (compile-time!) +#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X))) + +/// Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)" ) +#define KiB * 1024 +#define MiB * 1024 * 1024 + +/// Faster than (int)floorf((float)x / (float)div) +#define FAST_FLOOR_DIV( x, div ) ( (x) < 0 ? (((int)x / div) - 1) : ((int)x / div) ) + +// Own version of assert() that writes failed assertions to the log for review +#ifdef NDEBUG + #define ASSERT(x) ((void)0) +#else + #define ASSERT assert +#endif + +// Pretty much the same as ASSERT() but stays in Release builds +#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) ) + + + + + +/// A generic interface used mainly in ForEach() functions +template <typename Type> class cItemCallback +{ +public: + /// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating + virtual bool Item(Type * a_Type) = 0; +} ; + + + + diff --git a/Tools/MCADefrag/MCADefrag.cpp b/Tools/MCADefrag/MCADefrag.cpp new file mode 100644 index 000000000..a2de7f957 --- /dev/null +++ b/Tools/MCADefrag/MCADefrag.cpp @@ -0,0 +1,421 @@ + +// MCADefrag.cpp + +// Implements the main app entrypoint and the cMCADefrag class representing the entire app + +#include "Globals.h" +#include "MCADefrag.h" +#include "MCLogger.h" +#include "zlib/zlib.h" + + + + + +// An array of 4096 zero bytes, used for writing the padding +static const Byte g_Zeroes[4096] = {0}; + + + + + +int main(int argc, char ** argv) +{ + new cMCLogger(Printf("Defrag_%08x.log", time(NULL))); + cMCADefrag Defrag; + if (!Defrag.Init(argc, argv)) + { + return 1; + } + + Defrag.Run(); + + return 0; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMCADefrag: + +cMCADefrag::cMCADefrag(void) : + m_NumThreads(4), + m_ShouldRecompress(true) +{ +} + + + + + +bool cMCADefrag::Init(int argc, char ** argv) +{ + // Nothing needed yet + return true; +} + + + + + +void cMCADefrag::Run(void) +{ + // Fill the queue with MCA files + m_Queue = cFile::GetFolderContents("."); + + // Start the processing threads: + for (int i = 0; i < m_NumThreads; i++) + { + StartThread(); + } + + // Wait for all the threads to finish: + while (!m_Threads.empty()) + { + m_Threads.front()->Wait(); + delete m_Threads.front(); + m_Threads.pop_front(); + } +} + + + + +void cMCADefrag::StartThread(void) +{ + cThread * Thread = new cThread(*this); + m_Threads.push_back(Thread); + Thread->Start(); +} + + + + + +AString cMCADefrag::GetNextFileName(void) +{ + cCSLock Lock(m_CS); + if (m_Queue.empty()) + { + return AString(); + } + AString res = m_Queue.back(); + m_Queue.pop_back(); + return res; +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cMCADefrag::cThread: + +cMCADefrag::cThread::cThread(cMCADefrag & a_Parent) : + super("MCADefrag thread"), + m_Parent(a_Parent), + m_IsChunkUncompressed(false) +{ +} + + + + + +void cMCADefrag::cThread::Execute(void) +{ + for (;;) + { + AString FileName = m_Parent.GetNextFileName(); + if (FileName.empty()) + { + return; + } + ProcessFile(FileName); + } +} + + + + + +void cMCADefrag::cThread::ProcessFile(const AString & a_FileName) +{ + // Filter out non-MCA files: + if ((a_FileName.length() < 4) || (a_FileName.substr(a_FileName.length() - 4, 4) != ".mca")) + { + return; + } + LOGINFO("%s", a_FileName.c_str()); + + // Open input and output files: + AString OutFileName = a_FileName + ".new"; + cFile In, Out; + if (!In.Open(a_FileName, cFile::fmRead)) + { + LOGWARNING("Cannot open file %s for reading, skipping file.", a_FileName.c_str()); + return; + } + if (!Out.Open(OutFileName.c_str(), cFile::fmWrite)) + { + LOGWARNING("Cannot open file %s for writing, skipping file.", OutFileName.c_str()); + return; + } + + // Read the Locations and Timestamps from the input file: + Byte Locations[4096]; + UInt32 Timestamps[1024]; + if (In.Read(Locations, sizeof(Locations)) != sizeof(Locations)) + { + LOGWARNING("Cannot read Locations in file %s, skipping file.", a_FileName.c_str()); + return; + } + if (In.Read(Timestamps, sizeof(Timestamps)) != sizeof(Timestamps)) + { + LOGWARNING("Cannot read Timestamps in file %s, skipping file.", a_FileName.c_str()); + return; + } + + // Write dummy Locations to the Out file (will be overwritten once the correct ones are known) + if (Out.Write(Locations, sizeof(Locations)) != sizeof(Locations)) + { + LOGWARNING("Cannot write Locations to file %s, skipping file.", OutFileName.c_str()); + return; + } + m_CurrentSectorOut = 2; + + // Write a copy of the Timestamps into the Out file: + if (Out.Write(Timestamps, sizeof(Timestamps)) != sizeof(Timestamps)) + { + LOGWARNING("Cannot write Timestamps to file %s, skipping file.", OutFileName.c_str()); + return; + } + + // Process each chunk: + for (size_t i = 0; i < 1024; i++) + { + size_t idx = i * 4; + if ( + (Locations[idx] == 0) && + (Locations[idx + 1] == 0) && + (Locations[idx + 2] == 0) && + (Locations[idx + 3] == 0) + ) + { + // Chunk not present + continue; + } + m_IsChunkUncompressed = false; + if (!ReadChunk(In, Locations + idx)) + { + LOGWARNING("Cannot read chunk #%d from file %s. Skipping file.", i, a_FileName.c_str()); + return; + } + if (!WriteChunk(Out, Locations + idx)) + { + LOGWARNING("Cannot write chunk #%d to file %s. Skipping file.", i, OutFileName.c_str()); + return; + } + } + + // Write the new Locations into the MCA header: + Out.Seek(0); + if (Out.Write(Locations, sizeof(Locations)) != sizeof(Locations)) + { + LOGWARNING("Cannot write updated Locations to file %s, skipping file.", OutFileName.c_str()); + return; + } + + // Close the files, delete orig, rename new: + In.Close(); + Out.Close(); + cFile::Delete(a_FileName); + cFile::Rename(OutFileName, a_FileName); +} + + + + + +bool cMCADefrag::cThread::ReadChunk(cFile & a_File, const Byte * a_LocationRaw) +{ + int SectorNum = (a_LocationRaw[0] << 16) | (a_LocationRaw[1] << 8) | a_LocationRaw[2]; + int SizeInSectors = a_LocationRaw[3] * (4 KiB); + if (a_File.Seek(SectorNum * (4 KiB)) < 0) + { + LOGWARNING("Failed to seek to chunk data - file pos %llu (%d KiB, %.02f MiB)!", (Int64)SectorNum * (4 KiB), SectorNum * 4, ((double)SectorNum) / 256); + return false; + } + + // Read the exact size: + Byte Buf[4]; + if (a_File.Read(Buf, 4) != 4) + { + LOGWARNING("Failed to read chunk data length"); + return false; + } + m_CompressedChunkDataSize = (Buf[0] << 24) | (Buf[1] << 16) | (Buf[2] << 8) | Buf[3]; + if (m_CompressedChunkDataSize > SizeInSectors) + { + LOGWARNING("Invalid chunk data - SizeInSectors (%d) smaller that RealSize (%d)", SizeInSectors, m_CompressedChunkDataSize); + return false; + } + + // Read the data: + if (a_File.Read(m_CompressedChunkData, m_CompressedChunkDataSize) != m_CompressedChunkDataSize) + { + LOGWARNING("Failed to read chunk data!"); + return false; + } + + // Uncompress the data if recompression is active + if (m_Parent.m_ShouldRecompress) + { + m_IsChunkUncompressed = UncompressChunk(); + if (!m_IsChunkUncompressed) + { + LOGINFO("Chunk failed to uncompress, will be copied verbatim instead."); + } + } + + return true; +} + + + + + +bool cMCADefrag::cThread::WriteChunk(cFile & a_File, Byte * a_LocationRaw) +{ + // Recompress the data if recompression is active: + if (m_Parent.m_ShouldRecompress) + { + if (!CompressChunk()) + { + LOGINFO("Chunk failed to recompress, will be coped verbatim instead."); + } + } + + // Update the Location: + a_LocationRaw[0] = m_CurrentSectorOut >> 16; + a_LocationRaw[1] = (m_CurrentSectorOut >> 8) & 0xff; + a_LocationRaw[2] = m_CurrentSectorOut & 0xff; + a_LocationRaw[3] = (m_CompressedChunkDataSize + (4 KiB) + 3) / (4 KiB); // +3 because the m_CompressedChunkDataSize doesn't include the exact-length + m_CurrentSectorOut += a_LocationRaw[3]; + + // Write the data length: + Byte Buf[4]; + Buf[0] = m_CompressedChunkDataSize >> 24; + Buf[1] = (m_CompressedChunkDataSize >> 16) & 0xff; + Buf[2] = (m_CompressedChunkDataSize >> 8) & 0xff; + Buf[3] = m_CompressedChunkDataSize & 0xff; + if (a_File.Write(Buf, 4) != 4) + { + LOGWARNING("Failed to write chunk length!"); + return false; + } + + // Write the data: + if (a_File.Write(m_CompressedChunkData, m_CompressedChunkDataSize) != m_CompressedChunkDataSize) + { + LOGWARNING("Failed to write chunk data!"); + return false; + } + + // Pad onto the next sector: + int NumPadding = a_LocationRaw[3] * 4096 - (m_CompressedChunkDataSize + 4); + ASSERT(NumPadding >= 0); + if ((NumPadding > 0) && (a_File.Write(g_Zeroes, NumPadding) != NumPadding)) + { + LOGWARNING("Failed to write padding"); + return false; + } + + return true; +} + + + + + +bool cMCADefrag::cThread::UncompressChunk(void) +{ + switch (m_CompressedChunkData[0]) + { + case COMPRESSION_GZIP: return UncompressChunkGzip(); + case COMPRESSION_ZLIB: return UncompressChunkZlib(); + } + LOGINFO("Chunk is compressed with in an unknown algorithm"); + return false; +} + + + + + +bool cMCADefrag::cThread::UncompressChunkGzip(void) +{ + // TODO + // This format is not used in practice + return false; +} + + + + + +bool cMCADefrag::cThread::UncompressChunkZlib(void) +{ + // Uncompress the data: + z_stream strm; + strm.zalloc = (alloc_func)NULL; + strm.zfree = (free_func)NULL; + strm.opaque = NULL; + inflateInit(&strm); + strm.next_out = m_RawChunkData; + strm.avail_out = sizeof(m_RawChunkData); + strm.next_in = m_CompressedChunkData + 1; // The first byte is the compression method, skip it + strm.avail_in = m_CompressedChunkDataSize; + int res = inflate(&strm, Z_FINISH); + inflateEnd(&strm); + if (res != Z_STREAM_END) + { + LOGWARNING("Failed to uncompress chunk data: %s", strm.msg); + return false; + } + m_RawChunkDataSize = strm.total_out; + + return true; +} + + + + + +bool cMCADefrag::cThread::CompressChunk(void) +{ + // Check that the compressed data can fit: + uLongf CompressedSize = compressBound(m_RawChunkDataSize); + if (CompressedSize > sizeof(m_CompressedChunkData)) + { + LOGINFO("Too much data for the internal compression buffer!"); + return false; + } + + // Compress the data using the highest compression factor: + int errorcode = compress2(m_CompressedChunkData + 1, &CompressedSize, m_RawChunkData, m_RawChunkDataSize, Z_BEST_COMPRESSION); + if (errorcode != Z_OK) + { + LOGINFO("Recompression failed: %d", errorcode); + return false; + } + m_CompressedChunkData[0] = COMPRESSION_ZLIB; + m_CompressedChunkDataSize = CompressedSize + 1; + return true; +} + + + + diff --git a/Tools/MCADefrag/MCADefrag.h b/Tools/MCADefrag/MCADefrag.h new file mode 100644 index 000000000..d7fa1fc6e --- /dev/null +++ b/Tools/MCADefrag/MCADefrag.h @@ -0,0 +1,144 @@ + +// MCADefrag.h + +// Interfaces to the cMCADefrag class encapsulating the entire app + + + + + +#pragma once + + + + + + +class cMCADefrag +{ +public: + enum + { + MAX_COMPRESSED_CHUNK_DATA_SIZE = (1 MiB), + MAX_RAW_CHUNK_DATA_SIZE = (100 MiB), + } ; + + cMCADefrag(void); + + /** Reads the cmdline params and initializes the app. + Returns true if the app should continue, false if not. */ + bool Init(int argc, char ** argv); + + /** Runs the entire app. */ + void Run(void); + +protected: + /** A single thread processing MCA files from the queue */ + class cThread : + public cIsThread + { + typedef cIsThread super; + + public: + cThread(cMCADefrag & a_Parent); + + protected: + /** The compression methods, as specified by the MCA compression method byte. */ + enum + { + COMPRESSION_GZIP = 1, + COMPRESSION_ZLIB = 2, + } ; + + + cMCADefrag & m_Parent; + + /** The current compressed chunk data. Valid after a successful ReadChunk(). + This contains only the compression method byte and the compressed data, + but not the exact-length preceding the data in the MCA file. */ + unsigned char m_CompressedChunkData[MAX_COMPRESSED_CHUNK_DATA_SIZE]; + + /** Size of the actual current compressed chunk data, excluding the 4 exact-length bytes. + This is the amount of bytes in m_CompressedChunkData[] that are valid. */ + int m_CompressedChunkDataSize; + + /** The current raw chunk data. Valid after a successful ReadChunk(), if recompression is active. */ + unsigned char m_RawChunkData[MAX_RAW_CHUNK_DATA_SIZE]; + + /** Size of the actual current raw chunk data. */ + int m_RawChunkDataSize; + + /** Number of the sector where the next chunk will be written by WriteChunk(). */ + int m_CurrentSectorOut; + + /** Set to true when the chunk has been successfully uncompressed. Only used if recompression is active. + WriteChunk() tests this flag to decide whether to call Compress(). */ + bool m_IsChunkUncompressed; + + + /** Processes the specified file. */ + void ProcessFile(const AString & a_FileName); + + /** Reads the chunk data into m_CompressedChunkData. + Calls DecompressChunkData() if recompression is active. + a_LocationRaw is the pointer to the first byte of the Location data in the MCA header. + Returns true if successful. */ + bool ReadChunk(cFile & a_File, const Byte * a_LocationRaw); + + /** Writes the chunk data from m_CompressedData or m_RawChunkData (performing recompression) into file. + Calls CompressChunkData() for the actual compression, if recompression is active. + a_LocationRaw is the pointer to the first byte of the Location data to be put into the MCA header, + the chunk's location is stored in that memory area. Updates m_CurrentSectorOut. + Returns true if successful. */ + bool WriteChunk(cFile & a_File, Byte * a_LocationRaw); + + /** Uncompresses the chunk data from m_CompressedChunkData into m_RawChunkData. + Returns true if successful, false on failure. */ + bool UncompressChunk(void); + + /** Uncompresses the chunk data from m_CompressedChunkData into m_RawChunkData, using Gzip. + Returns true if successful, false on failure. */ + bool UncompressChunkGzip(void); + + /** Uncompresses the chunk data from m_CompressedChunkData into m_RawChunkData, using Zlib. + Returns true if successful, false on failure. */ + bool UncompressChunkZlib(void); + + /** Compresses the chunk data from m_RawChunkData into m_CompressedChunkData. + Returns true if successful, false on failure. */ + bool CompressChunk(void); + + // cIsThread overrides: + virtual void Execute(void) override; + } ; + + typedef std::list<cThread *> cThreads; + + + /** The mutex protecting m_Files agains multithreaded access. */ + cCriticalSection m_CS; + + /** The queue of MCA files to be processed by the threads. Protected by m_CS. */ + AStringVector m_Queue; + + /** List of threads that the server has running. */ + cThreads m_Threads; + + /** The number of threads that should be started. Configurable on the command line. */ + int m_NumThreads; + + /** If set to true, the chunk data is recompressed while saving each MCA file. */ + bool m_ShouldRecompress; + + + /** Starts a new processing thread and adds it to cThreads. */ + void StartThread(void); + + /** Retrieves one file from the queue (and removes it from the queue). + Returns an empty string when queue empty. */ + AString GetNextFileName(void); +} ; + + + + diff --git a/Tools/ProtoProxy/.gitignore b/Tools/ProtoProxy/.gitignore index 8def77d0b..158a30080 100644 --- a/Tools/ProtoProxy/.gitignore +++ b/Tools/ProtoProxy/.gitignore @@ -1,6 +1,7 @@ Debug Release Logs/ +lib/ *.log *.nbt *.sln diff --git a/Tools/ProtoProxy/CMakeLists.txt b/Tools/ProtoProxy/CMakeLists.txt index 9e233a688..01f1e88ad 100644 --- a/Tools/ProtoProxy/CMakeLists.txt +++ b/Tools/ProtoProxy/CMakeLists.txt @@ -3,62 +3,10 @@ cmake_minimum_required (VERSION 2.6) project (ProtoProxy) +include(../../SetFlags.cmake) - -macro(add_flags_cxx FLAGS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}") -endmacro() - - - - -# Add the preprocessor macros used for distinguishing between debug and release builds (CMake does this automatically for MSVC): -if (NOT MSVC) - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG") - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG") -endif() - - - -if(MSVC) - # Make build use multiple threads under MSVC: - add_flags_cxx("/MP") - - # Make release builds use link-time code generation: - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /GL") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") - set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG") - set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /LTCG") -elseif(APPLE) - #on os x clang adds pthread for us but we need to add it for gcc - if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - add_flags_cxx("-pthread") - endif() -else() - # Let gcc / clang know that we're compiling a multi-threaded app: - add_flags_cxx("-pthread") -endif() - - - - -# Use static CRT in MSVC builds: -if (MSVC) - string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") - string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") - string(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") - string(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}") -endif() - - +set_flags() +set_lib_flags() # Set include paths to the used libraries: @@ -77,20 +25,10 @@ function(flatten_files arg1) set(${arg1} "${res}" PARENT_SCOPE) endfunction() +include(../../lib/polarssl.cmake) +add_subdirectory(../../lib/zlib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/lib/zlib) -# Include the libraries: -file(GLOB POLARSSL_SRC "../../lib/polarssl/library/*.c") -file(GLOB POLARSSL_HDR "../../lib/polarssl/include/polarssl/*.h") -flatten_files(POLARSSL_SRC) -flatten_files(POLARSSL_HDR) -source_group("PolarSSL" FILES ${POLARSSL_SRC} ${POLARSSL_HDR}) - -file(GLOB ZLIB_SRC "../../lib/zlib/*.c") -file(GLOB ZLIB_HDR "../../lib/zlib/*.h") -flatten_files(ZLIB_SRC) -flatten_files(ZLIB_HDR) -source_group("ZLib" FILES ${ZLIB_SRC} ${ZLIB_HDR}) - +set_exe_flags() # Include the shared files: set(SHARED_SRC @@ -149,9 +87,7 @@ add_executable(ProtoProxy ${SHARED_HDR} ${SHARED_OSS_SRC} ${SHARED_OSS_HDR} - ${POLARSSL_SRC} - ${POLARSSL_HDR} - ${ZLIB_SRC} - ${ZLIB_HDR} ) +target_link_libraries(ProtoProxy zlib polarssl) + diff --git a/lib/inifile/iniFile.cpp b/lib/inifile/iniFile.cpp index afa1c110d..cf8b63987 100644 --- a/lib/inifile/iniFile.cpp +++ b/lib/inifile/iniFile.cpp @@ -83,6 +83,8 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect) } } + bool IsFirstLine = true; + while (getline(f, line)) { // To be compatible with Win32, check for existence of '\r'. @@ -90,6 +92,14 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect) // Note that the '\r' will be written to INI files from // Unix so that the created INI file can be read under Win32 // without change. + + // Removes UTF-8 Byte Order Markers (BOM) if, present. + if (IsFirstLine) + { + RemoveBom(line); + IsFirstLine = false; + } + size_t lineLength = line.length(); if (lineLength == 0) { @@ -162,11 +172,12 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect) { return false; } - + if (IsFromExampleRedirect) { WriteFile(FILE_IO_PREFIX + a_FileName); } + return true; } @@ -824,3 +835,28 @@ AString cIniFile::CheckCase(const AString & s) const + +void cIniFile::RemoveBom(AString & a_line) const +{ + // The BOM sequence for UTF-8 is 0xEF,0xBB,0xBF + static unsigned const char BOM[] = { 0xEF, 0xBB, 0xBF }; + + // The BOM sequence, if present, is always th e first three characters of the input. + const AString ref = a_line.substr(0, 3); + + // If any of the first three chars do not match, return and do nothing. + for (int i = 0; i < 3; ++i) + { + if (static_cast<unsigned char>(ref[i]) != BOM[i]) + { + return; + } + } + + // First three characters match; erase them. + a_line.erase(0, 3); +} + + + + diff --git a/lib/inifile/iniFile.h b/lib/inifile/iniFile.h index 40af618dc..0bf1d917e 100644 --- a/lib/inifile/iniFile.h +++ b/lib/inifile/iniFile.h @@ -51,6 +51,8 @@ private: /// If the object is case-insensitive, returns s as lowercase; otherwise returns s as-is AString CheckCase(const AString & s) const; + /// Removes the UTF-8 BOMs (Byte order makers), if present. + void RemoveBom(AString & a_line) const; public: enum errors { diff --git a/lib/polarssl b/lib/polarssl -Subproject 2cb1a0c4009ecf368ecc74eb428394e10f9e6d0 +Subproject 2ceda579893ceb23c5eb0d56df47dc235644e0f diff --git a/lib/polarssl.cmake b/lib/polarssl.cmake new file mode 100644 index 000000000..d57cc9220 --- /dev/null +++ b/lib/polarssl.cmake @@ -0,0 +1,5 @@ + +if(NOT TARGET polarssl) + message("including polarssl") + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl EXCLUDE_FROM_ALL ) +endif() diff --git a/lib/tolua++/CMakeLists.txt b/lib/tolua++/CMakeLists.txt index 239232c38..5ec8ee822 100644 --- a/lib/tolua++/CMakeLists.txt +++ b/lib/tolua++/CMakeLists.txt @@ -19,7 +19,7 @@ add_library(tolualib ${LIB_SOURCE}) #m is the standard math librarys if(UNIX) -target_link_libraries(tolua m) +target_link_libraries(tolua m ${DYNAMIC_LOADER}) endif() target_link_libraries(tolua lua tolualib) diff --git a/lib/zlib/CMakeLists.txt b/lib/zlib/CMakeLists.txt index b1b74031d..6c52578ee 100644 --- a/lib/zlib/CMakeLists.txt +++ b/lib/zlib/CMakeLists.txt @@ -8,12 +8,14 @@ file(GLOB SOURCE "*.c" ) -add_library(zlib ${SOURCE}) +if(NOT TARGET zlib) + add_library(zlib ${SOURCE}) -if (MSVC) - # Remove SCL warnings, we expect this library to have been tested safe - SET_TARGET_PROPERTIES( - zlib PROPERTIES COMPILE_FLAGS "-D_CRT_SECURE_NO_WARNINGS" - ) + if (MSVC) + # Remove SCL warnings, we expect this library to have been tested safe + SET_TARGET_PROPERTIES( + zlib PROPERTIES COMPILE_FLAGS "-D_CRT_SECURE_NO_WARNINGS" + ) + endif() endif() diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 335acff95..ef61f55f0 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -34,6 +34,7 @@ $cfile "../Entities/Entity.h" $cfile "../Entities/Floater.h" $cfile "../Entities/Pawn.h" $cfile "../Entities/Player.h" +$cfile "../Entities/Painting.h" $cfile "../Entities/Pickup.h" $cfile "../Entities/ProjectileEntity.h" $cfile "../Entities/TNTEntity.h" @@ -71,6 +72,7 @@ $cfile "../Generating/ChunkDesc.h" $cfile "../CraftingRecipes.h" $cfile "../UI/Window.h" $cfile "../Mobs/Monster.h" +$cfile "../CompositeChat.h" diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index c6be7be3c..ca7f6b255 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -173,6 +173,31 @@ void cLuaState::Detach(void) +void cLuaState::AddPackagePath(const AString & a_PathVariable, const AString & a_Path) +{ + // Get the current path: + lua_getfield(m_LuaState, LUA_GLOBALSINDEX, "package"); // Stk: <package> + lua_getfield(m_LuaState, -1, a_PathVariable.c_str()); // Stk: <package> <package.path> + size_t len = 0; + const char * PackagePath = lua_tolstring(m_LuaState, -1, &len); + + // Append the new path: + AString NewPackagePath(PackagePath, len); + NewPackagePath.append(LUA_PATHSEP); + NewPackagePath.append(a_Path); + + // Set the new path to the environment: + lua_pop(m_LuaState, 1); // Stk: <package> + lua_pushlstring(m_LuaState, NewPackagePath.c_str(), NewPackagePath.length()); // Stk: <package> <NewPackagePath> + lua_setfield(m_LuaState, -2, a_PathVariable.c_str()); // Stk: <package> + lua_pop(m_LuaState, 1); + lua_pop(m_LuaState, 1); // Stk: - +} + + + + + bool cLuaState::LoadFile(const AString & a_FileName) { ASSERT(IsValid()); @@ -1251,6 +1276,7 @@ void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header) case LUA_TLIGHTUSERDATA: Printf(Value, "%p", lua_touserdata(a_LuaState, i)); break; case LUA_TNUMBER: Printf(Value, "%f", (double)lua_tonumber(a_LuaState, i)); break; case LUA_TSTRING: Printf(Value, "%s", lua_tostring(a_LuaState, i)); break; + case LUA_TTABLE: Printf(Value, "%p", lua_topointer(a_LuaState, i)); break; default: break; } LOGD(" Idx %d: type %d (%s) %s", i, Type, lua_typename(a_LuaState, Type), Value.c_str()); diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index b9bf10142..dcb660c3f 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -154,6 +154,9 @@ public: /** Returns true if the m_LuaState is valid */ bool IsValid(void) const { return (m_LuaState != NULL); } + /** Adds the specified path to package.<a_PathVariable> */ + void AddPackagePath(const AString & a_PathVariable, const AString & a_Path); + /** Loads the specified file Returns false and logs a warning to the console if not successful (but the LuaState is kept open). m_SubsystemName is displayed in the warning log message. diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 2a7631120..c220e5e0a 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -22,6 +22,7 @@ #include "../BlockEntities/FurnaceEntity.h" #include "../BlockEntities/HopperEntity.h" #include "../BlockEntities/NoteEntity.h" +#include "../BlockEntities/MobHeadEntity.h" #include "md5/md5.h" #include "../LineBlockTracer.h" #include "../WorldStorage/SchematicFileSerializer.h" @@ -212,7 +213,7 @@ static int tolua_DoWith(lua_State* tolua_S) return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs); } - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, NULL); const char * ItemName = tolua_tocppstring(tolua_S, 2, ""); if ((ItemName == NULL) || (ItemName[0] == 0)) @@ -306,7 +307,7 @@ static int tolua_DoWithID(lua_State* tolua_S) return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs); } - Ty1 * self = (Ty1 *)tolua_tousertype(tolua_S, 1, 0); + Ty1 * self = (Ty1 *)tolua_tousertype(tolua_S, 1, NULL); int ItemID = (int)tolua_tonumber(tolua_S, 2, 0); if (!lua_isfunction(tolua_S, 3)) @@ -396,7 +397,7 @@ static int tolua_DoWithXYZ(lua_State* tolua_S) return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 4 or 5 arguments, got %i", NumArgs); } - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, NULL); if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3) || !lua_isnumber(tolua_S, 4)) { return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1, #2 and #3"); @@ -490,7 +491,7 @@ static int tolua_ForEachInChunk(lua_State* tolua_S) return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 3 or 4 arguments, got %i", NumArgs); } - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, NULL); if (!lua_isnumber(tolua_S, 2) || !lua_isnumber(tolua_S, 3)) { return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a number for parameters #1 and #2"); @@ -584,7 +585,7 @@ static int tolua_ForEach(lua_State * tolua_S) return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 1 or 2 arguments, got %i", NumArgs); } - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, 0); + Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, NULL); if (self == NULL) { return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); @@ -681,7 +682,7 @@ static int tolua_cWorld_GetBlockInfo(lua_State * tolua_S) else #endif { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, NULL); int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); @@ -736,7 +737,7 @@ static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S) else #endif { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, NULL); int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); @@ -788,7 +789,7 @@ static int tolua_cWorld_GetSignLines(lua_State * tolua_S) else #endif { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, NULL); int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); @@ -846,7 +847,7 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S) else #endif { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, NULL); int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); @@ -895,7 +896,7 @@ static int tolua_cWorld_TryGetHeight(lua_State * tolua_S) else #endif { - cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, 0); + cWorld * self = (cWorld *) tolua_tousertype (tolua_S, 1, NULL); int BlockX = (int) tolua_tonumber (tolua_S, 2, 0); int BlockZ = (int) tolua_tonumber (tolua_S, 3, 0); #ifndef TOLUA_RELEASE @@ -967,7 +968,7 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S) } // Retrieve the args: - cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, 0); + cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, NULL); if (self == NULL) { return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); @@ -1065,7 +1066,7 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) { - cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); + cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, NULL); const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins(); @@ -1289,7 +1290,7 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) return 0; } - cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); + cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, NULL); if (self == NULL) { LOGWARN("Error in function call 'ForEachCommand': Not called on an object instance"); @@ -1364,7 +1365,7 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) return 0; } - cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); + cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, NULL); if (self == NULL) { LOGWARN("Error in function call 'ForEachConsoleCommand': Not called on an object instance"); @@ -1686,7 +1687,7 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S) static int tolua_cPlayer_GetGroups(lua_State* tolua_S) { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL); const cPlayer::GroupList & AllGroups = self->GetGroups(); @@ -1711,7 +1712,7 @@ static int tolua_cPlayer_GetGroups(lua_State* tolua_S) static int tolua_cPlayer_GetResolvedPermissions(lua_State* tolua_S) { - cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); + cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL); cPlayer::StringList AllPermissions = self->GetResolvedPermissions(); @@ -1824,7 +1825,7 @@ static int tolua_SetObjectCallback(lua_State * tolua_S) static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) { - cPluginLua * self = (cPluginLua *)tolua_tousertype(tolua_S,1,0); + cPluginLua * self = (cPluginLua *)tolua_tousertype(tolua_S, 1, NULL); tolua_Error tolua_err; tolua_err.array = 0; @@ -1868,7 +1869,7 @@ static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) static int tolua_cPluginLua_AddTab(lua_State* tolua_S) { - cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0); + cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, NULL); LOGWARN("WARNING: Using deprecated function AddTab()! Use AddWebTab() instead. (plugin \"%s\" in folder \"%s\")", self->GetName().c_str(), self->GetDirectory().c_str() ); @@ -1888,7 +1889,7 @@ static int tolua_cPlugin_Call(lua_State * tolua_S) L.LogStackTrace(); // Retrieve the params: plugin and the function name to call - cPluginLua * TargetPlugin = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0); + cPluginLua * TargetPlugin = (cPluginLua *) tolua_tousertype(tolua_S, 1, NULL); AString FunctionName = tolua_tostring(tolua_S, 2, ""); // Call the function: @@ -1941,7 +1942,7 @@ static int tolua_push_StringStringMap(lua_State* tolua_S, std::map< std::string, static int tolua_get_HTTPRequest_Params(lua_State* tolua_S) { - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S, 1, NULL); return tolua_push_StringStringMap(tolua_S, self->Params); } @@ -1951,7 +1952,7 @@ static int tolua_get_HTTPRequest_Params(lua_State* tolua_S) static int tolua_get_HTTPRequest_PostParams(lua_State* tolua_S) { - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S, 1, NULL); return tolua_push_StringStringMap(tolua_S, self->PostParams); } @@ -1961,7 +1962,7 @@ static int tolua_get_HTTPRequest_PostParams(lua_State* tolua_S) static int tolua_get_HTTPRequest_FormData(lua_State* tolua_S) { - HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S,1,0); + HTTPRequest* self = (HTTPRequest*) tolua_tousertype(tolua_S, 1, NULL); std::map< std::string, HTTPFormData >& FormData = self->FormData; lua_newtable(tolua_S); @@ -1984,7 +1985,7 @@ 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,0); + cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S, 1, NULL); const cWebAdmin::PluginList & AllPlugins = self->GetPlugins(); @@ -2009,7 +2010,7 @@ static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S) { - cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S,1,0); + cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S, 1, NULL); const cWebPlugin::TabNameList & TabNames = self->GetTabNames(); @@ -2076,7 +2077,7 @@ static int Lua_ItemGrid_GetSlotCoords(lua_State * L) } { - const cItemGrid * self = (const cItemGrid *)tolua_tousertype(L, 1, 0); + const cItemGrid * self = (const cItemGrid *)tolua_tousertype(L, 1, NULL); int SlotNum = (int)tolua_tonumber(L, 2, 0); if (self == NULL) { @@ -2288,7 +2289,7 @@ static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S) { return 0; } - cHopperEntity * self = (cHopperEntity *)tolua_tousertype(tolua_S, 1, 0); + cHopperEntity * self = (cHopperEntity *)tolua_tousertype(tolua_S, 1, NULL); if (self == NULL) { tolua_error(tolua_S, "invalid 'self' in function 'cHopperEntity::GetOutputBlockPos()'", NULL); @@ -2314,6 +2315,76 @@ static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S) +static int tolua_cBlockArea_GetBlockTypeMeta(lua_State * tolua_S) +{ + // function cBlockArea::GetBlockTypeMeta() + // Exported manually because tolua generates extra input params for the outputs + + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cBlockArea") || + !L.CheckParamNumber (2, 4) + ) + { + return 0; + } + + cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL); + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetRelBlockTypeMeta'", NULL); + return 0; + } + int BlockX = (int)tolua_tonumber(tolua_S, 2, 0); + int BlockY = (int)tolua_tonumber(tolua_S, 3, 0); + int BlockZ = (int)tolua_tonumber(tolua_S, 4, 0); + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + self->GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta); + tolua_pushnumber(tolua_S, BlockType); + tolua_pushnumber(tolua_S, BlockMeta); + return 2; +} + + + + + +static int tolua_cBlockArea_GetRelBlockTypeMeta(lua_State * tolua_S) +{ + // function cBlockArea::GetRelBlockTypeMeta() + // Exported manually because tolua generates extra input params for the outputs + + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cBlockArea") || + !L.CheckParamNumber (2, 4) + ) + { + return 0; + } + + cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL); + if (self == NULL) + { + tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea:GetRelBlockTypeMeta'", NULL); + return 0; + } + int BlockX = (int)tolua_tonumber(tolua_S, 2, 0); + int BlockY = (int)tolua_tonumber(tolua_S, 3, 0); + int BlockZ = (int)tolua_tonumber(tolua_S, 4, 0); + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + self->GetRelBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta); + tolua_pushnumber(tolua_S, BlockType); + tolua_pushnumber(tolua_S, BlockMeta); + return 2; +} + + + + + static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S) { // function cBlockArea::LoadFromSchematicFile @@ -2327,7 +2398,7 @@ static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S) { return 0; } - cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, 0); + cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL); if (self == NULL) { tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::LoadFromSchematicFile'", NULL); @@ -2343,6 +2414,7 @@ static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S) + static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S) { // function cBlockArea::SaveToSchematicFile @@ -2356,7 +2428,7 @@ static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S) { return 0; } - cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, 0); + cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL); if (self == NULL) { tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::SaveToSchematicFile'", NULL); @@ -2370,6 +2442,8 @@ static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S) + + void ManualBindings::Bind(lua_State * tolua_S) { tolua_beginmodule(tolua_S, NULL); @@ -2386,8 +2460,10 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cBlockArea"); + tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cBlockArea_GetBlockTypeMeta); + tolua_function(tolua_S, "GetRelBlockTypeMeta", tolua_cBlockArea_GetRelBlockTypeMeta); tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile); - tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile); + tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cHopperEntity"); @@ -2416,6 +2492,7 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>); tolua_function(tolua_S, "DoWithNoteBlockAt", tolua_DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>); tolua_function(tolua_S, "DoWithCommandBlockAt", tolua_DoWithXYZ<cWorld, cCommandBlockEntity, &cWorld::DoWithCommandBlockAt>); + tolua_function(tolua_S, "DoWithMobHeadBlockAt", tolua_DoWithXYZ<cWorld, cMobHeadEntity, &cWorld::DoWithMobHeadBlockAt>); tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>); tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>); tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>); diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 773d522c4..45c8216be 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -82,6 +82,14 @@ bool cPluginLua::Initialize(void) lua_pushstring(m_LuaState, GetName().c_str()); lua_setglobal(m_LuaState, LUA_PLUGIN_NAME_VAR_NAME); + // Add the plugin's folder to the package.path and package.cpath variables (#693): + m_LuaState.AddPackagePath("path", FILE_IO_PREFIX + GetLocalFolder() + "/?.lua"); + #ifdef _WIN32 + m_LuaState.AddPackagePath("cpath", GetLocalFolder() + "\\?.dll"); + #else + m_LuaState.AddPackagePath("cpath", FILE_IO_PREFIX + GetLocalFolder() + "/?.so"); + #endif + tolua_pushusertype(m_LuaState, this, "cPluginLua"); lua_setglobal(m_LuaState, "g_Plugin"); } diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index c6c8c081e..c7df6357e 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -56,7 +56,7 @@ void cPluginManager::ReloadPlugins(void) void cPluginManager::FindPlugins(void) { - AString PluginsPath = FILE_IO_PREFIX + AString( "Plugins/" ); + AString PluginsPath = GetPluginsPath() + "/"; // First get a clean list of only the currently running plugins, we don't want to mess those up for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();) diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index c78bceda1..44bc5a8d7 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -266,6 +266,10 @@ public: // tolua_export Returns false if plugin not found, and the value that the callback has returned otherwise. */ bool DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback); + /** Returns the path where individual plugins' folders are expected. + The path doesn't end in a slash. */ + static AString GetPluginsPath(void) { return FILE_IO_PREFIX + AString("Plugins"); } // tolua_export + private: friend class cRoot; diff --git a/src/BlockArea.h b/src/BlockArea.h index 59bc0f241..b4a161f32 100644 --- a/src/BlockArea.h +++ b/src/BlockArea.h @@ -184,9 +184,15 @@ public: void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); void SetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + // tolua_end + + // These need manual exporting, tolua generates the binding as requiring 2 extra input params void GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; void GetRelBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const; + // tolua_begin + int GetSizeX(void) const { return m_SizeX; } int GetSizeY(void) const { return m_SizeY; } int GetSizeZ(void) const { return m_SizeZ; } diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp index 97b5c1a66..57ad83de9 100644 --- a/src/BlockEntities/BlockEntity.cpp +++ b/src/BlockEntities/BlockEntity.cpp @@ -15,6 +15,7 @@ #include "JukeboxEntity.h" #include "NoteEntity.h" #include "SignEntity.h" +#include "MobHeadEntity.h" @@ -29,6 +30,7 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE case E_BLOCK_DISPENSER: return new cDispenserEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_ENDER_CHEST: return new cEnderChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_HEAD: return new cMobHeadEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h index bf6289a2f..918781a00 100644 --- a/src/BlockEntities/BlockEntityWithItems.h +++ b/src/BlockEntities/BlockEntityWithItems.h @@ -11,6 +11,7 @@ #include "BlockEntity.h" #include "../ItemGrid.h" +#include "../UI/WindowOwner.h" @@ -22,6 +23,7 @@ class cBlockEntityWithItems : // tolua_end // tolua doesn't seem to support multiple inheritance? , public cItemGrid::cListener + , public cBlockEntityWindowOwner // tolua_begin { typedef cBlockEntity super; @@ -77,6 +79,11 @@ protected: ASSERT(a_Grid == &m_Contents); if (m_World != NULL) { + if (GetWindow() != NULL) + { + GetWindow()->BroadcastWholeWindow(); + } + m_World->MarkChunkDirty(GetChunkX(), GetChunkZ()); } } diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h index 4110de1f3..ce16f84d7 100644 --- a/src/BlockEntities/ChestEntity.h +++ b/src/BlockEntities/ChestEntity.h @@ -2,7 +2,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -23,8 +22,7 @@ class cNBTData; // tolua_begin class cChestEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp index 7c9a40ce6..81df0fc8c 100644 --- a/src/BlockEntities/DropSpenserEntity.cpp +++ b/src/BlockEntities/DropSpenserEntity.cpp @@ -99,13 +99,6 @@ void cDropSpenserEntity::DropSpense(cChunk & a_Chunk) } m_World->BroadcastSoundParticleEffect(2000, m_PosX, m_PosY, m_PosZ, SmokeDir); m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f); - - // Update the UI window, if open: - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->BroadcastWholeWindow(); - } } diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h index f2f1eba36..47d3bd492 100644 --- a/src/BlockEntities/DropSpenserEntity.h +++ b/src/BlockEntities/DropSpenserEntity.h @@ -11,7 +11,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -31,8 +30,7 @@ class cServer; // tolua_begin class cDropSpenserEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/EnderChestEntity.h b/src/BlockEntities/EnderChestEntity.h index 0ee3cab3b..45beee45f 100644 --- a/src/BlockEntities/EnderChestEntity.h +++ b/src/BlockEntities/EnderChestEntity.h @@ -2,7 +2,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -23,8 +22,7 @@ class cNBTData; // tolua_begin class cEnderChestEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h index b08187300..5e08ae37a 100644 --- a/src/BlockEntities/FurnaceEntity.h +++ b/src/BlockEntities/FurnaceEntity.h @@ -2,7 +2,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" #include "../FurnaceRecipe.h" @@ -23,8 +22,7 @@ class cServer; // tolua_begin class cFurnaceEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index 2255cad64..31b23ac99 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -7,10 +7,12 @@ #include "HopperEntity.h" #include "../Chunk.h" #include "../Entities/Player.h" +#include "../Entities/Pickup.h" #include "../Bindings/PluginManager.h" #include "ChestEntity.h" #include "DropSpenserEntity.h" #include "FurnaceEntity.h" +#include "../BoundingBox.h" @@ -190,8 +192,87 @@ bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) /// Moves pickups from above this hopper into it. Returns true if the contents have changed. bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) { - // TODO - return false; + UNUSED(a_CurrentTick); + + class cHopperPickupSearchCallback : + public cEntityCallback + { + public: + cHopperPickupSearchCallback(const Vector3i & a_Pos, cItemGrid & a_Contents) : + m_Pos(a_Pos), + m_bFoundPickupsAbove(false), + m_Contents(a_Contents) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + ASSERT(a_Entity != NULL); + + if (!a_Entity->IsPickup() || a_Entity->IsDestroyed()) + { + return false; + } + + Vector3f EntityPos = a_Entity->GetPosition(); + Vector3f BlockPos(m_Pos.x + 0.5f, (float)m_Pos.y + 1, m_Pos.z + 0.5f); // One block above hopper, and search from center outwards + float Distance = (EntityPos - BlockPos).Length(); + + if (Distance < 0.5) + { + if (TrySuckPickupIn((cPickup *)a_Entity)) + { + return false; + } + } + + return false; + } + + bool TrySuckPickupIn(cPickup * a_Pickup) + { + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + if (m_Contents.IsSlotEmpty(i)) + { + m_bFoundPickupsAbove = true; + m_Contents.SetSlot(i, a_Pickup->GetItem()); + a_Pickup->Destroy(); // Kill pickup + + return true; + } + else if (m_Contents.GetSlot(i).IsEqual(a_Pickup->GetItem()) && !m_Contents.GetSlot(i).IsFullStack()) + { + m_bFoundPickupsAbove = true; + + int PreviousCount = m_Contents.GetSlot(i).m_ItemCount; + a_Pickup->GetItem().m_ItemCount -= m_Contents.ChangeSlotCount(i, a_Pickup->GetItem().m_ItemCount) - PreviousCount; // Set count to however many items were added + + if (a_Pickup->GetItem().IsEmpty()) + { + a_Pickup->Destroy(); // Kill pickup if all items were added + } + return true; + } + } + return false; + } + + bool FoundPickupsAbove(void) const + { + return m_bFoundPickupsAbove; + } + + protected: + Vector3i m_Pos; + bool m_bFoundPickupsAbove; + cItemGrid & m_Contents; + }; + + cHopperPickupSearchCallback HopperPickupSearchCallback(Vector3i(GetPosX(), GetPosY(), GetPosZ()), m_Contents); + a_Chunk.ForEachEntity(HopperPickupSearchCallback); + + return HopperPickupSearchCallback.FoundPickupsAbove(); } diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h index 2c8b301fe..6ef98f43a 100644 --- a/src/BlockEntities/HopperEntity.h +++ b/src/BlockEntities/HopperEntity.h @@ -10,7 +10,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -18,8 +17,7 @@ // tolua_begin class cHopperEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/JukeboxEntity.h b/src/BlockEntities/JukeboxEntity.h index 996de965b..734d7bb66 100644 --- a/src/BlockEntities/JukeboxEntity.h +++ b/src/BlockEntities/JukeboxEntity.h @@ -43,6 +43,8 @@ public: void EjectRecord(void); // tolua_end + + static const char * GetClassStatic(void) { return "cJukeboxEntity"; } virtual void UsedBy(cPlayer * a_Player) override; virtual void SendTo(cClientHandle &) override { }; diff --git a/src/BlockEntities/MobHeadEntity.cpp b/src/BlockEntities/MobHeadEntity.cpp new file mode 100644 index 000000000..c0a1781f6 --- /dev/null +++ b/src/BlockEntities/MobHeadEntity.cpp @@ -0,0 +1,108 @@ + +// MobHeadEntity.cpp + +// Implements the cMobHeadEntity class representing a single skull/head in the world + +#include "Globals.h" +#include "json/json.h" +#include "MobHeadEntity.h" +#include "../Entities/Player.h" + + + + + +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_Owner("") +{ +} + + + + + +void cMobHeadEntity::UsedBy(cPlayer * a_Player) +{ + UNUSED(a_Player); +} + + + + + +void cMobHeadEntity::SetType(const eMobHeadType & a_Type) +{ + if ((!m_Owner.empty()) && (a_Type != SKULL_TYPE_PLAYER)) + { + m_Owner = ""; + } + m_Type = a_Type; +} + + + + + +void cMobHeadEntity::SetRotation(eMobHeadRotation a_Rotation) +{ + m_Rotation = a_Rotation; +} + + + + + +void cMobHeadEntity::SetOwner(const AString & a_Owner) +{ + if ((a_Owner.length() > 16) || (m_Type != SKULL_TYPE_PLAYER)) + { + return; + } + m_Owner = a_Owner; +} + + + + + +void cMobHeadEntity::SendTo(cClientHandle & a_Client) +{ + a_Client.SendUpdateBlockEntity(*this); +} + + + + + +bool cMobHeadEntity::LoadFromJson(const Json::Value & a_Value) +{ + m_PosX = a_Value.get("x", 0).asInt(); + m_PosY = a_Value.get("y", 0).asInt(); + m_PosZ = a_Value.get("z", 0).asInt(); + + m_Type = static_cast<eMobHeadType>(a_Value.get("Type", 0).asInt()); + m_Rotation = static_cast<eMobHeadRotation>(a_Value.get("Rotation", 0).asInt()); + m_Owner = a_Value.get("Owner", "").asString(); + + return true; +} + + + + + +void cMobHeadEntity::SaveToJson(Json::Value & a_Value) +{ + a_Value["x"] = m_PosX; + a_Value["y"] = m_PosY; + a_Value["z"] = m_PosZ; + + a_Value["Type"] = m_Type; + a_Value["Rotation"] = m_Rotation; + a_Value["Owner"] = m_Owner; +} + + + + diff --git a/src/BlockEntities/MobHeadEntity.h b/src/BlockEntities/MobHeadEntity.h new file mode 100644 index 000000000..367eb15e7 --- /dev/null +++ b/src/BlockEntities/MobHeadEntity.h @@ -0,0 +1,79 @@ +// MobHeadEntity.h + +// Declares the cMobHeadEntity class representing a single skull/head in the world + + + + + +#pragma once + +#include "BlockEntity.h" + + + + + +namespace Json +{ + class Value; +} + + + + + +// tolua_begin + +class cMobHeadEntity : + public cBlockEntity +{ + typedef cBlockEntity super; + +public: + + // tolua_end + + /** Creates a new mob head entity at the specified block coords. a_World may be NULL */ + cMobHeadEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + + bool LoadFromJson( const Json::Value& a_Value ); + virtual void SaveToJson(Json::Value& a_Value ) override; + + // tolua_begin + + /** Set the Type */ + void SetType(const eMobHeadType & a_SkullType); + + /** Set the Rotation */ + void SetRotation(eMobHeadRotation a_Rotation); + + /** Set the Player Name for Mobheads with Player type */ + void SetOwner(const AString & a_Owner); + + /** Get the Type */ + eMobHeadType GetType(void) const { return m_Type; } + + /** Get the Rotation */ + eMobHeadRotation GetRotation(void) const { return m_Rotation; } + + /** Get the setted Player Name */ + AString GetOwner(void) const { return m_Owner; } + + // tolua_end + + virtual void UsedBy(cPlayer * a_Player) override; + virtual void SendTo(cClientHandle & a_Client) override; + + static const char * GetClassStatic(void) { return "cMobHeadEntity"; } + +private: + + eMobHeadType m_Type; + eMobHeadRotation m_Rotation; + AString m_Owner; +} ; // tolua_export + + + + diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h index cf78aeac6..b698899c0 100644 --- a/src/BlockEntities/NoteEntity.h +++ b/src/BlockEntities/NoteEntity.h @@ -54,6 +54,8 @@ public: virtual void UsedBy(cPlayer * a_Player) override; virtual void SendTo(cClientHandle &) override { }; + static const char * GetClassStatic(void) { return "cNoteEntity"; } + private: char m_Pitch; } ; // tolua_export diff --git a/src/BlockEntities/SignEntity.h b/src/BlockEntities/SignEntity.h index d998ff1e8..80c7bbfdf 100644 --- a/src/BlockEntities/SignEntity.h +++ b/src/BlockEntities/SignEntity.h @@ -1,4 +1,3 @@ - // SignEntity.h // Declares the cSignEntity class representing a single sign in the world @@ -56,6 +55,8 @@ public: virtual void UsedBy(cPlayer * a_Player) override; virtual void SendTo(cClientHandle & a_Client) override; + + static const char * GetClassStatic(void) { return "cSignEntity"; } private: diff --git a/src/BlockID.h b/src/BlockID.h index d5b3da672..740c5fc90 100644 --- a/src/BlockID.h +++ b/src/BlockID.h @@ -266,7 +266,7 @@ enum ENUM_ITEM_ID E_ITEM_FLINT = 318, E_ITEM_RAW_PORKCHOP = 319, E_ITEM_COOKED_PORKCHOP = 320, - E_ITEM_PAINTINGS = 321, + E_ITEM_PAINTING = 321, E_ITEM_GOLDEN_APPLE = 322, E_ITEM_SIGN = 323, E_ITEM_WOODEN_DOOR = 324, diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index 6c9bbeb02..09a1244ea 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -34,6 +34,7 @@ #include "BlockGlass.h" #include "BlockGlowstone.h" #include "BlockGravel.h" +#include "BlockMobHead.h" #include "BlockHopper.h" #include "BlockIce.h" #include "BlockLadder.h" @@ -146,6 +147,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_GRASS: return new cBlockDirtHandler (a_BlockType); case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType); case E_BLOCK_HAY_BALE: return new cBlockSidewaysHandler (a_BlockType); + case E_BLOCK_HEAD: return new cBlockMobHeadHandler (a_BlockType); case E_BLOCK_HOPPER: return new cBlockHopperHandler (a_BlockType); case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType); case E_BLOCK_INACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType); diff --git a/src/Blocks/BlockMobHead.h b/src/Blocks/BlockMobHead.h new file mode 100644 index 000000000..6a00c3acd --- /dev/null +++ b/src/Blocks/BlockMobHead.h @@ -0,0 +1,69 @@ + +#pragma once + +#include "BlockEntity.h" +#include "../BlockEntities/MobHeadEntity.h" + + + + + +class cBlockMobHeadHandler : + public cBlockEntityHandler +{ +public: + cBlockMobHeadHandler(BLOCKTYPE a_BlockType) + : cBlockEntityHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_HEAD, 1, 0)); + } + + virtual void OnPlacedByPlayer( + cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta + ) override + { + class cCallback : public cMobHeadBlockCallback + { + cPlayer * m_Player; + NIBBLETYPE m_OldBlockMeta; + NIBBLETYPE m_NewBlockMeta; + + virtual bool Item (cMobHeadEntity * a_MobHeadEntity) + { + int Rotation = 0; + if (m_NewBlockMeta == 1) + { + Rotation = (int) floor(m_Player->GetYaw() * 16.0F / 360.0F + 0.5) & 0xF; + } + + a_MobHeadEntity->SetType(static_cast<eMobHeadType>(m_OldBlockMeta)); + a_MobHeadEntity->SetRotation(static_cast<eMobHeadRotation>(Rotation)); + return false; + } + + public: + cCallback (cPlayer * a_Player, NIBBLETYPE a_OldBlockMeta, NIBBLETYPE a_NewBlockMeta) : + m_Player(a_Player), + m_OldBlockMeta(a_OldBlockMeta), + m_NewBlockMeta(a_NewBlockMeta) + {} + }; + cCallback Callback(a_Player, a_BlockMeta, static_cast<NIBBLETYPE>(a_BlockFace)); + + a_BlockMeta = a_BlockFace; + cWorld * World = (cWorld *) &a_WorldInterface; + World->DoWithMobHeadBlockAt(a_BlockX, a_BlockY, a_BlockZ, Callback); + a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta); + } +} ; + + + + diff --git a/src/BoundingBox.cpp b/src/BoundingBox.cpp index 47b135688..aab51c539 100644 --- a/src/BoundingBox.cpp +++ b/src/BoundingBox.cpp @@ -12,25 +12,25 @@ #if SELF_TEST -/// A simple self-test that is executed on program start, used to verify bbox functionality -class SelfTest +/** A simple self-test that is executed on program start, used to verify bbox functionality */ +static class SelfTest_BoundingBox { public: - SelfTest(void) + SelfTest_BoundingBox(void) { Vector3d Min(1, 1, 1); Vector3d Max(2, 2, 2); Vector3d LineDefs[] = { - Vector3d(1.5, 4, 1.5), Vector3d(1.5, 3, 1.5), // Should intersect at 2, face 1 (YP) + Vector3d(1.5, 4, 1.5), Vector3d(1.5, 3, 1.5), // Should intersect at 2, face 1 (YP) Vector3d(1.5, 0, 1.5), Vector3d(1.5, 4, 1.5), // Should intersect at 0.25, face 0 (YM) Vector3d(0, 0, 0), Vector3d(2, 2, 2), // Should intersect at 0.5, face 0, 3 or 5 (anyM) Vector3d(0.999, 0, 1.5), Vector3d(0.999, 4, 1.5), // Should not intersect Vector3d(1.999, 0, 1.5), Vector3d(1.999, 4, 1.5), // Should intersect at 0.25, face 0 (YM) Vector3d(2.001, 0, 1.5), Vector3d(2.001, 4, 1.5), // Should not intersect } ; - bool Results[] = {true,true,true,false,true,false}; - double LineCoeffs[] = {2,0.25,0.5,0,0.25,0}; + bool Results[] = {true, true, true, false, true, false}; + double LineCoeffs[] = {2, 0.25, 0.5, 0, 0.25, 0}; for (size_t i = 0; i < ARRAYCOUNT(LineDefs) / 2; i++) { @@ -41,7 +41,7 @@ public: bool res = cBoundingBox::CalcLineIntersection(Min, Max, Line1, Line2, LineCoeff, Face); if (res != Results[i]) { - fprintf(stderr,"LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n", + fprintf(stderr, "LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n", Line1.x, Line1.y, Line1.z, Line2.x, Line2.y, Line2.z, res ? 1 : 0, LineCoeff, Face @@ -52,7 +52,7 @@ public: { if (LineCoeff != LineCoeffs[i]) { - fprintf(stderr,"LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n", + fprintf(stderr, "LineIntersection({%.02f, %.02f, %.02f}, {%.02f, %.02f, %.02f}) -> %d, %.05f, %d\n", Line1.x, Line1.y, Line1.z, Line2.x, Line2.y, Line2.z, res ? 1 : 0, LineCoeff, Face @@ -61,9 +61,9 @@ public: } } } // for i - LineDefs[] - fprintf(stderr,"BoundingBox selftest complete."); + fprintf(stderr, "BoundingBox selftest complete.\n"); } -} Test; +} gTest; #endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6ba952f92..387556775 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,6 +39,7 @@ if (NOT MSVC) BlockEntities/JukeboxEntity.h BlockEntities/NoteEntity.h BlockEntities/SignEntity.h + BlockEntities/MobHeadEntity.h BlockID.h BoundingBox.h ChatColor.h @@ -52,6 +53,7 @@ if (NOT MSVC) Entities/Entity.h Entities/Floater.h Entities/Pawn.h + Entities/Painting.h Entities/Pickup.h Entities/Player.h Entities/ProjectileEntity.h diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 3028d24d0..4f301c209 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -19,6 +19,7 @@ #include "BlockEntities/JukeboxEntity.h" #include "BlockEntities/NoteEntity.h" #include "BlockEntities/SignEntity.h" +#include "BlockEntities/MobHeadEntity.h" #include "Entities/Pickup.h" #include "Item.h" #include "Noise.h" @@ -1314,6 +1315,7 @@ void cChunk::CreateBlockEntities(void) case E_BLOCK_HOPPER: case E_BLOCK_SIGN_POST: case E_BLOCK_WALLSIGN: + case E_BLOCK_HEAD: case E_BLOCK_NOTE_BLOCK: case E_BLOCK_JUKEBOX: { @@ -1442,6 +1444,7 @@ void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, case E_BLOCK_HOPPER: case E_BLOCK_SIGN_POST: case E_BLOCK_WALLSIGN: + case E_BLOCK_HEAD: case E_BLOCK_NOTE_BLOCK: case E_BLOCK_JUKEBOX: { @@ -1652,6 +1655,38 @@ void cChunk::UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z) +void cChunk::SetBiomeAt(int a_RelX, int a_RelZ, EMCSBiome a_Biome) +{ + cChunkDef::SetBiome(m_BiomeMap, a_RelX, a_RelZ, a_Biome); + MarkDirty(); +} + + + + + +void cChunk::SetAreaBiome(int a_MinRelX, int a_MaxRelX, int a_MinRelZ, int a_MaxRelZ, EMCSBiome a_Biome) +{ + for (int z = a_MinRelZ; z <= a_MaxRelZ; z++) + { + for (int x = a_MinRelX; x <= a_MaxRelX; x++) + { + cChunkDef::SetBiome(m_BiomeMap, x, z, a_Biome); + } + } + MarkDirty(); + + // Re-send the chunk to all clients: + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr) + { + m_World->ForceSendChunkTo(m_PosX, m_PosZ, (*itr)); + } // for itr - m_LoadedByClient[] +} + + + + + void cChunk::CollectPickupsByPlayer(cPlayer * a_Player) { double PosX = a_Player->GetPosX(); @@ -2309,6 +2344,38 @@ bool cChunk::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCom +bool cChunk::DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if ((*itr)->GetBlockType() != E_BLOCK_HEAD) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here, + if (a_Callback.Item((cMobHeadEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + bool cChunk::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) { // The blockentity list is locked by the parent chunkmap's CS diff --git a/src/Chunk.h b/src/Chunk.h index 93eba217e..1b7a6fa07 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -31,6 +31,7 @@ class cChestEntity; class cDispenserEntity; class cFurnaceEntity; class cNoteEntity; +class cMobHeadEntity; class cBlockArea; class cPawn; class cPickup; @@ -47,6 +48,7 @@ typedef cItemCallback<cDispenserEntity> cDispenserCallback; typedef cItemCallback<cFurnaceEntity> cFurnaceCallback; typedef cItemCallback<cNoteEntity> cNoteBlockCallback; typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback; +typedef cItemCallback<cMobHeadEntity> cMobHeadBlockCallback; @@ -175,6 +177,14 @@ public: EMCSBiome GetBiomeAt(int a_RelX, int a_RelZ) const {return cChunkDef::GetBiome(m_BiomeMap, a_RelX, a_RelZ); } + /** Sets the biome at the specified relative coords. + Doesn't resend the chunk to clients. */ + void SetBiomeAt(int a_RelX, int a_RelZ, EMCSBiome a_Biome); + + /** Sets the biome in the specified relative coords area. All the coords are inclusive. + Sends the chunk to all relevant clients. */ + void SetAreaBiome(int a_MinRelX, int a_MaxRelX, int a_MinRelZ, int a_MaxRelZ, EMCSBiome a_Biome); + void CollectPickupsByPlayer(cPlayer * a_Player); /** Sets the sign text. Returns true if successful. Also sends update packets to all clients in the chunk */ @@ -241,7 +251,10 @@ public: bool DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBlockCallback & a_Callback); /** Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found */ - bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); + bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); + + /** Calls the callback for the mob head block at the specified coords; returns false if there's no mob header block at those coords or callback returns true, returns true if found */ + bool DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback); /** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */ bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible @@ -343,12 +356,17 @@ public: NIBBLETYPE GetTimeAlteredLight(NIBBLETYPE a_Skylight) const; - // Simulator data: + // Per-chunk simulator data: cFireSimulatorChunkData & GetFireSimulatorData (void) { return m_FireSimulatorData; } cFluidSimulatorData * GetWaterSimulatorData(void) { return m_WaterSimulatorData; } cFluidSimulatorData * GetLavaSimulatorData (void) { return m_LavaSimulatorData; } cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; } - cRedstoneSimulatorChunkData & GetRedstoneSimulatorData(void) { return m_RedstoneSimulatorData; } + + cRedstoneSimulatorChunkData * GetRedstoneSimulatorData(void) { return &m_RedstoneSimulatorData; } + cIncrementalRedstoneSimulator::PoweredBlocksList * GetRedstoneSimulatorPoweredBlocksList(void) { return &m_RedstoneSimulatorPoweredBlocksList; } + cIncrementalRedstoneSimulator::LinkedBlocksList * GetRedstoneSimulatorLinkedBlocksList(void) { return &m_RedstoneSimulatorLinkedBlocksList; }; + cIncrementalRedstoneSimulator::SimulatedPlayerToggleableList * GetRedstoneSimulatorSimulatedPlayerToggleableList(void) { return &m_RedstoneSimulatorSimulatedPlayerToggleableList; }; + cIncrementalRedstoneSimulator::RepeatersDelayList * GetRedstoneSimulatorRepeatersDelayList(void) { return &m_RedstoneSimulatorRepeatersDelayList; }; cBlockEntity * GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ); cBlockEntity * GetBlockEntity(const Vector3i & a_BlockPos) { return GetBlockEntity(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); } @@ -419,7 +437,12 @@ private: cFluidSimulatorData * m_WaterSimulatorData; cFluidSimulatorData * m_LavaSimulatorData; cSandSimulatorChunkData m_SandSimulatorData; + cRedstoneSimulatorChunkData m_RedstoneSimulatorData; + cIncrementalRedstoneSimulator::PoweredBlocksList m_RedstoneSimulatorPoweredBlocksList; + cIncrementalRedstoneSimulator::LinkedBlocksList m_RedstoneSimulatorLinkedBlocksList; + cIncrementalRedstoneSimulator::SimulatedPlayerToggleableList m_RedstoneSimulatorSimulatedPlayerToggleableList; + cIncrementalRedstoneSimulator::RepeatersDelayList m_RedstoneSimulatorRepeatersDelayList; // pick up a random block of this chunk diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 0c5a8d9b9..fbb8706e0 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -1390,10 +1390,10 @@ void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks) EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ) { int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = a_BlockZ; - cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ ); + cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ ); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); if ((Chunk != NULL) && Chunk->IsValid()) { return Chunk->GetBiomeAt(X, Z); @@ -1408,6 +1408,63 @@ EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ) +bool cChunkMap::SetBiomeAt(int a_BlockX, int a_BlockZ, EMCSBiome a_Biome) +{ + int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = a_BlockZ; + cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); + + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->SetBiomeAt(X, Z, a_Biome); + return true; + } + return false; +} + + + + + +bool cChunkMap::SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMCSBiome a_Biome) +{ + // Translate coords to relative: + int Y = 0; + int MinChunkX, MinChunkZ, MinX = a_MinX, MinZ = a_MinZ; + int MaxChunkX, MaxChunkZ, MaxX = a_MaxX, MaxZ = a_MaxZ; + cChunkDef::AbsoluteToRelative(MinX, Y, MinZ, MinChunkX, MinChunkZ); + cChunkDef::AbsoluteToRelative(MaxX, Y, MaxZ, MaxChunkX, MaxChunkZ); + + // Go through all chunks, set: + bool res = true; + cCSLock Lock(m_CSLayers); + for (int x = MinChunkX; x <= MaxChunkX; x++) + { + int MinRelX = (x == MinChunkX) ? MinX : 0; + int MaxRelX = (x == MaxChunkX) ? MaxX : cChunkDef::Width - 1; + for (int z = MinChunkZ; z <= MaxChunkZ; z++) + { + int MinRelZ = (z == MinChunkZ) ? MinZ : 0; + int MaxRelZ = (z == MaxChunkZ) ? MaxZ : cChunkDef::Width - 1; + cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z); + if ((Chunk != NULL) && Chunk->IsValid()) + { + Chunk->SetAreaBiome(MinRelX, MaxRelX, MinRelZ, MaxRelZ, a_Biome); + } + else + { + res = false; + } + } // for z + } // for x + return res; +} + + + + + bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure) { bool res = true; @@ -2121,6 +2178,24 @@ bool cChunkMap::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, c +bool cChunkMap::DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithMobHeadBlockAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) { int ChunkX, ChunkZ; diff --git a/src/ChunkMap.h b/src/ChunkMap.h index d713d0cf5..9df68c403 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -25,6 +25,7 @@ class cDropSpenserEntity; class cFurnaceEntity; class cNoteEntity; class cCommandBlockEntity; +class cMobHeadEntity; class cPawn; class cPickup; class cChunkDataSerializer; @@ -43,6 +44,7 @@ typedef cItemCallback<cDropSpenserEntity> cDropSpenserCallback; typedef cItemCallback<cFurnaceEntity> cFurnaceCallback; typedef cItemCallback<cNoteEntity> cNoteBlockCallback; typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback; +typedef cItemCallback<cMobHeadEntity> cMobHeadBlockCallback; typedef cItemCallback<cChunk> cChunkCallback; @@ -159,8 +161,17 @@ public: /** Special function used for growing trees, replaces only blocks that tree may overwrite */ void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks); + /** Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, otherwise uses the world generator to provide the biome value */ EMCSBiome GetBiomeAt (int a_BlockX, int a_BlockZ); + /** Sets the biome at the specified coords. Returns true if successful, false if not (chunk not loaded). + Doesn't resend the chunk to clients. */ + bool SetBiomeAt(int a_BlockX, int a_BlockZ, EMCSBiome a_Biome); + + /** Sets the biome at the area. Returns true if successful, false if any subarea failed (chunk not loaded). + (Re)sends the chunks to their relevant clients if successful. */ + bool SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMCSBiome a_Biome); + /** Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. */ bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure); @@ -245,6 +256,9 @@ public: /** Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found */ bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Lua-accessible + /** Calls the callback for the mob head block at the specified coords; returns false if there's no mob head block at those coords or callback returns true, returns true if found */ + bool DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback); // Lua-accessible + /** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */ bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index efc5a9f64..a6a74bf4a 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -31,6 +31,7 @@ #include "MersenneTwister.h" #include "Protocol/ProtocolRecognizer.h" +#include "CompositeChat.h" @@ -94,6 +95,7 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) : m_ShouldCheckDownloaded(false), m_NumExplosionsThisTick(0), m_UniqueID(0), + m_Locale("en_GB"), m_HasSentPlayerChunk(false) { m_Protocol = new cProtocolRecognizer(this); @@ -1087,14 +1089,20 @@ void cClientHandle::HandleChat(const AString & a_Message) return; } - // Not a command, broadcast as a simple message: - AString Msg; - Printf(Msg, "%s<%s>%s %s", - m_Player->GetColor().c_str(), - m_Player->GetName().c_str(), - cChatColor::White.c_str(), - Message.c_str() - ); + // Not a command, broadcast as a message: + cCompositeChat Msg; + AString Color = m_Player->GetColor(); + if (Color.length() == 3) + { + Color = AString("@") + Color[2]; + } + else + { + Color.empty(); + } + Msg.AddTextPart(AString("<") + m_Player->GetName() + "> ", Color); + Msg.ParseText(a_Message); + Msg.UnderlineUrls(); m_Player->GetWorld()->BroadcastChat(Msg); } @@ -1729,7 +1737,7 @@ void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlock -void cClientHandle::SendChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const AString & a_AdditionalData) +void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData) { bool ShouldAppendChatPrefixes = true; @@ -1840,6 +1848,15 @@ void cClientHandle::SendChat(const AString & a_Message, ChatPrefixCodes a_ChatPr +void cClientHandle::SendChat(const cCompositeChat & a_Message) +{ + m_Protocol->SendChat(a_Message); +} + + + + + void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) { ASSERT(m_Player != NULL); @@ -2101,6 +2118,14 @@ void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup) +void cClientHandle::SendPaintingSpawn(const cPainting & a_Painting) +{ + m_Protocol->SendPaintingSpawn(a_Painting); +} + + + + void cClientHandle::SendEntityAnimation(const cEntity & a_Entity, char a_Animation) { diff --git a/src/ClientHandle.h b/src/ClientHandle.h index a613a76e8..8911164bf 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -28,6 +28,7 @@ class cInventory; class cMonster; class cPawn; class cExpOrb; +class cPainting; class cPickup; class cPlayer; class cProtocol; @@ -35,6 +36,7 @@ class cWindow; class cFallingBlock; class cItemHandler; class cWorld; +class cCompositeChat; @@ -90,7 +92,8 @@ public: void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage); void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes); - void SendChat (const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const AString & a_AdditionalData = ""); + void SendChat (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = ""); + void SendChat (const cCompositeChat & a_Message); void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer); void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player); void SendDestroyEntity (const cEntity & a_Entity); @@ -113,6 +116,7 @@ public: void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length); void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators); void SendMapInfo (int a_ID, unsigned int a_Scale); + void SendPaintingSpawn (const cPainting & a_Painting); void SendPickupSpawn (const cPickup & a_Pickup); void SendEntityAnimation (const cEntity & a_Entity, char a_Animation); void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount); @@ -157,6 +161,9 @@ public: void SetViewDistance(int a_ViewDistance); // tolua_export int GetViewDistance(void) const { return m_ViewDistance; } // tolua_export + + void SetLocale(AString & a_Locale) { m_Locale = a_Locale; } // tolua_export + AString GetLocale(void) const { return m_Locale; } // tolua_export int GetUniqueID() const { return m_UniqueID; } // tolua_export @@ -310,7 +317,9 @@ private: /// Set to true when the chunk where the player is is sent to the client. Used for spawning the player bool m_HasSentPlayerChunk; - + + /// Client Settings + AString m_Locale; /// Returns true if the rate block interactions is within a reasonable limit (bot protection) diff --git a/src/CompositeChat.cpp b/src/CompositeChat.cpp new file mode 100644 index 000000000..3eec35657 --- /dev/null +++ b/src/CompositeChat.cpp @@ -0,0 +1,426 @@ + +// CompositeChat.cpp + +// Implements the cCompositeChat class used to wrap a chat message with multiple parts (text, url, cmd) + +#include "Globals.h" +#include "CompositeChat.h" + + + + + +#if SELF_TEST + +/** A simple self-test that verifies that the composite chat parser is working properly. */ +class SelfTest_CompositeChat +{ +public: + SelfTest_CompositeChat(void) + { + fprintf(stderr, "cCompositeChat self test...\n"); + TestParser1(); + TestParser2(); + TestParser3(); + TestParser4(); + TestParser5(); + fprintf(stderr, "cCompositeChat self test finished.\n"); + } + + void TestParser1(void) + { + cCompositeChat Msg; + Msg.ParseText("Testing @2color codes and http://links parser"); + const cCompositeChat::cParts & Parts = Msg.GetParts(); + assert(Parts.size() == 4); + assert(Parts[0]->m_PartType == cCompositeChat::ptText); + assert(Parts[1]->m_PartType == cCompositeChat::ptText); + assert(Parts[2]->m_PartType == cCompositeChat::ptUrl); + assert(Parts[3]->m_PartType == cCompositeChat::ptText); + assert(Parts[0]->m_Style == ""); + assert(Parts[1]->m_Style == "@2"); + assert(Parts[2]->m_Style == "@2"); + assert(Parts[3]->m_Style == "@2"); + } + + void TestParser2(void) + { + cCompositeChat Msg; + Msg.ParseText("@3Advanced stuff: @5overriding color codes and http://links.with/@4color-in-them handling"); + const cCompositeChat::cParts & Parts = Msg.GetParts(); + assert(Parts.size() == 4); + assert(Parts[0]->m_PartType == cCompositeChat::ptText); + assert(Parts[1]->m_PartType == cCompositeChat::ptText); + assert(Parts[2]->m_PartType == cCompositeChat::ptUrl); + assert(Parts[3]->m_PartType == cCompositeChat::ptText); + assert(Parts[0]->m_Style == "@3"); + assert(Parts[1]->m_Style == "@5"); + assert(Parts[2]->m_Style == "@5"); + assert(Parts[3]->m_Style == "@5"); + } + + void TestParser3(void) + { + cCompositeChat Msg; + Msg.ParseText("http://links.starting the text"); + const cCompositeChat::cParts & Parts = Msg.GetParts(); + assert(Parts.size() == 2); + assert(Parts[0]->m_PartType == cCompositeChat::ptUrl); + assert(Parts[1]->m_PartType == cCompositeChat::ptText); + assert(Parts[0]->m_Style == ""); + assert(Parts[1]->m_Style == ""); + } + + void TestParser4(void) + { + cCompositeChat Msg; + Msg.ParseText("links finishing the text: http://some.server"); + const cCompositeChat::cParts & Parts = Msg.GetParts(); + assert(Parts.size() == 2); + assert(Parts[0]->m_PartType == cCompositeChat::ptText); + assert(Parts[1]->m_PartType == cCompositeChat::ptUrl); + assert(Parts[0]->m_Style == ""); + assert(Parts[1]->m_Style == ""); + } + + void TestParser5(void) + { + cCompositeChat Msg; + Msg.ParseText("http://only.links"); + const cCompositeChat::cParts & Parts = Msg.GetParts(); + assert(Parts.size() == 1); + assert(Parts[0]->m_PartType == cCompositeChat::ptUrl); + assert(Parts[0]->m_Style == ""); + } + +} gTest; +#endif // SELF_TEST + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat: + +cCompositeChat::cCompositeChat(void) : + m_MessageType(mtCustom) +{ +} + + + + + +cCompositeChat::cCompositeChat(const AString & a_ParseText) : + m_MessageType(mtCustom) +{ + ParseText(a_ParseText); +} + + + + + +cCompositeChat::~cCompositeChat() +{ + Clear(); +} + + + + + +void cCompositeChat::Clear(void) +{ + for (cParts::iterator itr = m_Parts.begin(), end = m_Parts.end(); itr != end; ++itr) + { + delete *itr; + } // for itr - m_Parts[] + m_Parts.clear(); +} + + + + + +void cCompositeChat::AddTextPart(const AString & a_Message, const AString & a_Style) +{ + m_Parts.push_back(new cTextPart(a_Message, a_Style)); +} + + + + + +void cCompositeChat::AddClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style) +{ + m_Parts.push_back(new cClientTranslatedPart(a_TranslationID, a_Parameters, a_Style)); +} + + + + + +void cCompositeChat::AddUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style) +{ + m_Parts.push_back(new cUrlPart(a_Text, a_Url, a_Style)); +} + + + + + +void cCompositeChat::AddRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style) +{ + m_Parts.push_back(new cRunCommandPart(a_Text, a_Command, a_Style)); +} + + + + + +void cCompositeChat::AddSuggestCommandPart(const AString & a_Text, const AString & a_SuggestedCommand, const AString & a_Style) +{ + m_Parts.push_back(new cSuggestCommandPart(a_Text, a_SuggestedCommand, a_Style)); +} + + + + + +void cCompositeChat::ParseText(const AString & a_ParseText) +{ + size_t len = a_ParseText.length(); + size_t first = 0; // First character of the currently parsed block + AString CurrentStyle; + AString CurrentText; + for (size_t i = 0; i < len; i++) + { + switch (a_ParseText[i]) + { + case '@': + { + // Color code + i++; + if (i >= len) + { + // Not enough following text + break; + } + if (a_ParseText[i] == '@') + { + // "@@" escape, just put a "@" into the current text and keep parsing as text + if (i > first + 1) + { + CurrentText.append(a_ParseText.c_str() + first, i - first - 1); + } + first = i + 1; + continue; + } + else + { + // True color code. Create a part for the CurrentText and start parsing anew: + if (i >= first) + { + CurrentText.append(a_ParseText.c_str() + first, i - first - 1); + first = i + 1; + } + if (!CurrentText.empty()) + { + m_Parts.push_back(new cTextPart(CurrentText, CurrentStyle)); + CurrentText.clear(); + } + AddStyle(CurrentStyle, a_ParseText.substr(i - 1, 2)); + } + break; + } + + case ':': + { + const char * LinkPrefixes[] = + { + "http", + "https" + }; + for (size_t Prefix = 0; Prefix < ARRAYCOUNT(LinkPrefixes); Prefix++) + { + size_t PrefixLen = strlen(LinkPrefixes[Prefix]); + if ( + (i >= first + PrefixLen) && // There is enough space in front of the colon for the prefix + (strncmp(a_ParseText.c_str() + i - PrefixLen, LinkPrefixes[Prefix], PrefixLen) == 0) // the prefix matches + ) + { + // Add everything before this as a text part: + if (i > first + PrefixLen) + { + CurrentText.append(a_ParseText.c_str() + first, i - first - PrefixLen); + first = i - PrefixLen; + } + if (!CurrentText.empty()) + { + AddTextPart(CurrentText, CurrentStyle); + CurrentText.clear(); + } + + // Go till the last non-whitespace char in the text: + for (; i < len; i++) + { + if (isspace(a_ParseText[i])) + { + break; + } + } + AddUrlPart(a_ParseText.substr(first, i - first), a_ParseText.substr(first, i - first), CurrentStyle); + first = i; + break; + } + } // for Prefix - LinkPrefix[] + break; + } // case ':' + } // switch (a_ParseText[i]) + } // for i - a_ParseText[] + if (first < len) + { + AddTextPart(a_ParseText.substr(first, len - first), CurrentStyle); + } +} + + + + + +void cCompositeChat::SetMessageType(eMessageType a_MessageType) +{ + m_MessageType = a_MessageType; +} + + + + + +void cCompositeChat::UnderlineUrls(void) +{ + for (cParts::iterator itr = m_Parts.begin(), end = m_Parts.end(); itr != end; ++itr) + { + if ((*itr)->m_PartType == ptUrl) + { + (*itr)->m_Style.append("u"); + } + } // for itr - m_Parts[] +} + + + + + +void cCompositeChat::AddStyle(AString & a_Style, const AString & a_AddStyle) +{ + if (a_AddStyle.empty()) + { + return; + } + if (a_AddStyle[0] == '@') + { + size_t idx = a_Style.find('@'); + if ((idx != AString::npos) && (idx != a_Style.length())) + { + a_Style.erase(idx, 2); + } + a_Style.append(a_AddStyle); + return; + } + a_Style.append(a_AddStyle); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cBasePart: + +cCompositeChat::cBasePart::cBasePart(cCompositeChat::ePartType a_PartType, const AString & a_Text, const AString & a_Style) : + m_PartType(a_PartType), + m_Text(a_Text), + m_Style(a_Style) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cTextPart: + +cCompositeChat::cTextPart::cTextPart(const AString & a_Text, const AString &a_Style) : + super(ptText, a_Text, a_Style) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cClientTranslatedPart: + +cCompositeChat::cClientTranslatedPart::cClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style) : + super(ptClientTranslated, a_TranslationID, a_Style), + m_Parameters(a_Parameters) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cUrlPart: + +cCompositeChat::cUrlPart::cUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style) : + super(ptUrl, a_Text, a_Style), + m_Url(a_Url) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cCommandPart: + +cCompositeChat::cCommandPart::cCommandPart(ePartType a_PartType, const AString & a_Text, const AString & a_Command, const AString & a_Style) : + super(a_PartType, a_Text, a_Style), + m_Command(a_Command) +{ +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cRunCommandPart: + +cCompositeChat::cRunCommandPart::cRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style) : + super(ptRunCommand, a_Text, a_Command, a_Style) +{ +} + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCompositeChat::cSuggestCommandPart: + +cCompositeChat::cSuggestCommandPart::cSuggestCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style) : + super(ptSuggestCommand, a_Text, a_Command, a_Style) +{ +} + + + + diff --git a/src/CompositeChat.h b/src/CompositeChat.h new file mode 100644 index 000000000..51600da4f --- /dev/null +++ b/src/CompositeChat.h @@ -0,0 +1,183 @@ + +// CompositeChat.h + +// Declares the cCompositeChat class used to wrap a chat message with multiple parts (text, url, cmd) + +#include "Defines.h" + + + + + +// tolua_begin +/** Container for a single chat message composed of multiple functional parts. +Each part corresponds roughly to the behavior supported by the client messaging: + - plain text, optionaly colorized / styled + - clickable URLs + - clickable commands (run) + - clickable commands (suggest) +Each part has a text assigned to it that can be styled. The style is specified using a string, +each character / character combination in the string specifies the style to use: + - b = bold + - i = italic + - u = underlined + - s = strikethrough + - o = obfuscated + - @X = color X (X is 0 - 9 or a - f, same as dye meta +If the protocol version doesn't support all the features, it degrades gracefully. +*/ +class cCompositeChat +{ +public: + // tolua_end + + enum ePartType + { + ptText, + ptClientTranslated, + ptUrl, + ptRunCommand, + ptSuggestCommand, + } ; + + class cBasePart + { + public: + ePartType m_PartType; + AString m_Text; + AString m_Style; + + cBasePart(ePartType a_PartType, const AString & a_Text, const AString & a_Style = ""); + + // Force a virtual destructor in descendants + virtual ~cBasePart() {} + } ; + + class cTextPart : + public cBasePart + { + typedef cBasePart super; + public: + cTextPart(const AString & a_Text, const AString & a_Style = ""); + } ; + + class cClientTranslatedPart : + public cBasePart + { + typedef cBasePart super; + public: + AStringVector m_Parameters; + + cClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style = ""); + } ; + + class cUrlPart : + public cBasePart + { + typedef cBasePart super; + public: + AString m_Url; + + cUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style = ""); + } ; + + class cCommandPart : + public cBasePart + { + typedef cBasePart super; + public: + AString m_Command; + + cCommandPart(ePartType a_PartType, const AString & a_Text, const AString & a_Command, const AString & a_Style = ""); + } ; + + class cRunCommandPart : + public cCommandPart + { + typedef cCommandPart super; + public: + cRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style = ""); + } ; + + class cSuggestCommandPart : + public cCommandPart + { + typedef cCommandPart super; + public: + cSuggestCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style = ""); + } ; + + typedef std::vector<cBasePart *> cParts; + + // tolua_begin + + /** Creates a new empty chat message */ + cCompositeChat(void); + + /** Creates a new chat message and parses the text into parts. + Recognizes "http:" and "https:" links and @color-codes. + Uses ParseText() for the actual parsing. */ + cCompositeChat(const AString & a_ParseText); + + ~cCompositeChat(); + + /** Removes all parts from the object. */ + void Clear(void); + + /** Adds a plain text part, with optional style. + The default style is plain white text. */ + void AddTextPart(const AString & a_Message, const AString & a_Style = ""); + + // tolua_end + + /** Adds a part that is translated client-side, with the formatting parameters and optional style. + Exported in ManualBindings due to AStringVector usage - Lua uses an array-table of strings. */ + void AddClientTranslatedPart(const AString & a_TranslationID, const AStringVector & a_Parameters, const AString & a_Style = ""); + + // tolua_begin + + /** Adds a part that opens an URL when clicked. + The default style is underlined light blue text. */ + void AddUrlPart(const AString & a_Text, const AString & a_Url, const AString & a_Style = "u@c"); + + /** Adds a part that runs a command when clicked. + The default style is underlined light green text. */ + void AddRunCommandPart(const AString & a_Text, const AString & a_Command, const AString & a_Style = "u@a"); + + /** Adds a part that suggests a command (enters it into the chat message area, but doesn't send) when clicked. + The default style is underlined yellow text. */ + void AddSuggestCommandPart(const AString & a_Text, const AString & a_SuggestedCommand, const AString & a_Style = "u@b"); + + /** Parses text into various parts, adds those. + Recognizes "http:" and "https:" URLs and @color-codes. */ + void ParseText(const AString & a_ParseText); + + /** Sets the message type, which is indicated by prefixes added to the message when serializing. */ + void SetMessageType(eMessageType a_MessageType); + + /** Returns the message type set previously by SetMessageType(). */ + eMessageType GetMessageType(void) const { return m_MessageType; } + + /** Adds the "underline" style to each part that is an URL. */ + void UnderlineUrls(void); + + // tolua_end + + const cParts & GetParts(void) const { return m_Parts; } + +protected: + /** All the parts that */ + cParts m_Parts; + + /** The message type, as indicated by prefixes. */ + eMessageType m_MessageType; + + + /** Adds a_AddStyle to a_Style; overwrites the existing style if appropriate. + If the style already contains something that a_AddStyle overrides, it is erased first. */ + void AddStyle(AString & a_Style, const AString & a_AddStyle); +} ; // tolua_export + + + + diff --git a/src/Defines.h b/src/Defines.h index 290f862ef..ba2866f83 100644 --- a/src/Defines.h +++ b/src/Defines.h @@ -174,6 +174,43 @@ enum eWeather +enum eMobHeadType +{ + SKULL_TYPE_SKELETON = 0, + SKULL_TYPE_WITHER = 1, + SKULL_TYPE_ZOMBIE = 2, + SKULL_TYPE_PLAYER = 3, + SKULL_TYPE_CREEPER = 4, +} ; + + + + + +enum eMobHeadRotation +{ + SKULL_ROTATION_NORTH = 0, + SKULL_ROTATION_NORTH_NORTH_EAST = 1, + SKULL_ROTATION_NORTH_EAST = 2, + SKULL_ROTATION_EAST_NORTH_EAST = 3, + SKULL_ROTATION_EAST = 4, + SKULL_ROTATION_EAST_SOUTH_EAST = 5, + SKULL_ROTATION_SOUTH_EAST = 6, + SKULL_ROTATION_SOUTH_SOUTH_EAST = 7, + SKULL_ROTATION_SOUTH = 8, + SKULL_ROTATION_SOUTH_SOUTH_WEST = 9, + SKULL_ROTATION_SOUTH_WEST = 10, + SKULL_ROTATION_WEST_SOUTH_WEST = 11, + SKULL_ROTATION_WEST = 12, + SKULL_ROTATION_WEST_NORTH_WEST = 13, + SKULL_ROTATION_NORTH_WEST = 14, + SKULL_ROTATION_NORTH_NORTH_WEST = 15, +} ; + + + + + inline const char * ClickActionToString(eClickAction a_ClickAction) { switch (a_ClickAction) @@ -441,7 +478,10 @@ inline float GetSpecialSignf( float a_Val ) -enum ChatPrefixCodes + +// tolua_begin + +enum eMessageType { // http://forum.mc-server.org/showthread.php?tid=1212 // MessageType... @@ -458,7 +498,9 @@ enum ChatPrefixCodes mtLeave, // A player has left the server }; -// tolua_begin + + + /** Normalizes an angle in degrees to the [-180, +180) range: */ inline double NormalizeAngleDegrees(const double a_Degrees) diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index b2edfc2b4..b3b1cef83 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -81,6 +81,8 @@ public: etProjectile, etExpOrb, etFloater, + etItemFrame, + etPainting, // Common variations etMob = etMonster, // DEPRECATED, use etMonster instead! @@ -139,6 +141,8 @@ public: bool IsProjectile (void) const { return (m_EntityType == etProjectile); } bool IsExpOrb (void) const { return (m_EntityType == etExpOrb); } bool IsFloater (void) const { return (m_EntityType == etFloater); } + bool IsItemFrame (void) const { return (m_EntityType == etItemFrame); } + bool IsPainting (void) const { return (m_EntityType == etPainting); } /// Returns true if the entity is of the specified class or a subclass (cPawn's IsA("cEntity") returns true) virtual bool IsA(const char * a_ClassName) const; diff --git a/src/Entities/ExpOrb.cpp b/src/Entities/ExpOrb.cpp index 04ee85823..3398f1c7b 100644 --- a/src/Entities/ExpOrb.cpp +++ b/src/Entities/ExpOrb.cpp @@ -51,7 +51,10 @@ void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk) { LOGD("Player %s picked up an ExpOrb. His reward is %i", a_ClosestPlayer->GetName().c_str(), m_Reward); a_ClosestPlayer->DeltaExperience(m_Reward); - Destroy(true); + + m_World->BroadcastSoundEffect("random.orb", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + + Destroy(); } a_Distance.Normalize(); a_Distance *= ((float) (5.5 - Distance)); @@ -61,4 +64,4 @@ void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk) BroadcastMovementUpdate(); } HandlePhysics(a_Dt, a_Chunk); -}
\ No newline at end of file +} diff --git a/src/Entities/ItemFrame.cpp b/src/Entities/ItemFrame.cpp new file mode 100644 index 000000000..8cfa5e18d --- /dev/null +++ b/src/Entities/ItemFrame.cpp @@ -0,0 +1,124 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ItemFrame.h" +#include "ClientHandle.h" +#include "Player.h" + + + + + +cItemFrame::cItemFrame(eBlockFace a_BlockFace, double a_X, double a_Y, double a_Z) + : cEntity(etItemFrame, a_X, a_Y, a_Z, 0.8, 0.8), + m_BlockFace(a_BlockFace), + m_Item(E_BLOCK_AIR), + m_Rotation(0) +{ + SetMaxHealth(1); + SetHealth(1); +} + + + + + +void cItemFrame::SpawnOn(cClientHandle & a_ClientHandle) +{ + int Dir = 0; + + // The client uses different values for item frame directions and block faces. Our constants are for the block faces, so we convert them here to item frame faces + switch (m_BlockFace) + { + case BLOCK_FACE_ZP: break; // Initialised to zero + case BLOCK_FACE_ZM: Dir = 2; break; + case BLOCK_FACE_XM: Dir = 1; break; + case BLOCK_FACE_XP: Dir = 3; break; + default: ASSERT(!"Unhandled block face when trying to spawn item frame!"); return; + } + + if ((Dir == 0) || (Dir == 2)) // Probably a client bug, but two directions are flipped and contrary to the norm, so we do -180 + { + SetYaw((Dir * 90) - 180); + } + else + { + SetYaw(Dir * 90); + } + + a_ClientHandle.SendSpawnObject(*this, 71, Dir, (Byte)GetYaw(), (Byte)GetPitch()); +} + + + + + +void cItemFrame::OnRightClicked(cPlayer & a_Player) +{ + if (!m_Item.IsEmpty()) + { + // Item not empty, rotate, clipping values to zero to three inclusive + m_Rotation++; + if (m_Rotation >= 4) + { + m_Rotation = 0; + } + } + else if (!a_Player.GetEquippedItem().IsEmpty()) + { + // Item empty, and player held item not empty - add this item to self + m_Item = a_Player.GetEquippedItem(); + m_Item.m_ItemCount = 1; + + if (!a_Player.IsGameModeCreative()) + { + a_Player.GetInventory().RemoveOneEquippedItem(); + } + } + + GetWorld()->BroadcastEntityMetadata(*this); // Update clients +} + + + + + + +void cItemFrame::KilledBy(cEntity * a_Killer) +{ + if (m_Item.IsEmpty()) + { + super::KilledBy(a_Killer); + Destroy(); + return; + } + + if ((a_Killer != NULL) && a_Killer->IsPlayer() && !((cPlayer *)a_Killer)->IsGameModeCreative()) + { + cItems Item; + Item.push_back(m_Item); + + GetWorld()->SpawnItemPickups(Item, GetPosX(), GetPosY(), GetPosZ()); + } + + SetHealth(GetMaxHealth()); + m_Item.Clear(); + m_Rotation = 0; + GetWorld()->BroadcastEntityMetadata(*this); +} + + + + + +void cItemFrame::GetDrops(cItems & a_Items, cEntity * a_Killer) +{ + if ((a_Killer != NULL) && a_Killer->IsPlayer() && !((cPlayer *)a_Killer)->IsGameModeCreative()) + { + a_Items.push_back(cItem(E_ITEM_ITEM_FRAME)); + } +} + + + + diff --git a/src/Entities/ItemFrame.h b/src/Entities/ItemFrame.h new file mode 100644 index 000000000..43915e3f9 --- /dev/null +++ b/src/Entities/ItemFrame.h @@ -0,0 +1,42 @@ + +#pragma once + +#include "Entity.h" + + + + + +// tolua_begin +class cItemFrame : + public cEntity +{ + // tolua_end + typedef cEntity super; + +public: + + CLASS_PROTODEF(cItemFrame); + + cItemFrame(eBlockFace a_BlockFace, double a_X, double a_Y, double a_Z); + + const cItem & GetItem(void) { return m_Item; } + Byte GetRotation(void) const { return m_Rotation; } + +private: + + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override {}; + virtual void KilledBy(cEntity * a_Killer) override; + virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override; + + eBlockFace m_BlockFace; + cItem m_Item; + Byte m_Rotation; + +}; // tolua_export + + + + diff --git a/src/Entities/Painting.cpp b/src/Entities/Painting.cpp new file mode 100644 index 000000000..b98c1e67a --- /dev/null +++ b/src/Entities/Painting.cpp @@ -0,0 +1,43 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "Painting.h" +#include "ClientHandle.h" +#include "Player.h" + + + + + +cPainting::cPainting(const AString & a_Name, int a_Direction, double a_X, double a_Y, double a_Z) + : cEntity(etPainting, a_X, a_Y, a_Z, 1, 1), + m_Name(a_Name), + m_Direction(a_Direction) +{ +} + + + + + + +void cPainting::SpawnOn(cClientHandle & a_Client) +{ + a_Client.SendPaintingSpawn(*this); +} + + + + + +void cPainting::GetDrops(cItems & a_Items, cEntity * a_Killer) +{ + if ((a_Killer != NULL) && a_Killer->IsPlayer() && !((cPlayer *)a_Killer)->IsGameModeCreative()) + { + a_Items.push_back(cItem(E_ITEM_PAINTING)); + } +} + + + + diff --git a/src/Entities/Painting.h b/src/Entities/Painting.h new file mode 100644 index 000000000..95afbed1e --- /dev/null +++ b/src/Entities/Painting.h @@ -0,0 +1,42 @@ + +#pragma once + +#include "Entity.h" + + + + + +// tolua_begin +class cPainting : + public cEntity +{ + // tolua_end + typedef cEntity super; + +public: + CLASS_PROTODEF(cPainting); + + cPainting(const AString & a_Name, int a_Direction, double a_X, double a_Y, double a_Z); + const AString & GetName(void) const { return m_Name; } // tolua_export + int GetDirection(void) const { return m_Direction; } // tolua_export + +private: + + virtual void SpawnOn(cClientHandle & a_Client) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override {}; + virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override; + virtual void KilledBy(cEntity * a_Killer) override + { + super::KilledBy(a_Killer); + Destroy(); + } + + AString m_Name; + int m_Direction; + +}; // tolua_export + + + + diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index fdf8d4303..0152bfc5b 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -842,6 +842,12 @@ void cPlayer::KilledBy(cEntity * a_Killer) cItems Pickups; m_Inventory.CopyToItems(Pickups); m_Inventory.Clear(); + + if (GetName() == "Notch") + { + Pickups.Add(cItem(E_ITEM_RED_APPLE)); + } + m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); SaveToDisk(); // Save it, yeah the world is a tough place ! @@ -1125,8 +1131,9 @@ void cPlayer::SetIP(const AString & a_IP) void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { - SetPosition( a_PosX, a_PosY, a_PosZ ); + SetPosition(a_PosX, a_PosY, a_PosZ); m_LastGroundHeight = (float)a_PosY; + m_LastJumpHeight = (float)a_PosY; m_World->BroadcastTeleportEntity(*this, GetClientHandle()); m_ClientHandle->SendPlayerMoveLook(); @@ -1760,6 +1767,12 @@ void cPlayer::HandleFood(void) { // Ref.: http://www.minecraftwiki.net/wiki/Hunger + if (IsGameModeCreative()) + { + // Hunger is disabled for Creative + return; + } + // Remember the food level before processing, for later comparison int LastFoodLevel = m_FoodLevel; @@ -1777,7 +1790,7 @@ void cPlayer::HandleFood(void) Heal(1); m_FoodExhaustionLevel += 3; } - else if (m_FoodLevel <= 0) + else if ((m_FoodLevel <= 0) && (m_Health > 1)) { // Damage from starving TakeDamage(dtStarving, NULL, 1, 1, 0); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 7db9544cb..a795bb9eb 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -203,6 +203,7 @@ public: void SendMessageWarning (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtWarning); } void SendMessageFatal (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtFailure); } void SendMessagePrivateMsg(const AString & a_Message, const AString & a_Sender) { m_ClientHandle->SendChat(a_Message, mtPrivateMessage, a_Sender); } + void SendMessage (const cCompositeChat & a_Message) { m_ClientHandle->SendChat(a_Message); } const AString & GetName(void) const { return m_PlayerName; } void SetName(const AString & a_Name) { m_PlayerName = a_Name; } @@ -225,7 +226,8 @@ public: // tolua_begin - /// Returns the full color code to use for this player, based on their primary group or set in m_Color + /** Returns the full color code to use for this player, based on their primary group or set in m_Color. + The returned value includes the cChatColor::Delimiter. */ AString GetColor(void) const; /** tosses the item in the selected hotbar slot */ diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index a3fa9d557..ef82c6e94 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -680,6 +680,7 @@ super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25) 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/ForEachChunkProvider.h b/src/ForEachChunkProvider.h index 70cd2196a..6017173ee 100644 --- a/src/ForEachChunkProvider.h +++ b/src/ForEachChunkProvider.h @@ -1,14 +1,39 @@ +// ForEachChunkProvider.h + +// Declares the cForEachChunkProvider class which acts as an interface for classes that provide a ForEachChunkInRect() function +// Primarily serves as a decoupling between cBlockArea and cWorld + + + + + #pragma once -class cChunkDataCallback; + + +// fwd: +class cChunkDataCallback; class cBlockArea; + + + + class cForEachChunkProvider { public: + /** Calls the callback for each chunk in the specified range. */ virtual bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) = 0; + /** Writes the block area into the specified coords. + Returns true if all chunks have been processed. + a_DataTypes is a bitmask of cBlockArea::baXXX constants ORed together. + */ virtual bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) = 0; }; + + + + diff --git a/src/Globals.h b/src/Globals.h index 1e90d83e9..7ee045130 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -235,6 +235,16 @@ public: +/** Clamp a_X to the specified range. */ +template <typename T> +T Clamp(T a_X, T a_Min, T a_Max) +{ + return std::min(std::max(a_X, a_Min), a_Max); +} + + + + // Common headers (part 2, with macros): #include "ChunkDef.h" diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp index e8b58695f..34a267bab 100644 --- a/src/ItemGrid.cpp +++ b/src/ItemGrid.cpp @@ -369,6 +369,13 @@ int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount) } m_Slots[a_SlotNum].m_ItemCount += a_AddToCount; + + cItemHandler * Handler = cItemHandler::GetItemHandler(m_Slots[a_SlotNum].m_ItemType); + if (m_Slots[a_SlotNum].m_ItemCount > Handler->GetMaxStackSize()) + { + m_Slots[a_SlotNum].m_ItemCount = Handler->GetMaxStackSize(); + } + TriggerListeners(a_SlotNum); return m_Slots[a_SlotNum].m_ItemCount; } diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index cab8dec97..c10d13edc 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -22,12 +22,14 @@ #include "ItemFishingRod.h" #include "ItemFlowerPot.h" #include "ItemFood.h" +#include "ItemItemFrame.h" #include "ItemHoe.h" #include "ItemLeaves.h" #include "ItemLighter.h" #include "ItemMap.h" #include "ItemMinecart.h" #include "ItemNetherWart.h" +#include "ItemPainting.h" #include "ItemPickaxe.h" #include "ItemThrowable.h" #include "ItemRedstoneDust.h" @@ -37,6 +39,7 @@ #include "ItemShears.h" #include "ItemShovel.h" #include "ItemSign.h" +#include "ItemMobHead.h" #include "ItemSpawnEgg.h" #include "ItemSugarcane.h" #include "ItemSword.h" @@ -109,11 +112,14 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType); case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); case E_ITEM_MAP: return new cItemMapHandler(); + case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType); case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType); + case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType); case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); case E_ITEM_SIGN: return new cItemSignHandler(a_ItemType); + case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType); case E_ITEM_SNOWBALL: return new cItemSnowballHandler(); case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType); @@ -309,7 +315,7 @@ char cItemHandler::GetMaxStackSize(void) case E_ITEM_BOWL: return 64; case E_ITEM_BREAD: return 64; case E_ITEM_BREWING_STAND: return 64; - case E_ITEM_BUCKET: return 1; // TODO: change this to 16 when turning compatibility to 1.3 + case E_ITEM_BUCKET: return 16; case E_ITEM_CARROT: return 64; case E_ITEM_CAULDRON: return 64; case E_ITEM_CLAY: return 64; @@ -346,6 +352,7 @@ char cItemHandler::GetMaxStackSize(void) case E_ITEM_GUNPOWDER: return 64; case E_ITEM_HEAD: return 64; case E_ITEM_IRON: return 64; + case E_ITEM_ITEM_FRAME: return 64; case E_ITEM_LEATHER: return 64; case E_ITEM_MAGMA_CREAM: return 64; case E_ITEM_MAP: return 64; @@ -353,7 +360,7 @@ char cItemHandler::GetMaxStackSize(void) case E_ITEM_MELON_SLICE: return 64; case E_ITEM_NETHER_BRICK: return 64; case E_ITEM_NETHER_WART: return 64; - case E_ITEM_PAINTINGS: return 64; + case E_ITEM_PAINTING: return 64; case E_ITEM_PAPER: return 64; case E_ITEM_POISONOUS_POTATO: return 64; case E_ITEM_POTATO: return 64; diff --git a/src/Items/ItemItemFrame.h b/src/Items/ItemItemFrame.h new file mode 100644 index 000000000..74e987445 --- /dev/null +++ b/src/Items/ItemItemFrame.h @@ -0,0 +1,53 @@ + +#pragma once + +#include "ItemHandler.h" +#include "Entities/ItemFrame.h" +#include "../Entities/Player.h" + + + + + +class cItemItemFrameHandler : + public cItemHandler +{ +public: + cItemItemFrameHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + + } + + virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + { + if ((a_Dir == BLOCK_FACE_NONE) || (a_Dir == BLOCK_FACE_YP) || (a_Dir == BLOCK_FACE_YM)) + { + // Client sends this if clicked on top or bottom face + return false; + } + + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir); // Make sure block that will be occupied is free + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir, true); // We want the clicked block, so go back again + + if (Block == E_BLOCK_AIR) + { + cItemFrame * ItemFrame = new cItemFrame(a_Dir, a_BlockX, a_BlockY, a_BlockZ); + ItemFrame->Initialize(a_World); + + if (!a_Player->IsGameModeCreative()) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + + return true; + + } + return false; + } +}; + + + + diff --git a/src/Items/ItemLighter.h b/src/Items/ItemLighter.h index 6681a08d4..cc7daeb08 100644 --- a/src/Items/ItemLighter.h +++ b/src/Items/ItemLighter.h @@ -33,7 +33,7 @@ public: case E_BLOCK_TNT: { // Activate the TNT: - a_World->BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + a_World->BroadcastSoundEffect("game.tnt.primed", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0); break; diff --git a/src/Items/ItemMobHead.h b/src/Items/ItemMobHead.h new file mode 100644 index 000000000..5ae040282 --- /dev/null +++ b/src/Items/ItemMobHead.h @@ -0,0 +1,42 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" + + + + + +class cItemMobHeadHandler : + public cItemHandler +{ +public: + cItemMobHeadHandler(int a_ItemType) : + cItemHandler(a_ItemType) + { + } + + + virtual bool IsPlaceable(void) override + { + return true; + } + + + virtual bool GetPlacementBlockTypeMeta( + cWorld * a_World, cPlayer * a_Player, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ, + BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta + ) override + { + a_BlockType = E_BLOCK_HEAD; + a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f); + return true; + } +} ; + + + + diff --git a/src/Items/ItemPainting.h b/src/Items/ItemPainting.h new file mode 100644 index 000000000..b85098221 --- /dev/null +++ b/src/Items/ItemPainting.h @@ -0,0 +1,98 @@ + +#pragma once + +#include "ItemHandler.h" +#include "../World.h" +#include "../Entities/Player.h" +#include "../Entities/Painting.h" + + + + + +class cItemPaintingHandler : + public cItemHandler +{ +public: + cItemPaintingHandler(int a_ItemType) + : cItemHandler(a_ItemType) + { + } + + virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override + { + if (a_Dir == BLOCK_FACE_NONE) + { + // Client sends this if clicked on top or bottom face + return false; + } + + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir); // Make sure block that will be occupied is free + BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir, true); // We want the clicked block, so go back again + + if (Block == E_BLOCK_AIR) + { + int Dir = 0; + + // The client uses different values for painting directions and block faces. Our constants are for the block faces, so we convert them here to painting faces + switch (a_Dir) + { + case BLOCK_FACE_ZP: break; // Initialised to zero + case BLOCK_FACE_ZM: Dir = 2; break; + case BLOCK_FACE_XM: Dir = 1; break; + case BLOCK_FACE_XP: Dir = 3; break; + default: ASSERT(!"Unhandled block face when trying spawn painting!"); return false; + } + + static const struct // Define all the possible painting titles + { + AString Title; + } gPaintingTitlesList[] = + { + { "Kebab" }, + { "Aztec" }, + { "Alban" }, + { "Aztec2" }, + { "Bomb" }, + { "Plant" }, + { "Wasteland" }, + { "Wanderer" }, + { "Graham" }, + { "Pool" }, + { "Courbet" }, + { "Sunset" }, + { "Sea" }, + { "Creebet" }, + { "Match" }, + { "Bust" }, + { "Stage" }, + { "Void" }, + { "SkullAndRoses" }, + { "Wither" }, + { "Fighters" }, + { "Skeleton" }, + { "DonkeyKong" }, + { "Pointer" }, + { "Pigscene" }, + { "BurningSkull" } + }; + + cPainting * Painting = new cPainting(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, Dir, a_BlockX, a_BlockY, a_BlockZ); + Painting->Initialize(a_World); + + if (!a_Player->IsGameModeCreative()) + { + a_Player->GetInventory().RemoveOneEquippedItem(); + } + + return true; + + } + return false; + } +}; + + + + diff --git a/src/Map.cpp b/src/Map.cpp index a194dbd96..cb5472a22 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -41,16 +41,6 @@ cMapDecorator::cMapDecorator(cMap * a_Map, cPlayer * a_Player) -template <typename T> -T Clamp(T a_X, T a_Min, T a_Max) -{ - return std::min(std::max(a_X, a_Min), a_Max); -} - - - - - void cMapDecorator::Update(void) { if (m_Player != NULL) diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index f2f0c404c..0901f85a9 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -5,7 +5,7 @@ #include "../World.h" #include "../Entities/Player.h" -#include "../MersenneTwister.h" +#include "../Tracer.h" @@ -73,6 +73,18 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) { CheckEventSeePlayer(); } + + if (m_Target == NULL) + return; + + cTracer LineOfSight(GetWorld()); + Vector3d AttackDirection(m_Target->GetPosition() - GetPosition()); + + if (ReachedFinalDestination() && !LineOfSight.Trace(GetPosition(), AttackDirection, (int)AttackDirection.Length())) + { + // Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls) + Attack(a_Dt / 1000); + } } @@ -81,7 +93,7 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) void cAggressiveMonster::Attack(float a_Dt) { - super::Attack(a_Dt); + m_AttackInterval += a_Dt * m_AttackRate; if ((m_Target != NULL) && (m_AttackInterval > 3.0)) { diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h index 9cee4e7a7..152260f95 100644 --- a/src/Mobs/AggressiveMonster.h +++ b/src/Mobs/AggressiveMonster.h @@ -20,7 +20,7 @@ public: virtual void InStateChasing(float a_Dt) override; virtual void EventSeePlayer(cEntity *) override; - virtual void Attack(float a_Dt) override; + virtual void Attack(float a_Dt); } ; diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp index 2e1ece865..ff0abfdca 100644 --- a/src/Mobs/Creeper.cpp +++ b/src/Mobs/Creeper.cpp @@ -79,7 +79,7 @@ void cCreeper::Attack(float a_Dt) if (!m_bIsBlowing) { - m_World->BroadcastSoundEffect("random.fuse", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 1.f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + m_World->BroadcastSoundEffect("game.tnt.primed", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 1.f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); m_bIsBlowing = true; m_World->BroadcastEntityMetadata(*this); } diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index ad3a87725..b5cf693cb 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -142,11 +142,11 @@ void cMonster::TickPathFinding() BLOCKTYPE BlockAtYPP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ); BLOCKTYPE BlockAtYM = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY - 1, gCrossCoords[i].z + PosZ); - if (!g_BlockIsSolid[BlockAtY] && !g_BlockIsSolid[BlockAtYP] && !IsBlockLava(BlockAtYM)) + if ((!g_BlockIsSolid[BlockAtY]) && (!g_BlockIsSolid[BlockAtYP]) && (!IsBlockLava(BlockAtYM)) && (BlockAtY != E_BLOCK_FENCE) && (BlockAtY != E_BLOCK_FENCE_GATE)) { m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY, gCrossCoords[i].z + PosZ)); } - else if (g_BlockIsSolid[BlockAtY] && !g_BlockIsSolid[BlockAtYP] && !g_BlockIsSolid[BlockAtYPP] && !IsBlockLava(BlockAtYM)) + else if ((g_BlockIsSolid[BlockAtY]) && (!g_BlockIsSolid[BlockAtYP]) && (!g_BlockIsSolid[BlockAtYPP]) && (!IsBlockLava(BlockAtYM)) && (BlockAtY != E_BLOCK_FENCE) && (BlockAtY != E_BLOCK_FENCE_GATE)) { m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY + 1, gCrossCoords[i].z + PosZ)); } @@ -311,9 +311,6 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) } } - if (ReachedFinalDestination() && (m_Target != NULL)) - Attack(a_Dt); - SetPitchAndYawFromDestination(); HandleFalling(); @@ -657,17 +654,6 @@ void cMonster::InStateEscaping(float a_Dt) -// Do attack here -// a_Dt is passed so we can set attack rate -void cMonster::Attack(float a_Dt) -{ - m_AttackInterval += a_Dt * m_AttackRate; -} - - - - - void cMonster::GetMonsterConfig(const AString & a_Name) { cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 714feddb9..4d2e099c5 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -112,8 +112,6 @@ public: virtual void InStateChasing (float a_Dt); virtual void InStateEscaping(float a_Dt); - virtual void Attack(float a_Dt); - int GetAttackRate() { return (int)m_AttackRate; } void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; } void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; } diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp index ff324d073..2736c3dd1 100644 --- a/src/Mobs/Wolf.cpp +++ b/src/Mobs/Wolf.cpp @@ -124,12 +124,6 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); } - - // The wolf is sitting so don't move him at all. - if (IsSitting()) - { - m_bMovingToDestination = false; - } cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), (float)m_SightDistance); if (a_Closest_Player != NULL) @@ -148,18 +142,15 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk) SetIsBegging(true); m_World->BroadcastEntityMetadata(*this); } + + m_FinalDestination = a_Closest_Player->GetPosition(); // So that we will look at a player holding food + // Don't move to the player if the wolf is sitting. - if (IsSitting()) - { - m_bMovingToDestination = false; - } - else + if (!IsSitting()) { - m_bMovingToDestination = true; + MoveToPosition(a_Closest_Player->GetPosition()); } - Vector3d PlayerPos = a_Closest_Player->GetPosition(); - PlayerPos.y++; - m_FinalDestination = PlayerPos; + break; } default: @@ -173,10 +164,14 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk) } } - if (IsTame()) + if (IsTame() && !IsSitting()) { TickFollowPlayer(); } + else if (IsSitting()) + { + m_bMovingToDestination = false; + } } @@ -196,29 +191,19 @@ void cWolf::TickFollowPlayer() public: Vector3d OwnerPos; } Callback; + if (m_World->DoWithPlayer(m_OwnerName, Callback)) { // The player is present in the world, follow him: double Distance = (Callback.OwnerPos - GetPosition()).Length(); if (Distance > 30) { - if (!IsSitting()) - { - Callback.OwnerPos.y = FindFirstNonAirBlockPosition(Callback.OwnerPos.x, Callback.OwnerPos.z); - TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z); - } + Callback.OwnerPos.y = FindFirstNonAirBlockPosition(Callback.OwnerPos.x, Callback.OwnerPos.z); + TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z); } else { - m_FinalDestination = Callback.OwnerPos; - if (IsSitting()) - { - m_bMovingToDestination = false; - } - else - { - m_bMovingToDestination = true; - } + MoveToPosition(Callback.OwnerPos); } } } diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 4a1601487..b5560f7c1 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -25,10 +25,12 @@ class cWindow; class cInventory; class cPawn; class cPickup; +class cPainting; class cWorld; class cMonster; class cChunkDataSerializer; class cFallingBlock; +class cCompositeChat; @@ -59,6 +61,7 @@ public: virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0; virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0; virtual void SendChat (const AString & a_Message) = 0; + virtual void SendChat (const cCompositeChat & a_Message) = 0; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0; virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0; virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; @@ -83,6 +86,7 @@ public: virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) = 0; virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) = 0; virtual void SendMapInfo (int a_ID, unsigned int a_Scale) = 0; + virtual void SendPaintingSpawn (const cPainting & a_Painting) = 0; virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0; virtual void SendPlayerAbilities (void) = 0; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) = 0; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index 220fa18cf..3980350f5 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -32,6 +32,8 @@ Documentation: #include "../Mobs/IncludeAllMonsters.h" +#include "../CompositeChat.h" + @@ -234,6 +236,42 @@ void cProtocol125::SendChat(const AString & a_Message) +void cProtocol125::SendChat(const cCompositeChat & a_Message) +{ + // This version doesn't support composite messages, just extract each part's text and use it: + AString Msg; + const cCompositeChat::cParts & Parts = a_Message.GetParts(); + for (cCompositeChat::cParts::const_iterator itr = Parts.begin(), end = Parts.end(); itr != end; ++itr) + { + switch ((*itr)->m_PartType) + { + case cCompositeChat::ptText: + case cCompositeChat::ptClientTranslated: + case cCompositeChat::ptRunCommand: + case cCompositeChat::ptSuggestCommand: + { + Msg.append((*itr)->m_Text); + break; + } + case cCompositeChat::ptUrl: + { + Msg.append(((cCompositeChat::cUrlPart *)(*itr))->m_Url); + break; + } + } // switch (PartType) + } // for itr - Parts[] + + // Send the message: + cCSLock Lock(m_CSPacket); + WriteByte (PACKET_CHAT); + WriteString(Msg); + Flush(); +} + + + + + void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) { cCSLock Lock(m_CSPacket); diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index 1eeb15120..1d1484a60 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -33,6 +33,7 @@ public: virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; virtual void SendChat (const AString & a_Message) override; + virtual void SendChat (const cCompositeChat & a_Message) override; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; @@ -58,6 +59,7 @@ public: virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override; virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override {} // This protocol doesn't support such message virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; + virtual void SendPaintingSpawn (const cPainting & a_Painting) override {}; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index 648e70151..1f9222a69 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -560,7 +560,7 @@ int cProtocol132::ParseLocaleViewDistance(void) HANDLE_PACKET_READ(ReadChar, char, ViewDistance); HANDLE_PACKET_READ(ReadChar, char, ChatFlags); HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); - // TODO: m_Client->HandleLocale(Locale); + m_Client->SetLocale(Locale); // TODO: m_Client->HandleViewDistance(ViewDistance); // TODO: m_Client->HandleChatFlags(ChatFlags); // Ignoring client difficulty diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp index f82e6de45..232b2718e 100644 --- a/src/Protocol/Protocol14x.cpp +++ b/src/Protocol/Protocol14x.cpp @@ -85,7 +85,7 @@ int cProtocol142::ParseLocaleViewDistance(void) HANDLE_PACKET_READ(ReadChar, char, ChatFlags); HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2 - // TODO: m_Client->HandleLocale(Locale); + m_Client->SetLocale(Locale); // TODO: m_Client->HandleViewDistance(ViewDistance); // TODO: m_Client->HandleChatFlags(ChatFlags); // Ignoring client difficulty diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 38b4ed786..992023464 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -8,6 +8,7 @@ Implements the 1.7.x protocol classes: */ #include "Globals.h" +#include "json/json.h" #include "Protocol17x.h" #include "ChunkDataSerializer.h" #include "../ClientHandle.h" @@ -20,11 +21,15 @@ Implements the 1.7.x protocol classes: #include "../Entities/ExpOrb.h" #include "../Entities/Minecart.h" #include "../Entities/FallingBlock.h" +#include "../Entities/Painting.h" #include "../Entities/Pickup.h" #include "../Entities/Player.h" +#include "../Entities/ItemFrame.h" #include "../Mobs/IncludeAllMonsters.h" #include "../UI/Window.h" #include "../BlockEntities/CommandBlockEntity.h" +#include "../BlockEntities/MobHeadEntity.h" +#include "../CompositeChat.h" @@ -200,6 +205,78 @@ void cProtocol172::SendChat(const AString & a_Message) +void cProtocol172::SendChat(const cCompositeChat & a_Message) +{ + // Compose the complete Json string to send: + Json::Value msg; + msg["text"] = ""; // The client crashes without this + const cCompositeChat::cParts & Parts = a_Message.GetParts(); + for (cCompositeChat::cParts::const_iterator itr = Parts.begin(), end = Parts.end(); itr != end; ++itr) + { + Json::Value Part; + switch ((*itr)->m_PartType) + { + case cCompositeChat::ptText: + { + Part["text"] = (*itr)->m_Text; + AddChatPartStyle(Part, (*itr)->m_Style); + break; + } + + case cCompositeChat::ptClientTranslated: + { + const cCompositeChat::cClientTranslatedPart & p = (const cCompositeChat::cClientTranslatedPart &)**itr; + Part["translate"] = p.m_Text; + Json::Value With; + for (AStringVector::const_iterator itrW = p.m_Parameters.begin(), endW = p.m_Parameters.end(); itrW != endW; ++itr) + { + With.append(*itrW); + } + if (!p.m_Parameters.empty()) + { + Part["with"] = With; + } + AddChatPartStyle(Part, p.m_Style); + break; + } + + case cCompositeChat::ptUrl: + { + const cCompositeChat::cUrlPart & p = (const cCompositeChat::cUrlPart &)**itr; + Part["text"] = p.m_Text; + Json::Value Url; + Url["action"] = "open_url"; + Url["value"] = p.m_Url; + Part["clickEvent"] = Url; + AddChatPartStyle(Part, p.m_Style); + break; + } + + case cCompositeChat::ptSuggestCommand: + case cCompositeChat::ptRunCommand: + { + const cCompositeChat::cCommandPart & p = (const cCompositeChat::cCommandPart &)**itr; + Part["text"] = p.m_Text; + Json::Value Cmd; + Cmd["action"] = (p.m_PartType == cCompositeChat::ptRunCommand) ? "run_command" : "suggest_command"; + Cmd["value"] = p.m_Command; + Part["clickEvent"] = Cmd; + AddChatPartStyle(Part, p.m_Style); + break; + } + } + msg["extra"].append(Part); + } // for itr - Parts[] + + // Send the message to the client: + cPacketizer Pkt(*this, 0x02); + Pkt.WriteString(msg.toStyledString()); +} + + + + + void cProtocol172::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) { // Serialize first, before creating the Packetizer (the packetizer locks a CS) @@ -476,7 +553,7 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World) Pkt.WriteByte((Byte)a_Player.GetEffectiveGameMode() | (cRoot::Get()->GetServer()->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4 Pkt.WriteChar((char)a_World.GetDimension()); Pkt.WriteByte(2); // TODO: Difficulty (set to Normal) - Pkt.WriteByte(cRoot::Get()->GetServer()->GetMaxPlayers()); + Pkt.WriteByte(std::min(cRoot::Get()->GetServer()->GetMaxPlayers(), 60)); Pkt.WriteString("default"); // Level type - wtf? } @@ -495,6 +572,20 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting) +{ + cPacketizer Pkt(*this, 0x10); // Spawn Painting packet + Pkt.WriteVarInt(a_Painting.GetUniqueID()); + Pkt.WriteString(a_Painting.GetName().c_str()); + Pkt.WriteInt((int)a_Painting.GetPosX()); + Pkt.WriteInt((int)a_Painting.GetPosY()); + Pkt.WriteInt((int)a_Painting.GetPosZ()); + Pkt.WriteInt(a_Painting.GetDirection()); +} + + + + void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) { @@ -904,8 +995,8 @@ void cProtocol172::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, Pkt.WriteFPInt(a_Entity.GetPosX()); Pkt.WriteFPInt(a_Entity.GetPosY()); Pkt.WriteFPInt(a_Entity.GetPosZ()); - Pkt.WriteByteAngle(a_Entity.GetYaw()); Pkt.WriteByteAngle(a_Entity.GetPitch()); + Pkt.WriteByteAngle(a_Entity.GetYaw()); Pkt.WriteInt(a_ObjectData); if (a_ObjectData != 0) { @@ -927,8 +1018,8 @@ void cProtocol172::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp Pkt.WriteFPInt(a_Vehicle.GetPosX()); Pkt.WriteFPInt(a_Vehicle.GetPosY()); Pkt.WriteFPInt(a_Vehicle.GetPosZ()); - Pkt.WriteByteAngle(a_Vehicle.GetYaw()); Pkt.WriteByteAngle(a_Vehicle.GetPitch()); + Pkt.WriteByteAngle(a_Vehicle.GetYaw()); Pkt.WriteInt(a_VehicleSubType); if (a_VehicleSubType != 0) { @@ -1023,6 +1114,7 @@ void cProtocol172::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity) { case E_BLOCK_MOB_SPAWNER: Action = 1; break; // Update mob spawner spinny mob thing case E_BLOCK_COMMAND_BLOCK: Action = 2; break; // Update command block text + case E_BLOCK_HEAD: Action = 4; break; // Update Mobhead entity default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break; } Pkt.WriteByte(Action); @@ -1579,6 +1671,8 @@ void cProtocol172::HandlePacketClientSettings(cByteBuffer & a_ByteBuffer) HANDLE_READ(a_ByteBuffer, ReadByte, Byte, ChatColors); HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Difficulty); HANDLE_READ(a_ByteBuffer, ReadByte, Byte, ShowCape); + + m_Client->SetLocale(Locale); // TODO: handle in m_Client } @@ -2034,6 +2128,85 @@ void cProtocol172::StartEncryption(const Byte * a_Key) +void cProtocol172::AddChatPartStyle(Json::Value & a_Value, const AString & a_PartStyle) +{ + size_t len = a_PartStyle.length(); + for (size_t i = 0; i < len; i++) + { + switch (a_PartStyle[i]) + { + case 'b': + { + // bold + a_Value["bold"] = Json::Value(true); + break; + } + + case 'i': + { + // italic + a_Value["italic"] = Json::Value(true); + break; + } + + case 'u': + { + // Underlined + a_Value["underlined"] = Json::Value(true); + break; + } + + case 's': + { + // strikethrough + a_Value["strikethrough"] = Json::Value(true); + break; + } + + case 'o': + { + // obfuscated + a_Value["obfuscated"] = Json::Value(true); + break; + } + + case '@': + { + // Color, specified by the next char: + i++; + if (i >= len) + { + // String too short, didn't contain a color + break; + } + switch (a_PartStyle[i]) + { + case '0': a_Value["color"] = Json::Value("black"); break; + case '1': a_Value["color"] = Json::Value("dark_blue"); break; + case '2': a_Value["color"] = Json::Value("dark_green"); break; + case '3': a_Value["color"] = Json::Value("dark_aqua"); break; + case '4': a_Value["color"] = Json::Value("dark_red"); break; + case '5': a_Value["color"] = Json::Value("dark_purple"); break; + case '6': a_Value["color"] = Json::Value("gold"); break; + case '7': a_Value["color"] = Json::Value("gray"); break; + case '8': a_Value["color"] = Json::Value("dark_gray"); break; + case '9': a_Value["color"] = Json::Value("blue"); break; + case 'a': a_Value["color"] = Json::Value("green"); break; + case 'b': a_Value["color"] = Json::Value("aqua"); break; + case 'c': a_Value["color"] = Json::Value("red"); break; + case 'd': a_Value["color"] = Json::Value("light_purple"); break; + case 'e': a_Value["color"] = Json::Value("yellow"); break; + case 'f': a_Value["color"] = Json::Value("white"); break; + } // switch (color) + } // case '@' + } // switch (Style[i]) + } // for i - a_PartStyle[] +} + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cProtocol172::cPacketizer: @@ -2169,6 +2342,19 @@ void cProtocol172::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEnt } break; } + case E_BLOCK_HEAD: + { + cMobHeadEntity & MobHeadEntity = (cMobHeadEntity &)a_BlockEntity; + + Writer.AddInt("x", MobHeadEntity.GetPosX()); + Writer.AddInt("y", MobHeadEntity.GetPosY()); + Writer.AddInt("z", MobHeadEntity.GetPosZ()); + Writer.AddByte("SkullType", MobHeadEntity.GetType() & 0xFF); + Writer.AddByte("Rot", MobHeadEntity.GetRotation() & 0xFF); + Writer.AddString("ExtraType", MobHeadEntity.GetOwner().c_str()); + Writer.AddString("id", "Skull"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though + break; + } default: break; } @@ -2291,6 +2477,15 @@ void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity) WriteMobMetadata((const cMonster &)a_Entity); break; } + case cEntity::etItemFrame: + { + cItemFrame & Frame = (cItemFrame &)a_Entity; + WriteByte(0xA2); + WriteItem(Frame.GetItem()); + WriteByte(0x3); + WriteByte(Frame.GetRotation()); + break; + } } } diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 4edf51d2f..113501568 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -36,6 +36,16 @@ Declares the 1.7.x protocol classes: +// fwd: +namespace Json +{ + class Value; +} + + + + + class cProtocol172 : public cProtocol { @@ -45,16 +55,17 @@ public: cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); - /// Called when client sends some data: + /** Called when client sends some data: */ virtual void DataReceived(const char * a_Data, int a_Size) override; - /// Sending stuff to clients (alphabetically sorted): + /** Sending stuff to clients (alphabetically sorted): */ virtual void SendAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle) override; virtual void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType) override; virtual void SendBlockBreakAnim (int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override; virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; virtual void SendChat (const AString & a_Message) override; + virtual void SendChat (const cCompositeChat & a_Message) override; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; @@ -79,6 +90,7 @@ public: virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override; virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override; virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override; + virtual void SendPaintingSpawn (const cPainting & a_Painting) override; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; @@ -120,7 +132,7 @@ public: protected: - /// Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed + /** Composes individual packets in the protocol's m_OutPacketBuffer; sends them upon being destructed */ class cPacketizer { public: @@ -209,16 +221,16 @@ protected: AString m_AuthServerID; - /// State of the protocol. 1 = status, 2 = login, 3 = game + /** State of the protocol. 1 = status, 2 = login, 3 = game */ UInt32 m_State; - /// Buffer for the received data + /** Buffer for the received data */ cByteBuffer m_ReceivedData; - /// Buffer for composing the outgoing packets, through cPacketizer + /** Buffer for composing the outgoing packets, through cPacketizer */ cByteBuffer m_OutPacketBuffer; - /// Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer) + /** Buffer for composing packet length (so that each cPacketizer instance doesn't allocate a new cPacketBuffer) */ cByteBuffer m_OutPacketLenBuffer; bool m_IsEncrypted; @@ -230,7 +242,7 @@ protected: cFile m_CommLogFile; - /// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets + /** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */ void AddReceivedData(const char * a_Data, int a_Size); /** Reads and handles the packet. The packet length and type have already been read. @@ -271,21 +283,24 @@ protected: void HandlePacketWindowClose (cByteBuffer & a_ByteBuffer); - /// Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here. + /** Writes an entire packet into the output stream. a_Packet is expected to start with the packet type; data length is prepended here. */ void WritePacket(cByteBuffer & a_Packet); - /// Sends the data to the client, encrypting them if needed. + /** Sends the data to the client, encrypting them if needed. */ virtual void SendData(const char * a_Data, int a_Size) override; void SendCompass(const cWorld & a_World); - /// Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data + /** Reads an item out of the received data, sets a_Item to the values read. Returns false if not enough received data */ bool ReadItem(cByteBuffer & a_ByteBuffer, cItem & a_Item); - /// Parses item metadata as read by ReadItem(), into the item enchantments. + /** Parses item metadata as read by ReadItem(), into the item enchantments. */ void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata); void StartEncryption(const Byte * a_Key); + + /** Adds the chat part's style (represented by the part's stylestring) into the Json object. */ + void AddChatPartStyle(Json::Value & a_Value, const AString & a_PartStyle); } ; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 0a9369c0d..84b052146 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -159,6 +159,16 @@ void cProtocolRecognizer::SendChat(const AString & a_Message) +void cProtocolRecognizer::SendChat(const cCompositeChat & a_Message) +{ + ASSERT(m_Protocol != NULL); + m_Protocol->SendChat(a_Message); +} + + + + + void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) { ASSERT(m_Protocol != NULL); @@ -425,6 +435,14 @@ void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, flo +void cProtocolRecognizer::SendPaintingSpawn(const cPainting & a_Painting) +{ + m_Protocol->SendPaintingSpawn(a_Painting); +} + + + + void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup) { diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index ff4549ff5..6aaafedeb 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -68,6 +68,7 @@ public: virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override; virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override; virtual void SendChat (const AString & a_Message) override; + virtual void SendChat (const cCompositeChat & a_Message) override; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; @@ -93,6 +94,7 @@ public: virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override; virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override; virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override; + virtual void SendPaintingSpawn (const cPainting & a_Painting) override; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; diff --git a/src/Root.cpp b/src/Root.cpp index 749fbd288..206255916 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -543,11 +543,23 @@ void cRoot::ReloadGroups(void) -void cRoot::LoopWorldsAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix) +void cRoot::BroadcastChat(const AString & a_Message, eMessageType a_ChatPrefix) { for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) { - itr->second->LoopPlayersAndBroadcastChat(a_Message, a_ChatPrefix); + itr->second->BroadcastChat(a_Message, NULL, a_ChatPrefix); + } // for itr - m_WorldsByName[] +} + + + + + +void cRoot::BroadcastChat(const cCompositeChat & a_Message) +{ + for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr) + { + itr->second->BroadcastChat(a_Message); } // for itr - m_WorldsByName[] } diff --git a/src/Root.h b/src/Root.h index 13e208b8d..4bbd7586f 100644 --- a/src/Root.h +++ b/src/Root.h @@ -20,7 +20,8 @@ class cPluginManager; class cServer; class cWorld; class cPlayer; -class cCommandOutputCallback ; +class cCommandOutputCallback; +class cCompositeChat; typedef cItemCallback<cPlayer> cPlayerListCallback; typedef cItemCallback<cWorld> cWorldListCallback; @@ -108,20 +109,19 @@ public: /// Finds a player from a partial or complete player name and calls the callback - case-insensitive bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - void LoopWorldsAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix); - void BroadcastChatJoin (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtJoin); } - void BroadcastChatLeave (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtLeave); } - void BroadcastChatDeath (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtDeath); } - // tolua_begin /// Sends a chat message to all connected clients (in all worlds) - void BroadcastChat (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtCustom); } - void BroadcastChatInfo (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtInformation); } - void BroadcastChatFailure(const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtFailure); } - void BroadcastChatSuccess(const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtSuccess); } - void BroadcastChatWarning(const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtWarning); } - void BroadcastChatFatal (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtFailure); } + void BroadcastChat (const AString & a_Message, eMessageType a_ChatPrefix = mtCustom); + void BroadcastChatInfo (const AString & a_Message) { BroadcastChat(a_Message, mtInformation); } + void BroadcastChatFailure(const AString & a_Message) { BroadcastChat(a_Message, mtFailure); } + void BroadcastChatSuccess(const AString & a_Message) { BroadcastChat(a_Message, mtSuccess); } + void BroadcastChatWarning(const AString & a_Message) { BroadcastChat(a_Message, mtWarning); } + void BroadcastChatFatal (const AString & a_Message) { BroadcastChat(a_Message, mtFailure); } + void BroadcastChatJoin (const AString & a_Message) { BroadcastChat(a_Message, mtJoin); } + void BroadcastChatLeave (const AString & a_Message) { BroadcastChat(a_Message, mtLeave); } + void BroadcastChatDeath (const AString & a_Message) { BroadcastChat(a_Message, mtDeath); } + void BroadcastChat (const cCompositeChat & a_Message); /// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 5dba69455..60dabaf84 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -31,7 +31,7 @@ cIncrementalRedstoneSimulator::~cIncrementalRedstoneSimulator() -void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk, cChunk * a_OtherChunk) { if ((a_Chunk == NULL) || !a_Chunk->IsValid()) { @@ -42,17 +42,33 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B return; } - int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; - int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; - + // We may be called with coordinates in a chunk that is not the first chunk parameter + // In that case, the actual chunk (which the coordinates are in), will be passed as the second parameter + // Use that Chunk pointer to get a relative position + + int RelX = 0; + int RelZ = 0; BLOCKTYPE Block; NIBBLETYPE Meta; - a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); + + if (a_OtherChunk != NULL) + { + RelX = a_BlockX - a_OtherChunk->GetPosX() * cChunkDef::Width; + RelZ = a_BlockZ - a_OtherChunk->GetPosZ() * cChunkDef::Width; + a_OtherChunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); + } + else + { + RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; + RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; + a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta); + } // Every time a block is changed (AddBlock called), we want to go through all lists and check to see if the coordiantes stored within are still valid // Checking only when a block is changed, as opposed to every tick, also improves performance - for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) + PoweredBlocksList * PoweredBlocks = a_Chunk->GetRedstoneSimulatorPoweredBlocksList(); + for (PoweredBlocksList::iterator itr = PoweredBlocks->begin(); itr != PoweredBlocks->end(); ++itr) { if (!itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { @@ -62,7 +78,7 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B if (!IsPotentialSource(Block)) { LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list as it no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); - m_PoweredBlocks.erase(itr); + PoweredBlocks->erase(itr); break; } else if ( @@ -75,7 +91,7 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B ) { LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); - m_PoweredBlocks.erase(itr); + PoweredBlocks->erase(itr); break; } else if (Block == E_BLOCK_DAYLIGHT_SENSOR) @@ -93,22 +109,24 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B if (a_Chunk->GetTimeAlteredLight(SkyLight) <= 8) // Could use SkyLight - m_World.GetSkyDarkness(); { LOGD("cIncrementalRedstoneSimulator: Erased daylight sensor from powered blocks list due to insufficient light level"); - m_PoweredBlocks.erase(itr); + PoweredBlocks->erase(itr); break; } } } } - for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) + LinkedBlocksList * LinkedPoweredBlocks = a_Chunk->GetRedstoneSimulatorLinkedBlocksList(); + // We loop through all values (insteading of breaking out at the first) to make sure everything is gone, as there can be multiple SourceBlock entries for one AddBlock coordinate + for (LinkedBlocksList::iterator itr = LinkedPoweredBlocks->begin(); itr != LinkedPoweredBlocks->end();) { if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { if (!IsPotentialSource(Block)) { LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); - m_LinkedPoweredBlocks.erase(itr); - break; + itr = LinkedPoweredBlocks->erase(itr); + continue; } else if ( // Things that can send power through a block but which depends on meta @@ -118,8 +136,8 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B ) { 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); - m_LinkedPoweredBlocks.erase(itr); - break; + itr = LinkedPoweredBlocks->erase(itr); + continue; } } else if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) @@ -127,13 +145,15 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B if (!IsViableMiddleBlock(Block)) { LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer powered through a valid middle block", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); - m_LinkedPoweredBlocks.erase(itr); - break; + itr = LinkedPoweredBlocks->erase(itr); + continue; } } + ++itr; } - for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr) + SimulatedPlayerToggleableList * SimulatedPlayerToggleableBlocks = a_Chunk->GetRedstoneSimulatorSimulatedPlayerToggleableList(); + for (SimulatedPlayerToggleableList::iterator itr = SimulatedPlayerToggleableBlocks->begin(); itr != SimulatedPlayerToggleableBlocks->end(); ++itr) { if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { @@ -143,12 +163,13 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B if (!IsAllowedBlock(Block)) { LOGD("cIncrementalRedstoneSimulator: Erased block @ {%i, %i, %i} from toggleable simulated list as it is no longer redstone", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z); - m_SimulatedPlayerToggleableBlocks.erase(itr); + SimulatedPlayerToggleableBlocks->erase(itr); break; } } - for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) + RepeatersDelayList * RepeatersDelayList = a_Chunk->GetRedstoneSimulatorRepeatersDelayList(); + for (RepeatersDelayList::iterator itr = RepeatersDelayList->begin(); itr != RepeatersDelayList->end(); ++itr) { if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { @@ -157,13 +178,19 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B if ((Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF)) { - m_RepeatersDelayList.erase(itr); + RepeatersDelayList->erase(itr); break; } } - cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); - for (cRedstoneSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr) + if (a_OtherChunk != NULL) + { + // DO NOT touch our chunk's data structure if we are being called with coordinates from another chunk - this one caused me massive grief :P + return; + } + + cRedstoneSimulatorChunkData * RedstoneSimulatorChunkData = a_Chunk->GetRedstoneSimulatorData(); + for (cRedstoneSimulatorChunkData::iterator itr = RedstoneSimulatorChunkData->begin(); itr != RedstoneSimulatorChunkData->end(); ++itr) { if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) // We are at an entry matching the current (changed) block { @@ -184,7 +211,7 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B return; } - ChunkData.push_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false)); + RedstoneSimulatorChunkData->push_back(cCoordWithBlockAndBool(RelX, a_BlockY, RelZ, Block, false)); } @@ -198,20 +225,26 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int // The easiest way to make this more efficient is probably just to reduce code within the handlers that put too much strain on server, like getting or setting blocks // A marking dirty system might be a TODO for later on, perhaps - cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); - if (ChunkData.empty()) + m_RedstoneSimulatorChunkData = a_Chunk->GetRedstoneSimulatorData(); + if (m_RedstoneSimulatorChunkData->empty()) { return; } + m_PoweredBlocks = a_Chunk->GetRedstoneSimulatorPoweredBlocksList(); + m_RepeatersDelayList = a_Chunk->GetRedstoneSimulatorRepeatersDelayList(); + m_SimulatedPlayerToggleableBlocks = a_Chunk->GetRedstoneSimulatorSimulatedPlayerToggleableList(); + m_LinkedPoweredBlocks = a_Chunk->GetRedstoneSimulatorLinkedBlocksList(); + m_Chunk = a_Chunk; + int BaseX = a_Chunk->GetPosX() * cChunkDef::Width; int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width; - for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(); dataitr != ChunkData.end();) + for (cRedstoneSimulatorChunkData::iterator dataitr = m_RedstoneSimulatorChunkData->begin(); dataitr != m_RedstoneSimulatorChunkData->end();) { if (dataitr->DataTwo) { - dataitr = ChunkData.erase(dataitr); + dataitr = m_RedstoneSimulatorChunkData->erase(dataitr); continue; } @@ -221,6 +254,7 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int { case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(a_X, dataitr->y, a_Z); break; case E_BLOCK_LEVER: HandleRedstoneLever(a_X, dataitr->y, a_Z); break; + case E_BLOCK_FENCE_GATE: HandleFenceGate(a_X, dataitr->y, a_Z); break; case E_BLOCK_TNT: HandleTNT(a_X, dataitr->y, a_Z); break; case E_BLOCK_TRAPDOOR: HandleTrapdoor(a_X, dataitr->y, a_Z); break; case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(a_X, dataitr->y, a_Z); break; @@ -283,6 +317,7 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int HandlePressurePlate(a_X, dataitr->y, a_Z, dataitr->Data); break; } + default: LOGD("Unhandled block (!) or unimplemented redstone block: %s", ItemToString(dataitr->Data).c_str()); break; } ++dataitr; } @@ -291,6 +326,35 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int +void cIncrementalRedstoneSimulator::WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) +{ + if ( + ((a_BlockX % cChunkDef::Width) <= 1) || + ((a_BlockX % cChunkDef::Width) >= 14) || + ((a_BlockZ % cChunkDef::Width) <= 1) || + ((a_BlockZ % cChunkDef::Width) >= 14) + ) // Are we on a chunk boundary? ± 2 because of LinkedPowered blocks + { + // On a chunk boundary, alert all four sides (i.e. at least one neighbouring chunk) + AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); + + // Pass the original coordinates, because when adding things to our simulator lists, we get the chunk that they are in, and therefore any updates need to preseve their position + // RedstoneAddBlock to pass both the neighbouring chunk and the chunk which the coordiantes are in and ± 2 in GetNeighbour() to accomodate for LinkedPowered blocks being 2 away from chunk boundaries + RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX - 2, a_BlockZ), a_Chunk); + RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX + 2, a_BlockZ), a_Chunk); + RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ - 2), a_Chunk); + RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ + 2), a_Chunk); + + return; + } + + // Not on boundary, just alert this chunk for speed + AddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); +} + + + + void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState) { @@ -402,6 +466,35 @@ void cIncrementalRedstoneSimulator::HandleRedstoneLever(int a_BlockX, int a_Bloc +void cIncrementalRedstoneSimulator::HandleFenceGate(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cChunkInterface ChunkInterface(m_World.GetChunkMap()); + NIBBLETYPE MetaData = ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true)) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, MetaData | 0x4); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); + } + } + else + { + if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, MetaData & 0xFFFFFFFB); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); + } + } +} + + + + + void cIncrementalRedstoneSimulator::HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) { if (IsButtonOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ))) @@ -507,7 +600,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_Block } } - if (TimesMetaSmaller == TimesFoundAWire) + if ((TimesMetaSmaller == TimesFoundAWire) && (MyMeta != 0)) { // All surrounding metas were smaller - self must have been a wire that was // transferring power to other wires around. @@ -516,7 +609,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_Block m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE, 0); // SetMeta & WakeUpSims doesn't seem to work here, so SetBlock return; // No need to process block power sets because self not powered } - else + else if (MyMeta != MetaToSet) { m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, MetaToSet); } @@ -540,12 +633,15 @@ void cIncrementalRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_Block { case REDSTONE_NONE: { - SetAllDirsAsPowered(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + SetBlockPowered(a_BlockX, a_BlockY - 1, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + SetBlockPowered(a_BlockX + 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + SetBlockPowered(a_BlockX - 1, a_BlockY, a_BlockZ, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ + 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); + SetBlockPowered(a_BlockX, a_BlockY, a_BlockZ - 1, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE); SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XM, E_BLOCK_REDSTONE_WIRE); SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_XP, E_BLOCK_REDSTONE_WIRE); SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YM, E_BLOCK_REDSTONE_WIRE); - SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_YP, E_BLOCK_REDSTONE_WIRE); SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZM, E_BLOCK_REDSTONE_WIRE); SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_WIRE); break; @@ -591,14 +687,14 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_B if (IsSelfPowered && !IsOn) // Queue a power change if I am receiving power but not on { - QueueRepeaterPowerChange(a_BlockX, a_BlockY, a_BlockZ, a_Meta, 0, true); + QueueRepeaterPowerChange(a_BlockX, a_BlockY, a_BlockZ, a_Meta, true); } else if (!IsSelfPowered && IsOn) // Queue a power change if I am not receiving power but on { - QueueRepeaterPowerChange(a_BlockX, a_BlockY, a_BlockZ, a_Meta, 0, false); + QueueRepeaterPowerChange(a_BlockX, a_BlockY, a_BlockZ, a_Meta, false); } - for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) + for (RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); ++itr) { if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { @@ -652,7 +748,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_B { m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, a_Meta); } - m_RepeatersDelayList.erase(itr); // We can remove off repeaters which don't need further updating + m_RepeatersDelayList->erase(itr); // We can remove off repeaters which don't need further updating return; } } @@ -737,7 +833,7 @@ void cIncrementalRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_ { if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { - m_World.BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + m_World.BroadcastSoundEffect("game.tnt.primed", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); m_World.SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); } @@ -755,6 +851,7 @@ void cIncrementalRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a { cChunkInterface ChunkInterface(m_World.GetChunkMap()); cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); } } @@ -764,6 +861,7 @@ void cIncrementalRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a { cChunkInterface ChunkInterface(m_World.GetChunkMap()); cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); } } @@ -836,6 +934,7 @@ void cIncrementalRedstoneSimulator::HandleTrapdoor(int a_BlockX, int a_BlockY, i if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true)) { m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x4); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); } } @@ -844,6 +943,7 @@ void cIncrementalRedstoneSimulator::HandleTrapdoor(int a_BlockX, int a_BlockY, i if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) { m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0xB); // Take into account that the fourth bit is needed for trapdoors too + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); } } @@ -960,7 +1060,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_Bloc Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); float Distance = (EntityPos - BlockPos).Length(); - if (Distance < 0.5) + if (Distance <= 0.7) { m_Entity = a_Entity; return true; // Break out, we only need to know for wooden plates that at least one entity is on top @@ -1009,7 +1109,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_Bloc bool cIncrementalRedstoneSimulator::AreCoordsDirectlyPowered(int a_BlockX, int a_BlockY, int a_BlockZ) { - for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) // Check powered list + for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) // Check powered list { if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { @@ -1025,7 +1125,7 @@ bool cIncrementalRedstoneSimulator::AreCoordsDirectlyPowered(int a_BlockX, int a bool cIncrementalRedstoneSimulator::AreCoordsLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ) { - for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) // Check linked powered list + for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) // Check linked powered list { if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { @@ -1043,7 +1143,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_BlockX, int a_BlockY { // Repeaters cannot be powered by any face except their back; verify that this is true for a source - for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) + for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) { if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } @@ -1073,7 +1173,7 @@ bool cIncrementalRedstoneSimulator::IsRepeaterPowered(int a_BlockX, int a_BlockY } } - for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) + for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) { if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } @@ -1114,7 +1214,7 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_BlockX, int a_BlockY, int OldX = a_BlockX, OldY = a_BlockY, OldZ = a_BlockZ; eBlockFace Face = cPiston::MetaDataToDirection(a_Meta); - for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) + for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) { if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } @@ -1130,7 +1230,7 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_BlockX, int a_BlockY, a_BlockZ = OldZ; } - for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) + for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) { if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } @@ -1153,7 +1253,7 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_BlockX, int a_BlockY, bool cIncrementalRedstoneSimulator::IsWirePowered(int a_BlockX, int a_BlockY, int a_BlockZ) { - for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) + for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) { if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } @@ -1163,7 +1263,7 @@ bool cIncrementalRedstoneSimulator::IsWirePowered(int a_BlockX, int a_BlockY, in } } - for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) + for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) { if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; } @@ -1181,7 +1281,7 @@ bool cIncrementalRedstoneSimulator::IsWirePowered(int a_BlockX, int a_BlockY, in bool cIncrementalRedstoneSimulator::AreCoordsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool IsCurrentStatePowered) { - for (SimulatedPlayerToggleableList::const_iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr) + for (SimulatedPlayerToggleableList::const_iterator itr = m_SimulatedPlayerToggleableBlocks->begin(); itr != m_SimulatedPlayerToggleableBlocks->end(); ++itr) { if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { @@ -1324,7 +1424,9 @@ void cIncrementalRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, return; } - for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) // Check powered list + PoweredBlocksList * Powered = m_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ)->GetRedstoneSimulatorPoweredBlocksList(); + + for (PoweredBlocksList::const_iterator itr = Powered->begin(); itr != Powered->end(); ++itr) // Check powered list { if ( itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)) && @@ -1339,7 +1441,7 @@ void cIncrementalRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, sPoweredBlocks RC; RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ); - m_PoweredBlocks.push_back(RC); + Powered->push_back(RC); } @@ -1364,7 +1466,9 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered( return; } - for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) // Check linked powered list + LinkedBlocksList * Linked = m_Chunk->GetNeighborChunk(a_BlockX, a_BlockZ)->GetRedstoneSimulatorLinkedBlocksList(); + + for (LinkedBlocksList::const_iterator itr = Linked->begin(); itr != Linked->end(); ++itr) // Check linked powered list { if ( itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)) && @@ -1381,7 +1485,7 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered( RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); RC.a_MiddlePos = Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ); RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ); - m_LinkedPoweredBlocks.push_back(RC); + Linked->push_back(RC); } @@ -1390,7 +1494,7 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered( void cIncrementalRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool WasLastStatePowered) { - for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr) + for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks->begin(); itr != m_SimulatedPlayerToggleableBlocks->end(); ++itr) { if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { @@ -1414,16 +1518,16 @@ void cIncrementalRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_Bl sSimulatedPlayerToggleableList RC; RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); RC.WasLastStatePowered = WasLastStatePowered; - m_SimulatedPlayerToggleableBlocks.push_back(RC); + m_SimulatedPlayerToggleableBlocks->push_back(RC); } -void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, short a_ElapsedTicks, bool ShouldPowerOn) +void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn) { - for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr) + for (RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end(); ++itr) { if (itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { @@ -1433,7 +1537,7 @@ void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a } // Already in here (normal to allow repeater to continue on powering and updating blocks in front) - just update info and quit - itr->a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + (ShouldPowerOn ? 1 : 0)) * 2; // See below for description + itr->a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; // See below for description itr->a_ElapsedTicks = 0; itr->ShouldPowerOn = ShouldPowerOn; return; @@ -1441,18 +1545,16 @@ void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a } // Self not in list, add self to list - sRepeatersDelayList RC; + sRepeatersDelayList RC; RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); // Gets the top two bits (delay time), shifts them into the lower two bits, and adds one (meta 0 = 1 tick; 1 = 2 etc.) // * 2 because apparently, MCS ticks are way faster than vanilla ticks, so repeater aren't noticeably delayed - // We don't +1 when powering off because everything seems to already delay a tick when powering off, why? No idea :P - RC.a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + (ShouldPowerOn ? 1 : 0)) * 2; - + RC.a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + 1) * 2; RC.a_ElapsedTicks = 0; RC.ShouldPowerOn = ShouldPowerOn; - m_RepeatersDelayList.push_back(RC); + m_RepeatersDelayList->push_back(RC); return; } diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h index 3397e143c..e6bc28621 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.h +++ b/src/Simulator/IncrementalRedstoneSimulator.h @@ -3,7 +3,7 @@ #include "RedstoneSimulator.h" -/// Per-chunk data for the simulator, specified individual chunks to simulate; 'Data' is not used +/// Per-chunk data for the simulator, specified individual chunks to simulate typedef cCoordWithBlockAndBoolVector cRedstoneSimulatorChunkData; @@ -21,7 +21,8 @@ public: virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override; - virtual bool IsAllowedBlock( BLOCKTYPE a_BlockType ) override { return IsRedstone(a_BlockType); } + virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override { return IsRedstone(a_BlockType); } + virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; enum eRedstoneDirection { @@ -57,22 +58,29 @@ private: struct sRepeatersDelayList { Vector3i a_BlockPos; - short a_DelayTicks; - short a_ElapsedTicks; + unsigned char a_DelayTicks; + unsigned char a_ElapsedTicks; bool ShouldPowerOn; }; - + +public: + typedef std::vector <sPoweredBlocks> PoweredBlocksList; typedef std::vector <sLinkedPoweredBlocks> LinkedBlocksList; typedef std::vector <sSimulatedPlayerToggleableList> SimulatedPlayerToggleableList; typedef std::vector <sRepeatersDelayList> RepeatersDelayList; - PoweredBlocksList m_PoweredBlocks; - LinkedBlocksList m_LinkedPoweredBlocks; - SimulatedPlayerToggleableList m_SimulatedPlayerToggleableBlocks; - RepeatersDelayList m_RepeatersDelayList; +private: - virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override; + cRedstoneSimulatorChunkData * m_RedstoneSimulatorChunkData; + PoweredBlocksList * m_PoweredBlocks; + LinkedBlocksList * m_LinkedPoweredBlocks; + SimulatedPlayerToggleableList * m_SimulatedPlayerToggleableBlocks; + RepeatersDelayList * m_RepeatersDelayList; + + virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override { RedstoneAddBlock(a_BlockX, a_BlockY, a_BlockZ, a_Chunk); } + void RedstoneAddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk, cChunk * a_OtherChunk = NULL); + cChunk * m_Chunk; // We want a_MyState for devices needing a full FastSetBlock (as opposed to meta) because with our simulation model, we cannot keep setting the block if it is already set correctly // In addition to being non-performant, it would stop the player from actually breaking said device @@ -116,6 +124,8 @@ private: void HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType); /** Handles trapdoors */ void HandleTrapdoor(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Handles fence gates */ + void HandleFenceGate(int a_BlockX, int a_BlockY, int a_BlockZ); /** Handles noteblocks */ void HandleNoteBlock(int a_BlockX, int a_BlockY, int a_BlockZ); /* ===================== */ @@ -132,7 +142,7 @@ private: /** Marks all blocks immediately surrounding a coordinate as powered */ void SetAllDirsAsPowered(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SourceBlock); /** Queues a repeater to be powered or unpowered */ - void QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, short a_ElapsedTicks, bool ShouldPowerOn); + void QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn); /** Returns if a coordinate is powered or linked powered */ bool AreCoordsPowered(int a_BlockX, int a_BlockY, int a_BlockZ) { return AreCoordsDirectlyPowered(a_BlockX, a_BlockY, a_BlockZ) || AreCoordsLinkedPowered(a_BlockX, a_BlockY, a_BlockZ); } @@ -207,6 +217,10 @@ private: case E_BLOCK_REDSTONE_REPEATER_ON: case E_BLOCK_BLOCK_OF_REDSTONE: case E_BLOCK_ACTIVE_COMPARATOR: + case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_WOODEN_PRESSURE_PLATE: { return true; } @@ -261,3 +275,7 @@ private: } } }; + + + + diff --git a/src/World.cpp b/src/World.cpp index 55c6fbb7a..c1e0731c1 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1171,6 +1171,15 @@ bool cWorld::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCom +bool cWorld::DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback) +{ + return m_ChunkMap->DoWithMobHeadBlockAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + bool cWorld::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4) { return m_ChunkMap->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4); @@ -1493,96 +1502,92 @@ EMCSBiome cWorld::GetBiomeAt (int a_BlockX, int a_BlockZ) -void cWorld::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) +bool cWorld::SetBiomeAt(int a_BlockX, int a_BlockZ, EMCSBiome a_Biome) { - m_ChunkMap->SetBlock(*this, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); + return m_ChunkMap->SetBiomeAt(a_BlockX, a_BlockZ, a_Biome); } -void cWorld::SetBlockMeta(int a_X, int a_Y, int a_Z, NIBBLETYPE a_MetaData) + + +bool cWorld::SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMCSBiome a_Biome) { - m_ChunkMap->SetBlockMeta(a_X, a_Y, a_Z, a_MetaData); + return m_ChunkMap->SetAreaBiome(a_MinX, a_MaxX, a_MinZ, a_MaxZ, a_Biome); } -NIBBLETYPE cWorld::GetBlockSkyLight(int a_X, int a_Y, int a_Z) +bool cWorld::SetAreaBiome(const cCuboid & a_Area, EMCSBiome a_Biome) { - return m_ChunkMap->GetBlockSkyLight(a_X, a_Y, a_Z); + return SetAreaBiome( + std::min(a_Area.p1.x, a_Area.p2.x), std::max(a_Area.p1.x, a_Area.p2.x), + std::min(a_Area.p1.z, a_Area.p2.z), std::max(a_Area.p1.z, a_Area.p2.z), + a_Biome + ); } -NIBBLETYPE cWorld::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) +void cWorld::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - return m_ChunkMap->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ); + m_ChunkMap->SetBlock(*this, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta); } - - -bool cWorld::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) +void cWorld::SetBlockMeta(int a_X, int a_Y, int a_Z, NIBBLETYPE a_MetaData) { - return m_ChunkMap->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, (BLOCKTYPE &)a_BlockType, (NIBBLETYPE &)a_BlockMeta); + m_ChunkMap->SetBlockMeta(a_X, a_Y, a_Z, a_MetaData); } -bool cWorld::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) +NIBBLETYPE cWorld::GetBlockSkyLight(int a_X, int a_Y, int a_Z) { - return m_ChunkMap->GetBlockInfo(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); + return m_ChunkMap->GetBlockSkyLight(a_X, a_Y, a_Z); } -bool cWorld::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) +NIBBLETYPE cWorld::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ) { - return m_ChunkMap->WriteBlockArea(a_Area, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); + return m_ChunkMap->GetBlockBlockLight(a_BlockX, a_BlockY, a_BlockZ); } -cMap * cWorld::GetMapData(unsigned int a_ID) +bool cWorld::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) { - if (a_ID < m_MapData.size()) - { - return &m_MapData[a_ID]; - } - else - { - return NULL; - } + return m_ChunkMap->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, (BLOCKTYPE &)a_BlockType, (NIBBLETYPE &)a_BlockMeta); } -cMap * cWorld::CreateMap(int a_CenterX, int a_CenterY, int a_Scale) +bool cWorld::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight) { - if (m_MapData.size() >= 65536) - { - LOGWARN("Could not craft map - Too many maps in use"); - return NULL; - } + return m_ChunkMap->GetBlockInfo(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Meta, a_SkyLight, a_BlockLight); +} + + - cMap Map(m_MapData.size(), a_CenterX, a_CenterY, this, a_Scale); - m_MapData.push_back(Map); - return &m_MapData[Map.GetID()]; +bool cWorld::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) +{ + return m_ChunkMap->WriteBlockArea(a_Area, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes); } @@ -1788,7 +1793,7 @@ void cWorld::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cons -void cWorld::LoopPlayersAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const cClientHandle * a_Exclude) +void cWorld::BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude, eMessageType a_ChatPrefix) { cCSLock Lock(m_CSPlayers); for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) @@ -1806,6 +1811,24 @@ void cWorld::LoopPlayersAndBroadcastChat(const AString & a_Message, ChatPrefixCo +void cWorld::BroadcastChat(const cCompositeChat & a_Message, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + cClientHandle * ch = (*itr)->GetClientHandle(); + if ((ch == a_Exclude) || (ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed()) + { + continue; + } + ch->SendChat(a_Message); + } +} + + + + + void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) { m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude); @@ -2254,6 +2277,15 @@ void cWorld::UnloadUnusedChunks(void) +void cWorld::QueueUnloadUnusedChunks(void) +{ + QueueTask(new cWorld::cTaskUnloadUnusedChunks); +} + + + + + void cWorld::CollectPickupsByPlayer(cPlayer * a_Player) { m_ChunkMap->CollectPickupsByPlayer(a_Player); @@ -2504,6 +2536,16 @@ void cWorld::SendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +void cWorld::ForceSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + a_Client->AddWantedChunk(a_ChunkX, a_ChunkZ); + m_ChunkSender.QueueSendChunkTo(a_ChunkX, a_ChunkZ, a_Client); +} + + + + + void cWorld::RemoveClientFromChunkSender(cClientHandle * a_Client) { m_ChunkSender.RemoveClient(a_Client); @@ -2987,77 +3029,25 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c -void cWorld::LoadMapData(void) -{ - cIDCountSerializer IDSerializer(GetName()); - - if (!IDSerializer.Load()) - { - return; - } - - unsigned int MapCount = IDSerializer.GetMapCount(); - - m_MapData.clear(); - - for (unsigned int i = 0; i < MapCount; ++i) - { - cMap Map(i, this); - - cMapSerializer Serializer(GetName(), &Map); - - if (!Serializer.Load()) - { - LOGWARN("Could not load map #%i", Map.GetID()); - } - - m_MapData.push_back(Map); - } -} - - - +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld::cTaskSaveAllChunks: -void cWorld::SaveMapData(void) +void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World) { - if (m_MapData.empty()) - { - return; - } - - cIDCountSerializer IDSerializer(GetName()); - - IDSerializer.SetMapCount(m_MapData.size()); - - if (!IDSerializer.Save()) - { - LOGERROR("Could not save idcounts.dat"); - return; - } - - for (cMapList::iterator it = m_MapData.begin(); it != m_MapData.end(); ++it) - { - cMap & Map = *it; - - cMapSerializer Serializer(GetName(), &Map); - - if (!Serializer.Save()) - { - LOGWARN("Could not save map #%i", Map.GetID()); - } - } + a_World.SaveAllChunks(); } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// cWorld::cTaskSaveAllChunks: +// cWorld::cTaskUnloadUnusedChunks -void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World) +void cWorld::cTaskUnloadUnusedChunks::Run(cWorld & a_World) { - a_World.SaveAllChunks(); + a_World.UnloadUnusedChunks(); } diff --git a/src/World.h b/src/World.h index a9b1ca2cb..92fc66c8c 100644 --- a/src/World.h +++ b/src/World.h @@ -46,7 +46,10 @@ class cChestEntity; class cDispenserEntity; class cFurnaceEntity; class cNoteEntity; +class cMobHeadEntity; class cMobCensus; +class cCompositeChat; +class cCuboid; typedef std::list< cPlayer * > cPlayerList; @@ -57,6 +60,7 @@ typedef cItemCallback<cDispenserEntity> cDispenserCallback; typedef cItemCallback<cFurnaceEntity> cFurnaceCallback; typedef cItemCallback<cNoteEntity> cNoteBlockCallback; typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback; +typedef cItemCallback<cMobHeadEntity> cMobHeadBlockCallback; @@ -64,7 +68,10 @@ typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback; // tolua_begin -class cWorld : public cForEachChunkProvider, public cWorldInterface, public cBroadcastInterface +class cWorld : + public cForEachChunkProvider, + public cWorldInterface, + public cBroadcastInterface { public: @@ -100,6 +107,15 @@ public: } ; + class cTaskUnloadUnusedChunks : + public cTask + { + protected: + // cTask overrides: + virtual void Run(cWorld & a_World) override; + }; + + static const char * GetClassStatic(void) // Needed for ManualBindings's ForEach templates { return "cWorld"; @@ -159,16 +175,15 @@ public: void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL); void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); ///< If there is a block entity at the specified coods, sends it to all clients except a_Exclude - void LoopPlayersAndBroadcastChat(const AString & a_Message, ChatPrefixCodes a_ChatPrefix, const cClientHandle * a_Exclude = NULL); - void BroadcastChatDeath (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtDeath, a_Exclude); } - // tolua_begin - void BroadcastChat (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtCustom, a_Exclude); } - void BroadcastChatInfo (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtInformation, a_Exclude); } - void BroadcastChatFailure(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtFailure, a_Exclude); } - void BroadcastChatSuccess(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtSuccess, a_Exclude); } - void BroadcastChatWarning(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtWarning, a_Exclude); } - void BroadcastChatFatal (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { LoopPlayersAndBroadcastChat(a_Message, mtFailure, a_Exclude); } + void BroadcastChat (const AString & a_Message, const cClientHandle * a_Exclude = NULL, eMessageType a_ChatPrefix = mtCustom); + void BroadcastChatInfo (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtInformation); } + void BroadcastChatFailure(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtFailure); } + void BroadcastChatSuccess(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtSuccess); } + void BroadcastChatWarning(const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtWarning); } + void BroadcastChatFatal (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtFailure); } + void BroadcastChatDeath (const AString & a_Message, const cClientHandle * a_Exclude = NULL) { BroadcastChat(a_Message, a_Exclude, mtDeath); } + void BroadcastChat (const cCompositeChat & a_Message, const cClientHandle * a_Exclude = NULL); // tolua_end void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); @@ -184,7 +199,7 @@ public: void BroadcastEntityStatus (const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude = NULL); void BroadcastEntityVelocity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); void BroadcastEntityAnimation (const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = NULL); - void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude = NULL); + void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude = NULL); // tolua_export void BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude = NULL); void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = NULL); void BroadcastScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode); @@ -244,7 +259,8 @@ public: bool IsChunkValid (int a_ChunkX, int a_ChunkZ) const; bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const; - void UnloadUnusedChunks(void); // tolua_export + /** Queues a task to unload unused chunks onto the tick thread. The prefferred way of unloading*/ + void QueueUnloadUnusedChunks(void); // tolua_export void CollectPickupsByPlayer(cPlayer * a_Player); @@ -294,9 +310,14 @@ public: /** Removes the client from all chunks it is present in */ void RemoveClientFromChunks(cClientHandle * a_Client); - /** Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid+lighted) */ + /** Sends the chunk to the client specified, if the client doesn't have the chunk yet. + If chunk not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid + lighted). */ void SendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + /** Sends the chunk to the client specified, even if the client already has the chunk. + If the chunk's not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid + lighted). */ + void ForceSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + /** Removes client from ChunkSender's queue of chunks to be sent */ void RemoveClientFromChunkSender(cClientHandle * a_Client); @@ -392,15 +413,15 @@ public: Prefer cBlockArea::Write() instead, this is the internal implementation; cBlockArea does error checking, too. a_DataTypes is a bitmask of cBlockArea::baXXX constants ORed together. */ - virtual bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes); + virtual bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) override; // tolua_begin /** Spawns item pickups for each item in the list. May compress pickups if too many entities: */ - virtual void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0, bool IsPlayerCreated = false); + virtual void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0, bool IsPlayerCreated = false); // override; cannot specify it here due to tolua /** Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified: */ - virtual void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated = false); + virtual void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated = false); // override; cannot specify it here due to tolua /** Spawns an falling block entity at the given position. It returns the UniqueID of the spawned falling block. */ int SpawnFallingBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE BlockType, NIBBLETYPE BlockMeta); @@ -501,6 +522,9 @@ public: /** Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found */ bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Exported in ManualBindings.cpp + /** Calls the callback for the mob head block at the specified coords; returns false if there's no mob head block at those coords or callback returns true, returns true if found */ + bool DoWithMobHeadBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadBlockCallback & a_Callback); // Exported in ManualBindings.cpp + /** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */ bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Exported in ManualBindings.cpp @@ -537,6 +561,19 @@ public: /** Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, otherwise uses the world generator to provide the biome value */ EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ); + + /** Sets the biome at the specified coords. Returns true if successful, false if not (chunk not loaded). + Doesn't resend the chunk to clients, use ForceSendChunkTo() for that. */ + bool SetBiomeAt(int a_BlockX, int a_BlockZ, EMCSBiome a_Biome); + + /** Sets the biome at the area. Returns true if successful, false if any subarea failed (chunk not loaded). + (Re)sends the chunks to their relevant clients if successful. */ + bool SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMCSBiome a_Biome); + + /** Sets the biome at the area. Returns true if successful, false if any subarea failed (chunk not loaded). + (Re)sends the chunks to their relevant clients if successful. + The cuboid needn't be sorted. */ + bool SetAreaBiome(const cCuboid & a_Area, EMCSBiome a_Biome); /** Returns the name of the world */ const AString & GetName(void) const { return m_WorldName; } @@ -552,12 +589,6 @@ public: bool ShouldUseChatPrefixes(void) const { return m_bUseChatPrefixes; } void SetShouldUseChatPrefixes(bool a_Flag) { m_bUseChatPrefixes = a_Flag; } - - /** Returns the map with the specified ID, NULL if out of range. */ - cMap * GetMapData(unsigned int a_ID); - - /** Creates a new map. Returns NULL on error */ - cMap * CreateMap(int a_CenterX, int a_CenterY, int a_Scale = 3); // tolua_end @@ -818,10 +849,6 @@ private: cChunkGenerator m_Generator; cScoreboard m_Scoreboard; - - typedef std::vector<cMap> cMapList; - - cMapList m_MapData; /** The callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */ cChunkGeneratorCallbacks m_GeneratorCallbacks; @@ -877,6 +904,9 @@ private: /** Ticks all clients that are in this world */ void TickClients(float a_Dt); + /** Unloads all chunks immediately.*/ + void UnloadUnusedChunks(void); + void UpdateSkyDarkness(void); /** <summary>Generates a random spawnpoint on solid land by walking chunks and finding their biomes</summary> */ @@ -887,12 +917,6 @@ private: /** Creates a new redstone simulator.*/ cRedstoneSimulator * InitializeRedstoneSimulator(cIniFile & a_IniFile); - - /** Loads the map data from the disk */ - void LoadMapData(void); - - /** Saves the map data to the disk */ - void SaveMapData(void); }; // tolua_export diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 95b5e66d2..2a1eda523 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -19,6 +19,7 @@ #include "../BlockEntities/JukeboxEntity.h" #include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/SignEntity.h" +#include "../BlockEntities/MobHeadEntity.h" #include "../Entities/Entity.h" #include "../Entities/FallingBlock.h" @@ -248,11 +249,25 @@ void cNBTChunkSerializer::AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign) { m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Sign, "Sign"); - m_Writer.AddString("Text1", a_Sign->GetLine(0)); - m_Writer.AddString("Text2", a_Sign->GetLine(1)); - m_Writer.AddString("Text3", a_Sign->GetLine(2)); - m_Writer.AddString("Text4", a_Sign->GetLine(3)); + AddBasicTileEntity(a_Sign, "Sign"); + m_Writer.AddString("Text1", a_Sign->GetLine(0)); + m_Writer.AddString("Text2", a_Sign->GetLine(1)); + m_Writer.AddString("Text3", a_Sign->GetLine(2)); + m_Writer.AddString("Text4", a_Sign->GetLine(3)); + m_Writer.EndCompound(); +} + + + + + +void cNBTChunkSerializer::AddMobHeadEntity(cMobHeadEntity * a_MobHead) +{ + m_Writer.BeginCompound(""); + AddBasicTileEntity(a_MobHead, "Skull"); + m_Writer.AddByte ("SkullType", a_MobHead->GetType() & 0xFF); + m_Writer.AddByte ("Rot", a_MobHead->GetRotation() & 0xFF); + m_Writer.AddString("ExtraType", a_MobHead->GetOwner()); m_Writer.EndCompound(); } @@ -627,6 +642,8 @@ void cNBTChunkSerializer::Entity(cEntity * a_Entity) case cEntity::etProjectile: AddProjectileEntity ((cProjectileEntity *)a_Entity); break; case cEntity::etTNT: /* TODO */ break; case cEntity::etExpOrb: /* TODO */ break; + case cEntity::etItemFrame: /* TODO */ break; + case cEntity::etPainting: /* TODO */ break; case cEntity::etPlayer: return; // Players aren't saved into the world default: { @@ -666,6 +683,7 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity) case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break; case E_BLOCK_SIGN_POST: case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break; + case E_BLOCK_HEAD: AddMobHeadEntity ((cMobHeadEntity *) a_Entity); break; case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break; case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break; case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity((cCommandBlockEntity *) a_Entity); break; diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h index 245b68063..5f9e16ed1 100644 --- a/src/WorldStorage/NBTChunkSerializer.h +++ b/src/WorldStorage/NBTChunkSerializer.h @@ -29,6 +29,7 @@ class cHopperEntity; class cJukeboxEntity; class cNoteEntity; class cSignEntity; +class cMobHeadEntity; class cFallingBlock; class cMinecart; class cMinecartWithChest; @@ -93,6 +94,7 @@ protected: void AddJukeboxEntity (cJukeboxEntity * a_Jukebox); void AddNoteEntity (cNoteEntity * a_Note); void AddSignEntity (cSignEntity * a_Sign); + void AddMobHeadEntity (cMobHeadEntity * a_MobHead); void AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock); // Entities: diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index e95813a3c..d4490c7fe 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -24,6 +24,7 @@ #include "../BlockEntities/JukeboxEntity.h" #include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/SignEntity.h" +#include "../BlockEntities/MobHeadEntity.h" #include "../Mobs/Monster.h" @@ -597,6 +598,10 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con { LoadSignFromNBT(a_BlockEntities, a_NBT, Child); } + else if (strncmp(a_NBT.GetData(sID), "Skull", a_NBT.GetDataLength(sID)) == 0) + { + LoadMobHeadFromNBT(a_BlockEntities, a_NBT, Child); + } else if (strncmp(a_NBT.GetData(sID), "Trap", a_NBT.GetDataLength(sID)) == 0) { LoadDispenserFromNBT(a_BlockEntities, a_NBT, Child); @@ -927,6 +932,41 @@ void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParse +void cWSSAnvil::LoadMobHeadFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); + int x, y, z; + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) + { + return; + } + std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(x, y, z, m_World)); + + int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType"); + if (currentLine >= 0) + { + MobHead->SetType(static_cast<eMobHeadType>(a_NBT.GetByte(currentLine))); + } + + currentLine = a_NBT.FindChildByName(a_TagIdx, "Rot"); + if (currentLine >= 0) + { + MobHead->SetRotation(static_cast<eMobHeadRotation>(a_NBT.GetByte(currentLine))); + } + + currentLine = a_NBT.FindChildByName(a_TagIdx, "ExtraType"); + if (currentLine >= 0) + { + MobHead->SetOwner(a_NBT.GetString(currentLine)); + } + + a_BlockEntities.push_back(MobHead.release()); +} + + + + + void cWSSAnvil::LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) { ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 5093ad083..541371560 100644 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -139,6 +139,7 @@ protected: void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadMobHeadFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength); |