1
0

Manual merge (Fixed conflicts)

This commit is contained in:
andrew 2014-02-20 15:37:15 +02:00
commit 83d3a2eedf
106 changed files with 4207 additions and 688 deletions

5
.gitignore vendored
View File

@ -22,6 +22,9 @@ cloc.xsl
*.*~ *.*~
*~ *~
*.orig *.orig
## Eclipse
.cproject
.project
# world inside source # world inside source
ChunkWorx.ini ChunkWorx.ini
@ -57,7 +60,7 @@ install_mainfest.txt
src/MCServer src/MCServer
lib/tolua++/tolua lib/tolua++/tolua
src/Bindings/Bindings.* src/Bindings/Bindings.*
src/Bindings/BindingsDependecies.txt src/Bindings/BindingDependecies.txt
MCServer.dir/ MCServer.dir/
#win32 cmake stuff #win32 cmake stuff

View File

@ -3,7 +3,7 @@ compiler:
- gcc - gcc
- clang - clang
# Build MCServer # 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 # Notification Settings
notifications: notifications:

View File

@ -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 ) # 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) enable_language(CXX C)
macro (add_flags_lnk FLAGS) # This has to be done before any flags have been set up.
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAGS}") if(${BUILD_TOOLS})
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} ${FLAGS}") add_subdirectory(Tools/MCADefrag/)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${FLAGS}") add_subdirectory(Tools/ProtoProxy/)
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() endif()
if(MSVC) if(${BUILD_UNSTABLE_TOOLS})
# Make build use multiple threads under MSVC: add_subdirectory(Tools/GeneratorPerformanceTest/)
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()
# 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() 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: # Under Windows, we need Lua as DLL; on *nix we need it linked statically:
if (WIN32) if (WIN32)
@ -109,18 +24,6 @@ if (WIN32)
endif() 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: # The Expat library is linked in statically, make the source files aware of that:
add_definitions(-DXML_STATIC) add_definitions(-DXML_STATIC)
@ -129,64 +32,10 @@ if(${SELF_TEST})
add_definitions(-DSELF_TEST) add_definitions(-DSELF_TEST)
endif() 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) project (MCServer)
# Include all the libraries: # 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 # 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) # (PolarSSL also has test and example programs in their CMakeLists.txt, we don't want those)
add_subdirectory(lib/polarssl/ EXCLUDE_FROM_ALL) include(lib/polarssl.cmake)
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")
endif()
if(${BUILD_TOOLS})
add_subdirectory(Tools/GeneratorPerformanceTest/)
endif()
add_subdirectory (src) add_subdirectory (src)

View File

@ -69,7 +69,7 @@ Assuming you are in the MCServer folder created in the initial setup step, you n
``` ```
mkdir Release mkdir Release
cd 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`. 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 mkdir Debug
cd 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`. The executable will be built in the `MCServer/MCServer` folder and will be named `MCServer_debug`.

1
MCServer/.gitignore vendored
View File

@ -4,6 +4,7 @@
*.lib *.lib
*.ini *.ini
MCServer MCServer
MCServer_debug
CommLogs/ CommLogs/
logs logs
players players

View File

@ -118,7 +118,7 @@ g_APIDesc =
GetRelBlockMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the block meta at the specified relative coords" }, 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" }, 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" }, 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" }, 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" }, 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" }, 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}}" }, 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}}." }, 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" }, 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}}." }, 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}}" }, 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." }, 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}}" }, etPickup = { Notes = "The entity is a {{cPickup}}" },
etProjectile = { Notes = "The entity is a {{cProjectileEntity}} descendant" }, etProjectile = { Notes = "The entity is a {{cProjectileEntity}} descendant" },
etTNT = { Notes = "The entity is a {{cTNTEntity}}" }, etTNT = { Notes = "The entity is a {{cTNTEntity}}" },
etPainting = { Notes = "The entity is a {{cPainting}}" },
}, },
ConstantGroups = 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" }, 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" }, 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" }, 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 = 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_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_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_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 = AdditionalInfo =
{ {
@ -1160,6 +1167,17 @@ local Item5 = cItem(E_ITEM_DIAMOND_CHESTPLATE, 1, 0, "thorns=1;unbreaking=3");
}, },
}, -- cItem }, -- 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 = 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: 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." }, 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" }, 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." }, 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)" }, 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)" }, 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." }, 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" }, 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." }, 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." }, 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." }, 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." }, 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." }, 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." }, 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." }, 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" }, 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" }, 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" }, 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." }, 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." }, 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." }, 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." }, 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." }, 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." }, 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." }, 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." }, 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." }, 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." }, 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" }, 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" }, 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" }, 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." }, 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." }, 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." }, 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." }, 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." }, 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." }, 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 = 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" }, 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." }, 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." }, 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()" }, 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." }, 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." }, WakeUpSimulators = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Wakes up the simulators for the specified block." },

View File

@ -20,13 +20,7 @@
<p> <p>
Let us begin. In order to begin development, we must firstly obtain a compiled copy 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. 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 Core handles much of the MCServer end-user experience and gameplay will be very bland without it.
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.
</p> </p>
<h2>Creating the basic template</h2> <h2>Creating the basic template</h2>
<p> <p>
@ -41,7 +35,11 @@ function Initialize(Plugin)
Plugin:SetName("NewPlugin") Plugin:SetName("NewPlugin")
Plugin:SetVersion(1) 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()) LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
return true return true
@ -58,7 +56,8 @@ end
<li><b>Plugin:SetName</b> sets the name of the plugin.</li> <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>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><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> <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> </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. 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? 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>
<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 There are dedicated functions used for sending a player formatted messages. By format, I refer to coloured prefixes/coloured text (depending on configuration)
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: 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> </p>
<pre class="prettyprint lang-lua"> <pre class="prettyprint lang-lua">
-- Format: §yellow[INFO] §white%text% (yellow [INFO], white text following it) -- Format: §yellow[INFO] §white%text% (yellow [INFO], white text following it)
-- Use: Informational message, such as instructions for usage of a command -- 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.) -- Format: §green[INFO] §white%text% (green [INFO] etc.)
-- Use: Success message, like when a command executes successfully -- 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.) -- 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 -- 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> </pre>
<p> <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 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

View File

@ -122,7 +122,7 @@ function OnTick( DeltaTime )
end end
end end
WW_instance:QueueSaveAllChunks() WW_instance:QueueSaveAllChunks()
WW_instance:UnloadUnusedChunks() WW_instance:QueueUnloadUnusedChunks()
end end
end end
end end

View File

@ -44,7 +44,7 @@ function HandleRequest_Generation( Request )
if (Request.PostParams["AGHRRRR"] ~= nil) then if (Request.PostParams["AGHRRRR"] ~= nil) then
GENERATION_STATE = 0 GENERATION_STATE = 0
WW_instance:SaveAllChunks() WW_instance:SaveAllChunks()
WW_instance:UnloadUnusedChunks() WW_instance:QueueUnloadUnusedChunks()
LOGERROR("" .. PLUGIN:GetName() .. " v" .. PLUGIN:GetVersion() .. ": works ABORTED by admin") LOGERROR("" .. PLUGIN:GetName() .. " v" .. PLUGIN:GetVersion() .. ": works ABORTED by admin")
end end
--Content = Content .. "<head><meta http-equiv=\"refresh\" content=\"2;\"></head>" --Content = Content .. "<head><meta http-equiv=\"refresh\" content=\"2;\"></head>"

@ -1 +1 @@
Subproject commit d6ed2041469ab959bbf3842db41c0e25fd249dc1 Subproject commit 3b416b07a339b3abcbc127070d56eea05b05373d

View File

@ -19,18 +19,18 @@ function Initialize(Plugin)
cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick2); cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick2);
--]] --]]
cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock); local PM = cPluginManager;
cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem); PM:AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock);
cPluginManager:AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage); PM:AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem);
cPluginManager:AddHook(cPluginManager.HOOK_TICK, OnTick); PM:AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage);
cPluginManager:AddHook(cPluginManager.HOOK_CHAT, OnChat); PM:AddHook(cPluginManager.HOOK_TICK, OnTick);
cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity); PM:AddHook(cPluginManager.HOOK_CHAT, OnChat);
cPluginManager:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick); PM:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity);
cPluginManager:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); PM:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick);
cPluginManager:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded); PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated);
cPluginManager:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); PM:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded);
PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage);
PM = cRoot:Get():GetPluginManager();
PM:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities"); 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("/ke", "debuggers", HandleKillEntitiesCmd, "- Kills all the loaded entities");
PM:BindCommand("/wool", "debuggers", HandleWoolCmd, "- Sets all your armor to blue wool"); 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("/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("/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("/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: -- Enable the following line for BlockArea / Generator interface testing:
-- PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATED); -- 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) function OnPluginMessage(a_Client, a_Channel, a_Message)
LOGINFO("Received a plugin message from client " .. a_Client:GetUsername() .. ": channel '" .. a_Channel .. "', message '" .. 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

View File

@ -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

189
SetFlags.cmake Normal file
View File

@ -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()

1
Tools/MCADefrag/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.mca

View File

@ -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)

View File

@ -0,0 +1,10 @@
// Globals.cpp
// This file is used for precompiled header generation in MSVC environments
#include "Globals.h"

229
Tools/MCADefrag/Globals.h Normal file
View File

@ -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;
} ;

View File

@ -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;
}

144
Tools/MCADefrag/MCADefrag.h Normal file
View File

@ -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);
} ;

View File

@ -1,6 +1,7 @@
Debug Debug
Release Release
Logs/ Logs/
lib/
*.log *.log
*.nbt *.nbt
*.sln *.sln

View File

@ -3,62 +3,10 @@ cmake_minimum_required (VERSION 2.6)
project (ProtoProxy) project (ProtoProxy)
include(../../SetFlags.cmake)
set_flags()
macro(add_flags_cxx FLAGS) set_lib_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 include paths to the used libraries: # Set include paths to the used libraries:
@ -77,20 +25,10 @@ function(flatten_files arg1)
set(${arg1} "${res}" PARENT_SCOPE) set(${arg1} "${res}" PARENT_SCOPE)
endfunction() endfunction()
include(../../lib/polarssl.cmake)
add_subdirectory(../../lib/zlib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/lib/zlib)
# Include the libraries: set_exe_flags()
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})
# Include the shared files: # Include the shared files:
set(SHARED_SRC set(SHARED_SRC
@ -149,9 +87,7 @@ add_executable(ProtoProxy
${SHARED_HDR} ${SHARED_HDR}
${SHARED_OSS_SRC} ${SHARED_OSS_SRC}
${SHARED_OSS_HDR} ${SHARED_OSS_HDR}
${POLARSSL_SRC}
${POLARSSL_HDR}
${ZLIB_SRC}
${ZLIB_HDR}
) )
target_link_libraries(ProtoProxy zlib polarssl)

View File

@ -83,6 +83,8 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect)
} }
} }
bool IsFirstLine = true;
while (getline(f, line)) while (getline(f, line))
{ {
// To be compatible with Win32, check for existence of '\r'. // 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 // Note that the '\r' will be written to INI files from
// Unix so that the created INI file can be read under Win32 // Unix so that the created INI file can be read under Win32
// without change. // without change.
// Removes UTF-8 Byte Order Markers (BOM) if, present.
if (IsFirstLine)
{
RemoveBom(line);
IsFirstLine = false;
}
size_t lineLength = line.length(); size_t lineLength = line.length();
if (lineLength == 0) if (lineLength == 0)
{ {
@ -162,11 +172,12 @@ bool cIniFile::ReadFile(const AString & a_FileName, bool a_AllowExampleRedirect)
{ {
return false; return false;
} }
if (IsFromExampleRedirect) if (IsFromExampleRedirect)
{ {
WriteFile(FILE_IO_PREFIX + a_FileName); WriteFile(FILE_IO_PREFIX + a_FileName);
} }
return true; 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);
}

View File

@ -51,6 +51,8 @@ private:
/// If the object is case-insensitive, returns s as lowercase; otherwise returns s as-is /// If the object is case-insensitive, returns s as lowercase; otherwise returns s as-is
AString CheckCase(const AString & s) const; AString CheckCase(const AString & s) const;
/// Removes the UTF-8 BOMs (Byte order makers), if present.
void RemoveBom(AString & a_line) const;
public: public:
enum errors enum errors
{ {

@ -1 +1 @@
Subproject commit 2cb1a0c4009ecf368ecc74eb428394e10f9e6d00 Subproject commit 2ceda579893ceb23c5eb0d56df47dc235644e0f4

5
lib/polarssl.cmake Normal file
View File

@ -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()

View File

@ -19,7 +19,7 @@ add_library(tolualib ${LIB_SOURCE})
#m is the standard math librarys #m is the standard math librarys
if(UNIX) if(UNIX)
target_link_libraries(tolua m) target_link_libraries(tolua m ${DYNAMIC_LOADER})
endif() endif()
target_link_libraries(tolua lua tolualib) target_link_libraries(tolua lua tolualib)

View File

@ -8,12 +8,14 @@ file(GLOB SOURCE
"*.c" "*.c"
) )
add_library(zlib ${SOURCE}) if(NOT TARGET zlib)
add_library(zlib ${SOURCE})
if (MSVC) if (MSVC)
# Remove SCL warnings, we expect this library to have been tested safe # Remove SCL warnings, we expect this library to have been tested safe
SET_TARGET_PROPERTIES( SET_TARGET_PROPERTIES(
zlib PROPERTIES COMPILE_FLAGS "-D_CRT_SECURE_NO_WARNINGS" zlib PROPERTIES COMPILE_FLAGS "-D_CRT_SECURE_NO_WARNINGS"
) )
endif()
endif() endif()

View File

@ -34,6 +34,7 @@ $cfile "../Entities/Entity.h"
$cfile "../Entities/Floater.h" $cfile "../Entities/Floater.h"
$cfile "../Entities/Pawn.h" $cfile "../Entities/Pawn.h"
$cfile "../Entities/Player.h" $cfile "../Entities/Player.h"
$cfile "../Entities/Painting.h"
$cfile "../Entities/Pickup.h" $cfile "../Entities/Pickup.h"
$cfile "../Entities/ProjectileEntity.h" $cfile "../Entities/ProjectileEntity.h"
$cfile "../Entities/TNTEntity.h" $cfile "../Entities/TNTEntity.h"
@ -71,6 +72,7 @@ $cfile "../Generating/ChunkDesc.h"
$cfile "../CraftingRecipes.h" $cfile "../CraftingRecipes.h"
$cfile "../UI/Window.h" $cfile "../UI/Window.h"
$cfile "../Mobs/Monster.h" $cfile "../Mobs/Monster.h"
$cfile "../CompositeChat.h"

View File

@ -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) bool cLuaState::LoadFile(const AString & a_FileName)
{ {
ASSERT(IsValid()); 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_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_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_TSTRING: Printf(Value, "%s", lua_tostring(a_LuaState, i)); break;
case LUA_TTABLE: Printf(Value, "%p", lua_topointer(a_LuaState, i)); break;
default: break; default: break;
} }
LOGD(" Idx %d: type %d (%s) %s", i, Type, lua_typename(a_LuaState, Type), Value.c_str()); LOGD(" Idx %d: type %d (%s) %s", i, Type, lua_typename(a_LuaState, Type), Value.c_str());

View File

@ -154,6 +154,9 @@ public:
/** Returns true if the m_LuaState is valid */ /** Returns true if the m_LuaState is valid */
bool IsValid(void) const { return (m_LuaState != NULL); } 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 /** Loads the specified file
Returns false and logs a warning to the console if not successful (but the LuaState is kept open). 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. m_SubsystemName is displayed in the warning log message.

View File

@ -22,6 +22,7 @@
#include "../BlockEntities/FurnaceEntity.h" #include "../BlockEntities/FurnaceEntity.h"
#include "../BlockEntities/HopperEntity.h" #include "../BlockEntities/HopperEntity.h"
#include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
#include "md5/md5.h" #include "md5/md5.h"
#include "../LineBlockTracer.h" #include "../LineBlockTracer.h"
#include "../WorldStorage/SchematicFileSerializer.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); 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, ""); const char * ItemName = tolua_tocppstring(tolua_S, 2, "");
if ((ItemName == NULL) || (ItemName[0] == 0)) 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); 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); int ItemID = (int)tolua_tonumber(tolua_S, 2, 0);
if (!lua_isfunction(tolua_S, 3)) 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); 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)) 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"); 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); 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)) 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"); 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); 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) if (self == NULL)
{ {
return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); 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 else
#endif #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 BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
@ -736,7 +737,7 @@ static int tolua_cWorld_GetBlockTypeMeta(lua_State * tolua_S)
else else
#endif #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 BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
@ -788,7 +789,7 @@ static int tolua_cWorld_GetSignLines(lua_State * tolua_S)
else else
#endif #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 BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
@ -846,7 +847,7 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
else else
#endif #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 BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
int BlockY = (int) tolua_tonumber (tolua_S, 3, 0); int BlockY = (int) tolua_tonumber (tolua_S, 3, 0);
int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0); int BlockZ = (int) tolua_tonumber (tolua_S, 4, 0);
@ -895,7 +896,7 @@ static int tolua_cWorld_TryGetHeight(lua_State * tolua_S)
else else
#endif #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 BlockX = (int) tolua_tonumber (tolua_S, 2, 0);
int BlockZ = (int) tolua_tonumber (tolua_S, 3, 0); int BlockZ = (int) tolua_tonumber (tolua_S, 3, 0);
#ifndef TOLUA_RELEASE #ifndef TOLUA_RELEASE
@ -967,7 +968,7 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S)
} }
// Retrieve the args: // Retrieve the args:
cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, 0); cWorld * self = (cWorld *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL) if (self == NULL)
{ {
return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); 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) 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(); const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins();
@ -1289,7 +1290,7 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S)
return 0; return 0;
} }
cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL) if (self == NULL)
{ {
LOGWARN("Error in function call 'ForEachCommand': Not called on an object instance"); 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; return 0;
} }
cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL) if (self == NULL)
{ {
LOGWARN("Error in function call 'ForEachConsoleCommand': Not called on an object instance"); 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) 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(); 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) 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(); 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) 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_Error tolua_err;
tolua_err.array = 0; 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) 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\")", LOGWARN("WARNING: Using deprecated function AddTab()! Use AddWebTab() instead. (plugin \"%s\" in folder \"%s\")",
self->GetName().c_str(), self->GetDirectory().c_str() self->GetName().c_str(), self->GetDirectory().c_str()
); );
@ -1888,7 +1889,7 @@ static int tolua_cPlugin_Call(lua_State * tolua_S)
L.LogStackTrace(); L.LogStackTrace();
// Retrieve the params: plugin and the function name to call // 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, ""); AString FunctionName = tolua_tostring(tolua_S, 2, "");
// Call the function: // 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) 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); 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) 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); 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) 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; std::map< std::string, HTTPFormData >& FormData = self->FormData;
lua_newtable(tolua_S); 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) 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(); 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) 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(); 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); int SlotNum = (int)tolua_tonumber(L, 2, 0);
if (self == NULL) if (self == NULL)
{ {
@ -2288,7 +2289,7 @@ static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S)
{ {
return 0; return 0;
} }
cHopperEntity * self = (cHopperEntity *)tolua_tousertype(tolua_S, 1, 0); cHopperEntity * self = (cHopperEntity *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL) if (self == NULL)
{ {
tolua_error(tolua_S, "invalid 'self' in function 'cHopperEntity::GetOutputBlockPos()'", 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) static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S)
{ {
// function cBlockArea::LoadFromSchematicFile // function cBlockArea::LoadFromSchematicFile
@ -2327,7 +2398,7 @@ static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S)
{ {
return 0; return 0;
} }
cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, 0); cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL) if (self == NULL)
{ {
tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::LoadFromSchematicFile'", 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) static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S)
{ {
// function cBlockArea::SaveToSchematicFile // function cBlockArea::SaveToSchematicFile
@ -2356,7 +2428,7 @@ static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S)
{ {
return 0; return 0;
} }
cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, 0); cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, NULL);
if (self == NULL) if (self == NULL)
{ {
tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::SaveToSchematicFile'", 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) void ManualBindings::Bind(lua_State * tolua_S)
{ {
tolua_beginmodule(tolua_S, NULL); tolua_beginmodule(tolua_S, NULL);
@ -2386,8 +2460,10 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S); tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cBlockArea"); 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, "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_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cHopperEntity"); 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, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
tolua_function(tolua_S, "DoWithNoteBlockAt", tolua_DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>); 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, "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, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>); tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>); tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);

View File

@ -82,6 +82,14 @@ bool cPluginLua::Initialize(void)
lua_pushstring(m_LuaState, GetName().c_str()); lua_pushstring(m_LuaState, GetName().c_str());
lua_setglobal(m_LuaState, LUA_PLUGIN_NAME_VAR_NAME); 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"); tolua_pushusertype(m_LuaState, this, "cPluginLua");
lua_setglobal(m_LuaState, "g_Plugin"); lua_setglobal(m_LuaState, "g_Plugin");
} }

View File

@ -56,7 +56,7 @@ void cPluginManager::ReloadPlugins(void)
void cPluginManager::FindPlugins(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 // 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();) for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();)

View File

@ -266,6 +266,10 @@ public: // tolua_export
Returns false if plugin not found, and the value that the callback has returned otherwise. */ Returns false if plugin not found, and the value that the callback has returned otherwise. */
bool DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback); 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: private:
friend class cRoot; friend class cRoot;

View File

@ -184,9 +184,15 @@ public:
void SetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); 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); 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 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; 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 GetSizeX(void) const { return m_SizeX; }
int GetSizeY(void) const { return m_SizeY; } int GetSizeY(void) const { return m_SizeY; }
int GetSizeZ(void) const { return m_SizeZ; } int GetSizeZ(void) const { return m_SizeZ; }

View File

@ -15,6 +15,7 @@
#include "JukeboxEntity.h" #include "JukeboxEntity.h"
#include "NoteEntity.h" #include "NoteEntity.h"
#include "SignEntity.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_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_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_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_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_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); case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);

View File

@ -11,6 +11,7 @@
#include "BlockEntity.h" #include "BlockEntity.h"
#include "../ItemGrid.h" #include "../ItemGrid.h"
#include "../UI/WindowOwner.h"
@ -22,6 +23,7 @@ class cBlockEntityWithItems :
// tolua_end // tolua_end
// tolua doesn't seem to support multiple inheritance? // tolua doesn't seem to support multiple inheritance?
, public cItemGrid::cListener , public cItemGrid::cListener
, public cBlockEntityWindowOwner
// tolua_begin // tolua_begin
{ {
typedef cBlockEntity super; typedef cBlockEntity super;
@ -77,6 +79,11 @@ protected:
ASSERT(a_Grid == &m_Contents); ASSERT(a_Grid == &m_Contents);
if (m_World != NULL) if (m_World != NULL)
{ {
if (GetWindow() != NULL)
{
GetWindow()->BroadcastWholeWindow();
}
m_World->MarkChunkDirty(GetChunkX(), GetChunkZ()); m_World->MarkChunkDirty(GetChunkX(), GetChunkZ());
} }
} }

View File

@ -2,7 +2,6 @@
#pragma once #pragma once
#include "BlockEntityWithItems.h" #include "BlockEntityWithItems.h"
#include "../UI/WindowOwner.h"
@ -23,8 +22,7 @@ class cNBTData;
// tolua_begin // tolua_begin
class cChestEntity : class cChestEntity :
public cBlockEntityWithItems, public cBlockEntityWithItems
public cBlockEntityWindowOwner
{ {
typedef cBlockEntityWithItems super; typedef cBlockEntityWithItems super;

View File

@ -99,13 +99,6 @@ void cDropSpenserEntity::DropSpense(cChunk & a_Chunk)
} }
m_World->BroadcastSoundParticleEffect(2000, m_PosX, m_PosY, m_PosZ, SmokeDir); 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); 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();
}
} }

View File

@ -11,7 +11,6 @@
#pragma once #pragma once
#include "BlockEntityWithItems.h" #include "BlockEntityWithItems.h"
#include "../UI/WindowOwner.h"
@ -31,8 +30,7 @@ class cServer;
// tolua_begin // tolua_begin
class cDropSpenserEntity : class cDropSpenserEntity :
public cBlockEntityWithItems, public cBlockEntityWithItems
public cBlockEntityWindowOwner
{ {
typedef cBlockEntityWithItems super; typedef cBlockEntityWithItems super;

View File

@ -2,7 +2,6 @@
#pragma once #pragma once
#include "BlockEntityWithItems.h" #include "BlockEntityWithItems.h"
#include "../UI/WindowOwner.h"
@ -23,8 +22,7 @@ class cNBTData;
// tolua_begin // tolua_begin
class cEnderChestEntity : class cEnderChestEntity :
public cBlockEntityWithItems, public cBlockEntityWithItems
public cBlockEntityWindowOwner
{ {
typedef cBlockEntityWithItems super; typedef cBlockEntityWithItems super;

View File

@ -2,7 +2,6 @@
#pragma once #pragma once
#include "BlockEntityWithItems.h" #include "BlockEntityWithItems.h"
#include "../UI/WindowOwner.h"
#include "../FurnaceRecipe.h" #include "../FurnaceRecipe.h"
@ -23,8 +22,7 @@ class cServer;
// tolua_begin // tolua_begin
class cFurnaceEntity : class cFurnaceEntity :
public cBlockEntityWithItems, public cBlockEntityWithItems
public cBlockEntityWindowOwner
{ {
typedef cBlockEntityWithItems super; typedef cBlockEntityWithItems super;

View File

@ -7,10 +7,12 @@
#include "HopperEntity.h" #include "HopperEntity.h"
#include "../Chunk.h" #include "../Chunk.h"
#include "../Entities/Player.h" #include "../Entities/Player.h"
#include "../Entities/Pickup.h"
#include "../Bindings/PluginManager.h" #include "../Bindings/PluginManager.h"
#include "ChestEntity.h" #include "ChestEntity.h"
#include "DropSpenserEntity.h" #include "DropSpenserEntity.h"
#include "FurnaceEntity.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. /// Moves pickups from above this hopper into it. Returns true if the contents have changed.
bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick)
{ {
// TODO UNUSED(a_CurrentTick);
return false;
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();
} }

View File

@ -10,7 +10,6 @@
#pragma once #pragma once
#include "BlockEntityWithItems.h" #include "BlockEntityWithItems.h"
#include "../UI/WindowOwner.h"
@ -18,8 +17,7 @@
// tolua_begin // tolua_begin
class cHopperEntity : class cHopperEntity :
public cBlockEntityWithItems, public cBlockEntityWithItems
public cBlockEntityWindowOwner
{ {
typedef cBlockEntityWithItems super; typedef cBlockEntityWithItems super;

View File

@ -43,6 +43,8 @@ public:
void EjectRecord(void); void EjectRecord(void);
// tolua_end // tolua_end
static const char * GetClassStatic(void) { return "cJukeboxEntity"; }
virtual void UsedBy(cPlayer * a_Player) override; virtual void UsedBy(cPlayer * a_Player) override;
virtual void SendTo(cClientHandle &) override { }; virtual void SendTo(cClientHandle &) override { };

View File

@ -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;
}

View File

@ -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

View File

@ -54,6 +54,8 @@ public:
virtual void UsedBy(cPlayer * a_Player) override; virtual void UsedBy(cPlayer * a_Player) override;
virtual void SendTo(cClientHandle &) override { }; virtual void SendTo(cClientHandle &) override { };
static const char * GetClassStatic(void) { return "cNoteEntity"; }
private: private:
char m_Pitch; char m_Pitch;
} ; // tolua_export } ; // tolua_export

View File

@ -1,4 +1,3 @@
// SignEntity.h // SignEntity.h
// Declares the cSignEntity class representing a single sign in the world // 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 UsedBy(cPlayer * a_Player) override;
virtual void SendTo(cClientHandle & a_Client) override; virtual void SendTo(cClientHandle & a_Client) override;
static const char * GetClassStatic(void) { return "cSignEntity"; }
private: private:

View File

@ -266,7 +266,7 @@ enum ENUM_ITEM_ID
E_ITEM_FLINT = 318, E_ITEM_FLINT = 318,
E_ITEM_RAW_PORKCHOP = 319, E_ITEM_RAW_PORKCHOP = 319,
E_ITEM_COOKED_PORKCHOP = 320, E_ITEM_COOKED_PORKCHOP = 320,
E_ITEM_PAINTINGS = 321, E_ITEM_PAINTING = 321,
E_ITEM_GOLDEN_APPLE = 322, E_ITEM_GOLDEN_APPLE = 322,
E_ITEM_SIGN = 323, E_ITEM_SIGN = 323,
E_ITEM_WOODEN_DOOR = 324, E_ITEM_WOODEN_DOOR = 324,

View File

@ -34,6 +34,7 @@
#include "BlockGlass.h" #include "BlockGlass.h"
#include "BlockGlowstone.h" #include "BlockGlowstone.h"
#include "BlockGravel.h" #include "BlockGravel.h"
#include "BlockMobHead.h"
#include "BlockHopper.h" #include "BlockHopper.h"
#include "BlockIce.h" #include "BlockIce.h"
#include "BlockLadder.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_GRASS: return new cBlockDirtHandler (a_BlockType);
case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType); case E_BLOCK_GRAVEL: return new cBlockGravelHandler (a_BlockType);
case E_BLOCK_HAY_BALE: return new cBlockSidewaysHandler (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_HOPPER: return new cBlockHopperHandler (a_BlockType);
case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType); case E_BLOCK_ICE: return new cBlockIceHandler (a_BlockType);
case E_BLOCK_INACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType); case E_BLOCK_INACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType);

69
src/Blocks/BlockMobHead.h Normal file
View File

@ -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);
}
} ;

View File

@ -12,25 +12,25 @@
#if SELF_TEST #if SELF_TEST
/// A simple self-test that is executed on program start, used to verify bbox functionality /** A simple self-test that is executed on program start, used to verify bbox functionality */
class SelfTest static class SelfTest_BoundingBox
{ {
public: public:
SelfTest(void) SelfTest_BoundingBox(void)
{ {
Vector3d Min(1, 1, 1); Vector3d Min(1, 1, 1);
Vector3d Max(2, 2, 2); Vector3d Max(2, 2, 2);
Vector3d LineDefs[] = 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(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, 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(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(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 Vector3d(2.001, 0, 1.5), Vector3d(2.001, 4, 1.5), // Should not intersect
} ; } ;
bool Results[] = {true,true,true,false,true,false}; bool Results[] = {true, true, true, false, true, false};
double LineCoeffs[] = {2,0.25,0.5,0,0.25,0}; double LineCoeffs[] = {2, 0.25, 0.5, 0, 0.25, 0};
for (size_t i = 0; i < ARRAYCOUNT(LineDefs) / 2; i++) 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); bool res = cBoundingBox::CalcLineIntersection(Min, Max, Line1, Line2, LineCoeff, Face);
if (res != Results[i]) 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, Line1.x, Line1.y, Line1.z,
Line2.x, Line2.y, Line2.z, Line2.x, Line2.y, Line2.z,
res ? 1 : 0, LineCoeff, Face res ? 1 : 0, LineCoeff, Face
@ -52,7 +52,7 @@ public:
{ {
if (LineCoeff != LineCoeffs[i]) 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, Line1.x, Line1.y, Line1.z,
Line2.x, Line2.y, Line2.z, Line2.x, Line2.y, Line2.z,
res ? 1 : 0, LineCoeff, Face res ? 1 : 0, LineCoeff, Face
@ -61,9 +61,9 @@ public:
} }
} }
} // for i - LineDefs[] } // for i - LineDefs[]
fprintf(stderr,"BoundingBox selftest complete."); fprintf(stderr, "BoundingBox selftest complete.\n");
} }
} Test; } gTest;
#endif #endif

View File

@ -39,6 +39,7 @@ if (NOT MSVC)
BlockEntities/JukeboxEntity.h BlockEntities/JukeboxEntity.h
BlockEntities/NoteEntity.h BlockEntities/NoteEntity.h
BlockEntities/SignEntity.h BlockEntities/SignEntity.h
BlockEntities/MobHeadEntity.h
BlockID.h BlockID.h
BoundingBox.h BoundingBox.h
ChatColor.h ChatColor.h
@ -52,6 +53,7 @@ if (NOT MSVC)
Entities/Entity.h Entities/Entity.h
Entities/Floater.h Entities/Floater.h
Entities/Pawn.h Entities/Pawn.h
Entities/Painting.h
Entities/Pickup.h Entities/Pickup.h
Entities/Player.h Entities/Player.h
Entities/ProjectileEntity.h Entities/ProjectileEntity.h

View File

@ -19,6 +19,7 @@
#include "BlockEntities/JukeboxEntity.h" #include "BlockEntities/JukeboxEntity.h"
#include "BlockEntities/NoteEntity.h" #include "BlockEntities/NoteEntity.h"
#include "BlockEntities/SignEntity.h" #include "BlockEntities/SignEntity.h"
#include "BlockEntities/MobHeadEntity.h"
#include "Entities/Pickup.h" #include "Entities/Pickup.h"
#include "Item.h" #include "Item.h"
#include "Noise.h" #include "Noise.h"
@ -1314,6 +1315,7 @@ void cChunk::CreateBlockEntities(void)
case E_BLOCK_HOPPER: case E_BLOCK_HOPPER:
case E_BLOCK_SIGN_POST: case E_BLOCK_SIGN_POST:
case E_BLOCK_WALLSIGN: case E_BLOCK_WALLSIGN:
case E_BLOCK_HEAD:
case E_BLOCK_NOTE_BLOCK: case E_BLOCK_NOTE_BLOCK:
case E_BLOCK_JUKEBOX: 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_HOPPER:
case E_BLOCK_SIGN_POST: case E_BLOCK_SIGN_POST:
case E_BLOCK_WALLSIGN: case E_BLOCK_WALLSIGN:
case E_BLOCK_HEAD:
case E_BLOCK_NOTE_BLOCK: case E_BLOCK_NOTE_BLOCK:
case E_BLOCK_JUKEBOX: 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) void cChunk::CollectPickupsByPlayer(cPlayer * a_Player)
{ {
double PosX = a_Player->GetPosX(); 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) 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 // The blockentity list is locked by the parent chunkmap's CS

View File

@ -31,6 +31,7 @@ class cChestEntity;
class cDispenserEntity; class cDispenserEntity;
class cFurnaceEntity; class cFurnaceEntity;
class cNoteEntity; class cNoteEntity;
class cMobHeadEntity;
class cBlockArea; class cBlockArea;
class cPawn; class cPawn;
class cPickup; class cPickup;
@ -47,6 +48,7 @@ typedef cItemCallback<cDispenserEntity> cDispenserCallback;
typedef cItemCallback<cFurnaceEntity> cFurnaceCallback; typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
typedef cItemCallback<cNoteEntity> cNoteBlockCallback; typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback; 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); } 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); void CollectPickupsByPlayer(cPlayer * a_Player);
/** Sets the sign text. Returns true if successful. Also sends update packets to all clients in the chunk */ /** 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); 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 */ /** 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 */ /** 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 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; NIBBLETYPE GetTimeAlteredLight(NIBBLETYPE a_Skylight) const;
// Simulator data: // Per-chunk simulator data:
cFireSimulatorChunkData & GetFireSimulatorData (void) { return m_FireSimulatorData; } cFireSimulatorChunkData & GetFireSimulatorData (void) { return m_FireSimulatorData; }
cFluidSimulatorData * GetWaterSimulatorData(void) { return m_WaterSimulatorData; } cFluidSimulatorData * GetWaterSimulatorData(void) { return m_WaterSimulatorData; }
cFluidSimulatorData * GetLavaSimulatorData (void) { return m_LavaSimulatorData; } cFluidSimulatorData * GetLavaSimulatorData (void) { return m_LavaSimulatorData; }
cSandSimulatorChunkData & GetSandSimulatorData (void) { return m_SandSimulatorData; } 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(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); } 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_WaterSimulatorData;
cFluidSimulatorData * m_LavaSimulatorData; cFluidSimulatorData * m_LavaSimulatorData;
cSandSimulatorChunkData m_SandSimulatorData; cSandSimulatorChunkData m_SandSimulatorData;
cRedstoneSimulatorChunkData m_RedstoneSimulatorData; 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 // pick up a random block of this chunk

View File

@ -1390,10 +1390,10 @@ void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks)
EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ) EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ)
{ {
int ChunkX, ChunkZ, X = a_BlockX, Y = 0, Z = 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); 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()) if ((Chunk != NULL) && Chunk->IsValid())
{ {
return Chunk->GetBiomeAt(X, Z); 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 cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
{ {
bool res = true; 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) 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; int ChunkX, ChunkZ;

View File

@ -25,6 +25,7 @@ class cDropSpenserEntity;
class cFurnaceEntity; class cFurnaceEntity;
class cNoteEntity; class cNoteEntity;
class cCommandBlockEntity; class cCommandBlockEntity;
class cMobHeadEntity;
class cPawn; class cPawn;
class cPickup; class cPickup;
class cChunkDataSerializer; class cChunkDataSerializer;
@ -43,6 +44,7 @@ typedef cItemCallback<cDropSpenserEntity> cDropSpenserCallback;
typedef cItemCallback<cFurnaceEntity> cFurnaceCallback; typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
typedef cItemCallback<cNoteEntity> cNoteBlockCallback; typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback; typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
typedef cItemCallback<cMobHeadEntity> cMobHeadBlockCallback;
typedef cItemCallback<cChunk> cChunkCallback; typedef cItemCallback<cChunk> cChunkCallback;
@ -159,8 +161,17 @@ public:
/** Special function used for growing trees, replaces only blocks that tree may overwrite */ /** Special function used for growing trees, replaces only blocks that tree may overwrite */
void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks); 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); 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. */ /** 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); 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 */ /** 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 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 */ /** 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 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

View File

@ -31,6 +31,7 @@
#include "MersenneTwister.h" #include "MersenneTwister.h"
#include "Protocol/ProtocolRecognizer.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_ShouldCheckDownloaded(false),
m_NumExplosionsThisTick(0), m_NumExplosionsThisTick(0),
m_UniqueID(0), m_UniqueID(0),
m_Locale("en_GB"),
m_HasSentPlayerChunk(false) m_HasSentPlayerChunk(false)
{ {
m_Protocol = new cProtocolRecognizer(this); m_Protocol = new cProtocolRecognizer(this);
@ -1087,14 +1089,20 @@ void cClientHandle::HandleChat(const AString & a_Message)
return; return;
} }
// Not a command, broadcast as a simple message: // Not a command, broadcast as a message:
AString Msg; cCompositeChat Msg;
Printf(Msg, "%s<%s>%s %s", AString Color = m_Player->GetColor();
m_Player->GetColor().c_str(), if (Color.length() == 3)
m_Player->GetName().c_str(), {
cChatColor::White.c_str(), Color = AString("@") + Color[2];
Message.c_str() }
); else
{
Color.empty();
}
Msg.AddTextPart(AString("<") + m_Player->GetName() + "> ", Color);
Msg.ParseText(a_Message);
Msg.UnderlineUrls();
m_Player->GetWorld()->BroadcastChat(Msg); 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; 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) void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{ {
ASSERT(m_Player != NULL); 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) void cClientHandle::SendEntityAnimation(const cEntity & a_Entity, char a_Animation)
{ {

View File

@ -28,6 +28,7 @@ class cInventory;
class cMonster; class cMonster;
class cPawn; class cPawn;
class cExpOrb; class cExpOrb;
class cPainting;
class cPickup; class cPickup;
class cPlayer; class cPlayer;
class cProtocol; class cProtocol;
@ -35,6 +36,7 @@ class cWindow;
class cFallingBlock; class cFallingBlock;
class cItemHandler; class cItemHandler;
class cWorld; 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 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 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 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 SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer);
void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player); void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player);
void SendDestroyEntity (const cEntity & a_Entity); 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 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 SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators);
void SendMapInfo (int a_ID, unsigned int a_Scale); void SendMapInfo (int a_ID, unsigned int a_Scale);
void SendPaintingSpawn (const cPainting & a_Painting);
void SendPickupSpawn (const cPickup & a_Pickup); void SendPickupSpawn (const cPickup & a_Pickup);
void SendEntityAnimation (const cEntity & a_Entity, char a_Animation); 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); 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 void SetViewDistance(int a_ViewDistance); // tolua_export
int GetViewDistance(void) const { return m_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 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 /// Set to true when the chunk where the player is is sent to the client. Used for spawning the player
bool m_HasSentPlayerChunk; bool m_HasSentPlayerChunk;
/// Client Settings
AString m_Locale;
/// Returns true if the rate block interactions is within a reasonable limit (bot protection) /// Returns true if the rate block interactions is within a reasonable limit (bot protection)

426
src/CompositeChat.cpp Normal file
View File

@ -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)
{
}

183
src/CompositeChat.h Normal file
View File

@ -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

View File

@ -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) inline const char * ClickActionToString(eClickAction a_ClickAction)
{ {
switch (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 // http://forum.mc-server.org/showthread.php?tid=1212
// MessageType... // MessageType...
@ -458,7 +498,9 @@ enum ChatPrefixCodes
mtLeave, // A player has left the server mtLeave, // A player has left the server
}; };
// tolua_begin
/** Normalizes an angle in degrees to the [-180, +180) range: */ /** Normalizes an angle in degrees to the [-180, +180) range: */
inline double NormalizeAngleDegrees(const double a_Degrees) inline double NormalizeAngleDegrees(const double a_Degrees)

View File

@ -81,6 +81,8 @@ public:
etProjectile, etProjectile,
etExpOrb, etExpOrb,
etFloater, etFloater,
etItemFrame,
etPainting,
// Common variations // Common variations
etMob = etMonster, // DEPRECATED, use etMonster instead! etMob = etMonster, // DEPRECATED, use etMonster instead!
@ -139,6 +141,8 @@ public:
bool IsProjectile (void) const { return (m_EntityType == etProjectile); } bool IsProjectile (void) const { return (m_EntityType == etProjectile); }
bool IsExpOrb (void) const { return (m_EntityType == etExpOrb); } bool IsExpOrb (void) const { return (m_EntityType == etExpOrb); }
bool IsFloater (void) const { return (m_EntityType == etFloater); } 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) /// 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; virtual bool IsA(const char * a_ClassName) const;

View File

@ -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); LOGD("Player %s picked up an ExpOrb. His reward is %i", a_ClosestPlayer->GetName().c_str(), m_Reward);
a_ClosestPlayer->DeltaExperience(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.Normalize();
a_Distance *= ((float) (5.5 - Distance)); a_Distance *= ((float) (5.5 - Distance));
@ -61,4 +64,4 @@ void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk)
BroadcastMovementUpdate(); BroadcastMovementUpdate();
} }
HandlePhysics(a_Dt, a_Chunk); HandlePhysics(a_Dt, a_Chunk);
} }

124
src/Entities/ItemFrame.cpp Normal file
View File

@ -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));
}
}

42
src/Entities/ItemFrame.h Normal file
View File

@ -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

43
src/Entities/Painting.cpp Normal file
View File

@ -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));
}
}

42
src/Entities/Painting.h Normal file
View File

@ -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

View File

@ -842,6 +842,12 @@ void cPlayer::KilledBy(cEntity * a_Killer)
cItems Pickups; cItems Pickups;
m_Inventory.CopyToItems(Pickups); m_Inventory.CopyToItems(Pickups);
m_Inventory.Clear(); m_Inventory.Clear();
if (GetName() == "Notch")
{
Pickups.Add(cItem(E_ITEM_RED_APPLE));
}
m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10); m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10);
SaveToDisk(); // Save it, yeah the world is a tough place ! 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) 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_LastGroundHeight = (float)a_PosY;
m_LastJumpHeight = (float)a_PosY;
m_World->BroadcastTeleportEntity(*this, GetClientHandle()); m_World->BroadcastTeleportEntity(*this, GetClientHandle());
m_ClientHandle->SendPlayerMoveLook(); m_ClientHandle->SendPlayerMoveLook();
@ -1760,6 +1767,12 @@ void cPlayer::HandleFood(void)
{ {
// Ref.: http://www.minecraftwiki.net/wiki/Hunger // Ref.: http://www.minecraftwiki.net/wiki/Hunger
if (IsGameModeCreative())
{
// Hunger is disabled for Creative
return;
}
// Remember the food level before processing, for later comparison // Remember the food level before processing, for later comparison
int LastFoodLevel = m_FoodLevel; int LastFoodLevel = m_FoodLevel;
@ -1777,7 +1790,7 @@ void cPlayer::HandleFood(void)
Heal(1); Heal(1);
m_FoodExhaustionLevel += 3; m_FoodExhaustionLevel += 3;
} }
else if (m_FoodLevel <= 0) else if ((m_FoodLevel <= 0) && (m_Health > 1))
{ {
// Damage from starving // Damage from starving
TakeDamage(dtStarving, NULL, 1, 1, 0); TakeDamage(dtStarving, NULL, 1, 1, 0);

View File

@ -203,6 +203,7 @@ public:
void SendMessageWarning (const AString & a_Message) { m_ClientHandle->SendChat(a_Message, mtWarning); } 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 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 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; } const AString & GetName(void) const { return m_PlayerName; }
void SetName(const AString & a_Name) { m_PlayerName = a_Name; } void SetName(const AString & a_Name) { m_PlayerName = a_Name; }
@ -225,7 +226,8 @@ public:
// tolua_begin // 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; AString GetColor(void) const;
/** tosses the item in the selected hotbar slot */ /** tosses the item in the selected hotbar slot */

View File

@ -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) void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{ {
// Spawn an experience orb with a reward between 3 and 11. // 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)); m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8));
Destroy(); Destroy();

View File

@ -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 #pragma once
class cChunkDataCallback;
// fwd:
class cChunkDataCallback;
class cBlockArea; class cBlockArea;
class cForEachChunkProvider class cForEachChunkProvider
{ {
public: 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; 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; virtual bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) = 0;
}; };

View File

@ -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): // Common headers (part 2, with macros):
#include "ChunkDef.h" #include "ChunkDef.h"

View File

@ -369,6 +369,13 @@ int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount)
} }
m_Slots[a_SlotNum].m_ItemCount += 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); TriggerListeners(a_SlotNum);
return m_Slots[a_SlotNum].m_ItemCount; return m_Slots[a_SlotNum].m_ItemCount;
} }

View File

@ -22,12 +22,14 @@
#include "ItemFishingRod.h" #include "ItemFishingRod.h"
#include "ItemFlowerPot.h" #include "ItemFlowerPot.h"
#include "ItemFood.h" #include "ItemFood.h"
#include "ItemItemFrame.h"
#include "ItemHoe.h" #include "ItemHoe.h"
#include "ItemLeaves.h" #include "ItemLeaves.h"
#include "ItemLighter.h" #include "ItemLighter.h"
#include "ItemMap.h" #include "ItemMap.h"
#include "ItemMinecart.h" #include "ItemMinecart.h"
#include "ItemNetherWart.h" #include "ItemNetherWart.h"
#include "ItemPainting.h"
#include "ItemPickaxe.h" #include "ItemPickaxe.h"
#include "ItemThrowable.h" #include "ItemThrowable.h"
#include "ItemRedstoneDust.h" #include "ItemRedstoneDust.h"
@ -37,6 +39,7 @@
#include "ItemShears.h" #include "ItemShears.h"
#include "ItemShovel.h" #include "ItemShovel.h"
#include "ItemSign.h" #include "ItemSign.h"
#include "ItemMobHead.h"
#include "ItemSpawnEgg.h" #include "ItemSpawnEgg.h"
#include "ItemSugarcane.h" #include "ItemSugarcane.h"
#include "ItemSword.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_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType); case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
case E_ITEM_MAP: return new cItemMapHandler(); 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_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_DUST: return new cItemRedstoneDustHandler(a_ItemType);
case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType); case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(a_ItemType);
case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType); case E_ITEM_SHEARS: return new cItemShearsHandler(a_ItemType);
case E_ITEM_SIGN: return new cItemSignHandler(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_SNOWBALL: return new cItemSnowballHandler();
case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType);
case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(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_BOWL: return 64;
case E_ITEM_BREAD: return 64; case E_ITEM_BREAD: return 64;
case E_ITEM_BREWING_STAND: 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_CARROT: return 64;
case E_ITEM_CAULDRON: return 64; case E_ITEM_CAULDRON: return 64;
case E_ITEM_CLAY: 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_GUNPOWDER: return 64;
case E_ITEM_HEAD: return 64; case E_ITEM_HEAD: return 64;
case E_ITEM_IRON: return 64; case E_ITEM_IRON: return 64;
case E_ITEM_ITEM_FRAME: return 64;
case E_ITEM_LEATHER: return 64; case E_ITEM_LEATHER: return 64;
case E_ITEM_MAGMA_CREAM: return 64; case E_ITEM_MAGMA_CREAM: return 64;
case E_ITEM_MAP: 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_MELON_SLICE: return 64;
case E_ITEM_NETHER_BRICK: return 64; case E_ITEM_NETHER_BRICK: return 64;
case E_ITEM_NETHER_WART: 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_PAPER: return 64;
case E_ITEM_POISONOUS_POTATO: return 64; case E_ITEM_POISONOUS_POTATO: return 64;
case E_ITEM_POTATO: return 64; case E_ITEM_POTATO: return 64;

53
src/Items/ItemItemFrame.h Normal file
View File

@ -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;
}
};

View File

@ -33,7 +33,7 @@ public:
case E_BLOCK_TNT: case E_BLOCK_TNT:
{ {
// Activate the 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->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); a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0);
break; break;

42
src/Items/ItemMobHead.h Normal file
View File

@ -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;
}
} ;

98
src/Items/ItemPainting.h Normal file
View File

@ -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;
}
};

View File

@ -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) void cMapDecorator::Update(void)
{ {
if (m_Player != NULL) if (m_Player != NULL)

View File

@ -5,7 +5,7 @@
#include "../World.h" #include "../World.h"
#include "../Entities/Player.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(); 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) void cAggressiveMonster::Attack(float a_Dt)
{ {
super::Attack(a_Dt); m_AttackInterval += a_Dt * m_AttackRate;
if ((m_Target != NULL) && (m_AttackInterval > 3.0)) if ((m_Target != NULL) && (m_AttackInterval > 3.0))
{ {

View File

@ -20,7 +20,7 @@ public:
virtual void InStateChasing(float a_Dt) override; virtual void InStateChasing(float a_Dt) override;
virtual void EventSeePlayer(cEntity *) override; virtual void EventSeePlayer(cEntity *) override;
virtual void Attack(float a_Dt) override; virtual void Attack(float a_Dt);
} ; } ;

View File

@ -79,7 +79,7 @@ void cCreeper::Attack(float a_Dt)
if (!m_bIsBlowing) 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_bIsBlowing = true;
m_World->BroadcastEntityMetadata(*this); m_World->BroadcastEntityMetadata(*this);
} }

View File

@ -142,11 +142,11 @@ void cMonster::TickPathFinding()
BLOCKTYPE BlockAtYPP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ); 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); 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)); 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)); 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(); SetPitchAndYawFromDestination();
HandleFalling(); 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) void cMonster::GetMonsterConfig(const AString & a_Name)
{ {
cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name);

View File

@ -112,8 +112,6 @@ public:
virtual void InStateChasing (float a_Dt); virtual void InStateChasing (float a_Dt);
virtual void InStateEscaping(float a_Dt); virtual void InStateEscaping(float a_Dt);
virtual void Attack(float a_Dt);
int GetAttackRate() { return (int)m_AttackRate; } int GetAttackRate() { return (int)m_AttackRate; }
void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; } void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; }
void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; } void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; }

View File

@ -124,12 +124,6 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
{ {
super::Tick(a_Dt, 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); cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), (float)m_SightDistance);
if (a_Closest_Player != NULL) if (a_Closest_Player != NULL)
@ -148,18 +142,15 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
SetIsBegging(true); SetIsBegging(true);
m_World->BroadcastEntityMetadata(*this); 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. // Don't move to the player if the wolf is sitting.
if (IsSitting()) if (!IsSitting())
{ {
m_bMovingToDestination = false; MoveToPosition(a_Closest_Player->GetPosition());
} }
else
{
m_bMovingToDestination = true;
}
Vector3d PlayerPos = a_Closest_Player->GetPosition();
PlayerPos.y++;
m_FinalDestination = PlayerPos;
break; break;
} }
default: default:
@ -173,10 +164,14 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
} }
} }
if (IsTame()) if (IsTame() && !IsSitting())
{ {
TickFollowPlayer(); TickFollowPlayer();
} }
else if (IsSitting())
{
m_bMovingToDestination = false;
}
} }
@ -196,29 +191,19 @@ void cWolf::TickFollowPlayer()
public: public:
Vector3d OwnerPos; Vector3d OwnerPos;
} Callback; } Callback;
if (m_World->DoWithPlayer(m_OwnerName, Callback)) if (m_World->DoWithPlayer(m_OwnerName, Callback))
{ {
// The player is present in the world, follow him: // The player is present in the world, follow him:
double Distance = (Callback.OwnerPos - GetPosition()).Length(); double Distance = (Callback.OwnerPos - GetPosition()).Length();
if (Distance > 30) 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 else
{ {
m_FinalDestination = Callback.OwnerPos; MoveToPosition(Callback.OwnerPos);
if (IsSitting())
{
m_bMovingToDestination = false;
}
else
{
m_bMovingToDestination = true;
}
} }
} }
} }

View File

@ -25,10 +25,12 @@ class cWindow;
class cInventory; class cInventory;
class cPawn; class cPawn;
class cPickup; class cPickup;
class cPainting;
class cWorld; class cWorld;
class cMonster; class cMonster;
class cChunkDataSerializer; class cChunkDataSerializer;
class cFallingBlock; 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 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 SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0;
virtual void SendChat (const AString & a_Message) = 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 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 SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0;
virtual void SendDestroyEntity (const cEntity & a_Entity) = 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 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 SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) = 0;
virtual void SendMapInfo (int a_ID, unsigned int a_Scale) = 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 SendPickupSpawn (const cPickup & a_Pickup) = 0;
virtual void SendPlayerAbilities (void) = 0; virtual void SendPlayerAbilities (void) = 0;
virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) = 0; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) = 0;

View File

@ -32,6 +32,8 @@ Documentation:
#include "../Mobs/IncludeAllMonsters.h" #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) void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{ {
cCSLock Lock(m_CSPacket); cCSLock Lock(m_CSPacket);

View File

@ -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 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 SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
virtual void SendChat (const AString & a_Message) 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 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 SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) 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 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 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 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 SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message
virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;

View File

@ -560,7 +560,7 @@ int cProtocol132::ParseLocaleViewDistance(void)
HANDLE_PACKET_READ(ReadChar, char, ViewDistance); HANDLE_PACKET_READ(ReadChar, char, ViewDistance);
HANDLE_PACKET_READ(ReadChar, char, ChatFlags); HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
// TODO: m_Client->HandleLocale(Locale); m_Client->SetLocale(Locale);
// TODO: m_Client->HandleViewDistance(ViewDistance); // TODO: m_Client->HandleViewDistance(ViewDistance);
// TODO: m_Client->HandleChatFlags(ChatFlags); // TODO: m_Client->HandleChatFlags(ChatFlags);
// Ignoring client difficulty // Ignoring client difficulty

View File

@ -85,7 +85,7 @@ int cProtocol142::ParseLocaleViewDistance(void)
HANDLE_PACKET_READ(ReadChar, char, ChatFlags); HANDLE_PACKET_READ(ReadChar, char, ChatFlags);
HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty); HANDLE_PACKET_READ(ReadChar, char, ClientDifficulty);
HANDLE_PACKET_READ(ReadChar, char, ShouldShowCape); // <-- new in 1.4.2 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->HandleViewDistance(ViewDistance);
// TODO: m_Client->HandleChatFlags(ChatFlags); // TODO: m_Client->HandleChatFlags(ChatFlags);
// Ignoring client difficulty // Ignoring client difficulty

View File

@ -8,6 +8,7 @@ Implements the 1.7.x protocol classes:
*/ */
#include "Globals.h" #include "Globals.h"
#include "json/json.h"
#include "Protocol17x.h" #include "Protocol17x.h"
#include "ChunkDataSerializer.h" #include "ChunkDataSerializer.h"
#include "../ClientHandle.h" #include "../ClientHandle.h"
@ -20,11 +21,15 @@ Implements the 1.7.x protocol classes:
#include "../Entities/ExpOrb.h" #include "../Entities/ExpOrb.h"
#include "../Entities/Minecart.h" #include "../Entities/Minecart.h"
#include "../Entities/FallingBlock.h" #include "../Entities/FallingBlock.h"
#include "../Entities/Painting.h"
#include "../Entities/Pickup.h" #include "../Entities/Pickup.h"
#include "../Entities/Player.h" #include "../Entities/Player.h"
#include "../Entities/ItemFrame.h"
#include "../Mobs/IncludeAllMonsters.h" #include "../Mobs/IncludeAllMonsters.h"
#include "../UI/Window.h" #include "../UI/Window.h"
#include "../BlockEntities/CommandBlockEntity.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) void cProtocol172::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{ {
// Serialize first, before creating the Packetizer (the packetizer locks a CS) // 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.WriteByte((Byte)a_Player.GetEffectiveGameMode() | (cRoot::Get()->GetServer()->IsHardcore() ? 0x08 : 0)); // Hardcore flag bit 4
Pkt.WriteChar((char)a_World.GetDimension()); Pkt.WriteChar((char)a_World.GetDimension());
Pkt.WriteByte(2); // TODO: Difficulty (set to Normal) 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? 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) 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.GetPosX());
Pkt.WriteFPInt(a_Entity.GetPosY()); Pkt.WriteFPInt(a_Entity.GetPosY());
Pkt.WriteFPInt(a_Entity.GetPosZ()); Pkt.WriteFPInt(a_Entity.GetPosZ());
Pkt.WriteByteAngle(a_Entity.GetYaw());
Pkt.WriteByteAngle(a_Entity.GetPitch()); Pkt.WriteByteAngle(a_Entity.GetPitch());
Pkt.WriteByteAngle(a_Entity.GetYaw());
Pkt.WriteInt(a_ObjectData); Pkt.WriteInt(a_ObjectData);
if (a_ObjectData != 0) 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.GetPosX());
Pkt.WriteFPInt(a_Vehicle.GetPosY()); Pkt.WriteFPInt(a_Vehicle.GetPosY());
Pkt.WriteFPInt(a_Vehicle.GetPosZ()); Pkt.WriteFPInt(a_Vehicle.GetPosZ());
Pkt.WriteByteAngle(a_Vehicle.GetYaw());
Pkt.WriteByteAngle(a_Vehicle.GetPitch()); Pkt.WriteByteAngle(a_Vehicle.GetPitch());
Pkt.WriteByteAngle(a_Vehicle.GetYaw());
Pkt.WriteInt(a_VehicleSubType); Pkt.WriteInt(a_VehicleSubType);
if (a_VehicleSubType != 0) 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_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_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; default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break;
} }
Pkt.WriteByte(Action); 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, ChatColors);
HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Difficulty); HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Difficulty);
HANDLE_READ(a_ByteBuffer, ReadByte, Byte, ShowCape); HANDLE_READ(a_ByteBuffer, ReadByte, Byte, ShowCape);
m_Client->SetLocale(Locale);
// TODO: handle in m_Client // 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: // cProtocol172::cPacketizer:
@ -2169,6 +2342,19 @@ void cProtocol172::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEnt
} }
break; 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; default: break;
} }
@ -2291,6 +2477,15 @@ void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity)
WriteMobMetadata((const cMonster &)a_Entity); WriteMobMetadata((const cMonster &)a_Entity);
break; break;
} }
case cEntity::etItemFrame:
{
cItemFrame & Frame = (cItemFrame &)a_Entity;
WriteByte(0xA2);
WriteItem(Frame.GetItem());
WriteByte(0x3);
WriteByte(Frame.GetRotation());
break;
}
} }
} }

View File

@ -36,6 +36,16 @@ Declares the 1.7.x protocol classes:
// fwd:
namespace Json
{
class Value;
}
class cProtocol172 : class cProtocol172 :
public cProtocol public cProtocol
{ {
@ -45,16 +55,17 @@ public:
cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State); 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; 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 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 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 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 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 SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
virtual void SendChat (const AString & a_Message) 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 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 SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) 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 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 SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
virtual void SendMapInfo (int a_ID, unsigned int a_Scale) 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 SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendPlayerAbilities (void) override; virtual void SendPlayerAbilities (void) override;
virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;
@ -120,7 +132,7 @@ public:
protected: 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 class cPacketizer
{ {
public: public:
@ -209,16 +221,16 @@ protected:
AString m_AuthServerID; 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; UInt32 m_State;
/// Buffer for the received data /** Buffer for the received data */
cByteBuffer m_ReceivedData; cByteBuffer m_ReceivedData;
/// Buffer for composing the outgoing packets, through cPacketizer /** Buffer for composing the outgoing packets, through cPacketizer */
cByteBuffer m_OutPacketBuffer; 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; cByteBuffer m_OutPacketLenBuffer;
bool m_IsEncrypted; bool m_IsEncrypted;
@ -230,7 +242,7 @@ protected:
cFile m_CommLogFile; 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); void AddReceivedData(const char * a_Data, int a_Size);
/** Reads and handles the packet. The packet length and type have already been read. /** Reads and handles the packet. The packet length and type have already been read.
@ -271,21 +283,24 @@ protected:
void HandlePacketWindowClose (cByteBuffer & a_ByteBuffer); 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); 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; virtual void SendData(const char * a_Data, int a_Size) override;
void SendCompass(const cWorld & a_World); 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); 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 ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
void StartEncryption(const Byte * a_Key); 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);
} ; } ;

View File

@ -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) void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
{ {
ASSERT(m_Protocol != NULL); 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) void cProtocolRecognizer::SendPickupSpawn(const cPickup & a_Pickup)
{ {

View File

@ -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 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 SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
virtual void SendChat (const AString & a_Message) 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 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 SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override;
virtual void SendDestroyEntity (const cEntity & a_Entity) 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 SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
virtual void SendMapInfo (int a_ID, unsigned int a_Scale) 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 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 SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendPlayerAbilities (void) override; virtual void SendPlayerAbilities (void) override;
virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override;

View File

@ -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) 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[] } // for itr - m_WorldsByName[]
} }

View File

@ -20,7 +20,8 @@ class cPluginManager;
class cServer; class cServer;
class cWorld; class cWorld;
class cPlayer; class cPlayer;
class cCommandOutputCallback ; class cCommandOutputCallback;
class cCompositeChat;
typedef cItemCallback<cPlayer> cPlayerListCallback; typedef cItemCallback<cPlayer> cPlayerListCallback;
typedef cItemCallback<cWorld> cWorldListCallback; 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 /// 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 << 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 // tolua_begin
/// Sends a chat message to all connected clients (in all worlds) /// Sends a chat message to all connected clients (in all worlds)
void BroadcastChat (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtCustom); } void BroadcastChat (const AString & a_Message, eMessageType a_ChatPrefix = mtCustom);
void BroadcastChatInfo (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtInformation); } void BroadcastChatInfo (const AString & a_Message) { BroadcastChat(a_Message, mtInformation); }
void BroadcastChatFailure(const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtFailure); } void BroadcastChatFailure(const AString & a_Message) { BroadcastChat(a_Message, mtFailure); }
void BroadcastChatSuccess(const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtSuccess); } void BroadcastChatSuccess(const AString & a_Message) { BroadcastChat(a_Message, mtSuccess); }
void BroadcastChatWarning(const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtWarning); } void BroadcastChatWarning(const AString & a_Message) { BroadcastChat(a_Message, mtWarning); }
void BroadcastChatFatal (const AString & a_Message) { LoopWorldsAndBroadcastChat(a_Message, mtFailure); } 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 /// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API
static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum);

View File

@ -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()) if ((a_Chunk == NULL) || !a_Chunk->IsValid())
{ {
@ -42,17 +42,33 @@ void cIncrementalRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_B
return; return;
} }
int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width; // We may be called with coordinates in a chunk that is not the first chunk parameter
int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width; // 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; BLOCKTYPE Block;
NIBBLETYPE Meta; 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 // 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 // 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))) 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)) 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); 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; break;
} }
else if ( 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); 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; break;
} }
else if (Block == E_BLOCK_DAYLIGHT_SENSOR) 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(); 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"); LOGD("cIncrementalRedstoneSimulator: Erased daylight sensor from powered blocks list due to insufficient light level");
m_PoweredBlocks.erase(itr); PoweredBlocks->erase(itr);
break; 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 (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{ {
if (!IsPotentialSource(Block)) 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); 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); itr = LinkedPoweredBlocks->erase(itr);
break; continue;
} }
else if ( else if (
// Things that can send power through a block but which depends on meta // 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); 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); itr = LinkedPoweredBlocks->erase(itr);
break; continue;
} }
} }
else if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) 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)) 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); 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); itr = LinkedPoweredBlocks->erase(itr);
break; 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))) 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)) 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); 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; 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))) 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)) if ((Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF))
{ {
m_RepeatersDelayList.erase(itr); RepeatersDelayList->erase(itr);
break; break;
} }
} }
cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); if (a_OtherChunk != NULL)
for (cRedstoneSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr) {
// 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 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; 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 // 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 // A marking dirty system might be a TODO for later on, perhaps
cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData(); m_RedstoneSimulatorChunkData = a_Chunk->GetRedstoneSimulatorData();
if (ChunkData.empty()) if (m_RedstoneSimulatorChunkData->empty())
{ {
return; 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 BaseX = a_Chunk->GetPosX() * cChunkDef::Width;
int BaseZ = a_Chunk->GetPosZ() * 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) if (dataitr->DataTwo)
{ {
dataitr = ChunkData.erase(dataitr); dataitr = m_RedstoneSimulatorChunkData->erase(dataitr);
continue; 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_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_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_TNT: HandleTNT(a_X, dataitr->y, a_Z); break;
case E_BLOCK_TRAPDOOR: HandleTrapdoor(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; 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); HandlePressurePlate(a_X, dataitr->y, a_Z, dataitr->Data);
break; break;
} }
default: LOGD("Unhandled block (!) or unimplemented redstone block: %s", ItemToString(dataitr->Data).c_str()); break;
} }
++dataitr; ++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) 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) 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))) 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 // All surrounding metas were smaller - self must have been a wire that was
// transferring power to other wires around. // 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 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 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); 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: 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_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_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_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_ZM, E_BLOCK_REDSTONE_WIRE);
SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_WIRE); SetDirectionLinkedPowered(a_BlockX, a_BlockY, a_BlockZ, BLOCK_FACE_ZP, E_BLOCK_REDSTONE_WIRE);
break; 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 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 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))) 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_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; return;
} }
} }
@ -737,7 +833,7 @@ void cIncrementalRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_
{ {
if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) 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.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); 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()); cChunkInterface ChunkInterface(m_World.GetChunkMap());
cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); 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); 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()); cChunkInterface ChunkInterface(m_World.GetChunkMap());
cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); 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); 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)) 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.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); 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)) 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.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); 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); Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f);
float Distance = (EntityPos - BlockPos).Length(); float Distance = (EntityPos - BlockPos).Length();
if (Distance < 0.5) if (Distance <= 0.7)
{ {
m_Entity = a_Entity; m_Entity = a_Entity;
return true; // Break out, we only need to know for wooden plates that at least one entity is on top 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) 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))) 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) 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))) 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 // 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; } 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; } 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; int OldX = a_BlockX, OldY = a_BlockY, OldZ = a_BlockZ;
eBlockFace Face = cPiston::MetaDataToDirection(a_Meta); 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; } 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; 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; } 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) 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; } 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; } 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) 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))) 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; 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 ( if (
itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)) && 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; sPoweredBlocks RC;
RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ); 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; 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 ( if (
itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)) && 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_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
RC.a_MiddlePos = Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ); RC.a_MiddlePos = Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ);
RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ); 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) 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))) if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{ {
@ -1414,16 +1518,16 @@ void cIncrementalRedstoneSimulator::SetPlayerToggleableBlockAsSimulated(int a_Bl
sSimulatedPlayerToggleableList RC; sSimulatedPlayerToggleableList RC;
RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
RC.WasLastStatePowered = WasLastStatePowered; 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))) 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 // 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->a_ElapsedTicks = 0;
itr->ShouldPowerOn = ShouldPowerOn; itr->ShouldPowerOn = ShouldPowerOn;
return; return;
@ -1441,18 +1545,16 @@ void cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_BlockX, int a
} }
// Self not in list, add self to list // Self not in list, add self to list
sRepeatersDelayList RC; sRepeatersDelayList RC;
RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ); 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.) // 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 // * 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) + 1) * 2;
RC.a_DelayTicks = (((a_Meta & 0xC) >> 0x2) + (ShouldPowerOn ? 1 : 0)) * 2;
RC.a_ElapsedTicks = 0; RC.a_ElapsedTicks = 0;
RC.ShouldPowerOn = ShouldPowerOn; RC.ShouldPowerOn = ShouldPowerOn;
m_RepeatersDelayList.push_back(RC); m_RepeatersDelayList->push_back(RC);
return; return;
} }

View File

@ -3,7 +3,7 @@
#include "RedstoneSimulator.h" #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; typedef cCoordWithBlockAndBoolVector cRedstoneSimulatorChunkData;
@ -21,7 +21,8 @@ public:
virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used 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 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 enum eRedstoneDirection
{ {
@ -57,22 +58,29 @@ private:
struct sRepeatersDelayList struct sRepeatersDelayList
{ {
Vector3i a_BlockPos; Vector3i a_BlockPos;
short a_DelayTicks; unsigned char a_DelayTicks;
short a_ElapsedTicks; unsigned char a_ElapsedTicks;
bool ShouldPowerOn; bool ShouldPowerOn;
}; };
public:
typedef std::vector <sPoweredBlocks> PoweredBlocksList; typedef std::vector <sPoweredBlocks> PoweredBlocksList;
typedef std::vector <sLinkedPoweredBlocks> LinkedBlocksList; typedef std::vector <sLinkedPoweredBlocks> LinkedBlocksList;
typedef std::vector <sSimulatedPlayerToggleableList> SimulatedPlayerToggleableList; typedef std::vector <sSimulatedPlayerToggleableList> SimulatedPlayerToggleableList;
typedef std::vector <sRepeatersDelayList> RepeatersDelayList; typedef std::vector <sRepeatersDelayList> RepeatersDelayList;
PoweredBlocksList m_PoweredBlocks; private:
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; 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 // 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 // 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); void HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType);
/** Handles trapdoors */ /** Handles trapdoors */
void HandleTrapdoor(int a_BlockX, int a_BlockY, int a_BlockZ); 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 */ /** Handles noteblocks */
void HandleNoteBlock(int a_BlockX, int a_BlockY, int a_BlockZ); 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 */ /** Marks all blocks immediately surrounding a coordinate as powered */
void SetAllDirsAsPowered(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SourceBlock); void SetAllDirsAsPowered(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SourceBlock);
/** Queues a repeater to be powered or unpowered */ /** 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 */ /** 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); } 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_REDSTONE_REPEATER_ON:
case E_BLOCK_BLOCK_OF_REDSTONE: case E_BLOCK_BLOCK_OF_REDSTONE:
case E_BLOCK_ACTIVE_COMPARATOR: 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; return true;
} }
@ -261,3 +275,7 @@ private:
} }
} }
}; };

Some files were not shown because too many files have changed in this diff Show More