diff options
141 files changed, 3655 insertions, 2693 deletions
diff --git a/.gitignore b/.gitignore index 859ef28ea..4a319c5ef 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ cloc.xsl ## Eclipse .cproject .project +*.cbp # world inside source ChunkWorx.ini diff --git a/CMakeLists.txt b/CMakeLists.txt index 56dea1a34..a6400c1b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,7 +62,10 @@ add_subdirectory(lib/tolua++/) add_subdirectory(lib/sqlite/) add_subdirectory(lib/expat/) add_subdirectory(lib/luaexpat/) -add_subdirectory(lib/md5/) + +if (WIN32) + add_subdirectory(lib/luaproxy/) +endif() # We use EXCLUDE_FROM_ALL so that only the explicit dependencies are used diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 06e333cf9..ea9f38db4 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -1875,9 +1875,9 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); }, CallPlugin = { Params = "PluginName, FunctionName, [FunctionArgs...]", Return = "[FunctionRets]", Notes = "(STATIC) Calls the specified function in the specified plugin, passing all the given arguments to it. If it succeeds, it returns all the values returned by that function. If it fails, returns no value at all. Note that only strings, numbers, bools, nils and classes can be used for parameters and return values; tables and functions cannot be copied across plugins." }, DisablePlugin = { Params = "PluginName", Return = "bool", Notes = "Disables a plugin specified by its name. Returns true if the plugin was disabled, false if it wasn't found or wasn't active." }, - ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "bool", Notes = "Executes the command as if given by the specified Player. Checks permissions. Returns true if executed." }, + ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Executes the command as if given by the specified Player. Checks permissions." }, FindPlugins = { Params = "", Return = "", Notes = "Refreshes the list of plugins to include all folders inside the Plugins folder (potentially new disabled plugins)" }, - ForceExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "bool", Notes = "Same as ExecuteCommand, but doesn't check permissions" }, + ForceExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Same as ExecuteCommand, but doesn't check permissions" }, ForEachCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function(Command, Permission, HelpString)</pre>. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, ForEachConsoleCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindConsoleCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function (Command, HelpString)</pre>. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, Get = { Params = "", Return = "cPluginManager", Notes = "(STATIC) Returns the single instance of the plugin manager" }, @@ -1893,8 +1893,23 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); LogStackTrace = { Params = "", Return = "", Notes = "(STATIC) Logs a current stack trace of the Lua engine to the server console log. Same format as is used when the plugin fails." }, ReloadPlugins = { Params = "", Return = "", Notes = "Reloads all active plugins" }, }, + ConstantGroups= + { + CommandResult = + { + Include = "^cr.*", + TextBefore = [[ + Results that the (Force)ExecuteCommand return. This gives information if the command is executed or not and the reason. + ]], + }, + }, Constants = { + crBlocked = { Notes = "When a plugin stopped the command using the OnExecuteCommand hook" }, + crError = { Notes = "When the command handler for the given command results in an error" }, + crExecuted = { Notes = "When the command is successfully executed." }, + crNoPermission = { Notes = "When the player doesn't have permission to execute the given command." }, + crUnknownCommand = { Notes = "When the given command doesn't exist." }, HOOK_BLOCK_SPREAD = { Notes = "Called when a block spreads based on world conditions" }, HOOK_BLOCK_TO_PICKUPS = { Notes = "Called when a block has been dug and is being converted to pickups. The server has provided the default pickups and the plugins may modify them." }, HOOK_CHAT = { Notes = "Called when a client sends a chat message that is not a command. The plugin may modify the chat message" }, @@ -2325,6 +2340,7 @@ end { Params = "BlockX, BlockY, BlockZ, BlockMeta", Return = "", Notes = "Sets the meta for the block at the specified coords." }, { Params = "{{Vector3i|BlockCoords}}, BlockMeta", Return = "", Notes = "Sets the meta for the block at the specified coords." }, }, + SetChunkAlwaysTicked = { Params = "ChunkX, ChunkZ, IsAlwaysTicked", Return = "", Notes = "Sets the chunk to always be ticked even when it doesn't contain any clients. IsAlwaysTicked set to true turns forced ticking on, set to false turns it off. Every call with 'true' should be paired with a later call with 'false', otherwise the ticking won't stop. Multiple actions can request ticking independently, the ticking will continue until the last call with 'false'. Note that when the chunk unloads, it loses the value of this flag." }, SetNextBlockTick = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Sets the blockticking to start at the specified block in the next tick." }, SetCommandBlockCommand = { Params = "BlockX, BlockY, BlockZ, Command", Return = "bool", Notes = "Sets the command to be executed in a command block at the specified coordinates. Returns if command was changed." }, SetCommandBlocksEnabled = { Params = "IsEnabled (bool)", Return = "", Notes = "Sets whether command blocks should be enabled on the (entire) server." }, diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerFoodLevelChange.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerFoodLevelChange.lua new file mode 100644 index 000000000..53637d5f1 --- /dev/null +++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerFoodLevelChange.lua @@ -0,0 +1,27 @@ +return +{ + HOOK_PLAYER_FOOD_LEVEL_CHANGE = + { + CalledWhen = "Called before the player food level changed. Plugin may override", + DefaultFnName = "OnPlayerFoodLevelChange", -- also used as pagename + Desc = [[ + This hook is called before the food level changes. + The food level is not changed yet, plugins may choose + to refuse the change. + ]], + Params = + { + { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who changes the food level." }, + { Name = "NewFoodLevel", Type = "number", Notes = "The new food level." }, + }, + Returns = [[ + If the function returns false or no value, the next plugin's callback is called. Afterwards, the + server changes the food level of the player. If the function returns true, no + other callback is called for this event and the player's food level doesn't change. + ]], + }, -- HOOK_PLAYER_FOOD_LEVEL_CHANGE +}; + + + + diff --git a/MCServer/Plugins/APIDump/Hooks/OnWeatherChanging.lua b/MCServer/Plugins/APIDump/Hooks/OnWeatherChanging.lua index d36164e8e..bb809af11 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnWeatherChanging.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnWeatherChanging.lua @@ -6,7 +6,7 @@ return DefaultFnName = "OnWeatherChanging", -- also used as pagename Desc = [[ This hook is called when the current weather has expired and a new weather is selected. Plugins may - override the new weather setting.</p> + override the new weather being set.</p> <p> The new weather setting is sent to the clients only after this hook has been processed.</p> <p> @@ -19,9 +19,12 @@ return { Name = "Weather", Type = "number", Notes = "The newly selected weather. One of wSunny, wRain, wStorm" }, }, Returns = [[ - If the function returns false or no value, the server calls other plugins' callbacks and finally - sets the weather. If the function returns true, the server takes the second returned value (wSunny - by default) and sets it as the new weather. No other plugins' callbacks are called in this case. + The hook handler can return up to two values. If the first value is false or not present, the server + calls other plugins' callbacks and finally sets the weather. If it is true, the server doesn't call any + more callbacks for this hook. The second value returned is used as the new weather. If no value is + given, the weather from the parameters is used as the weather. Returning false as the first value and a + specific weather constant as the second value makes the server call the rest of the hook handlers with + the new weather value. ]], }, -- HOOK_WEATHER_CHANGING } diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 534426d25..918204deb 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -31,6 +31,8 @@ function Initialize(Plugin) PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); PM:AddHook(cPluginManager.HOOK_PLAYER_JOINED, OnPlayerJoined); PM:AddHook(cPluginManager.HOOK_PROJECTILE_HIT_BLOCK, OnProjectileHitBlock); + PM:AddHook(cPluginManager.HOOK_CHUNK_UNLOADING, OnChunkUnloading); + PM:AddHook(cPluginManager.HOOK_WORLD_STARTED, OnWorldStarted); -- _X: Disabled so that the normal operation doesn't interfere with anything -- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); @@ -346,7 +348,7 @@ end function OnUsingBlazeRod(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, CursorY, CursorZ) -- Magic rod of query: show block types and metas for both neighbors of the pointed face - local Type, Meta, Valid = Player:GetWorld():GetBlockTypeMeta(BlockX, BlockY, BlockZ, Type, Meta); + local Valid, Type, Meta = Player:GetWorld():GetBlockTypeMeta(BlockX, BlockY, BlockZ); if (Type == E_BLOCK_AIR) then Player:SendMessage(cChatColor.LightGray .. "Block {" .. BlockX .. ", " .. BlockY .. ", " .. BlockZ .. "}: air:" .. Meta); @@ -356,7 +358,7 @@ function OnUsingBlazeRod(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, Cur end local X, Y, Z = AddFaceDirection(BlockX, BlockY, BlockZ, BlockFace); - Valid, Type, Meta = Player:GetWorld():GetBlockTypeMeta(X, Y, Z, Type, Meta); + Valid, Type, Meta = Player:GetWorld():GetBlockTypeMeta(X, Y, Z); if (Type == E_BLOCK_AIR) then Player:SendMessage(cChatColor.LightGray .. "Block {" .. X .. ", " .. Y .. ", " .. Z .. "}: air:" .. Meta); else @@ -1382,6 +1384,7 @@ end function OnProjectileHitBlock(a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockHitPos) + -- Test projectile hooks by setting the blocks they hit on fire: local BlockX, BlockY, BlockZ = AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace) local World = a_Projectile:GetWorld() @@ -1391,3 +1394,28 @@ end + +function OnChunkUnloading(a_World, a_ChunkX, a_ChunkZ) + -- Do not let chunk [0, 0] unload, so that it continues ticking [cWorld:SetChunkAlwaysTicked() test] + if ((a_ChunkX == 0) and (a_ChunkZ == 0)) then + return true + end +end + + + + + +function OnWorldStarted(a_World) + -- Make the chunk [0, 0] in every world keep ticking [cWorld:SetChunkAlwaysTicked() test] + a_World:ChunkStay({{0, 0}}, nil, + function() + -- The chunk is loaded, make it always tick: + a_World:SetChunkAlwaysTicked(0, 0, true) + end + ) +end + + + + diff --git a/MCServer/Plugins/DiamondMover/DiamondMover.lua b/MCServer/Plugins/DiamondMover/DiamondMover.lua index 0fdd32250..d3e70acfc 100644 --- a/MCServer/Plugins/DiamondMover/DiamondMover.lua +++ b/MCServer/Plugins/DiamondMover/DiamondMover.lua @@ -22,6 +22,8 @@ function Initialize(Plugin) Plugin:SetVersion(1); cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USED_ITEM, OnPlayerUsedItem); + + LOG("Initialized " .. Plugin:GetName() .. " v." .. Plugin:GetVersion()); return true; end @@ -36,8 +38,8 @@ function OnPlayerUsedItem(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, Cu return false; end; - if (Player:HasPermission("diamondmover.move") == false) then - return true; + if (not Player:HasPermission("diamondmover.move")) then + return false; end; -- Rclk with a diamond to push in the direction the player is facing @@ -56,7 +58,7 @@ function OnPlayerUsedItem(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, Cu if (PlayerPitch > 70) then -- looking down BlockY = BlockY - 1; else - local PlayerRot = Player:GetRotation() + 180; -- Convert [-180, 180] into [0, 360] for simpler conditions + local PlayerRot = Player:GetYaw() + 180; -- Convert [-180, 180] into [0, 360] for simpler conditions if ((PlayerRot < 45) or (PlayerRot > 315)) then BlockZ = BlockZ - 1; else diff --git a/MCServer/lua5.1.dll b/MCServer/lua5.1.dll Binary files differdeleted file mode 100644 index cca0bcb25..000000000 --- a/MCServer/lua5.1.dll +++ /dev/null diff --git a/SetFlags.cmake b/SetFlags.cmake index 6e2417a51..bf467ca01 100644 --- a/SetFlags.cmake +++ b/SetFlags.cmake @@ -26,10 +26,18 @@ endmacro() macro(set_flags) + # Add coverage processing, if requested: + if (NOT MSVC) + + if (CMAKE_BUILD_TYPE STREQUAL "COVERAGE") + message("Including CodeCoverage") + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/lib/cmake-coverage/") + include(CodeCoverage) + endif() + endif() + # Add the preprocessor macros used for distinguishing between debug and release builds (CMake does this automatically for MSVC): if (NOT MSVC) - set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/lib/cmake-coverage/") - include(CodeCoverage) 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_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -D_DEBUG") @@ -63,12 +71,16 @@ macro(set_flags) else() # Let gcc / clang know that we're compiling a multi-threaded app: - add_flags_cxx("-pthread") + if (UNIX) + add_flags_cxx("-pthread") + endif() + + # Make CLang use C++11, otherwise MSVC2008-supported extensions don't work ("override" keyword etc.): 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 "${CMAKE_CXX_FLAGS} -std=c++11") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11") set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++11") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -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) diff --git a/lib/lua/CMakeLists.txt b/lib/lua/CMakeLists.txt index 6e5e0f565..0a04d1ee6 100644 --- a/lib/lua/CMakeLists.txt +++ b/lib/lua/CMakeLists.txt @@ -11,21 +11,16 @@ file(GLOB SOURCE list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/src/lua.c" "${PROJECT_SOURCE_DIR}/src/luac.c") # add headers to MSVC project files: -if (WIN32) +if (MSVC) file(GLOB HEADERS "src/*.h") list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/src/lua.h" "${PROJECT_SOURCE_DIR}/src/luac.h") set(SOURCE ${SOURCE} ${HEADERS}) source_group("Sources" FILES ${SOURCE}) endif() + # Lua needs to be linked dynamically on Windows and statically on *nix, so that LuaRocks work if (WIN32) - - #for compiliers other than msvc we need to tell lua that its building as a dll - if (NOT MSVC) - add_flags_cxx(-DLUA_BUILD_AS_DLL=1) - endif() - add_library(lua SHARED ${SOURCE}) set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/MCServer) @@ -53,7 +48,7 @@ if (WIN32) ) endif() - set_target_properties(lua PROPERTIES OUTPUT_NAME "lua51") + set_target_properties(lua PROPERTIES OUTPUT_NAME "lua51" PREFIX "") # NOTE: The DLL for each configuration is stored at the same place, thus overwriting each other. # This is known, however such behavior is needed for LuaRocks - they always load "lua5.1.dll" or "lua51.dll" @@ -63,6 +58,7 @@ else() add_library(lua ${SOURCE}) endif() + # Tell Lua what dynamic loader to use (for LuaRocks): if (UNIX) add_definitions(-DLUA_USE_DLOPEN) diff --git a/lib/luaproxy/CMakeLists.txt b/lib/luaproxy/CMakeLists.txt new file mode 100644 index 000000000..58ca87cd3 --- /dev/null +++ b/lib/luaproxy/CMakeLists.txt @@ -0,0 +1,61 @@ + +# This project adds a Lua Proxy DLL on Windows +# By an unfortunate choice in the popular LuaBinaries distribution, there are two names for the Lua DLL on Windows: lua51.dll and lua5.1.dll. +# Some binary Lua packages are built for one, the others for the other. Messy! +# In order to support both package flavors, we create a "proxy DLL": +# Basically the lua5.1.dll has its PE Exports section manipulated so that it points each exported function to its lua51.dll implementation. +# Effectively, this forwards all calls from lua5.1.dll to lua51.dll without any performance costs (the forwarding is done in the Windows PE loader on app start). + +# This project creates the proxy DLL by using a specially crafted .DEF file that is used to link the Proxy DLL. +# Note that it has been tested only on MSVC, it might not work with other compilers. +# The initial implementation was taken from http://lua-users.org/wiki/LuaProxyDllFour , but adapted to MSVC + + + + +if (WIN32) + + if (MSVC) + # Tell the linker to use the DEF file to generate the proxy: + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /NOENTRY /DEF:lua5.1.def /MANIFEST:NO") + set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /NOENTRY /DEF:lua5.1.def /MANIFEST:NO") + set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /NOENTRY /DEF:lua5.1.def /MANIFEST:NO") + set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NOENTRY /DEF:lua5.1.def /MANIFEST:NO") + set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} /NOENTRY /DEF:lua5.1.def /MANIFEST:NO") + set(CMAKE_MODULE_LINKER_FLAGS_DEBUG "${CMAKE_MODULE_LINKER_FLAGS_DEBUG} /NOENTRY /DEF:lua5.1.def /MANIFEST:NO") + elseif (MINGW) + # MinGW requires no further flags and has been tested + else() + message ("LuaProxy: This cmake code has not been tested on your compiler. Please report your success or failure in the forum.") + endif() + + add_library(luaproxy SHARED "lua5.1.def" "Dummy.c") + set(LIBRARY_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/MCServer) + set_target_properties(luaproxy PROPERTIES + OUTPUT_NAME "lua5.1" + PREFIX "" + ) + target_link_libraries(luaproxy lua) + + # Output the executable into the $/MCServer folder, so that MCServer can find it: + set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/MCServer) + SET_TARGET_PROPERTIES(luaproxy PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/MCServer + ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_SOURCE_DIR}/MCServer + ARCHIVE_OUTPUT_DIRECTORY_DEBUGPROFILE ${CMAKE_SOURCE_DIR}/MCServer + ARCHIVE_OUTPUT_DIRECTORY_RELEASEPROFILE ${CMAKE_SOURCE_DIR}/MCServer + LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/MCServer + LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_SOURCE_DIR}/MCServer + LIBRARY_OUTPUT_DIRECTORY_DEBUGPROFILE ${CMAKE_SOURCE_DIR}/MCServer + LIBRARY_OUTPUT_DIRECTORY_RELEASEPROFILE ${CMAKE_SOURCE_DIR}/MCServer + RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/MCServer + RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_SOURCE_DIR}/MCServer + RUNTIME_OUTPUT_DIRECTORY_DEBUGPROFILE ${CMAKE_SOURCE_DIR}/MCServer + RUNTIME_OUTPUT_DIRECTORY_RELEASEPROFILE ${CMAKE_SOURCE_DIR}/MCServer + ) + +else() + + message (FATAL_ERROR "This project is needed only for Windows, modify your cmake file not to include it on Linux") + +endif() diff --git a/lib/luaproxy/Dummy.c b/lib/luaproxy/Dummy.c new file mode 100644 index 000000000..4018d8392 --- /dev/null +++ b/lib/luaproxy/Dummy.c @@ -0,0 +1,4 @@ + +// Dummy.c + +// Because the MSVC compiler needs at least one C file to compile the project diff --git a/lib/luaproxy/lua5.1.def b/lib/luaproxy/lua5.1.def new file mode 100644 index 000000000..42986d22f --- /dev/null +++ b/lib/luaproxy/lua5.1.def @@ -0,0 +1,115 @@ +EXPORTS + luaL_addlstring=lua51.luaL_addlstring + luaL_addstring=lua51.luaL_addstring + luaL_addvalue=lua51.luaL_addvalue + luaL_argerror=lua51.luaL_argerror + luaL_buffinit=lua51.luaL_buffinit + luaL_callmeta=lua51.luaL_callmeta + luaL_checkany=lua51.luaL_checkany + luaL_checkinteger=lua51.luaL_checkinteger + luaL_checklstring=lua51.luaL_checklstring + luaL_checknumber=lua51.luaL_checknumber + luaL_checkoption=lua51.luaL_checkoption + luaL_checkstack=lua51.luaL_checkstack + luaL_checktype=lua51.luaL_checktype + luaL_checkudata=lua51.luaL_checkudata + luaL_error=lua51.luaL_error + luaL_findtable=lua51.luaL_findtable + luaL_getmetafield=lua51.luaL_getmetafield + luaL_gsub=lua51.luaL_gsub + luaL_loadbuffer=lua51.luaL_loadbuffer + luaL_loadfile=lua51.luaL_loadfile + luaL_loadstring=lua51.luaL_loadstring + luaL_newmetatable=lua51.luaL_newmetatable + luaL_newstate=lua51.luaL_newstate + luaL_openlib=lua51.luaL_openlib + luaL_openlibs=lua51.luaL_openlibs + luaL_optinteger=lua51.luaL_optinteger + luaL_optlstring=lua51.luaL_optlstring + luaL_optnumber=lua51.luaL_optnumber + luaL_prepbuffer=lua51.luaL_prepbuffer + luaL_pushresult=lua51.luaL_pushresult + luaL_ref=lua51.luaL_ref + luaL_register=lua51.luaL_register + luaL_typerror=lua51.luaL_typerror + luaL_unref=lua51.luaL_unref + luaL_where=lua51.luaL_where + lua_atpanic=lua51.lua_atpanic + lua_call=lua51.lua_call + lua_checkstack=lua51.lua_checkstack + lua_close=lua51.lua_close + lua_concat=lua51.lua_concat + lua_cpcall=lua51.lua_cpcall + lua_createtable=lua51.lua_createtable + lua_dump=lua51.lua_dump + lua_equal=lua51.lua_equal + lua_error=lua51.lua_error + lua_gc=lua51.lua_gc + lua_getallocf=lua51.lua_getallocf + lua_getfenv=lua51.lua_getfenv + lua_getfield=lua51.lua_getfield + lua_gethook=lua51.lua_gethook + lua_gethookcount=lua51.lua_gethookcount + lua_gethookmask=lua51.lua_gethookmask + lua_getinfo=lua51.lua_getinfo + lua_getlocal=lua51.lua_getlocal + lua_getmetatable=lua51.lua_getmetatable + lua_getstack=lua51.lua_getstack + lua_gettable=lua51.lua_gettable + lua_gettop=lua51.lua_gettop + lua_getupvalue=lua51.lua_getupvalue + lua_insert=lua51.lua_insert + lua_iscfunction=lua51.lua_iscfunction + lua_isnumber=lua51.lua_isnumber + lua_isstring=lua51.lua_isstring + lua_isuserdata=lua51.lua_isuserdata + lua_lessthan=lua51.lua_lessthan + lua_load=lua51.lua_load + lua_newstate=lua51.lua_newstate + lua_newthread=lua51.lua_newthread + lua_newuserdata=lua51.lua_newuserdata + lua_next=lua51.lua_next + lua_objlen=lua51.lua_objlen + lua_pcall=lua51.lua_pcall + lua_pushboolean=lua51.lua_pushboolean + lua_pushcclosure=lua51.lua_pushcclosure + lua_pushfstring=lua51.lua_pushfstring + lua_pushinteger=lua51.lua_pushinteger + lua_pushlightuserdata=lua51.lua_pushlightuserdata + lua_pushlstring=lua51.lua_pushlstring + lua_pushnil=lua51.lua_pushnil + lua_pushnumber=lua51.lua_pushnumber + lua_pushstring=lua51.lua_pushstring + lua_pushthread=lua51.lua_pushthread + lua_pushvalue=lua51.lua_pushvalue + lua_pushvfstring=lua51.lua_pushvfstring + lua_rawequal=lua51.lua_rawequal + lua_rawget=lua51.lua_rawget + lua_rawgeti=lua51.lua_rawgeti + lua_rawset=lua51.lua_rawset + lua_rawseti=lua51.lua_rawseti + lua_remove=lua51.lua_remove + lua_replace=lua51.lua_replace + lua_resume=lua51.lua_resume + lua_setallocf=lua51.lua_setallocf + lua_setfenv=lua51.lua_setfenv + lua_setfield=lua51.lua_setfield + lua_sethook=lua51.lua_sethook + lua_setlocal=lua51.lua_setlocal + lua_setmetatable=lua51.lua_setmetatable + lua_settable=lua51.lua_settable + lua_settop=lua51.lua_settop + lua_setupvalue=lua51.lua_setupvalue + lua_status=lua51.lua_status + lua_toboolean=lua51.lua_toboolean + lua_tocfunction=lua51.lua_tocfunction + lua_tointeger=lua51.lua_tointeger + lua_tolstring=lua51.lua_tolstring + lua_tonumber=lua51.lua_tonumber + lua_topointer=lua51.lua_topointer + lua_tothread=lua51.lua_tothread + lua_touserdata=lua51.lua_touserdata + lua_type=lua51.lua_type + lua_typename=lua51.lua_typename + lua_xmove=lua51.lua_xmove + lua_yield=lua51.lua_yield diff --git a/lib/luaproxy/lua5.1.lua b/lib/luaproxy/lua5.1.lua new file mode 100644 index 000000000..b826e12d0 --- /dev/null +++ b/lib/luaproxy/lua5.1.lua @@ -0,0 +1,140 @@ + +-- lua5.1.lua +-- Generates the lua5.1.def file from the list of Lua symbols below + + + + + +local symbols = +{ + "luaL_addlstring", + "luaL_addstring", + "luaL_addvalue", + "luaL_argerror", + "luaL_buffinit", + "luaL_callmeta", + "luaL_checkany", + "luaL_checkinteger", + "luaL_checklstring", + "luaL_checknumber", + "luaL_checkoption", + "luaL_checkstack", + "luaL_checktype", + "luaL_checkudata", + "luaL_error", + "luaL_findtable", + "luaL_getmetafield", + "luaL_gsub", + "luaL_loadbuffer", + "luaL_loadfile", + "luaL_loadstring", + "luaL_newmetatable", + "luaL_newstate", + "luaL_openlib", + "luaL_openlibs", + "luaL_optinteger", + "luaL_optlstring", + "luaL_optnumber", + "luaL_prepbuffer", + "luaL_pushresult", + "luaL_ref", + "luaL_register", + "luaL_typerror", + "luaL_unref", + "luaL_where", + "lua_atpanic", + "lua_call", + "lua_checkstack", + "lua_close", + "lua_concat", + "lua_cpcall", + "lua_createtable", + "lua_dump", + "lua_equal", + "lua_error", + "lua_gc", + "lua_getallocf", + "lua_getfenv", + "lua_getfield", + "lua_gethook", + "lua_gethookcount", + "lua_gethookmask", + "lua_getinfo", + "lua_getlocal", + "lua_getmetatable", + "lua_getstack", + "lua_gettable", + "lua_gettop", + "lua_getupvalue", + "lua_insert", + "lua_iscfunction", + "lua_isnumber", + "lua_isstring", + "lua_isuserdata", + "lua_lessthan", + "lua_load", + "lua_newstate", + "lua_newthread", + "lua_newuserdata", + "lua_next", + "lua_objlen", + "lua_pcall", + "lua_pushboolean", + "lua_pushcclosure", + "lua_pushfstring", + "lua_pushinteger", + "lua_pushlightuserdata", + "lua_pushlstring", + "lua_pushnil", + "lua_pushnumber", + "lua_pushstring", + "lua_pushthread", + "lua_pushvalue", + "lua_pushvfstring", + "lua_rawequal", + "lua_rawget", + "lua_rawgeti", + "lua_rawset", + "lua_rawseti", + "lua_remove", + "lua_replace", + "lua_resume", + "lua_setallocf", + "lua_setfenv", + "lua_setfield", + "lua_sethook", + "lua_setlocal", + "lua_setmetatable", + "lua_settable", + "lua_settop", + "lua_setupvalue", + "lua_status", + "lua_toboolean", + "lua_tocfunction", + "lua_tointeger", + "lua_tolstring", + "lua_tonumber", + "lua_topointer", + "lua_tothread", + "lua_touserdata", + "lua_type", + "lua_typename", + "lua_xmove", + "lua_yield", + -- "luaopen_base", + -- "luaopen_debug", + -- "luaopen_io", + -- "luaopen_math", + -- "luaopen_os", + -- "luaopen_package", + -- "luaopen_string", + -- "luaopen_table", +} + +local def = io.open("lua5.1.def", "w") +def:write("EXPORTS\n") +for _,symbol in ipairs(symbols) do + def:write("\t" .. symbol .. "=lua51." .. symbol .. "\n") +end +def:close() diff --git a/lib/md5/CMakeLists.txt b/lib/md5/CMakeLists.txt deleted file mode 100644 index cd9fe6320..000000000 --- a/lib/md5/CMakeLists.txt +++ /dev/null @@ -1,12 +0,0 @@ - -cmake_minimum_required (VERSION 2.6) -project (md5) - -include_directories ("${PROJECT_SOURCE_DIR}/../../src/") - -file(GLOB SOURCE - "*.cpp" - "*.h" -) - -add_library(md5 ${SOURCE}) diff --git a/lib/md5/md5.cpp b/lib/md5/md5.cpp deleted file mode 100644 index eae0fc3f2..000000000 --- a/lib/md5/md5.cpp +++ /dev/null @@ -1,369 +0,0 @@ -/* MD5 - converted to C++ class by Frank Thilo (thilo@unix-ag.org) - for bzflag (http://www.bzflag.org) - - based on: - - md5.h and md5.c - reference implemantion of RFC 1321 - - Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - -*/ - -/* interface header */ -#include "md5.h" - -/* system implementation headers */ -#include <stdio.h> - -#ifndef _WIN32 - #include <cstring> -#endif - - - - - -// Constants for MD5Transform routine. -#define S11 7 -#define S12 12 -#define S13 17 -#define S14 22 -#define S21 5 -#define S22 9 -#define S23 14 -#define S24 20 -#define S31 4 -#define S32 11 -#define S33 16 -#define S34 23 -#define S41 6 -#define S42 10 -#define S43 15 -#define S44 21 - -/////////////////////////////////////////////// - -// F, G, H and I are basic MD5 functions. -inline MD5::uint4 MD5::F(uint4 x, uint4 y, uint4 z) { - return x&y | ~x&z; -} - -inline MD5::uint4 MD5::G(uint4 x, uint4 y, uint4 z) { - return x&z | y&~z; -} - -inline MD5::uint4 MD5::H(uint4 x, uint4 y, uint4 z) { - return x^y^z; -} - -inline MD5::uint4 MD5::I(uint4 x, uint4 y, uint4 z) { - return y ^ (x | ~z); -} - -// rotate_left rotates x left n bits. -inline MD5::uint4 MD5::rotate_left(uint4 x, int n) { - return (x << n) | (x >> (32-n)); -} - -// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. -// Rotation is separate from addition to prevent recomputation. -inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a+ F(b,c,d) + x + ac, s) + b; -} - -inline void MD5::GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + G(b,c,d) + x + ac, s) + b; -} - -inline void MD5::HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + H(b,c,d) + x + ac, s) + b; -} - -inline void MD5::II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) { - a = rotate_left(a + I(b,c,d) + x + ac, s) + b; -} - -////////////////////////////////////////////// - -// default ctor, just initailize -MD5::MD5() -{ - init(); -} - -////////////////////////////////////////////// - -// nifty shortcut ctor, compute MD5 for string and finalize it right away -MD5::MD5(const std::string &text) -{ - init(); - update(text.c_str(), text.length()); - finalize(); -} - -////////////////////////////// - -void MD5::init() -{ - finalized=false; - - count[0] = 0; - count[1] = 0; - - // load magic initialization constants. - state[0] = 0x67452301; - state[1] = 0xefcdab89; - state[2] = 0x98badcfe; - state[3] = 0x10325476; -} - -////////////////////////////// - -// decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4. -void MD5::decode(uint4 output[], const uint1 input[], size_type len) -{ - for (unsigned int i = 0, j = 0; j < len; i++, j += 4) - output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) | - (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24); -} - -////////////////////////////// - -// encodes input (uint4) into output (unsigned char). Assumes len is -// a multiple of 4. -void MD5::encode(uint1 output[], const uint4 input[], size_type len) -{ - for (size_type i = 0, j = 0; j < len; i++, j += 4) { - output[j] = input[i] & 0xff; - output[j+1] = (input[i] >> 8) & 0xff; - output[j+2] = (input[i] >> 16) & 0xff; - output[j+3] = (input[i] >> 24) & 0xff; - } -} - -////////////////////////////// - -// apply MD5 algo on a block -void MD5::transform(const uint1 block[blocksize]) -{ - uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; - decode (x, block, blocksize); - - /* Round 1 */ - FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ - FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ - FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ - FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ - FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ - FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ - FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ - FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ - FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ - FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ - FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ - FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ - FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ - FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ - FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ - FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ - - /* Round 2 */ - GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ - GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ - GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ - GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ - GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ - GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ - GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ - GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ - GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ - GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ - GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ - GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ - GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ - GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ - GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ - GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ - - /* Round 3 */ - HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ - HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ - HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ - HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ - HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ - HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ - HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ - HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ - HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ - HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ - HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ - HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ - HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ - HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ - HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ - HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ - - /* Round 4 */ - II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ - II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ - II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ - II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ - II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ - II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ - II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ - II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ - II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ - II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ - II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ - II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ - II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ - II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ - II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ - II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ - - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - - // Zeroize sensitive information. - memset(x, 0, sizeof x); -} - -////////////////////////////// - -// MD5 block update operation. Continues an MD5 message-digest -// operation, processing another message block -void MD5::update(const unsigned char input[], size_type length) -{ - // compute number of bytes mod 64 - size_type index = count[0] / 8 % blocksize; - - // Update number of bits - if ((count[0] += (length << 3)) < (length << 3)) - count[1]++; - count[1] += (length >> 29); - - // number of bytes we need to fill in buffer - size_type firstpart = 64 - index; - - size_type i; - - // transform as many times as possible. - if (length >= firstpart) - { - // fill buffer first, transform - memcpy(&buffer[index], input, firstpart); - transform(buffer); - - // transform chunks of blocksize (64 bytes) - for (i = firstpart; i + blocksize <= length; i += blocksize) - transform(&input[i]); - - index = 0; - } - else - i = 0; - - // buffer remaining input - memcpy(&buffer[index], &input[i], length-i); -} - -////////////////////////////// - -// for convenience provide a verson with signed char -void MD5::update(const char input[], size_type length) -{ - update((const unsigned char*)input, length); -} - -////////////////////////////// - -// MD5 finalization. Ends an MD5 message-digest operation, writing the -// the message digest and zeroizing the context. -MD5& MD5::finalize() -{ - static unsigned char padding[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - if (!finalized) { - // Save number of bits - unsigned char bits[8]; - encode(bits, count, 8); - - // pad out to 56 mod 64. - size_type index = count[0] / 8 % 64; - size_type padLen = (index < 56) ? (56 - index) : (120 - index); - update(padding, padLen); - - // Append length (before padding) - update(bits, 8); - - // Store state in digest - encode(digest, state, 16); - - // Zeroize sensitive information. - memset(buffer, 0, sizeof buffer); - memset(count, 0, sizeof count); - - finalized=true; - } - - return *this; -} - -////////////////////////////// - -// return hex representation of digest as string -std::string MD5::hexdigest() const -{ - if (!finalized) - return ""; - - char buf[33]; - for (int i=0; i<16; i++) - sprintf(buf+i*2, "%02x", digest[i]); - buf[32]=0; - - return std::string(buf); -} - -////////////////////////////// - -std::ostream& operator<<(std::ostream& out, MD5 md5) -{ - return out << md5.hexdigest(); -} - -////////////////////////////// - -std::string md5(const std::string & str) -{ - MD5 md5 = MD5(str); - - return md5.hexdigest(); -} diff --git a/lib/md5/md5.h b/lib/md5/md5.h deleted file mode 100644 index 3aa88ac22..000000000 --- a/lib/md5/md5.h +++ /dev/null @@ -1,93 +0,0 @@ -/* MD5 - converted to C++ class by Frank Thilo (thilo@unix-ag.org) - for bzflag (http://www.bzflag.org) - - based on: - - md5.h and md5.c - reference implementation of RFC 1321 - - Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All -rights reserved. - -License to copy and use this software is granted provided that it -is identified as the "RSA Data Security, Inc. MD5 Message-Digest -Algorithm" in all material mentioning or referencing this software -or this function. - -License is also granted to make and use derivative works provided -that such works are identified as "derived from the RSA Data -Security, Inc. MD5 Message-Digest Algorithm" in all material -mentioning or referencing the derived work. - -RSA Data Security, Inc. makes no representations concerning either -the merchantability of this software or the suitability of this -software for any particular purpose. It is provided "as is" -without express or implied warranty of any kind. - -These notices must be retained in any copies of any part of this -documentation and/or software. - -*/ - -#ifndef BZF_MD5_H -#define BZF_MD5_H - -#include <string> -#include <iostream> - - -// a small class for calculating MD5 hashes of strings or byte arrays -// it is not meant to be fast or secure -// -// usage: 1) feed it blocks of uchars with update() -// 2) finalize() -// 3) get hexdigest() string -// or -// MD5(std::string).hexdigest() -// -// assumes that char is 8 bit and int is 32 bit -class MD5 -{ -public: - typedef unsigned int size_type; // must be 32bit - - MD5(); - MD5(const std::string& text); - void update(const unsigned char *buf, size_type length); - void update(const char *buf, size_type length); - MD5& finalize(); - std::string hexdigest() const; - friend std::ostream& operator<<(std::ostream&, MD5 md5); - -private: - void init(); - typedef unsigned char uint1; // 8bit - typedef unsigned int uint4; // 32bit - enum {blocksize = 64}; // VC6 won't eat a const static int here - - void transform(const uint1 block[blocksize]); - static void decode(uint4 output[], const uint1 input[], size_type len); - static void encode(uint1 output[], const uint4 input[], size_type len); - - bool finalized; - uint1 buffer[blocksize]; // bytes that didn't fit in last 64 byte chunk - uint4 count[2]; // 64bit counter for number of bits (lo, hi) - uint4 state[4]; // digest so far - uint1 digest[16]; // the result - - // low level logic operations - static inline uint4 F(uint4 x, uint4 y, uint4 z); - static inline uint4 G(uint4 x, uint4 y, uint4 z); - static inline uint4 H(uint4 x, uint4 y, uint4 z); - static inline uint4 I(uint4 x, uint4 y, uint4 z); - static inline uint4 rotate_left(uint4 x, int n); - static inline void FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); - static inline void II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac); -}; - -std::string md5(const std::string & str); - -#endif diff --git a/lib/polarssl b/lib/polarssl -Subproject 1ed82759c68f92c4acc7e3f33b850cf9f01c8ab +Subproject 784b04ff9afd5faeaeb15c3fa159ff98adf5518 diff --git a/lib/polarssl.cmake b/lib/polarssl.cmake index 91f0fa145..ced70b94e 100644 --- a/lib/polarssl.cmake +++ b/lib/polarssl.cmake @@ -1,9 +1,9 @@ if(NOT TARGET polarssl) message("including polarssl") + set(ENABLE_TESTING OFF CACHE BOOL "Disable tests") + set(ENABLE_PROGRAMS OFF CACHE BOOL "Disable programs") if (SELF_TEST) - set(ENABLE_TESTING OFF CACHE BOOL "Disable tests") - set(ENABLE_PROGRAMS OFF CACHE BOOL "Disable programs") add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl) else() add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/polarssl/ ${CMAKE_CURRENT_BINARY_DIR}/lib/polarssl EXCLUDE_FROM_ALL) diff --git a/lib/sqlite/CMakeLists.txt b/lib/sqlite/CMakeLists.txt index 9add2280b..993dac146 100644 --- a/lib/sqlite/CMakeLists.txt +++ b/lib/sqlite/CMakeLists.txt @@ -9,8 +9,14 @@ file(GLOB SOURCE ) -# add headers to MSVC project files: +# Lua is required as a DLL for LuaSQLite: if (WIN32) + add_definitions(-DLUA_BUILD_AS_DLL) +endif() + + +# add headers to MSVC project files: +if (MSVC) file(GLOB HEADERS "src/*.h") list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/src/lua.h" "${PROJECT_SOURCE_DIR}/src/luac.h") set(SOURCE ${SOURCE} ${HEADERS}) @@ -23,6 +29,7 @@ if (${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") endif() add_library(sqlite ${SOURCE}) +target_link_libraries(sqlite lua) if (UNIX) target_link_libraries(sqlite ${DYNAMIC_LOADER}) diff --git a/lib/tolua++/CMakeLists.txt b/lib/tolua++/CMakeLists.txt index e68a0e15b..12054323b 100644 --- a/lib/tolua++/CMakeLists.txt +++ b/lib/tolua++/CMakeLists.txt @@ -44,14 +44,13 @@ file(GLOB BIN_SOURCE "src/bin/*.c" ) - - add_executable(tolua ${BIN_SOURCE}) add_library(tolualib ${LIB_SOURCE}) +target_link_libraries(tolualib lua) #m is the standard math librarys if(UNIX) target_link_libraries(tolua m ${DYNAMIC_LOADER}) endif() -target_link_libraries(tolua lua tolualib) +target_link_libraries(tolua tolualib lua) diff --git a/lib/tolua++/src/bin/lua/_driver.lua b/lib/tolua++/src/bin/lua/_driver.lua index 21db96098..87ecd42ea 100644 --- a/lib/tolua++/src/bin/lua/_driver.lua +++ b/lib/tolua++/src/bin/lua/_driver.lua @@ -6,21 +6,21 @@ if mobdebugfound then mobdebug.start() end -- The list of valid arguments that the ToLua scripts can process: local KnownArgs = { ['v'] = true, - ['h'] = true, - ['p'] = true, - ['P'] = true, - ['o'] = true, - ['n'] = true, - ['H'] = true, - ['S'] = true, - ['1'] = true, - ['L'] = true, - ['D'] = true, - ['W'] = true, - ['C'] = true, - ['E'] = true, - ['t'] = true, - ['q'] = true, + ['h'] = true, + ['p'] = true, + ['P'] = true, + ['o'] = true, + ['n'] = true, + ['H'] = true, + ['S'] = true, + ['1'] = true, + ['L'] = true, + ['D'] = true, + ['W'] = true, + ['C'] = true, + ['E'] = true, + ['t'] = true, + ['q'] = true, } diff --git a/src/Bindings/.gitignore b/src/Bindings/.gitignore index af8aa76fa..0d00dd578 100644 --- a/src/Bindings/.gitignore +++ b/src/Bindings/.gitignore @@ -1 +1,2 @@ lua51.dll +LuaState_Call.inc diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 7a5ed1425..32638df96 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -811,6 +811,18 @@ void cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal) +void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal) +{ + if (lua_isnumber(m_LuaState, a_StackPos)) + { + a_ReturnedVal = (eWeather)Clamp((int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal), (int)wSunny, (int)wThunderstorm); + } +} + + + + + bool cLuaState::CallFunction(int a_NumResults) { ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 066390e39..b1ac3578a 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -9,10 +9,11 @@ Owned lua_State is created by calling Create() and the cLuaState automatically c Or, lua_State can be attached by calling Attach(), the cLuaState doesn't close such a state Attaching a state will automatically close an owned state. -Calling a Lua function is done by pushing the function, either by PushFunction() or PushFunctionFromRegistry(), -then pushing the arguments (PushString(), PushNumber(), PushUserData() etc.) and finally -executing CallFunction(). cLuaState automatically keeps track of the number of arguments and the name of the -function (for logging purposes), which makes the call less error-prone. +Calling a Lua function is done internally by pushing the function using PushFunction(), then pushing the +arguments and finally executing CallFunction(). cLuaState automatically keeps track of the number of +arguments and the name of the function (for logging purposes). After the call the return values are read from +the stack using GetStackValue(). All of this is wrapped in a templated function overloads cLuaState::Call(), +which is generated automatically by gen_LuaState_Call.lua script file into the LuaState_Call.inc file. Reference management is provided by the cLuaState::cRef class. This is used when you need to hold a reference to any Lua object across several function calls; usually this is used for callbacks. The class is RAII-like, with @@ -30,6 +31,7 @@ extern "C" } #include "../Vector3.h" +#include "../Defines.h" @@ -222,625 +224,13 @@ public: /** Retrieve value at a_StackPos, if it is a valid number. If not, a_Value is unchanged */ void GetStackValue(int a_StackPos, double & a_Value); + /** Retrieve value at a_StackPos, if it is a valid number, converting and clamping it to eWeather. + If not, a_Value is unchanged. */ + void GetStackValue(int a_StackPos, eWeather & a_Value); + - /** Call any 0-param 0-return Lua function in a single line: */ - template <typename FnT> - bool Call(FnT a_FnName) - { - if (!PushFunction(a_FnName)) - { - return false; - } - return CallFunction(0); - } - - /** Call any 1-param 0-return Lua function in a single line: */ - template< - typename FnT, - typename ArgT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - return CallFunction(0); - } - - /** Call any 2-param 0-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - return CallFunction(0); - } - - /** Call any 3-param 0-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3) - { - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - return CallFunction(0); - } - - /** Call any 0-param 1-return Lua function in a single line: */ - template< - typename FnT, typename RetT1 - > - bool Call(FnT a_FnName, const cRet & a_Mark, RetT1 & a_Ret1) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - if (!CallFunction(1)) - { - return false; - } - GetStackValue(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /** Call any 1-param 1-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1) - { - int InitialTop = lua_gettop(m_LuaState); - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - if (!CallFunction(1)) - { - return false; - } - GetStackValue(-1, a_Ret1); - lua_pop(m_LuaState, 1); - ASSERT(InitialTop == lua_gettop(m_LuaState)); - return true; - } - - /** Call any 2-param 1-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - if (!CallFunction(1)) - { - return false; - } - GetStackValue(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /** Call any 3-param 1-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - if (!CallFunction(1)) - { - return false; - } - GetStackValue(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /** Call any 4-param 1-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - if (!CallFunction(1)) - { - return false; - } - GetStackValue(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /** Call any 5-param 1-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - if (!CallFunction(1)) - { - return false; - } - GetStackValue(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /** Call any 6-param 1-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, - typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - if (!CallFunction(1)) - { - return false; - } - GetStackValue(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /** Call any 7-param 1-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, - typename ArgT7, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - if (!CallFunction(1)) - { - return false; - } - GetStackValue(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /** Call any 8-param 1-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, - typename ArgT7, typename ArgT8, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - Push(a_Arg8); - if (!CallFunction(1)) - { - return false; - } - GetStackValue(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /** Call any 9-param 1-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, - typename ArgT7, typename ArgT8, typename ArgT9, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - Push(a_Arg8); - Push(a_Arg9); - if (!CallFunction(1)) - { - return false; - } - GetStackValue(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /** Call any 10-param 1-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6, - typename ArgT7, typename ArgT8, typename ArgT9, typename ArgT10, typename RetT1 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, ArgT10 a_Arg10, const cRet & a_Mark, RetT1 & a_Ret1) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - Push(a_Arg8); - Push(a_Arg9); - Push(a_Arg10); - if (!CallFunction(1)) - { - return false; - } - GetStackValue(-1, a_Ret1); - lua_pop(m_LuaState, 1); - return true; - } - - /** Call any 1-param 2-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - if (!CallFunction(2)) - { - return false; - } - GetStackValue(-2, a_Ret1); - GetStackValue(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /** Call any 2-param 2-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - if (!CallFunction(2)) - { - return false; - } - GetStackValue(-2, a_Ret1); - GetStackValue(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /** Call any 3-param 2-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, - typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - if (!CallFunction(2)) - { - return false; - } - GetStackValue(-2, a_Ret1); - GetStackValue(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /** Call any 4-param 2-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, - typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - if (!CallFunction(2)) - { - return false; - } - GetStackValue(-2, a_Ret1); - GetStackValue(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /** Call any 5-param 2-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - if (!CallFunction(2)) - { - return false; - } - GetStackValue(-2, a_Ret1); - GetStackValue(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /** Call any 6-param 2-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename ArgT6, - typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - if (!CallFunction(2)) - { - return false; - } - GetStackValue(-2, a_Ret1); - GetStackValue(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /** Call any 7-param 2-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename ArgT6, typename ArgT7, - typename RetT1, typename RetT2 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - if (!CallFunction(2)) - { - return false; - } - GetStackValue(-2, a_Ret1); - GetStackValue(-1, a_Ret2); - lua_pop(m_LuaState, 2); - return true; - } - - /** Call any 7-param 3-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename ArgT6, typename ArgT7, - typename RetT1, typename RetT2, typename RetT3 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - if (!CallFunction(3)) - { - return false; - } - GetStackValue(-3, a_Ret1); - GetStackValue(-2, a_Ret2); - GetStackValue(-1, a_Ret3); - lua_pop(m_LuaState, 3); - return true; - } - - /** Call any 8-param 3-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename ArgT6, typename ArgT7, typename ArgT8, - typename RetT1, typename RetT2, typename RetT3 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - Push(a_Arg8); - if (!CallFunction(3)) - { - return false; - } - GetStackValue(-3, a_Ret1); - GetStackValue(-2, a_Ret2); - GetStackValue(-1, a_Ret3); - lua_pop(m_LuaState, 3); - return true; - } - - /** Call any 9-param 5-return Lua function in a single line: */ - template< - typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, - typename ArgT6, typename ArgT7, typename ArgT8, typename ArgT9, - typename RetT1, typename RetT2, typename RetT3, typename RetT4, typename RetT5 - > - bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3, RetT4 & a_Ret4, RetT5 & a_Ret5) - { - UNUSED(a_Mark); - if (!PushFunction(a_FnName)) - { - return false; - } - Push(a_Arg1); - Push(a_Arg2); - Push(a_Arg3); - Push(a_Arg4); - Push(a_Arg5); - Push(a_Arg6); - Push(a_Arg7); - Push(a_Arg8); - Push(a_Arg9); - if (!CallFunction(5)) - { - return false; - } - GetStackValue(-5, a_Ret1); - GetStackValue(-4, a_Ret2); - GetStackValue(-3, a_Ret3); - GetStackValue(-2, a_Ret4); - GetStackValue(-1, a_Ret5); - lua_pop(m_LuaState, 5); - return true; - } + // Include the cLuaState::Call() overload implementation that is generated by the gen_LuaState_Call.lua script: + #include "LuaState_Call.inc" /** Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions */ diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index f52d970bf..88d40bfd9 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -4,7 +4,7 @@ #include "ManualBindings.h" #undef TOLUA_TEMPLATE_BIND #include "tolua++/include/tolua++.h" - +#include "polarssl/md5.h" #include "Plugin.h" #include "PluginLua.h" #include "PluginManager.h" @@ -25,7 +25,6 @@ #include "../BlockEntities/NoteEntity.h" #include "../BlockEntities/MobHeadEntity.h" #include "../BlockEntities/FlowerPotEntity.h" -#include "md5/md5.h" #include "../LineBlockTracer.h" #include "../WorldStorage/SchematicFileSerializer.h" #include "../CompositeChat.h" @@ -2001,9 +2000,11 @@ static int tolua_cPlugin_Call(lua_State * tolua_S) static int tolua_md5(lua_State* tolua_S) { - std::string SourceString = tolua_tostring(tolua_S, 1, 0); - std::string CryptedString = md5( SourceString ); - tolua_pushstring( tolua_S, CryptedString.c_str() ); + unsigned char Output[16]; + size_t len = 0; + const unsigned char * SourceString = (const unsigned char *)lua_tolstring(tolua_S, 1, &len); + md5(SourceString, len, Output); + lua_pushlstring(tolua_S, (const char *)Output, ARRAYCOUNT(Output)); return 1; } diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index c6461c861..8ba20c026 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -72,6 +72,7 @@ public: virtual bool OnPlayerEating (cPlayer & a_Player) = 0; virtual bool OnPlayerFished (cPlayer & a_Player, const cItems & a_Reward) = 0; virtual bool OnPlayerFishing (cPlayer & a_Player, cItems & a_Reward) = 0; + virtual bool OnPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel) = 0; virtual bool OnPlayerJoined (cPlayer & a_Player) = 0; virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0; virtual bool OnPlayerMoved (cPlayer & a_Player) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 96c5ccde7..104380ea4 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -715,6 +715,26 @@ bool cPluginLua::OnPlayerEating(cPlayer & a_Player) +bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FOOD_LEVEL_CHANGE]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Player, a_NewFoodLevel, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward) { cCSLock Lock(m_CriticalSection); @@ -1327,18 +1347,15 @@ bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) { cCSLock Lock(m_CriticalSection); bool res = false; - int NewWeather = a_NewWeather; cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) { - m_LuaState.Call((int)(**itr), &a_World, NewWeather, cLuaState::Return, res, NewWeather); + m_LuaState.Call((int)(**itr), &a_World, a_NewWeather, cLuaState::Return, res, a_NewWeather); if (res) { - a_NewWeather = (eWeather)NewWeather; return true; } } - a_NewWeather = (eWeather)NewWeather; return false; } @@ -1714,7 +1731,7 @@ bool cPluginLua::CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer ASSERT(a_FnRef != LUA_REFNIL); cCSLock Lock(m_CriticalSection); - bool res; + bool res = false; m_LuaState.Call(a_FnRef, &a_Window, &a_Player, a_CanRefuse, cLuaState::Return, res); return res; } diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index 598e031c0..9c9de95c6 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -95,6 +95,7 @@ public: virtual bool OnPlayerEating (cPlayer & a_Player) override; virtual bool OnPlayerFished (cPlayer & a_Player, const cItems & a_Reward) override; virtual bool OnPlayerFishing (cPlayer & a_Player, cItems & a_Reward) override; + virtual bool OnPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel) override; virtual bool OnPlayerJoined (cPlayer & a_Player) override; virtual bool OnPlayerMoved (cPlayer & a_Player) override; virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index c50100d6f..7e6502515 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -257,18 +257,44 @@ bool cPluginManager::CallHookBlockToPickups( bool cPluginManager::CallHookChat(cPlayer * a_Player, AString & a_Message) { - bool WasCommandForbidden = false; - if (HandleCommand(a_Player, a_Message, true, WasCommandForbidden)) // We use HandleCommand as opposed to ExecuteCommand to accomodate the need to the WasCommandForbidden bool + // Check if the message contains a command, execute it: + switch (HandleCommand(a_Player, a_Message, true)) { - return true; // Chat message was handled as command - } - else if (WasCommandForbidden) // Couldn't be handled as command, was it because of insufficient permissions? - { - return true; // Yes - message was sent in HandleCommand, abort + case crExecuted: + { + // The command has executed successfully + return true; + } + + case crBlocked: + { + // The command was blocked by a plugin using HOOK_EXECUTE_COMMAND + // The plugin has most likely sent a message to the player already + return true; + } + + case crError: + { + // An error in the plugin has prevented the command from executing. Report the error to the player: + a_Player->SendMessageFailure(Printf("Something went wrong while executing command \"%s\"", a_Message.c_str())); + return true; + } + + case crNoPermission: + { + // The player is not allowed to execute this command + a_Player->SendMessageFailure(Printf("Forbidden command; insufficient privileges: \"%s\"", a_Message.c_str())); + return true; + } + + case crUnknownCommand: + { + // This was not a known command, keep processing as a message + break; + } } - // Check if it was a standard command (starts with a slash) - // If it was, we know that it was completely unrecognised (WasCommandForbidden == false) + // Check if the message is a command (starts with a slash). If it is, we know that it wasn't recognised: if (!a_Message.empty() && (a_Message[0] == '/')) { AStringVector Split(StringSplit(a_Message, " ")); @@ -695,6 +721,25 @@ bool cPluginManager::CallHookPlayerEating(cPlayer & a_Player) +bool cPluginManager::CallHookPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) +{ + FIND_HOOK(HOOK_PLAYER_FOOD_LEVEL_CHANGE); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnPlayerFoodLevelChange(a_Player, a_NewFoodLevel)) + { + return true; + } + } + return false; +} + + + + + bool cPluginManager::CallHookPlayerFished(cPlayer & a_Player, const cItems a_Reward) { FIND_HOOK(HOOK_PLAYER_FISHED); @@ -1318,28 +1363,28 @@ bool cPluginManager::CallHookWorldTick(cWorld & a_World, float a_Dt, int a_LastT -bool cPluginManager::HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions, bool & a_WasCommandForbidden) +cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions) { ASSERT(a_Player != NULL); AStringVector Split(StringSplit(a_Command, " ")); if (Split.empty()) { - return false; + return crUnknownCommand; } CommandMap::iterator cmd = m_Commands.find(Split[0]); if (cmd == m_Commands.end()) { // Command not found - return false; + return crUnknownCommand; } // Ask plugins first if a command is okay to execute the command: if (CallHookExecuteCommand(a_Player, Split)) { LOGINFO("Player %s tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player->GetName().c_str(), Split[0].c_str()); - return false; + return crBlocked; } if ( @@ -1348,15 +1393,18 @@ bool cPluginManager::HandleCommand(cPlayer * a_Player, const AString & a_Command !a_Player->HasPermission(cmd->second.m_Permission) ) { - a_Player->SendMessageFailure(Printf("Forbidden command; insufficient privileges: \"%s\"", Split[0].c_str())); LOGINFO("Player %s tried to execute forbidden command: \"%s\"", a_Player->GetName().c_str(), Split[0].c_str()); - a_WasCommandForbidden = true; - return false; + return crNoPermission; } ASSERT(cmd->second.m_Plugin != NULL); - return cmd->second.m_Plugin->HandleCommand(Split, a_Player); + if (!cmd->second.m_Plugin->HandleCommand(Split, a_Player)) + { + return crError; + } + + return crExecuted; } @@ -1554,7 +1602,7 @@ AString cPluginManager::GetCommandPermission(const AString & a_Command) -bool cPluginManager::ExecuteCommand(cPlayer * a_Player, const AString & a_Command) +cPluginManager::CommandResult cPluginManager::ExecuteCommand(cPlayer * a_Player, const AString & a_Command) { return HandleCommand(a_Player, a_Command, true); } @@ -1563,7 +1611,7 @@ bool cPluginManager::ExecuteCommand(cPlayer * a_Player, const AString & a_Comman -bool cPluginManager::ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command) +cPluginManager::CommandResult cPluginManager::ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command) { return HandleCommand(a_Player, a_Command, false); } diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index be40bd2f7..d435024bb 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -57,8 +57,17 @@ public: // tolua_export // Called each tick virtual void Tick(float a_Dt); - + // tolua_begin + enum CommandResult + { + crExecuted, + crUnknownCommand, + crError, + crBlocked, + crNoPermission, + } ; + enum PluginHook { HOOK_BLOCK_SPREAD, @@ -87,6 +96,7 @@ public: // tolua_export HOOK_PLAYER_EATING, HOOK_PLAYER_FISHED, HOOK_PLAYER_FISHING, + HOOK_PLAYER_FOOD_LEVEL_CHANGE, HOOK_PLAYER_JOINED, HOOK_PLAYER_LEFT_CLICK, HOOK_PLAYER_MOVING, @@ -188,6 +198,7 @@ public: // tolua_export bool CallHookPlayerEating (cPlayer & a_Player); bool CallHookPlayerFished (cPlayer & a_Player, const cItems a_Reward); bool CallHookPlayerFishing (cPlayer & a_Player, cItems a_Reward); + bool CallHookPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel); bool CallHookPlayerJoined (cPlayer & a_Player); bool CallHookPlayerMoving (cPlayer & a_Player); bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status); @@ -244,11 +255,11 @@ public: // tolua_export /** Returns the permission needed for the specified command; empty string if command not found */ AString GetCommandPermission(const AString & a_Command); // tolua_export - /** Executes the command, as if it was requested by a_Player. Checks permissions first. Returns true if executed. */ - bool ExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export + /** Executes the command, as if it was requested by a_Player. Checks permissions first. Returns crExecuted if executed. */ + CommandResult ExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export - /** Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found) */ - bool ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export + /** Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns crExecuted if executed. */ + CommandResult ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export /** Removes all console command bindings that the specified plugin has made */ void RemovePluginConsoleCommands(cPlugin * a_Plugin); @@ -321,13 +332,8 @@ private: /** Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again. */ bool AddPlugin(cPlugin * a_Plugin); - /** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns true if the command is handled. */ - bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions, bool & a_WasCommandForbidden); - bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions) - { - bool DummyBoolean = false; - return HandleCommand(a_Player, a_Command, a_ShouldCheckPermissions, DummyBoolean); - } + /** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns crExecuted if the command is executed. */ + cPluginManager::CommandResult HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions); } ; // tolua_export diff --git a/src/Bindings/gen_LuaState_Call.lua b/src/Bindings/gen_LuaState_Call.lua new file mode 100644 index 000000000..fb1797dc0 --- /dev/null +++ b/src/Bindings/gen_LuaState_Call.lua @@ -0,0 +1,196 @@ + +-- gen_LuaState_Call.lua + +-- Generates the cLuaState::Call() function templates that are included from LuaState.h + +--[[ +The cLuaState::Call() family of functions provides a template-based system for calling any Lua function +either by name or by reference with almost any number of parameters and return values. This is done by +providing a number of overloads of the same name with variable number of template-type parameters. To +separate the arguments from the return values, a special type of cLuaState::cRet is used. +--]] + + + + +print("Generating LuaState_Call.inc...") + + + + +-- List of combinations (# params, # returns) to generate: +local Combinations = +{ + -- no return values: + {0, 0}, + {1, 0}, + {2, 0}, + {3, 0}, + {4, 0}, + + -- 1 return value: + {0, 1}, + {1, 1}, + {2, 1}, + {3, 1}, + {4, 1}, + {5, 1}, + {6, 1}, + {7, 1}, + {8, 1}, + {9, 1}, + {10, 1}, + + -- 2 return values: + {0, 2}, + {1, 2}, + {2, 2}, + {3, 2}, + {4, 2}, + {5, 2}, + {6, 2}, + {7, 2}, + {8, 2}, + {9, 2}, + + -- Special combinations: + {7, 3}, + {8, 3}, + {9, 5}, +} + + + + +--- Writes a single overloaded function definition for the specified number of params and returns into f +--[[ +The format for the generated function is this: +/** Call the specified 3-param 2-return Lua function: +Returns true if call succeeded, false if there was an error. */ +template <typename FnT, typename ParamT1, typename ParamT2, typename ParamT3, typename RetT1, typename RetT2> +bool Call(FnT a_Function, ParamT1 a_Param1, ParamT2 a_Param2, ParamT3 a_Param3, const cLuaState::cRet & a_RetMark, RetT1 & a_Ret1, RetT2 & a_Ret2) +{ + UNUSED(a_RetMark); + if (!PushFunction(a_Function)) + { + return false; + } + Push(a_Param1); + Push(a_Param2); + Push(a_Param3); + if (!CallFunction(2)) + { + return false; + } + GetStackValue(-2, a_Ret1); + GetStackValue(-1, a_Ret2); + lua_pop(m_LuaState, 2); + return true; +} +Note especially the negative numbers in GetStackValue() calls. +--]] +local function WriteOverload(f, a_NumParams, a_NumReturns) + -- Write the function doxy-comments: + f:write("/** Call the specified ", a_NumParams, "-param ", a_NumReturns, "-return Lua function:\n") + f:write("Returns true if call succeeded, false if there was an error. */\n") + + -- Write the template <...> line: + f:write("template <typename FnT") + for i = 1, a_NumParams do + f:write(", typename ParamT", i) + end + if (a_NumReturns > 0) then + for i = 1, a_NumReturns do + f:write(", typename RetT", i) + end + end + f:write(">\n") + + -- Write the function signature: + f:write("bool Call(") + f:write("FnT a_Function") + for i = 1, a_NumParams do + f:write(", ParamT", i, " a_Param", i) + end + if (a_NumReturns > 0) then + f:write(", const cLuaState::cRet & a_RetMark") + for i = 1, a_NumReturns do + f:write(", RetT", i, " & a_Ret", i) + end + end + f:write(")\n") + + -- Common code: + f:write("{\n") + if (a_NumReturns > 0) then + f:write("\tUNUSED(a_RetMark);\n") + end + f:write("\tif (!PushFunction(a_Function))\n") + f:write("\t{\n") + f:write("\t\treturn false;\n") + f:write("\t}\n") + + -- Push the params: + for i = 1, a_NumParams do + f:write("\tPush(a_Param", i, ");\n") + end + + -- Call the function: + f:write("\tif (!CallFunction(", a_NumReturns, "))\n") + f:write("\t{\n") + f:write("\t\treturn false;\n") + f:write("\t}\n") + + -- Get the return values: + for i = 1, a_NumReturns do + f:write("\tGetStackValue(", -1 - a_NumReturns + i, ", a_Ret", i, ");\n") + end + + -- Pop the returns off the stack, if needed: + if (a_NumReturns > 0) then + f:write("\tlua_pop(m_LuaState, ", a_NumReturns, ");\n") + end + + -- Everything ok: + f:write("\treturn true;\n") + f:write("}\n") + + -- Separate from the next function: + f:write("\n\n\n\n\n") +end + + + + + +local f = assert(io.open("LuaState_Call.inc", "w")) + +-- Write file header: +f:write([[ +// LuaState_Call.inc + +// This file is auto-generated by gen_LuaState_Call.lua +// Make changes to the generator instead of to this file! + +// This file contains the various overloads for the cLuaState::Call() function +// Each overload handles a different number of parameters / return values +]]) +f:write("\n\n\n\n\n") + +-- Write out a template function for each overload: +for _, combination in ipairs(Combinations) do + WriteOverload(f, combination[1], combination[2]) +end + +-- Close the generated file +f:close() + + + + + +print("LuaState_Call.inc generated") + + + + diff --git a/src/Bindings/virtual_method_hooks.lua b/src/Bindings/virtual_method_hooks.lua index c610d424f..8ad30bf78 100644 --- a/src/Bindings/virtual_method_hooks.lua +++ b/src/Bindings/virtual_method_hooks.lua @@ -3,6 +3,20 @@ local disable_virtual_hooks = true local enable_pure_virtual = true local default_private_access = false + + + + +-- Code generators used by the build +-- Note that these are not exactly needed for the bindings, but rather we +-- misuse tolua's Lua engine to process files for us +dofile("gen_LuaState_Call.lua") + + + + + + local access = {public = 0, protected = 1, private = 2} function preparse_hook(p) diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp index 185582ba3..0e4c42f0f 100644 --- a/src/BlockArea.cpp +++ b/src/BlockArea.cpp @@ -59,7 +59,7 @@ void InternalMergeBlocks( } else { - BLOCKTYPE FakeDestMeta = 0; + NIBBLETYPE FakeDestMeta = 0; Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], FakeDestMeta, (NIBBLETYPE)0); } ++DstIdx; diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp index 430f04551..05ad03a3d 100644 --- a/src/BlockEntities/BlockEntity.cpp +++ b/src/BlockEntities/BlockEntity.cpp @@ -28,24 +28,26 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE switch (a_BlockType) { case E_BLOCK_BEACON: return new cBeaconEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World, a_BlockType); case E_BLOCK_COMMAND_BLOCK: return new cCommandBlockEntity(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_ENDER_CHEST: return new cEnderChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_FLOWER_POT: return new cFlowerPotEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_HEAD: return new cMobHeadEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World); + case E_BLOCK_HEAD: return new cMobHeadEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_JUKEBOX: return new cJukeboxEntity (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_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); + case E_BLOCK_TRAPPED_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World, a_BlockType); case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World); case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); - case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World); } LOGD("%s: Requesting creation of an unknown block entity - block type %d (%s)", __FUNCTION__, a_BlockType, ItemTypeToString(a_BlockType).c_str() ); + ASSERT(!"Requesting creation of an unknown block entity"); return NULL; } diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp index cb9cc89bf..ca164464c 100644 --- a/src/BlockEntities/ChestEntity.cpp +++ b/src/BlockEntities/ChestEntity.cpp @@ -11,8 +11,9 @@ -cChestEntity::cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(E_BLOCK_CHEST, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World) +cChestEntity::cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World, BLOCKTYPE a_Type) : + super(a_Type, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_NumActivePlayers(0) { cBlockEntityWindowOwner::SetBlockEntity(this); } @@ -113,7 +114,7 @@ void cChestEntity::UsedBy(cPlayer * a_Player) // The few false positives aren't much to worry about int ChunkX, ChunkZ; cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ); - m_World->MarkChunkDirty(ChunkX, ChunkZ); + m_World->MarkChunkDirty(ChunkX, ChunkZ, true); } diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h index ce16f84d7..310618504 100644 --- a/src/BlockEntities/ChestEntity.h +++ b/src/BlockEntities/ChestEntity.h @@ -34,8 +34,8 @@ public: // tolua_end - /// Constructor used for normal operation - cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); + /** Constructor used for normal operation */ + cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World, BLOCKTYPE a_Type); virtual ~cChestEntity(); @@ -48,8 +48,20 @@ public: virtual void SendTo(cClientHandle & a_Client) override; virtual void UsedBy(cPlayer * a_Player) override; - /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate. + /** Opens a new chest window for this chest. + Scans for neighbors to open a double chest window, if appropriate. */ void OpenNewWindow(void); + + /** Gets the number of players who currently have this chest open */ + int GetNumberOfPlayers(void) const { return m_NumActivePlayers; } + + /** Sets the number of players who currently have this chest open */ + void SetNumberOfPlayers(int a_NumActivePlayers) { m_NumActivePlayers = a_NumActivePlayers; } + +private: + + /** Number of players who currently have this chest open */ + int m_NumActivePlayers; } ; // tolua_export diff --git a/src/BlockEntities/EnderChestEntity.cpp b/src/BlockEntities/EnderChestEntity.cpp index e53930798..17816d63e 100644 --- a/src/BlockEntities/EnderChestEntity.cpp +++ b/src/BlockEntities/EnderChestEntity.cpp @@ -12,9 +12,8 @@ cEnderChestEntity::cEnderChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) : - super(E_BLOCK_ENDER_CHEST, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World) + super(E_BLOCK_ENDER_CHEST, a_BlockX, a_BlockY, a_BlockZ, a_World) { - cBlockEntityWindowOwner::SetBlockEntity(this); } @@ -34,95 +33,63 @@ cEnderChestEntity::~cEnderChestEntity() -bool cEnderChestEntity::LoadFromJson(const Json::Value & a_Value) +void cEnderChestEntity::UsedBy(cPlayer * a_Player) { - m_PosX = a_Value.get("x", 0).asInt(); - m_PosY = a_Value.get("y", 0).asInt(); - m_PosZ = a_Value.get("z", 0).asInt(); - - Json::Value AllSlots = a_Value.get("Slots", 0); - int SlotIdx = 0; - for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr) + // If the window is not created, open it anew: + cWindow * Window = GetWindow(); + if (Window == NULL) { - cItem Item; - Item.FromJson(*itr); - SetSlot(SlotIdx, Item); - SlotIdx++; + OpenNewWindow(); + Window = GetWindow(); } - return true; -} - - - - - -void cEnderChestEntity::SaveToJson(Json::Value & a_Value) -{ - a_Value["x"] = m_PosX; - a_Value["y"] = m_PosY; - a_Value["z"] = m_PosZ; - - Json::Value AllSlots; - for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--) + + // Open the window for the player: + if (Window != NULL) { - Json::Value Slot; - m_Contents.GetSlot(i).GetJson(Slot); - AllSlots.append(Slot); + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } } - a_Value["Slots"] = AllSlots; } -void cEnderChestEntity::SendTo(cClientHandle & a_Client) +void cEnderChestEntity::OpenNewWindow() { - // The chest entity doesn't need anything sent to the client when it's created / gets in the viewdistance - // All the actual handling is in the cWindow UI code that gets called when the chest is rclked - - UNUSED(a_Client); + OpenWindow(new cEnderChestWindow(this)); } -void cEnderChestEntity::UsedBy(cPlayer * a_Player) +void cEnderChestEntity::LoadFromJson(const Json::Value & a_Value, cItemGrid & a_Grid) { - // If the window is not created, open it anew: - cWindow * Window = GetWindow(); - if (Window == NULL) - { - OpenNewWindow(); - Window = GetWindow(); - } - - // Open the window for the player: - if (Window != NULL) + int SlotIdx = 0; + for (Json::Value::iterator itr = a_Value.begin(); itr != a_Value.end(); ++itr) { - if (a_Player->GetWindow() != Window) - { - a_Player->OpenWindow(Window); - } + cItem Item; + Item.FromJson(*itr); + a_Grid.SetSlot(SlotIdx, Item); + SlotIdx++; } - - // This is rather a hack - // Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now - // We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first. - // The few false positives aren't much to worry about - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(m_PosX, m_PosZ, ChunkX, ChunkZ); - m_World->MarkChunkDirty(ChunkX, ChunkZ); } -void cEnderChestEntity::OpenNewWindow(void) +void cEnderChestEntity::SaveToJson(Json::Value & a_Value, const cItemGrid & a_Grid) { - OpenWindow(new cEnderChestWindow(this)); + for (int i = 0; i < a_Grid.GetNumSlots(); i++) + { + Json::Value Slot; + a_Grid.GetSlot(i).GetJson(Slot); + a_Value.append(Slot); + } } diff --git a/src/BlockEntities/EnderChestEntity.h b/src/BlockEntities/EnderChestEntity.h index 45beee45f..04af67683 100644 --- a/src/BlockEntities/EnderChestEntity.h +++ b/src/BlockEntities/EnderChestEntity.h @@ -1,20 +1,9 @@ #pragma once -#include "BlockEntityWithItems.h" - - - - - -namespace Json -{ - class Value; -}; - -class cClientHandle; -class cServer; -class cNBTData; +#include "BlockEntity.h" +#include "UI/WindowOwner.h" +#include "json/json.h" @@ -22,33 +11,28 @@ class cNBTData; // tolua_begin class cEnderChestEntity : - public cBlockEntityWithItems + public cBlockEntity, + public cBlockEntityWindowOwner { - typedef cBlockEntityWithItems super; - -public: - enum { - ContentsHeight = 3, - ContentsWidth = 9, - } ; + typedef cBlockEntity super; +public: // tolua_end - /// Constructor used for normal operation - cEnderChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); - + cEnderChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World); virtual ~cEnderChestEntity(); static const char * GetClassStatic(void) { return "cEnderChestEntity"; } - - bool LoadFromJson(const Json::Value & a_Value); // cBlockEntity overrides: - virtual void SaveToJson(Json::Value & a_Value) override; - virtual void SendTo(cClientHandle & a_Client) override; virtual void UsedBy(cPlayer * a_Player) override; + virtual void SaveToJson(Json::Value & a_Value) override { UNUSED(a_Value); } + virtual void SendTo(cClientHandle & a_Client) override { UNUSED(a_Client); } + + static void LoadFromJson(const Json::Value & a_Value, cItemGrid & a_Grid); + static void SaveToJson(Json::Value & a_Value, const cItemGrid & a_Grid); - /// Opens a new chest window for this chest. Scans for neighbors to open a double chest window, if appropriate. + /** Opens a new enderchest window for this enderchest */ void OpenNewWindow(void); } ; // tolua_export diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index 7f001c739..aaf82d150 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -157,6 +157,7 @@ bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) bool res = false; switch (a_Chunk.GetBlock(m_RelX, m_PosY + 1, m_RelZ)) { + case E_BLOCK_TRAPPED_CHEST: case E_BLOCK_CHEST: { // Chests have special handling because of double-chests @@ -294,23 +295,24 @@ bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) return false; } - int bx, by, bz; + // Get the coords of the block where to output items: + int OutX, OutY, OutZ; NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ); - if (!GetOutputBlockPos(Meta, bx, by, bz)) + if (!GetOutputBlockPos(Meta, OutX, OutY, OutZ)) { // Not attached to another container return false; } - if (by < 0) + if (OutY < 0) { // Cannot output below the zero-th block level return false; } // Convert coords to relative: - int rx = bx - a_Chunk.GetPosX() * cChunkDef::Width; - int rz = bz - a_Chunk.GetPosZ() * cChunkDef::Width; - cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(rx, rz); + int OutRelX = OutX - a_Chunk.GetPosX() * cChunkDef::Width; + int OutRelZ = OutZ - a_Chunk.GetPosZ() * cChunkDef::Width; + cChunk * DestChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(OutRelX, OutRelZ); if (DestChunk == NULL) { // The destination chunk has been unloaded, don't tick @@ -319,26 +321,33 @@ bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) // Call proper moving function, based on the blocktype present at the coords: bool res = false; - switch (DestChunk->GetBlock(rx, by, rz)) + switch (DestChunk->GetBlock(OutRelX, OutY, OutRelZ)) { + case E_BLOCK_TRAPPED_CHEST: case E_BLOCK_CHEST: { // Chests have special handling because of double-chests - res = MoveItemsToChest(*DestChunk, bx, by, bz); + res = MoveItemsToChest(*DestChunk, OutX, OutY, OutZ); break; } case E_BLOCK_LIT_FURNACE: case E_BLOCK_FURNACE: { // Furnaces have special handling because of the direction-to-slot relation - res = MoveItemsToFurnace(*DestChunk, bx, by, bz, Meta); + res = MoveItemsToFurnace(*DestChunk, OutX, OutY, OutZ, Meta); break; } case E_BLOCK_DISPENSER: case E_BLOCK_DROPPER: case E_BLOCK_HOPPER: { - res = MoveItemsToGrid(*(cBlockEntityWithItems *)DestChunk->GetBlockEntity(bx, by, bz)); + cBlockEntityWithItems * BlockEntity = (cBlockEntityWithItems *)DestChunk->GetBlockEntity(OutX, OutY, OutZ); + if (BlockEntity == NULL) + { + LOGWARNING("%s: A block entity was not found where expected at {%d, %d, %d}", __FUNCTION__, OutX, OutY, OutZ); + return false; + } + res = MoveItemsToGrid(*BlockEntity); break; } } @@ -359,13 +368,19 @@ bool cHopperEntity::MoveItemsOut(cChunk & a_Chunk, Int64 a_CurrentTick) /// Moves items from a chest (dblchest) above the hopper into this hopper. Returns true if contents have changed. bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) { - if (MoveItemsFromGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ))) + cChestEntity * Chest = (cChestEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); + if (Chest == NULL) + { + LOGWARNING("%s: A chest entity was not found where expected, at {%d, %d, %d}", __FUNCTION__, m_PosX, m_PosY + 1, m_PosZ); + return false; + } + if (MoveItemsFromGrid(*Chest)) { // Moved the item from the chest directly above the hopper return true; } - // Check if the chest is a double-chest, if so, try to move from there: + // Check if the chest is a double-chest (chest directly above was empty), if so, try to move from there: static const struct { int x, z; @@ -382,16 +397,29 @@ bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) int x = m_RelX + Coords[i].x; int z = m_RelZ + Coords[i].z; cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); - if ( - (Neighbor == NULL) || - (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) - ) + if (Neighbor == NULL) { continue; } - if (MoveItemsFromGrid(*(cChestEntity *)Neighbor->GetBlockEntity(x, m_PosY, z))) + + BLOCKTYPE Block = Neighbor->GetBlock(x, m_PosY + 1, z); + if (Block != Chest->GetBlockType()) { - return true; + // Not the same kind of chest + continue; + } + + Chest = (cChestEntity *)Neighbor->GetBlockEntity(m_PosX + Coords[i].x, m_PosY + 1, m_PosZ + Coords[i].z); + if (Chest == NULL) + { + LOGWARNING("%s: A chest entity was not found where expected, at {%d, %d, %d}", __FUNCTION__, m_PosX + Coords[i].x, m_PosY + 1, m_PosZ + Coords[i].z); + } + else + { + if (MoveItemsFromGrid(*Chest)) + { + return true; + } } return false; } @@ -408,7 +436,11 @@ bool cHopperEntity::MoveItemsFromChest(cChunk & a_Chunk) bool cHopperEntity::MoveItemsFromFurnace(cChunk & a_Chunk) { cFurnaceEntity * Furnace = (cFurnaceEntity *)a_Chunk.GetBlockEntity(m_PosX, m_PosY + 1, m_PosZ); - ASSERT(Furnace != NULL); + if (Furnace == NULL) + { + LOGWARNING("%s: A furnace entity was not found where expected, at {%d, %d, %d}", __FUNCTION__, m_PosX, m_PosY + 1, m_PosZ); + return false; + } // Try move from the output slot: if (MoveItemsFromSlot(*Furnace, cFurnaceEntity::fsOutput, true)) @@ -517,12 +549,19 @@ bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_Sl bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_BlockY, int a_BlockZ) { // Try the chest directly connected to the hopper: - if (MoveItemsToGrid(*(cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))) + cChestEntity * Chest = (cChestEntity *)a_Chunk.GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ); + if (Chest == NULL) + { + LOGWARNING("%s: A chest entity was not found where expected, at {%d, %d, %d}", __FUNCTION__, a_BlockX, a_BlockY, a_BlockZ); + return false; + } + if (MoveItemsToGrid(*Chest)) { + // Chest block directly connected was not full return true; } - // Check if the chest is a double-chest, if so, try to move into the other half: + // Check if the chest is a double-chest (chest block directly connected was full), if so, try to move into the other half: static const struct { int x, z; @@ -534,19 +573,32 @@ bool cHopperEntity::MoveItemsToChest(cChunk & a_Chunk, int a_BlockX, int a_Block {0, 1}, {0, -1}, } ; + int RelX = a_BlockX - a_Chunk.GetPosX() * cChunkDef::Width; + int RelZ = a_BlockZ - a_Chunk.GetPosZ() * cChunkDef::Width; for (size_t i = 0; i < ARRAYCOUNT(Coords); i++) { - int x = m_RelX + Coords[i].x; - int z = m_RelZ + Coords[i].z; + int x = RelX + Coords[i].x; + int z = RelZ + Coords[i].z; cChunk * Neighbor = a_Chunk.GetRelNeighborChunkAdjustCoords(x, z); - if ( - (Neighbor == NULL) || - (Neighbor->GetBlock(x, m_PosY + 1, z) != E_BLOCK_CHEST) - ) + if (Neighbor == NULL) + { + continue; + } + + BLOCKTYPE Block = Neighbor->GetBlock(x, a_BlockY, z); + if (Block != Chest->GetBlockType()) + { + // Not the same kind of chest + continue; + } + + Chest = (cChestEntity *)Neighbor->GetBlockEntity(a_BlockX + Coords[i].x, a_BlockY, a_BlockZ + Coords[i].z); + if (Chest == NULL) { + LOGWARNING("%s: A chest entity was not found where expected, at {%d, %d, %d} (%d, %d)", __FUNCTION__, a_BlockX + Coords[i].x, a_BlockY, a_BlockZ + Coords[i].z, x, z); continue; } - if (MoveItemsToGrid(*(cChestEntity *)Neighbor->GetBlockEntity(a_BlockX, a_BlockY, a_BlockZ))) + if (MoveItemsToGrid(*Chest)) { return true; } diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp index 58b05a324..95145c117 100644 --- a/src/BlockEntities/NoteEntity.cpp +++ b/src/BlockEntities/NoteEntity.cpp @@ -91,7 +91,7 @@ void cNoteEntity::MakeSound(void) // TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f); - m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch); + m_World->BroadcastSoundEffect(sampleName, (double)m_PosX, (double)m_PosY, (double)m_PosZ, 3.0f, calcPitch); } diff --git a/src/BlockInfo.cpp b/src/BlockInfo.cpp index 32fdec905..4fc1d602c 100644 --- a/src/BlockInfo.cpp +++ b/src/BlockInfo.cpp @@ -8,12 +8,6 @@ -cBlockInfo cBlockInfo::ms_Info[256]; - - - - - cBlockInfo::cBlockInfo() : m_LightValue(0x00) , m_SpreadLightFalloff(0x0f) @@ -41,8 +35,18 @@ cBlockInfo::~cBlockInfo() +/** This accessor makes sure that the cBlockInfo structures are properly initialized exactly once. +It does so by using the C++ singleton approximation - storing the actual singleton as the function's static variable. +It works only if it is called for the first time before the app spawns other threads. */ cBlockInfo & cBlockInfo::Get(BLOCKTYPE a_Type) { + static cBlockInfo ms_Info[256]; + static bool IsBlockInfoInitialized = false; + if (!IsBlockInfoInitialized) + { + cBlockInfo::Initialize(ms_Info); + IsBlockInfoInitialized = true; + } return ms_Info[a_Type]; } @@ -50,416 +54,412 @@ cBlockInfo & cBlockInfo::Get(BLOCKTYPE a_Type) -void cBlockInfo::Initialize(void) +void cBlockInfo::Initialize(cBlockInfoArray & a_Info) { for (unsigned int i = 0; i < 256; ++i) { - if (ms_Info[i].m_Handler == NULL) + if (a_Info[i].m_Handler == NULL) { - ms_Info[i].m_Handler = cBlockHandler::CreateBlockHandler((BLOCKTYPE) i); + a_Info[i].m_Handler = cBlockHandler::CreateBlockHandler((BLOCKTYPE) i); } } // Emissive blocks - ms_Info[E_BLOCK_FIRE ].m_LightValue = 15; - ms_Info[E_BLOCK_GLOWSTONE ].m_LightValue = 15; - ms_Info[E_BLOCK_JACK_O_LANTERN ].m_LightValue = 15; - ms_Info[E_BLOCK_LAVA ].m_LightValue = 15; - ms_Info[E_BLOCK_STATIONARY_LAVA ].m_LightValue = 15; - ms_Info[E_BLOCK_END_PORTAL ].m_LightValue = 15; - ms_Info[E_BLOCK_REDSTONE_LAMP_ON ].m_LightValue = 15; - ms_Info[E_BLOCK_TORCH ].m_LightValue = 14; - ms_Info[E_BLOCK_BURNING_FURNACE ].m_LightValue = 13; - ms_Info[E_BLOCK_NETHER_PORTAL ].m_LightValue = 11; - ms_Info[E_BLOCK_REDSTONE_ORE_GLOWING].m_LightValue = 9; - ms_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_LightValue = 9; - ms_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_LightValue = 7; - ms_Info[E_BLOCK_BREWING_STAND ].m_LightValue = 1; - ms_Info[E_BLOCK_BROWN_MUSHROOM ].m_LightValue = 1; - ms_Info[E_BLOCK_DRAGON_EGG ].m_LightValue = 1; + a_Info[E_BLOCK_FIRE ].m_LightValue = 15; + a_Info[E_BLOCK_GLOWSTONE ].m_LightValue = 15; + a_Info[E_BLOCK_JACK_O_LANTERN ].m_LightValue = 15; + a_Info[E_BLOCK_LAVA ].m_LightValue = 15; + a_Info[E_BLOCK_STATIONARY_LAVA ].m_LightValue = 15; + a_Info[E_BLOCK_END_PORTAL ].m_LightValue = 15; + a_Info[E_BLOCK_REDSTONE_LAMP_ON ].m_LightValue = 15; + a_Info[E_BLOCK_TORCH ].m_LightValue = 14; + a_Info[E_BLOCK_BURNING_FURNACE ].m_LightValue = 13; + a_Info[E_BLOCK_NETHER_PORTAL ].m_LightValue = 11; + a_Info[E_BLOCK_REDSTONE_ORE_GLOWING].m_LightValue = 9; + a_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_LightValue = 9; + a_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_LightValue = 7; + a_Info[E_BLOCK_BREWING_STAND ].m_LightValue = 1; + a_Info[E_BLOCK_BROWN_MUSHROOM ].m_LightValue = 1; + a_Info[E_BLOCK_DRAGON_EGG ].m_LightValue = 1; // Spread blocks - ms_Info[E_BLOCK_AIR ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_CAKE ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_CHEST ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_COBWEB ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_CROPS ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_FENCE ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_FENCE_GATE ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_FIRE ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_GLASS ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_GLASS_PANE ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_GLOWSTONE ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_IRON_BARS ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_IRON_DOOR ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_LEAVES ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_NEW_LEAVES ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_SIGN_POST ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_TORCH ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_VINES ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_WALLSIGN ].m_SpreadLightFalloff = 1; - ms_Info[E_BLOCK_WOODEN_DOOR ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_AIR ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_CAKE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_CHEST ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_COBWEB ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_CROPS ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_FENCE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_FENCE_GATE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_FIRE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_GLASS ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_GLASS_PANE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_GLOWSTONE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_IRON_BARS ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_IRON_DOOR ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_LEAVES ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_NEW_LEAVES ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_SIGN_POST ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_TORCH ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_TRAPPED_CHEST ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_TRIPWIRE ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_TRIPWIRE_HOOK ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_VINES ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_WALLSIGN ].m_SpreadLightFalloff = 1; + a_Info[E_BLOCK_WOODEN_DOOR ].m_SpreadLightFalloff = 1; // Light in water and lava dissapears faster: - ms_Info[E_BLOCK_LAVA ].m_SpreadLightFalloff = 3; - ms_Info[E_BLOCK_STATIONARY_LAVA ].m_SpreadLightFalloff = 3; - ms_Info[E_BLOCK_STATIONARY_WATER ].m_SpreadLightFalloff = 3; - ms_Info[E_BLOCK_WATER ].m_SpreadLightFalloff = 3; + a_Info[E_BLOCK_LAVA ].m_SpreadLightFalloff = 3; + a_Info[E_BLOCK_STATIONARY_LAVA ].m_SpreadLightFalloff = 3; + a_Info[E_BLOCK_STATIONARY_WATER ].m_SpreadLightFalloff = 3; + a_Info[E_BLOCK_WATER ].m_SpreadLightFalloff = 3; // Transparent blocks - ms_Info[E_BLOCK_ACTIVATOR_RAIL ].m_Transparent = true; - ms_Info[E_BLOCK_AIR ].m_Transparent = true; - ms_Info[E_BLOCK_ANVIL ].m_Transparent = true; - ms_Info[E_BLOCK_BIG_FLOWER ].m_Transparent = true; - ms_Info[E_BLOCK_BROWN_MUSHROOM ].m_Transparent = true; - ms_Info[E_BLOCK_CAKE ].m_Transparent = true; - ms_Info[E_BLOCK_CARROTS ].m_Transparent = true; - ms_Info[E_BLOCK_CHEST ].m_Transparent = true; - ms_Info[E_BLOCK_COBBLESTONE_WALL ].m_Transparent = true; - ms_Info[E_BLOCK_COBWEB ].m_Transparent = true; - ms_Info[E_BLOCK_CROPS ].m_Transparent = true; - ms_Info[E_BLOCK_DANDELION ].m_Transparent = true; - ms_Info[E_BLOCK_DETECTOR_RAIL ].m_Transparent = true; - ms_Info[E_BLOCK_ENDER_CHEST ].m_Transparent = true; - ms_Info[E_BLOCK_FENCE ].m_Transparent = true; - ms_Info[E_BLOCK_FENCE_GATE ].m_Transparent = true; - ms_Info[E_BLOCK_FIRE ].m_Transparent = true; - ms_Info[E_BLOCK_FLOWER ].m_Transparent = true; - ms_Info[E_BLOCK_FLOWER_POT ].m_Transparent = true; - ms_Info[E_BLOCK_GLASS ].m_Transparent = true; - ms_Info[E_BLOCK_GLASS_PANE ].m_Transparent = true; - ms_Info[E_BLOCK_HEAD ].m_Transparent = true; - ms_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_Transparent = true; - ms_Info[E_BLOCK_ICE ].m_Transparent = true; - ms_Info[E_BLOCK_IRON_DOOR ].m_Transparent = true; - ms_Info[E_BLOCK_LADDER ].m_Transparent = true; - ms_Info[E_BLOCK_LAVA ].m_Transparent = true; - ms_Info[E_BLOCK_LEAVES ].m_Transparent = true; - ms_Info[E_BLOCK_LEVER ].m_Transparent = true; - ms_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_Transparent = true; - ms_Info[E_BLOCK_MELON_STEM ].m_Transparent = true; - ms_Info[E_BLOCK_NETHER_BRICK_FENCE ].m_Transparent = true; - ms_Info[E_BLOCK_NEW_LEAVES ].m_Transparent = true; - ms_Info[E_BLOCK_POTATOES ].m_Transparent = true; - ms_Info[E_BLOCK_POWERED_RAIL ].m_Transparent = true; - ms_Info[E_BLOCK_PISTON_EXTENSION ].m_Transparent = true; - ms_Info[E_BLOCK_PUMPKIN_STEM ].m_Transparent = true; - ms_Info[E_BLOCK_RAIL ].m_Transparent = true; - ms_Info[E_BLOCK_RED_MUSHROOM ].m_Transparent = true; - ms_Info[E_BLOCK_SIGN_POST ].m_Transparent = true; - ms_Info[E_BLOCK_SNOW ].m_Transparent = true; - ms_Info[E_BLOCK_STAINED_GLASS ].m_Transparent = true; - ms_Info[E_BLOCK_STAINED_GLASS_PANE ].m_Transparent = true; - ms_Info[E_BLOCK_STATIONARY_LAVA ].m_Transparent = true; - ms_Info[E_BLOCK_STATIONARY_WATER ].m_Transparent = true; - ms_Info[E_BLOCK_STONE_BUTTON ].m_Transparent = true; - ms_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_Transparent = true; - ms_Info[E_BLOCK_TALL_GRASS ].m_Transparent = true; - ms_Info[E_BLOCK_TORCH ].m_Transparent = true; - ms_Info[E_BLOCK_VINES ].m_Transparent = true; - ms_Info[E_BLOCK_WALLSIGN ].m_Transparent = true; - ms_Info[E_BLOCK_WATER ].m_Transparent = true; - ms_Info[E_BLOCK_WOODEN_BUTTON ].m_Transparent = true; - ms_Info[E_BLOCK_WOODEN_DOOR ].m_Transparent = true; - ms_Info[E_BLOCK_WOODEN_PRESSURE_PLATE].m_Transparent = true; + a_Info[E_BLOCK_ACTIVATOR_RAIL ].m_Transparent = true; + a_Info[E_BLOCK_AIR ].m_Transparent = true; + a_Info[E_BLOCK_ANVIL ].m_Transparent = true; + a_Info[E_BLOCK_BIG_FLOWER ].m_Transparent = true; + a_Info[E_BLOCK_BROWN_MUSHROOM ].m_Transparent = true; + a_Info[E_BLOCK_CAKE ].m_Transparent = true; + a_Info[E_BLOCK_CARROTS ].m_Transparent = true; + a_Info[E_BLOCK_CHEST ].m_Transparent = true; + a_Info[E_BLOCK_COBBLESTONE_WALL ].m_Transparent = true; + a_Info[E_BLOCK_COBWEB ].m_Transparent = true; + a_Info[E_BLOCK_CROPS ].m_Transparent = true; + a_Info[E_BLOCK_DANDELION ].m_Transparent = true; + a_Info[E_BLOCK_DETECTOR_RAIL ].m_Transparent = true; + a_Info[E_BLOCK_ENDER_CHEST ].m_Transparent = true; + a_Info[E_BLOCK_FENCE ].m_Transparent = true; + a_Info[E_BLOCK_FENCE_GATE ].m_Transparent = true; + a_Info[E_BLOCK_FIRE ].m_Transparent = true; + a_Info[E_BLOCK_FLOWER ].m_Transparent = true; + a_Info[E_BLOCK_FLOWER_POT ].m_Transparent = true; + a_Info[E_BLOCK_GLASS ].m_Transparent = true; + a_Info[E_BLOCK_GLASS_PANE ].m_Transparent = true; + a_Info[E_BLOCK_HEAD ].m_Transparent = true; + a_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_Transparent = true; + a_Info[E_BLOCK_ICE ].m_Transparent = true; + a_Info[E_BLOCK_IRON_DOOR ].m_Transparent = true; + a_Info[E_BLOCK_LADDER ].m_Transparent = true; + a_Info[E_BLOCK_LAVA ].m_Transparent = true; + a_Info[E_BLOCK_LEAVES ].m_Transparent = true; + a_Info[E_BLOCK_LEVER ].m_Transparent = true; + a_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_Transparent = true; + a_Info[E_BLOCK_MELON_STEM ].m_Transparent = true; + a_Info[E_BLOCK_NETHER_BRICK_FENCE ].m_Transparent = true; + a_Info[E_BLOCK_NEW_LEAVES ].m_Transparent = true; + a_Info[E_BLOCK_POTATOES ].m_Transparent = true; + a_Info[E_BLOCK_POWERED_RAIL ].m_Transparent = true; + a_Info[E_BLOCK_PISTON_EXTENSION ].m_Transparent = true; + a_Info[E_BLOCK_PUMPKIN_STEM ].m_Transparent = true; + a_Info[E_BLOCK_RAIL ].m_Transparent = true; + a_Info[E_BLOCK_RED_MUSHROOM ].m_Transparent = true; + a_Info[E_BLOCK_SIGN_POST ].m_Transparent = true; + a_Info[E_BLOCK_SNOW ].m_Transparent = true; + a_Info[E_BLOCK_STAINED_GLASS ].m_Transparent = true; + a_Info[E_BLOCK_STAINED_GLASS_PANE ].m_Transparent = true; + a_Info[E_BLOCK_STATIONARY_LAVA ].m_Transparent = true; + a_Info[E_BLOCK_STATIONARY_WATER ].m_Transparent = true; + a_Info[E_BLOCK_STONE_BUTTON ].m_Transparent = true; + a_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_Transparent = true; + a_Info[E_BLOCK_TRAPPED_CHEST ].m_Transparent = true; + a_Info[E_BLOCK_TRIPWIRE ].m_Transparent = true; + a_Info[E_BLOCK_TRIPWIRE_HOOK ].m_Transparent = true; + a_Info[E_BLOCK_TALL_GRASS ].m_Transparent = true; + a_Info[E_BLOCK_TORCH ].m_Transparent = true; + a_Info[E_BLOCK_VINES ].m_Transparent = true; + a_Info[E_BLOCK_WALLSIGN ].m_Transparent = true; + a_Info[E_BLOCK_WATER ].m_Transparent = true; + a_Info[E_BLOCK_WOODEN_BUTTON ].m_Transparent = true; + a_Info[E_BLOCK_WOODEN_DOOR ].m_Transparent = true; + a_Info[E_BLOCK_WOODEN_PRESSURE_PLATE].m_Transparent = true; // TODO: Any other transparent blocks? // One hit break blocks: - ms_Info[E_BLOCK_ACTIVE_COMPARATOR ].m_OneHitDig = true; - ms_Info[E_BLOCK_BIG_FLOWER ].m_OneHitDig = true; - ms_Info[E_BLOCK_BROWN_MUSHROOM ].m_OneHitDig = true; - ms_Info[E_BLOCK_CARROTS ].m_OneHitDig = true; - ms_Info[E_BLOCK_CROPS ].m_OneHitDig = true; - ms_Info[E_BLOCK_DANDELION ].m_OneHitDig = true; - ms_Info[E_BLOCK_FIRE ].m_OneHitDig = true; - ms_Info[E_BLOCK_FLOWER ].m_OneHitDig = true; - ms_Info[E_BLOCK_FLOWER_POT ].m_OneHitDig = true; - ms_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_OneHitDig = true; - ms_Info[E_BLOCK_MELON_STEM ].m_OneHitDig = true; - ms_Info[E_BLOCK_POTATOES ].m_OneHitDig = true; - ms_Info[E_BLOCK_PUMPKIN_STEM ].m_OneHitDig = true; - ms_Info[E_BLOCK_REDSTONE_REPEATER_OFF].m_OneHitDig = true; - ms_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_OneHitDig = true; - ms_Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_OneHitDig = true; - ms_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_OneHitDig = true; - ms_Info[E_BLOCK_REDSTONE_WIRE ].m_OneHitDig = true; - ms_Info[E_BLOCK_RED_MUSHROOM ].m_OneHitDig = true; - ms_Info[E_BLOCK_REEDS ].m_OneHitDig = true; - ms_Info[E_BLOCK_SAPLING ].m_OneHitDig = true; - ms_Info[E_BLOCK_TNT ].m_OneHitDig = true; - ms_Info[E_BLOCK_TALL_GRASS ].m_OneHitDig = true; - ms_Info[E_BLOCK_TORCH ].m_OneHitDig = true; + a_Info[E_BLOCK_ACTIVE_COMPARATOR ].m_OneHitDig = true; + a_Info[E_BLOCK_BIG_FLOWER ].m_OneHitDig = true; + a_Info[E_BLOCK_BROWN_MUSHROOM ].m_OneHitDig = true; + a_Info[E_BLOCK_CARROTS ].m_OneHitDig = true; + a_Info[E_BLOCK_CROPS ].m_OneHitDig = true; + a_Info[E_BLOCK_DANDELION ].m_OneHitDig = true; + a_Info[E_BLOCK_FIRE ].m_OneHitDig = true; + a_Info[E_BLOCK_FLOWER ].m_OneHitDig = true; + a_Info[E_BLOCK_FLOWER_POT ].m_OneHitDig = true; + a_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_OneHitDig = true; + a_Info[E_BLOCK_MELON_STEM ].m_OneHitDig = true; + a_Info[E_BLOCK_POTATOES ].m_OneHitDig = true; + a_Info[E_BLOCK_PUMPKIN_STEM ].m_OneHitDig = true; + a_Info[E_BLOCK_REDSTONE_REPEATER_OFF].m_OneHitDig = true; + a_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_OneHitDig = true; + a_Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_OneHitDig = true; + a_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_OneHitDig = true; + a_Info[E_BLOCK_REDSTONE_WIRE ].m_OneHitDig = true; + a_Info[E_BLOCK_RED_MUSHROOM ].m_OneHitDig = true; + a_Info[E_BLOCK_REEDS ].m_OneHitDig = true; + a_Info[E_BLOCK_SAPLING ].m_OneHitDig = true; + a_Info[E_BLOCK_TNT ].m_OneHitDig = true; + a_Info[E_BLOCK_TALL_GRASS ].m_OneHitDig = true; + a_Info[E_BLOCK_TORCH ].m_OneHitDig = true; + a_Info[E_BLOCK_TRIPWIRE ].m_OneHitDig = true; // Blocks that break when pushed by piston: - ms_Info[E_BLOCK_ACTIVE_COMPARATOR ].m_PistonBreakable = true; - ms_Info[E_BLOCK_AIR ].m_PistonBreakable = true; - ms_Info[E_BLOCK_BED ].m_PistonBreakable = true; - ms_Info[E_BLOCK_BIG_FLOWER ].m_PistonBreakable = true; - ms_Info[E_BLOCK_BROWN_MUSHROOM ].m_PistonBreakable = true; - ms_Info[E_BLOCK_CAKE ].m_PistonBreakable = true; - ms_Info[E_BLOCK_COBWEB ].m_PistonBreakable = true; - ms_Info[E_BLOCK_CROPS ].m_PistonBreakable = true; - ms_Info[E_BLOCK_DANDELION ].m_PistonBreakable = true; - ms_Info[E_BLOCK_DEAD_BUSH ].m_PistonBreakable = true; - ms_Info[E_BLOCK_FIRE ].m_PistonBreakable = true; - ms_Info[E_BLOCK_FLOWER ].m_PistonBreakable = true; - ms_Info[E_BLOCK_HEAD ].m_PistonBreakable = true; - ms_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_PistonBreakable = true; - ms_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_PistonBreakable = true; - ms_Info[E_BLOCK_IRON_DOOR ].m_PistonBreakable = true; - ms_Info[E_BLOCK_JACK_O_LANTERN ].m_PistonBreakable = true; - ms_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_PistonBreakable = true; - ms_Info[E_BLOCK_LADDER ].m_PistonBreakable = true; - ms_Info[E_BLOCK_LAVA ].m_PistonBreakable = true; - ms_Info[E_BLOCK_LEVER ].m_PistonBreakable = true; - ms_Info[E_BLOCK_MELON ].m_PistonBreakable = true; - ms_Info[E_BLOCK_MELON_STEM ].m_PistonBreakable = true; - ms_Info[E_BLOCK_PUMPKIN ].m_PistonBreakable = true; - ms_Info[E_BLOCK_PUMPKIN_STEM ].m_PistonBreakable = true; - ms_Info[E_BLOCK_REDSTONE_REPEATER_OFF].m_PistonBreakable = true; - ms_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_PistonBreakable = true; - ms_Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_PistonBreakable = true; - ms_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_PistonBreakable = true; - ms_Info[E_BLOCK_REDSTONE_WIRE ].m_PistonBreakable = true; - ms_Info[E_BLOCK_RED_MUSHROOM ].m_PistonBreakable = true; - ms_Info[E_BLOCK_REEDS ].m_PistonBreakable = true; - ms_Info[E_BLOCK_SNOW ].m_PistonBreakable = true; - ms_Info[E_BLOCK_STATIONARY_LAVA ].m_PistonBreakable = true; - ms_Info[E_BLOCK_STATIONARY_WATER ].m_PistonBreakable = true; - ms_Info[E_BLOCK_STONE_BUTTON ].m_PistonBreakable = true; - ms_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_PistonBreakable = true; - ms_Info[E_BLOCK_TALL_GRASS ].m_PistonBreakable = true; - ms_Info[E_BLOCK_TORCH ].m_PistonBreakable = true; - ms_Info[E_BLOCK_VINES ].m_PistonBreakable = true; - ms_Info[E_BLOCK_WATER ].m_PistonBreakable = true; - ms_Info[E_BLOCK_WOODEN_BUTTON ].m_PistonBreakable = true; - ms_Info[E_BLOCK_WOODEN_DOOR ].m_PistonBreakable = true; - ms_Info[E_BLOCK_WOODEN_PRESSURE_PLATE].m_PistonBreakable = true; + a_Info[E_BLOCK_ACTIVE_COMPARATOR ].m_PistonBreakable = true; + a_Info[E_BLOCK_AIR ].m_PistonBreakable = true; + a_Info[E_BLOCK_BED ].m_PistonBreakable = true; + a_Info[E_BLOCK_BIG_FLOWER ].m_PistonBreakable = true; + a_Info[E_BLOCK_BROWN_MUSHROOM ].m_PistonBreakable = true; + a_Info[E_BLOCK_CAKE ].m_PistonBreakable = true; + a_Info[E_BLOCK_COBWEB ].m_PistonBreakable = true; + a_Info[E_BLOCK_CROPS ].m_PistonBreakable = true; + a_Info[E_BLOCK_DANDELION ].m_PistonBreakable = true; + a_Info[E_BLOCK_DEAD_BUSH ].m_PistonBreakable = true; + a_Info[E_BLOCK_FIRE ].m_PistonBreakable = true; + a_Info[E_BLOCK_FLOWER ].m_PistonBreakable = true; + a_Info[E_BLOCK_HEAD ].m_PistonBreakable = true; + a_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_PistonBreakable = true; + a_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_PistonBreakable = true; + a_Info[E_BLOCK_IRON_DOOR ].m_PistonBreakable = true; + a_Info[E_BLOCK_JACK_O_LANTERN ].m_PistonBreakable = true; + a_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_PistonBreakable = true; + a_Info[E_BLOCK_LADDER ].m_PistonBreakable = true; + a_Info[E_BLOCK_LAVA ].m_PistonBreakable = true; + a_Info[E_BLOCK_LEVER ].m_PistonBreakable = true; + a_Info[E_BLOCK_MELON ].m_PistonBreakable = true; + a_Info[E_BLOCK_MELON_STEM ].m_PistonBreakable = true; + a_Info[E_BLOCK_PUMPKIN ].m_PistonBreakable = true; + a_Info[E_BLOCK_PUMPKIN_STEM ].m_PistonBreakable = true; + a_Info[E_BLOCK_REDSTONE_REPEATER_OFF].m_PistonBreakable = true; + a_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_PistonBreakable = true; + a_Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_PistonBreakable = true; + a_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_PistonBreakable = true; + a_Info[E_BLOCK_REDSTONE_WIRE ].m_PistonBreakable = true; + a_Info[E_BLOCK_RED_MUSHROOM ].m_PistonBreakable = true; + a_Info[E_BLOCK_REEDS ].m_PistonBreakable = true; + a_Info[E_BLOCK_SNOW ].m_PistonBreakable = true; + a_Info[E_BLOCK_STATIONARY_LAVA ].m_PistonBreakable = true; + a_Info[E_BLOCK_STATIONARY_WATER ].m_PistonBreakable = true; + a_Info[E_BLOCK_STONE_BUTTON ].m_PistonBreakable = true; + a_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_PistonBreakable = true; + a_Info[E_BLOCK_TALL_GRASS ].m_PistonBreakable = true; + a_Info[E_BLOCK_TORCH ].m_PistonBreakable = true; + a_Info[E_BLOCK_TRIPWIRE ].m_PistonBreakable = true; + a_Info[E_BLOCK_TRIPWIRE_HOOK ].m_PistonBreakable = true; + a_Info[E_BLOCK_VINES ].m_PistonBreakable = true; + a_Info[E_BLOCK_WATER ].m_PistonBreakable = true; + a_Info[E_BLOCK_WOODEN_BUTTON ].m_PistonBreakable = true; + a_Info[E_BLOCK_WOODEN_DOOR ].m_PistonBreakable = true; + a_Info[E_BLOCK_WOODEN_PRESSURE_PLATE].m_PistonBreakable = true; // Blocks that cannot be snowed over: - ms_Info[E_BLOCK_ACTIVE_COMPARATOR ].m_IsSnowable = false; - ms_Info[E_BLOCK_AIR ].m_IsSnowable = false; - ms_Info[E_BLOCK_BIG_FLOWER ].m_IsSnowable = false; - ms_Info[E_BLOCK_BROWN_MUSHROOM ].m_IsSnowable = false; - ms_Info[E_BLOCK_CACTUS ].m_IsSnowable = false; - ms_Info[E_BLOCK_CHEST ].m_IsSnowable = false; - ms_Info[E_BLOCK_CROPS ].m_IsSnowable = false; - ms_Info[E_BLOCK_COBBLESTONE_WALL ].m_IsSnowable = false; - ms_Info[E_BLOCK_DANDELION ].m_IsSnowable = false; - ms_Info[E_BLOCK_FIRE ].m_IsSnowable = false; - ms_Info[E_BLOCK_FLOWER ].m_IsSnowable = false; - ms_Info[E_BLOCK_GLASS ].m_IsSnowable = false; - ms_Info[E_BLOCK_ICE ].m_IsSnowable = false; - ms_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_IsSnowable = false; - ms_Info[E_BLOCK_LAVA ].m_IsSnowable = false; - ms_Info[E_BLOCK_LILY_PAD ].m_IsSnowable = false; - ms_Info[E_BLOCK_REDSTONE_REPEATER_OFF].m_IsSnowable = false; - ms_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_IsSnowable = false; - ms_Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_IsSnowable = false; - ms_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_IsSnowable = false; - ms_Info[E_BLOCK_REDSTONE_WIRE ].m_IsSnowable = false; - ms_Info[E_BLOCK_RED_MUSHROOM ].m_IsSnowable = false; - ms_Info[E_BLOCK_REEDS ].m_IsSnowable = false; - ms_Info[E_BLOCK_SAPLING ].m_IsSnowable = false; - ms_Info[E_BLOCK_SIGN_POST ].m_IsSnowable = false; - ms_Info[E_BLOCK_SNOW ].m_IsSnowable = false; - ms_Info[E_BLOCK_STAINED_GLASS ].m_IsSnowable = false; - ms_Info[E_BLOCK_STAINED_GLASS_PANE ].m_IsSnowable = false; - ms_Info[E_BLOCK_STATIONARY_LAVA ].m_IsSnowable = false; - ms_Info[E_BLOCK_STATIONARY_WATER ].m_IsSnowable = false; - ms_Info[E_BLOCK_TALL_GRASS ].m_IsSnowable = false; - ms_Info[E_BLOCK_TNT ].m_IsSnowable = false; - ms_Info[E_BLOCK_TORCH ].m_IsSnowable = false; - ms_Info[E_BLOCK_VINES ].m_IsSnowable = false; - ms_Info[E_BLOCK_WALLSIGN ].m_IsSnowable = false; - ms_Info[E_BLOCK_WATER ].m_IsSnowable = false; - ms_Info[E_BLOCK_RAIL ].m_IsSnowable = false; - ms_Info[E_BLOCK_ACTIVATOR_RAIL ].m_IsSnowable = false; - ms_Info[E_BLOCK_POWERED_RAIL ].m_IsSnowable = false; - ms_Info[E_BLOCK_DETECTOR_RAIL ].m_IsSnowable = false; - ms_Info[E_BLOCK_COBWEB ].m_IsSnowable = false; - ms_Info[E_BLOCK_HEAD ].m_IsSnowable = false; + a_Info[E_BLOCK_ACTIVE_COMPARATOR ].m_IsSnowable = false; + a_Info[E_BLOCK_AIR ].m_IsSnowable = false; + a_Info[E_BLOCK_BIG_FLOWER ].m_IsSnowable = false; + a_Info[E_BLOCK_BROWN_MUSHROOM ].m_IsSnowable = false; + a_Info[E_BLOCK_CACTUS ].m_IsSnowable = false; + a_Info[E_BLOCK_CHEST ].m_IsSnowable = false; + a_Info[E_BLOCK_CROPS ].m_IsSnowable = false; + a_Info[E_BLOCK_COBBLESTONE_WALL ].m_IsSnowable = false; + a_Info[E_BLOCK_DANDELION ].m_IsSnowable = false; + a_Info[E_BLOCK_FIRE ].m_IsSnowable = false; + a_Info[E_BLOCK_FLOWER ].m_IsSnowable = false; + a_Info[E_BLOCK_GLASS ].m_IsSnowable = false; + a_Info[E_BLOCK_ICE ].m_IsSnowable = false; + a_Info[E_BLOCK_INACTIVE_COMPARATOR ].m_IsSnowable = false; + a_Info[E_BLOCK_LAVA ].m_IsSnowable = false; + a_Info[E_BLOCK_LILY_PAD ].m_IsSnowable = false; + a_Info[E_BLOCK_REDSTONE_REPEATER_OFF].m_IsSnowable = false; + a_Info[E_BLOCK_REDSTONE_REPEATER_ON].m_IsSnowable = false; + a_Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_IsSnowable = false; + a_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_IsSnowable = false; + a_Info[E_BLOCK_REDSTONE_WIRE ].m_IsSnowable = false; + a_Info[E_BLOCK_RED_MUSHROOM ].m_IsSnowable = false; + a_Info[E_BLOCK_REEDS ].m_IsSnowable = false; + a_Info[E_BLOCK_SAPLING ].m_IsSnowable = false; + a_Info[E_BLOCK_SIGN_POST ].m_IsSnowable = false; + a_Info[E_BLOCK_SNOW ].m_IsSnowable = false; + a_Info[E_BLOCK_STAINED_GLASS ].m_IsSnowable = false; + a_Info[E_BLOCK_STAINED_GLASS_PANE ].m_IsSnowable = false; + a_Info[E_BLOCK_STATIONARY_LAVA ].m_IsSnowable = false; + a_Info[E_BLOCK_STATIONARY_WATER ].m_IsSnowable = false; + a_Info[E_BLOCK_TALL_GRASS ].m_IsSnowable = false; + a_Info[E_BLOCK_TNT ].m_IsSnowable = false; + a_Info[E_BLOCK_TORCH ].m_IsSnowable = false; + a_Info[E_BLOCK_TRAPPED_CHEST ].m_IsSnowable = false; + a_Info[E_BLOCK_TRIPWIRE ].m_IsSnowable = false; + a_Info[E_BLOCK_TRIPWIRE_HOOK ].m_IsSnowable = false; + a_Info[E_BLOCK_VINES ].m_IsSnowable = false; + a_Info[E_BLOCK_WALLSIGN ].m_IsSnowable = false; + a_Info[E_BLOCK_WATER ].m_IsSnowable = false; + a_Info[E_BLOCK_RAIL ].m_IsSnowable = false; + a_Info[E_BLOCK_ACTIVATOR_RAIL ].m_IsSnowable = false; + a_Info[E_BLOCK_POWERED_RAIL ].m_IsSnowable = false; + a_Info[E_BLOCK_DETECTOR_RAIL ].m_IsSnowable = false; + a_Info[E_BLOCK_COBWEB ].m_IsSnowable = false; + a_Info[E_BLOCK_HEAD ].m_IsSnowable = false; // Blocks that don't drop without a special tool: - ms_Info[E_BLOCK_BRICK ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_CAULDRON ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_COAL_ORE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_COBBLESTONE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_COBBLESTONE_WALL ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_COBBLESTONE_STAIRS ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_COBWEB ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_DIAMOND_BLOCK ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_DIAMOND_ORE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_DOUBLE_STONE_SLAB ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_EMERALD_ORE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_END_STONE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_GOLD_BLOCK ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_GOLD_ORE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_IRON_BLOCK ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_IRON_ORE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_LAPIS_BLOCK ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_LAPIS_ORE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_MOSSY_COBBLESTONE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_NETHERRACK ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_NETHER_BRICK ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_NETHER_BRICK_STAIRS ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_OBSIDIAN ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_REDSTONE_ORE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_REDSTONE_ORE_GLOWING].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_SANDSTONE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_SANDSTONE_STAIRS ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_SNOW ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_STONE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_STONE_BRICKS ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_STONE_BRICK_STAIRS ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_STONE_SLAB ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_VINES ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_FURNACE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_LIT_FURNACE ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_ANVIL ].m_RequiresSpecialTool = true; - ms_Info[E_BLOCK_ENCHANTMENT_TABLE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_BRICK ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_CAULDRON ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_COAL_ORE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_COBBLESTONE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_COBBLESTONE_WALL ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_COBBLESTONE_STAIRS ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_COBWEB ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_DIAMOND_BLOCK ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_DIAMOND_ORE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_DOUBLE_STONE_SLAB ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_EMERALD_ORE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_END_STONE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_GOLD_BLOCK ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_GOLD_ORE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_IRON_BLOCK ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_IRON_ORE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_LAPIS_BLOCK ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_LAPIS_ORE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_MOSSY_COBBLESTONE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_NETHERRACK ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_NETHER_BRICK ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_NETHER_BRICK_STAIRS ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_OBSIDIAN ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_REDSTONE_ORE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_REDSTONE_ORE_GLOWING].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_SANDSTONE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_SANDSTONE_STAIRS ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_SNOW ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_STONE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_STONE_BRICKS ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_STONE_BRICK_STAIRS ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_STONE_SLAB ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_VINES ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_FURNACE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_LIT_FURNACE ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_ANVIL ].m_RequiresSpecialTool = true; + a_Info[E_BLOCK_ENCHANTMENT_TABLE ].m_RequiresSpecialTool = true; // Nonsolid blocks: - ms_Info[E_BLOCK_ACTIVATOR_RAIL ].m_IsSolid = false; - ms_Info[E_BLOCK_AIR ].m_IsSolid = false; - ms_Info[E_BLOCK_BIG_FLOWER ].m_IsSolid = false; - ms_Info[E_BLOCK_BROWN_MUSHROOM ].m_IsSolid = false; - ms_Info[E_BLOCK_CAKE ].m_IsSolid = false; - ms_Info[E_BLOCK_CARROTS ].m_IsSolid = false; - ms_Info[E_BLOCK_COBWEB ].m_IsSolid = false; - ms_Info[E_BLOCK_CROPS ].m_IsSolid = false; - ms_Info[E_BLOCK_DANDELION ].m_IsSolid = false; - ms_Info[E_BLOCK_DETECTOR_RAIL ].m_IsSolid = false; - ms_Info[E_BLOCK_END_PORTAL ].m_IsSolid = false; - ms_Info[E_BLOCK_FENCE ].m_IsSolid = false; - ms_Info[E_BLOCK_FENCE_GATE ].m_IsSolid = false; - ms_Info[E_BLOCK_FIRE ].m_IsSolid = false; - ms_Info[E_BLOCK_FLOWER ].m_IsSolid = false; - ms_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_IsSolid = false; - ms_Info[E_BLOCK_LAVA ].m_IsSolid = false; - ms_Info[E_BLOCK_LEVER ].m_IsSolid = false; - ms_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_IsSolid = false; - ms_Info[E_BLOCK_MELON_STEM ].m_IsSolid = false; - ms_Info[E_BLOCK_NETHER_PORTAL ].m_IsSolid = false; - ms_Info[E_BLOCK_PISTON_EXTENSION ].m_IsSolid = false; - ms_Info[E_BLOCK_POTATOES ].m_IsSolid = false; - ms_Info[E_BLOCK_POWERED_RAIL ].m_IsSolid = false; - ms_Info[E_BLOCK_RAIL ].m_IsSolid = false; - ms_Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_IsSolid = false; - ms_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_IsSolid = false; - ms_Info[E_BLOCK_REDSTONE_WIRE ].m_IsSolid = false; - ms_Info[E_BLOCK_RED_MUSHROOM ].m_IsSolid = false; - ms_Info[E_BLOCK_REEDS ].m_IsSolid = false; - ms_Info[E_BLOCK_SAPLING ].m_IsSolid = false; - ms_Info[E_BLOCK_SIGN_POST ].m_IsSolid = false; - ms_Info[E_BLOCK_SNOW ].m_IsSolid = false; - ms_Info[E_BLOCK_STATIONARY_LAVA ].m_IsSolid = false; - ms_Info[E_BLOCK_STATIONARY_WATER ].m_IsSolid = false; - ms_Info[E_BLOCK_STONE_BUTTON ].m_IsSolid = false; - ms_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_IsSolid = false; - ms_Info[E_BLOCK_TALL_GRASS ].m_IsSolid = false; - ms_Info[E_BLOCK_TORCH ].m_IsSolid = false; - ms_Info[E_BLOCK_TRIPWIRE ].m_IsSolid = false; - ms_Info[E_BLOCK_VINES ].m_IsSolid = false; - ms_Info[E_BLOCK_WALLSIGN ].m_IsSolid = false; - ms_Info[E_BLOCK_WATER ].m_IsSolid = false; - ms_Info[E_BLOCK_WOODEN_BUTTON ].m_IsSolid = false; - ms_Info[E_BLOCK_WOODEN_PRESSURE_PLATE].m_IsSolid = false; - ms_Info[E_BLOCK_WOODEN_SLAB ].m_IsSolid = false; + a_Info[E_BLOCK_ACTIVATOR_RAIL ].m_IsSolid = false; + a_Info[E_BLOCK_AIR ].m_IsSolid = false; + a_Info[E_BLOCK_BIG_FLOWER ].m_IsSolid = false; + a_Info[E_BLOCK_BROWN_MUSHROOM ].m_IsSolid = false; + a_Info[E_BLOCK_CAKE ].m_IsSolid = false; + a_Info[E_BLOCK_CARROTS ].m_IsSolid = false; + a_Info[E_BLOCK_COBWEB ].m_IsSolid = false; + a_Info[E_BLOCK_CROPS ].m_IsSolid = false; + a_Info[E_BLOCK_DANDELION ].m_IsSolid = false; + a_Info[E_BLOCK_DETECTOR_RAIL ].m_IsSolid = false; + a_Info[E_BLOCK_END_PORTAL ].m_IsSolid = false; + a_Info[E_BLOCK_FENCE ].m_IsSolid = false; + a_Info[E_BLOCK_FENCE_GATE ].m_IsSolid = false; + a_Info[E_BLOCK_FIRE ].m_IsSolid = false; + a_Info[E_BLOCK_FLOWER ].m_IsSolid = false; + a_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_IsSolid = false; + a_Info[E_BLOCK_LAVA ].m_IsSolid = false; + a_Info[E_BLOCK_LEVER ].m_IsSolid = false; + a_Info[E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE].m_IsSolid = false; + a_Info[E_BLOCK_MELON_STEM ].m_IsSolid = false; + a_Info[E_BLOCK_NETHER_PORTAL ].m_IsSolid = false; + a_Info[E_BLOCK_POTATOES ].m_IsSolid = false; + a_Info[E_BLOCK_POWERED_RAIL ].m_IsSolid = false; + a_Info[E_BLOCK_RAIL ].m_IsSolid = false; + a_Info[E_BLOCK_REDSTONE_TORCH_OFF ].m_IsSolid = false; + a_Info[E_BLOCK_REDSTONE_TORCH_ON ].m_IsSolid = false; + a_Info[E_BLOCK_REDSTONE_WIRE ].m_IsSolid = false; + a_Info[E_BLOCK_RED_MUSHROOM ].m_IsSolid = false; + a_Info[E_BLOCK_REEDS ].m_IsSolid = false; + a_Info[E_BLOCK_SAPLING ].m_IsSolid = false; + a_Info[E_BLOCK_SIGN_POST ].m_IsSolid = false; + a_Info[E_BLOCK_SNOW ].m_IsSolid = false; + a_Info[E_BLOCK_STATIONARY_LAVA ].m_IsSolid = false; + a_Info[E_BLOCK_STATIONARY_WATER ].m_IsSolid = false; + a_Info[E_BLOCK_STONE_BUTTON ].m_IsSolid = false; + a_Info[E_BLOCK_STONE_PRESSURE_PLATE].m_IsSolid = false; + a_Info[E_BLOCK_TALL_GRASS ].m_IsSolid = false; + a_Info[E_BLOCK_TORCH ].m_IsSolid = false; + a_Info[E_BLOCK_TRIPWIRE ].m_IsSolid = false; + a_Info[E_BLOCK_VINES ].m_IsSolid = false; + a_Info[E_BLOCK_WALLSIGN ].m_IsSolid = false; + a_Info[E_BLOCK_WATER ].m_IsSolid = false; + a_Info[E_BLOCK_WOODEN_BUTTON ].m_IsSolid = false; + a_Info[E_BLOCK_WOODEN_PRESSURE_PLATE].m_IsSolid = false; + a_Info[E_BLOCK_WOODEN_SLAB ].m_IsSolid = false; // Blocks that fully occupy their voxel - used as a guide for torch placeable blocks, amongst other things: - ms_Info[E_BLOCK_NEW_LOG ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_BEDROCK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_BLOCK_OF_COAL ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_BLOCK_OF_REDSTONE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_BOOKCASE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_BRICK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_CLAY ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_COAL_ORE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_COBBLESTONE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_COMMAND_BLOCK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_CRAFTING_TABLE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_DIAMOND_BLOCK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_DIAMOND_ORE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_DIRT ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_DISPENSER ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_DOUBLE_STONE_SLAB ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_DOUBLE_WOODEN_SLAB ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_DROPPER ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_EMERALD_BLOCK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_EMERALD_ORE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_END_STONE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_FURNACE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_GLOWSTONE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_GOLD_BLOCK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_GOLD_ORE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_GRASS ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_GRAVEL ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_HARDENED_CLAY ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_HAY_BALE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_HUGE_BROWN_MUSHROOM ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_HUGE_RED_MUSHROOM ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_ICE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_IRON_BLOCK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_IRON_ORE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_JACK_O_LANTERN ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_JUKEBOX ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_LAPIS_BLOCK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_LAPIS_ORE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_LOG ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_MELON ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_MOSSY_COBBLESTONE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_MYCELIUM ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_NETHERRACK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_NETHER_BRICK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_NETHER_QUARTZ_ORE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_NOTE_BLOCK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_OBSIDIAN ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_PACKED_ICE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_PLANKS ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_PUMPKIN ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_QUARTZ_BLOCK ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_REDSTONE_LAMP_OFF ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_REDSTONE_LAMP_ON ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_REDSTONE_ORE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_REDSTONE_ORE_GLOWING].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_SANDSTONE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_SAND ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_SILVERFISH_EGG ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_SPONGE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_STAINED_CLAY ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_WOOL ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_STONE ].m_FullyOccupiesVoxel = true; - ms_Info[E_BLOCK_STONE_BRICKS ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_NEW_LOG ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_BEDROCK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_BLOCK_OF_COAL ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_BLOCK_OF_REDSTONE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_BOOKCASE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_BRICK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_CLAY ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_COAL_ORE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_COBBLESTONE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_COMMAND_BLOCK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_CRAFTING_TABLE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_DIAMOND_BLOCK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_DIAMOND_ORE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_DIRT ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_DISPENSER ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_DOUBLE_STONE_SLAB ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_DOUBLE_WOODEN_SLAB ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_DROPPER ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_EMERALD_BLOCK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_EMERALD_ORE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_END_STONE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_FURNACE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_GLOWSTONE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_GOLD_BLOCK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_GOLD_ORE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_GRASS ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_GRAVEL ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_HARDENED_CLAY ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_HAY_BALE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_HUGE_BROWN_MUSHROOM ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_HUGE_RED_MUSHROOM ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_ICE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_IRON_BLOCK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_IRON_ORE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_JACK_O_LANTERN ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_JUKEBOX ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_LAPIS_BLOCK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_LAPIS_ORE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_LOG ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_MELON ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_MOSSY_COBBLESTONE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_MYCELIUM ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_NETHERRACK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_NETHER_BRICK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_NETHER_QUARTZ_ORE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_NOTE_BLOCK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_OBSIDIAN ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_PACKED_ICE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_PLANKS ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_PUMPKIN ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_QUARTZ_BLOCK ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_REDSTONE_LAMP_OFF ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_REDSTONE_LAMP_ON ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_REDSTONE_ORE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_REDSTONE_ORE_GLOWING].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_SANDSTONE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_SAND ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_SILVERFISH_EGG ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_SPONGE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_STAINED_CLAY ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_WOOL ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_STONE ].m_FullyOccupiesVoxel = true; + a_Info[E_BLOCK_STONE_BRICKS ].m_FullyOccupiesVoxel = true; } - -// This is actually just some code that needs to run at program startup, so it is wrapped into a global var's constructor: -class cBlockInfoInitializer -{ -public: - cBlockInfoInitializer(void) - { - cBlockInfo::Initialize(); - } -} BlockInfoInitializer; - - - - - diff --git a/src/BlockInfo.h b/src/BlockInfo.h index 40c1db867..d6d4e7430 100644 --- a/src/BlockInfo.h +++ b/src/BlockInfo.h @@ -16,18 +16,8 @@ class cBlockHandler; class cBlockInfo { public: - // tolua_end - - cBlockInfo(); - - ~cBlockInfo(); - - /** (Re-)Initializes the internal BlockInfo structures. */ - static void Initialize(void); - // tolua_begin - - /** Returns the associated BlockInfo structure. */ + /** Returns the associated BlockInfo structure for the specified block type. */ static cBlockInfo & Get(BLOCKTYPE a_Type); @@ -79,13 +69,18 @@ public: inline static cBlockHandler * GetHandler (BLOCKTYPE a_Type) { return Get(a_Type).m_Handler; } - protected: + /** Storage for all the BlockInfo structures. */ + typedef cBlockInfo cBlockInfoArray[256]; - // TODO xdot: Change to std::vector to support dynamic block IDs - static cBlockInfo ms_Info[256]; + /** Creates a default BlockInfo structure, initializes all values to their defaults */ + cBlockInfo(); + /** Cleans up the stored values */ + ~cBlockInfo(); + /** Initializes the specified BlockInfo structures with block-specific values. */ + static void Initialize(cBlockInfoArray & a_BlockInfos); }; // tolua_export diff --git a/src/Blocks/BlockButton.h b/src/Blocks/BlockButton.h index 4b2f6f618..7f92681dc 100644 --- a/src/Blocks/BlockButton.h +++ b/src/Blocks/BlockButton.h @@ -23,7 +23,8 @@ public: NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x08); a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); - a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f); + a_WorldInterface.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); + a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("random.click", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f); // Queue a button reset (unpress) a_ChunkInterface.QueueSetBlock(a_BlockX, a_BlockY, a_BlockZ, m_BlockType, (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x07), m_BlockType == E_BLOCK_STONE_BUTTON ? 20 : 30, m_BlockType, a_WorldInterface); @@ -102,7 +103,7 @@ public: AddFaceDirection(a_RelX, a_RelY, a_RelZ, BlockMetaDataToBlockFace(Meta), true); BLOCKTYPE BlockIsOn; a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, BlockIsOn); - return (a_RelY > 0) && (cBlockInfo::IsSolid(BlockIsOn)); + return (a_RelY > 0) && (cBlockInfo::FullyOccupiesVoxel(BlockIsOn)); } } ; diff --git a/src/Blocks/BlockChest.h b/src/Blocks/BlockChest.h index c9a769c75..f899f4bcb 100644 --- a/src/Blocks/BlockChest.h +++ b/src/Blocks/BlockChest.h @@ -44,16 +44,16 @@ public: } double yaw = a_Player->GetYaw(); if ( - (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + (Area.GetRelBlockType(0, 0, 1) == m_BlockType) || + (Area.GetRelBlockType(2, 0, 1) == m_BlockType) ) { a_BlockMeta = ((yaw >= -90) && (yaw < 90)) ? 2 : 3; return true; } if ( - (Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + (Area.GetRelBlockType(0, 0, 1) == m_BlockType) || + (Area.GetRelBlockType(2, 0, 1) == m_BlockType) ) { // FIXME: This is unreachable, as the condition is the same as the above one @@ -130,12 +130,12 @@ public: } int NumChestNeighbors = 0; - if (Area.GetRelBlockType(1, 0, 2) == E_BLOCK_CHEST) + if (Area.GetRelBlockType(1, 0, 2) == m_BlockType) { if ( - (Area.GetRelBlockType(0, 0, 2) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) + (Area.GetRelBlockType(0, 0, 2) == m_BlockType) || + (Area.GetRelBlockType(1, 0, 1) == m_BlockType) || + (Area.GetRelBlockType(1, 0, 3) == m_BlockType) ) { // Already a doublechest neighbor, disallow: @@ -143,12 +143,12 @@ public: } NumChestNeighbors += 1; } - if (Area.GetRelBlockType(3, 0, 2) == E_BLOCK_CHEST) + if (Area.GetRelBlockType(3, 0, 2) == m_BlockType) { if ( - (Area.GetRelBlockType(4, 0, 2) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) + (Area.GetRelBlockType(4, 0, 2) == m_BlockType) || + (Area.GetRelBlockType(3, 0, 1) == m_BlockType) || + (Area.GetRelBlockType(3, 0, 3) == m_BlockType) ) { // Already a doublechest neighbor, disallow: @@ -156,12 +156,12 @@ public: } NumChestNeighbors += 1; } - if (Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST) + if (Area.GetRelBlockType(2, 0, 1) == m_BlockType) { if ( - (Area.GetRelBlockType(2, 0, 0) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 1) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 1) == E_BLOCK_CHEST) + (Area.GetRelBlockType(2, 0, 0) == m_BlockType) || + (Area.GetRelBlockType(1, 0, 1) == m_BlockType) || + (Area.GetRelBlockType(3, 0, 1) == m_BlockType) ) { // Already a doublechest neighbor, disallow: @@ -169,12 +169,12 @@ public: } NumChestNeighbors += 1; } - if (Area.GetRelBlockType(2, 0, 3) == E_BLOCK_CHEST) + if (Area.GetRelBlockType(2, 0, 3) == m_BlockType) { if ( - (Area.GetRelBlockType(2, 0, 4) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(1, 0, 3) == E_BLOCK_CHEST) || - (Area.GetRelBlockType(3, 0, 3) == E_BLOCK_CHEST) + (Area.GetRelBlockType(2, 0, 4) == m_BlockType) || + (Area.GetRelBlockType(1, 0, 3) == m_BlockType) || + (Area.GetRelBlockType(3, 0, 3) == m_BlockType) ) { // Already a doublechest neighbor, disallow: @@ -217,7 +217,7 @@ public: /// If there's a chest in the a_Area in the specified coords, modifies its meta to a_NewMeta and returns true. bool CheckAndAdjustNeighbor(cChunkInterface & a_ChunkInterface, const cBlockArea & a_Area, int a_RelX, int a_RelZ, NIBBLETYPE a_NewMeta) { - if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != E_BLOCK_CHEST) + if (a_Area.GetRelBlockType(a_RelX, 0, a_RelZ) != m_BlockType) { return false; } @@ -228,7 +228,7 @@ public: virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { - a_Pickups.push_back(cItem(E_BLOCK_CHEST, 1, 0)); + a_Pickups.push_back(cItem(m_BlockType, 1, 0)); } } ; diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp index fb2d6f2dc..934a01994 100644 --- a/src/Blocks/BlockDoor.cpp +++ b/src/Blocks/BlockDoor.cpp @@ -45,9 +45,16 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn void cBlockDoorHandler::OnUse(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) { + UNUSED(a_WorldInterface); + UNUSED(a_BlockFace); + UNUSED(a_CursorX); + UNUSED(a_CursorY); + UNUSED(a_CursorZ); + if (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) { ChangeDoor(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + a_Player->GetWorld()->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle()); } } diff --git a/src/Blocks/BlockFenceGate.h b/src/Blocks/BlockFenceGate.h index e202c6610..e992870d4 100644 --- a/src/Blocks/BlockFenceGate.h +++ b/src/Blocks/BlockFenceGate.h @@ -45,6 +45,7 @@ public: // Standing aside - use last direction a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, OldMetaData); } + a_Player->GetWorld()->BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0, a_Player->GetClientHandle()); } diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp index a7b89fcb7..730774ab1 100644 --- a/src/Blocks/BlockHandler.cpp +++ b/src/Blocks/BlockHandler.cpp @@ -65,6 +65,8 @@ #include "BlockRedstoneRepeater.h" #include "BlockRedstoneTorch.h" #include "BlockTNT.h" +#include "BlockTripwire.h" +#include "BlockTripwireHook.h" #include "BlockSand.h" #include "BlockSapling.h" #include "BlockSideways.h" @@ -85,11 +87,96 @@ +/* +// Tests the meta rotation and mirroring. +// Note that the cMetaRotator needs to have its assert paths disabled for this test to work! +static class cBlockHandlerRotationTester +{ +public: + cBlockHandlerRotationTester(void) + { + printf("Performing block handlers test...\n"); + for (BLOCKTYPE Type = 0; Type < E_BLOCK_MAX_TYPE_ID; Type++) + { + cBlockHandler * Handler = cBlockInfo::GetHandler(Type); + if (Handler == NULL) + { + printf("NULL handler for block type %d!\n", Type); + continue; + } + AString BlockName = ItemTypeToString(Type); + for (NIBBLETYPE Meta = 0; Meta < 16; Meta++) + { + // Test the CW / CCW rotations: + NIBBLETYPE TestMeta; + TestMeta = Handler->MetaRotateCW(Handler->MetaRotateCW(Handler->MetaRotateCW(Handler->MetaRotateCW(Meta)))); + if (TestMeta != Meta) + { + // 4 CW rotations should produce no change in the meta + printf("Handler for blocktype %d (%s) fails CW 4-rotation test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + TestMeta = Handler->MetaRotateCCW(Handler->MetaRotateCCW(Handler->MetaRotateCCW(Handler->MetaRotateCCW(Meta)))); + if (TestMeta != Meta) + { + // 4 CCW rotations should produce no change in the meta + printf("Handler for blocktype %d (%s) fails CCW 4-rotation test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + TestMeta = Handler->MetaRotateCCW(Handler->MetaRotateCW(Meta)); + if (TestMeta != Meta) + { + // CCW rotation of a CW rotation should produce no change in the meta + printf("Handler for blocktype %d (%s) fails CCW(CW) rotation test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + TestMeta = Handler->MetaRotateCW(Handler->MetaRotateCCW(Meta)); + if (TestMeta != Meta) + { + // CW rotation of a CCW rotation should produce no change in the meta + printf("Handler for blocktype %d (%s) fails CW(CCW) rotation test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + + // Test the mirroring: + TestMeta = Handler->MetaMirrorXY(Handler->MetaMirrorXY(Meta)); + if (TestMeta != Meta) + { + // Double-mirroring should produce the same meta: + printf("Handler for blocktype %d (%s) fails XY mirror test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + TestMeta = Handler->MetaMirrorXZ(Handler->MetaMirrorXZ(Meta)); + if (TestMeta != Meta) + { + // Double-mirroring should produce the same meta: + printf("Handler for blocktype %d (%s) fails XZ mirror test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + TestMeta = Handler->MetaMirrorYZ(Handler->MetaMirrorYZ(Meta)); + if (TestMeta != Meta) + { + // Double-mirroring should produce the same meta: + printf("Handler for blocktype %d (%s) fails YZ mirror test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + + // Test mirror-rotating: + TestMeta = Handler->MetaRotateCW(Handler->MetaRotateCW(Handler->MetaMirrorXY(Handler->MetaMirrorYZ(Meta)))); + if (TestMeta != Meta) + { + // 2 CW rotations should be the same as XY, YZ mirroring: + printf("Handler for blocktype %d (%s) fails rotation-mirror test for meta %d: got back %d\n", Type, BlockName.c_str(), Meta, TestMeta); + } + } + } // for Type + printf("Block handlers test complete.\n"); + } +} g_BlockHandlerRotationTester; +//*/ + + + + + cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) { switch(a_BlockType) { - // Block handlers, alphabetically sorted: + // Block handlers, alphabetically sorted: case E_BLOCK_ACACIA_WOOD_STAIRS: return new cBlockStairsHandler (a_BlockType); case E_BLOCK_ACTIVATOR_RAIL: return new cBlockRailHandler (a_BlockType); case E_BLOCK_ANVIL: return new cBlockAnvilHandler (a_BlockType); @@ -151,9 +238,9 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_LAPIS_ORE: return new cBlockOreHandler (a_BlockType); case E_BLOCK_LAVA: return new cBlockLavaHandler (a_BlockType); case E_BLOCK_LEAVES: return new cBlockLeavesHandler (a_BlockType); + case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: return new cBlockPressurePlateHandler(a_BlockType); case E_BLOCK_LILY_PAD: return new cBlockLilypadHandler (a_BlockType); case E_BLOCK_LIT_FURNACE: return new cBlockFurnaceHandler (a_BlockType); - case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: return new cBlockPressurePlateHandler(a_BlockType); case E_BLOCK_LOG: return new cBlockSidewaysHandler (a_BlockType); case E_BLOCK_MELON: return new cBlockMelonHandler (a_BlockType); case E_BLOCK_MELON_STEM: return new cBlockStemsHandler (a_BlockType); @@ -175,7 +262,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_QUARTZ_BLOCK: return new cBlockQuartzHandler (a_BlockType); case E_BLOCK_QUARTZ_STAIRS: return new cBlockStairsHandler (a_BlockType); case E_BLOCK_RAIL: return new cBlockRailHandler (a_BlockType); - case E_BLOCK_REDSTONE_LAMP_ON: return new cBlockRedstoneLampHandler (a_BlockType); // We need this to change pickups to an off lamp; else 1.7+ clients crash + case E_BLOCK_REDSTONE_LAMP_ON: return new cBlockRedstoneLampHandler (a_BlockType); case E_BLOCK_REDSTONE_ORE: return new cBlockOreHandler (a_BlockType); case E_BLOCK_REDSTONE_ORE_GLOWING: return new cBlockOreHandler (a_BlockType); case E_BLOCK_REDSTONE_REPEATER_OFF: return new cBlockRedstoneRepeaterHandler(a_BlockType); @@ -206,8 +293,11 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType) case E_BLOCK_TORCH: return new cBlockTorchHandler (a_BlockType); case E_BLOCK_TRAPDOOR: return new cBlockTrapdoorHandler (a_BlockType); case E_BLOCK_TNT: return new cBlockTNTHandler (a_BlockType); + case E_BLOCK_TRAPPED_CHEST: return new cBlockChestHandler (a_BlockType); + case E_BLOCK_TRIPWIRE: return new cBlockTripwireHandler (a_BlockType); + case E_BLOCK_TRIPWIRE_HOOK: return new cBlockTripwireHookHandler (a_BlockType); case E_BLOCK_VINES: return new cBlockVineHandler (a_BlockType); - case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType); + case E_BLOCK_WALLSIGN: return new cBlockSignHandler (a_BlockType); // TODO: This needs a special handler case E_BLOCK_WATER: return new cBlockFluidHandler (a_BlockType); case E_BLOCK_WOODEN_BUTTON: return new cBlockButtonHandler (a_BlockType); case E_BLOCK_WOODEN_DOOR: return new cBlockDoorHandler (a_BlockType); diff --git a/src/Blocks/BlockLever.h b/src/Blocks/BlockLever.h index ad2ae29e5..7ce8d038e 100644 --- a/src/Blocks/BlockLever.h +++ b/src/Blocks/BlockLever.h @@ -7,12 +7,13 @@ class cBlockLeverHandler : - public cMetaRotator<cBlockHandler, 0x07, 0x04, 0x02, 0x03, 0x01, false> + public cMetaRotator<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, false> { - typedef cMetaRotator<cBlockHandler, 0x07, 0x04, 0x02, 0x03, 0x01, false> super; + typedef cMetaRotator<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, false> super; + public: - cBlockLeverHandler(BLOCKTYPE a_BlockType) - : cMetaRotator<cBlockHandler, 0x07, 0x04, 0x02, 0x03, 0x01, false>(a_BlockType) + cBlockLeverHandler(BLOCKTYPE a_BlockType) : + super(a_BlockType) { } @@ -21,8 +22,9 @@ public: // Flip the ON bit on/off using the XOR bitwise operation NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08); - a_ChunkInterface.SetBlock(a_WorldInterface, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LEVER, Meta); // SetMeta doesn't work for unpowering levers, so setblock - a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f); + a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); + a_WorldInterface.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ); + a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("random.click", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f); } @@ -103,7 +105,7 @@ public: AddFaceDirection(a_RelX, a_RelY, a_RelZ, BlockMetaDataToBlockFace(Meta), true); BLOCKTYPE BlockIsOn; a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, BlockIsOn); - return (a_RelY > 0) && cBlockInfo::IsSolid(BlockIsOn); + return (a_RelY > 0) && cBlockInfo::FullyOccupiesVoxel(BlockIsOn); } @@ -132,7 +134,7 @@ public: case 0x05: return 0x06; // Ground rotation case 0x06: return 0x05; - default: return super::MetaRotateCCW(a_Meta); // Wall Rotation + default: return super::MetaRotateCW(a_Meta); // Wall Rotation } } } ; diff --git a/src/Blocks/BlockPiston.cpp b/src/Blocks/BlockPiston.cpp index faf639312..ec480aaea 100644 --- a/src/Blocks/BlockPiston.cpp +++ b/src/Blocks/BlockPiston.cpp @@ -85,7 +85,7 @@ int cBlockPistonHandler::FirstPassthroughBlock(int a_PistonX, int a_PistonY, int NIBBLETYPE currMeta; AddPistonDir(a_PistonX, a_PistonY, a_PistonZ, pistonmeta, 1); a_World->GetBlockTypeMeta(a_PistonX, a_PistonY, a_PistonZ, currBlock, currMeta); - if (CanBreakPush(currBlock)) + if (cBlockInfo::IsPistonBreakable(currBlock)) { // This block breaks when pushed, extend up to here return ret; @@ -124,7 +124,7 @@ void cBlockPistonHandler::ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ, } a_World->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, 0, pistonMeta, pistonBlock); - a_World->BroadcastSoundEffect("tile.piston.out", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.7f); + a_World->BroadcastSoundEffect("tile.piston.out", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 0.5f, 0.7f); // Drop the breakable block in the line, if appropriate: AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, dist + 1); // "a_Block" now at the breakable / empty block @@ -198,7 +198,7 @@ void cBlockPistonHandler::RetractPiston(int a_BlockX, int a_BlockY, int a_BlockZ AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, -1); a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta & ~(8)); a_World->BroadcastBlockAction(a_BlockX, a_BlockY, a_BlockZ, 1, pistonMeta & ~(8), pistonBlock); - a_World->BroadcastSoundEffect("tile.piston.in", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.7f); + a_World->BroadcastSoundEffect("tile.piston.in", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 0.5f, 0.7f); AddPistonDir(a_BlockX, a_BlockY, a_BlockZ, pistonMeta, 1); // Retract the extension, pull block if appropriate diff --git a/src/Blocks/BlockPiston.h b/src/Blocks/BlockPiston.h index e6fa48e54..27a44d829 100644 --- a/src/Blocks/BlockPiston.h +++ b/src/Blocks/BlockPiston.h @@ -104,6 +104,7 @@ private: case E_BLOCK_ENCHANTMENT_TABLE: case E_BLOCK_END_PORTAL: case E_BLOCK_END_PORTAL_FRAME: + // Notice the lack of an E_BLOCK_ENDER_CHEST here; its because ender chests can totally be pushed/pulled in MCS :) case E_BLOCK_FURNACE: case E_BLOCK_LIT_FURNACE: case E_BLOCK_HOPPER: @@ -113,6 +114,7 @@ private: case E_BLOCK_NOTE_BLOCK: case E_BLOCK_OBSIDIAN: case E_BLOCK_PISTON_EXTENSION: + case E_BLOCK_TRAPPED_CHEST: { return false; } @@ -126,24 +128,10 @@ private: return true; } - /// Returns true if the specified block can be pushed by a piston and broken / replaced - static inline bool CanBreakPush(BLOCKTYPE a_BlockType) { return cBlockInfo::IsPistonBreakable(a_BlockType); } - /// Returns true if the specified block can be pulled by a sticky piston static inline bool CanPull(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - switch (a_BlockType) - { - case E_BLOCK_LAVA: - case E_BLOCK_STATIONARY_LAVA: - case E_BLOCK_STATIONARY_WATER: - case E_BLOCK_WATER: - { - return false; - } - } - - if (CanBreakPush(a_BlockType)) + if (cBlockInfo::IsPistonBreakable(a_BlockType)) { return false; // CanBreakPush returns true, but we need false to prevent pulling } diff --git a/src/Blocks/BlockSign.h b/src/Blocks/BlockSign.h index 9d6fede21..f5630bdb0 100644 --- a/src/Blocks/BlockSign.h +++ b/src/Blocks/BlockSign.h @@ -75,13 +75,13 @@ public: virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override { - return (++a_Meta) & 0x0F; + return (a_Meta + 4) & 0x0f; } virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override { - return (--a_Meta) & 0x0F; + return (a_Meta + 12) & 0x0f; } virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override @@ -90,7 +90,7 @@ public: // There are 16 meta values which correspond to different directions. // These values are equated to angles on a circle; 0x08 = 180 degrees. - return (a_Meta < 0x08) ? 0x08 + a_Meta : 0x08 - a_Meta; + return (a_Meta < 0x08) ? (0x08 + a_Meta) : (0x08 - a_Meta); } diff --git a/src/Blocks/BlockSlab.h b/src/Blocks/BlockSlab.h index f3f2366fd..6c861be86 100644 --- a/src/Blocks/BlockSlab.h +++ b/src/Blocks/BlockSlab.h @@ -124,6 +124,12 @@ public: return E_BLOCK_AIR; } + + virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override + { + // Toggle the 4th bit - up / down: + return (a_Meta ^ 0x08); + } } ; @@ -167,15 +173,6 @@ public: ASSERT(!"Unhandled double slab type!"); return ""; } - - - virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override - { - NIBBLETYPE OtherMeta = a_Meta & 0x07; // Contains unrelated meta data. - - // 8th bit is up/down. 1 right-side-up, 0 is up-side-down. - return (a_Meta & 0x08) ? 0x00 + OtherMeta : 0x01 + OtherMeta; - } } ; diff --git a/src/Blocks/BlockStone.h b/src/Blocks/BlockStone.h index af4c6509a..cd5230f49 100644 --- a/src/Blocks/BlockStone.h +++ b/src/Blocks/BlockStone.h @@ -2,8 +2,6 @@ #pragma once #include "BlockHandler.h" -#include "../MersenneTwister.h" -#include "../World.h" diff --git a/src/Blocks/BlockTorch.h b/src/Blocks/BlockTorch.h index 8ddec8de1..44c33c429 100644 --- a/src/Blocks/BlockTorch.h +++ b/src/Blocks/BlockTorch.h @@ -154,7 +154,11 @@ public: if ( (BlockInQuestion == E_BLOCK_GLASS) || + (BlockInQuestion == E_BLOCK_STAINED_GLASS) || (BlockInQuestion == E_BLOCK_FENCE) || + (BlockInQuestion == E_BLOCK_SOULSAND) || + (BlockInQuestion == E_BLOCK_MOB_SPAWNER) || + (BlockInQuestion == E_BLOCK_END_PORTAL_FRAME) || // Actual vanilla behaviour (BlockInQuestion == E_BLOCK_NETHER_BRICK_FENCE) || (BlockInQuestion == E_BLOCK_COBBLESTONE_WALL) ) diff --git a/src/Blocks/BlockTripwire.h b/src/Blocks/BlockTripwire.h new file mode 100644 index 000000000..3ab17bf4a --- /dev/null +++ b/src/Blocks/BlockTripwire.h @@ -0,0 +1,32 @@ + +#pragma once + +#include "BlockHandler.h" + + + + + +class cBlockTripwireHandler : + public cBlockHandler +{ +public: + cBlockTripwireHandler(BLOCKTYPE a_BlockType) + : cBlockHandler(a_BlockType) + { + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + a_Pickups.push_back(cItem(E_ITEM_STRING, 1, 0)); + } + + virtual const char * GetStepSound(void) override + { + return ""; + } +}; + + + + diff --git a/src/Blocks/BlockTripwireHook.h b/src/Blocks/BlockTripwireHook.h new file mode 100644 index 000000000..f849fb8ad --- /dev/null +++ b/src/Blocks/BlockTripwireHook.h @@ -0,0 +1,82 @@ +#pragma once + +#include "BlockHandler.h" +#include "MetaRotator.h" + + + + + +class cBlockTripwireHookHandler : + public cMetaRotator<cBlockHandler, 0x03, 0x02, 0x03, 0x00, 0x01> +{ +public: + cBlockTripwireHookHandler(BLOCKTYPE a_BlockType) + : cMetaRotator<cBlockHandler, 0x03, 0x02, 0x03, 0x00, 0x01>(a_BlockType) + { + } + + virtual bool GetPlacementBlockTypeMeta( + cChunkInterface & a_ChunkInterface, 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 = m_BlockType; + + a_BlockMeta = DirectionToMetadata(a_BlockFace); + + return true; + } + + inline static NIBBLETYPE DirectionToMetadata(eBlockFace a_Direction) + { + switch (a_Direction) + { + case BLOCK_FACE_XM: return 0x1; + case BLOCK_FACE_XP: return 0x3; + case BLOCK_FACE_ZM: return 0x2; + case BLOCK_FACE_ZP: return 0x0; + default: ASSERT(!"Unhandled tripwire hook direction!"); return 0x0; + } + } + + inline static eBlockFace MetadataToDirection(NIBBLETYPE a_Meta) + { + switch (a_Meta & 0x03) + { + case 0x1: return BLOCK_FACE_XM; + case 0x3: return BLOCK_FACE_XP; + case 0x2: return BLOCK_FACE_ZM; + case 0x0: return BLOCK_FACE_ZP; + default: ASSERT(!"Unhandled tripwire hook metadata!"); return BLOCK_FACE_NONE; + } + } + + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override + { + // Reset meta to 0 + a_Pickups.push_back(cItem(E_BLOCK_TRIPWIRE_HOOK, 1, 0)); + } + + virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override + { + NIBBLETYPE Meta; + a_Chunk.UnboundedRelGetBlockMeta(a_RelX, a_RelY, a_RelZ, Meta); + + AddFaceDirection(a_RelX, a_RelY, a_RelZ, MetadataToDirection(Meta), true); + BLOCKTYPE BlockIsOn; a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, BlockIsOn); + + return (a_RelY > 0) && cBlockInfo::FullyOccupiesVoxel(BlockIsOn); + } + + virtual const char * GetStepSound(void) override + { + return "step.wood"; + } +}; + + + + diff --git a/src/Blocks/BroadcastInterface.h b/src/Blocks/BroadcastInterface.h index b1b450690..fbe72e72f 100644 --- a/src/Blocks/BroadcastInterface.h +++ b/src/Blocks/BroadcastInterface.h @@ -7,6 +7,6 @@ public: virtual ~cBroadcastInterface() {} virtual void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0; - virtual void BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL) = 0; + virtual void BroadcastSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL) = 0; virtual void BroadcastEntityAnimation(const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = NULL) = 0; }; diff --git a/src/Blocks/WorldInterface.h b/src/Blocks/WorldInterface.h index 650a216c0..251b28d03 100644 --- a/src/Blocks/WorldInterface.h +++ b/src/Blocks/WorldInterface.h @@ -46,4 +46,7 @@ public: virtual void SetTimeOfDay(Int64 a_TimeOfDay) = 0; + /** Wakes up the simulators for the specified block */ + virtual void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) = 0; + }; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 335ce8315..5ff1c4e3c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required (VERSION 2.8.2) project (MCServer) -include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/") -include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/jsoncpp/include") -include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/polarssl/include") +include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/") +include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/jsoncpp/include") +include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include") set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++) set(FOLDERS ${FOLDERS} WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs) @@ -12,6 +12,7 @@ set(BINDING_DEPENDECIES tolua ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/virtual_method_hooks.lua ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/AllToLua.pkg + Bindings/gen_LuaState_Call.lua Bindings/LuaFunctions.h Bindings/LuaWindow.h Bindings/Plugin.h @@ -79,16 +80,22 @@ set(BINDING_DEPENDECIES World.h ) +# List all the files that are generated as part of the Bindings build process +set (BINDING_OUTPUTS + ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Bindings.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Bindings.h + ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/LuaState_Call.inc +) + include_directories(Bindings) include_directories(.) if (WIN32) ADD_CUSTOM_COMMAND( - # add any new generated bindings here - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Bindings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Bindings.h + OUTPUT ${BINDING_OUTPUTS} # Copy the Lua DLL into the Bindings folder, so that tolua can run from there: - COMMAND copy /y ..\\..\\MCServer\\lua51.dll . + COMMAND ${CMAKE_COMMAND} -E copy_if_different ../../MCServer/lua51.dll ./lua51.dll # Regenerate bindings: COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg @@ -119,7 +126,8 @@ if (NOT MSVC) # lib dependencies are not included - + include_directories ("${CMAKE_CURRENT_SOURCE_DIR}/../lib/polarssl/include") + #add cpp files here add_library(Bindings Bindings/Bindings @@ -134,7 +142,7 @@ if (NOT MSVC) Bindings/WebPlugin ) - target_link_libraries(Bindings lua sqlite tolualib) + target_link_libraries(Bindings lua sqlite tolualib polarssl) #clear file file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/BindingDependecies.txt) @@ -260,4 +268,4 @@ endif () if (WIN32) target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib) endif() -target_link_libraries(${EXECUTABLE} md5 luaexpat iniFile jsoncpp polarssl zlib lua sqlite) +target_link_libraries(${EXECUTABLE} luaexpat iniFile jsoncpp polarssl zlib sqlite lua) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 6ab49036d..8a249ea53 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -87,7 +87,8 @@ cChunk::cChunk( m_NeighborZM(a_NeighborZM), m_NeighborZP(a_NeighborZP), m_WaterSimulatorData(a_World->GetWaterSimulator()->CreateChunkData()), - m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()) + m_LavaSimulatorData (a_World->GetLavaSimulator ()->CreateChunkData()), + m_AlwaysTicked(0) { if (a_NeighborXM != NULL) { @@ -463,7 +464,7 @@ void cChunk::CollectMobCensus(cMobCensus& toFill) -void cChunk::getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ) +void cChunk::GetThreeRandomNumbers(int & a_X, int & a_Y, int & a_Z,int a_MaxX, int a_MaxY, int a_MaxZ) { ASSERT(a_MaxX * a_MaxY * a_MaxZ * 8 < 0x00ffffff); int Random = m_World->GetTickRandomNumber(0x00ffffff); @@ -479,12 +480,12 @@ void cChunk::getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a -void cChunk::getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z) +void cChunk::GetRandomBlockCoords(int & a_X, int & a_Y, int & a_Z) { // MG TODO : check if this kind of optimization (only one random call) is still needed // MG TODO : if so propagate it - getThreeRandomNumber(a_X, a_Y, a_Z, Width, Height-2, Width); + GetThreeRandomNumbers(a_X, a_Y, a_Z, Width, Height - 2, Width); a_Y++; } @@ -494,65 +495,68 @@ void cChunk::getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z) void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner) { - int Center_X,Center_Y,Center_Z; - getRandomBlockCoords(Center_X,Center_Y,Center_Z); - - BLOCKTYPE PackCenterBlock = GetBlock(Center_X, Center_Y, Center_Z); - if (a_MobSpawner.CheckPackCenter(PackCenterBlock)) - { - a_MobSpawner.NewPack(); - int NumberOfTries = 0; - int NumberOfSuccess = 0; - int MaxNbOfSuccess = 4; // this can be changed during the process for Wolves and Ghass - while (NumberOfTries < 12 && NumberOfSuccess < MaxNbOfSuccess) - { - const int HorizontalRange = 20; // MG TODO : relocate - const int VerticalRange = 0; // MG TODO : relocate - int Try_X, Try_Y, Try_Z; - getThreeRandomNumber(Try_X, Try_Y, Try_Z, 2*HorizontalRange+1 , 2*VerticalRange+1 , 2*HorizontalRange+1); - Try_X -= HorizontalRange; - Try_Y -= VerticalRange; - Try_Z -= HorizontalRange; - Try_X += Center_X; - Try_Y += Center_Y; - Try_Z += Center_Z; - - ASSERT(Try_Y > 0); - ASSERT(Try_Y < cChunkDef::Height-1); - - EMCSBiome Biome = m_ChunkMap->GetBiomeAt (Try_X, Try_Z); - // MG TODO : - // Moon cycle (for slime) - // check player and playerspawn presence < 24 blocks - // check mobs presence on the block - - // MG TODO : check that "Level" really means Y - - /* - NIBBLETYPE SkyLight = 0; - - NIBBLETYPE BlockLight = 0; - */ + int CenterX, CenterY, CenterZ; + GetRandomBlockCoords(CenterX, CenterY, CenterZ); - if (IsLightValid()) - { - cEntity* newMob = a_MobSpawner.TryToSpawnHere(this, Try_X, Try_Y, Try_Z, Biome, MaxNbOfSuccess); - if (newMob) - { - int WorldX, WorldY, WorldZ; - PositionToWorldPosition(Try_X, Try_Y, Try_Z, WorldX, WorldY, WorldZ); - double ActualX = WorldX + 0.5; - double ActualZ = WorldZ + 0.5; - newMob->SetPosition(ActualX, WorldY, ActualZ); - LOGD("Spawning %s #%i at %d,%d,%d",newMob->GetClass(),newMob->GetUniqueID(),WorldX, WorldY, WorldZ); - NumberOfSuccess++; - } - } - - NumberOfTries++; - } + BLOCKTYPE PackCenterBlock = GetBlock(CenterX, CenterY, CenterZ); + if (!a_MobSpawner.CheckPackCenter(PackCenterBlock)) + { + return; } + + a_MobSpawner.NewPack(); + int NumberOfTries = 0; + int NumberOfSuccess = 0; + int MaxNbOfSuccess = 4; // This can be changed during the process for Wolves and Ghasts + while ((NumberOfTries < 12) && (NumberOfSuccess < MaxNbOfSuccess)) + { + const int HorizontalRange = 20; // MG TODO : relocate + const int VerticalRange = 0; // MG TODO : relocate + int TryX, TryY, TryZ; + GetThreeRandomNumbers(TryX, TryY, TryZ, 2 * HorizontalRange + 1, 2 * VerticalRange + 1, 2 * HorizontalRange + 1); + TryX -= HorizontalRange; + TryY -= VerticalRange; + TryZ -= HorizontalRange; + TryX += CenterX; + TryY += CenterY; + TryZ += CenterZ; + + ASSERT(TryY > 0); + ASSERT(TryY < cChunkDef::Height - 1); + + EMCSBiome Biome = m_ChunkMap->GetBiomeAt(TryX, TryZ); + // MG TODO : + // Moon cycle (for slime) + // check player and playerspawn presence < 24 blocks + // check mobs presence on the block + + // MG TODO : check that "Level" really means Y + + /* + NIBBLETYPE SkyLight = 0; + + NIBBLETYPE BlockLight = 0; + */ + NumberOfTries++; + if (!IsLightValid()) + { + continue; + } + + cEntity * newMob = a_MobSpawner.TryToSpawnHere(this, TryX, TryY, TryZ, Biome, MaxNbOfSuccess); + if (newMob == NULL) + { + continue; + } + int WorldX, WorldY, WorldZ; + PositionToWorldPosition(TryX, TryY, TryZ, WorldX, WorldY, WorldZ); + double ActualX = WorldX + 0.5; + double ActualZ = WorldZ + 0.5; + newMob->SetPosition(ActualX, WorldY, ActualZ); + LOGD("Spawning %s #%i at {%d, %d, %d}", newMob->GetClass(), newMob->GetUniqueID(), WorldX, WorldY, WorldZ); + NumberOfSuccess++; + } // while (retry) } @@ -1297,6 +1301,7 @@ void cChunk::CreateBlockEntities(void) switch (BlockType) { case E_BLOCK_BEACON: + case E_BLOCK_TRAPPED_CHEST: case E_BLOCK_CHEST: case E_BLOCK_COMMAND_BLOCK: case E_BLOCK_DISPENSER: @@ -1427,6 +1432,7 @@ void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, switch (a_BlockType) { case E_BLOCK_BEACON: + case E_BLOCK_TRAPPED_CHEST: case E_BLOCK_CHEST: case E_BLOCK_COMMAND_BLOCK: case E_BLOCK_DISPENSER: @@ -1616,6 +1622,12 @@ void cChunk::AddBlockEntity(cBlockEntity * a_BlockEntity) cBlockEntity * cChunk::GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ) { + // Check that the query coords are within chunk bounds: + ASSERT(a_BlockX >= m_PosX * cChunkDef::Width); + ASSERT(a_BlockX < m_PosX * cChunkDef::Width + cChunkDef::Width); + ASSERT(a_BlockZ >= m_PosZ * cChunkDef::Width); + ASSERT(a_BlockZ < m_PosZ * cChunkDef::Width + cChunkDef::Width); + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) { if ( @@ -1635,6 +1647,31 @@ cBlockEntity * cChunk::GetBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ) +bool cChunk::ShouldBeTicked(void) const +{ + return (HasAnyClients() || (m_AlwaysTicked > 0)); +} + + + + + +void cChunk::SetAlwaysTicked(bool a_AlwaysTicked) +{ + if (a_AlwaysTicked) + { + m_AlwaysTicked += 1; + } + else + { + m_AlwaysTicked -= 1; + } +} + + + + + void cChunk::UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z) { cBlockEntity * be = GetBlockEntity(a_X, a_Y, a_Z); @@ -1846,7 +1883,7 @@ bool cChunk::HasClient( cClientHandle* a_Client ) -bool cChunk::HasAnyClients(void) +bool cChunk::HasAnyClients(void) const { return !m_LoadedByClient.empty(); } @@ -2115,7 +2152,7 @@ bool cChunk::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallb { continue; } - if ((*itr)->GetBlockType() != E_BLOCK_CHEST) + if (((*itr)->GetBlockType() != E_BLOCK_CHEST) && ((*itr)->GetBlockType() != E_BLOCK_TRAPPED_CHEST)) // Trapped chests use normal chests' handlers { // 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; @@ -2495,8 +2532,8 @@ cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ) { int BlockX = m_PosX * cChunkDef::Width + a_RelX; int BlockZ = m_PosZ * cChunkDef::Width + a_RelZ; - int BlockY, ChunkX, ChunkZ; - AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + int ChunkX, ChunkZ; + BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); return m_ChunkMap->GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ); } @@ -2695,7 +2732,7 @@ void cChunk::BroadcastChunkData(cChunkDataSerializer & a_Serializer, const cClie -void cChunk::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +void cChunk::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude) { for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) { @@ -2703,7 +2740,7 @@ void cChunk::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_ { continue; } - (*itr)->SendCollectPickup(a_Pickup, a_Player); + (*itr)->SendCollectEntity(a_Entity, a_Player); } // for itr - LoadedByClient[] } @@ -2919,7 +2956,7 @@ void cChunk::BroadcastRemoveEntityEffect(const cEntity & a_Entity, int a_EffectI -void cChunk::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +void cChunk::BroadcastSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) { for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) { @@ -2927,7 +2964,7 @@ void cChunk::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a { continue; } - (*itr)->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); + (*itr)->SendSoundEffect(a_SoundName, a_X, a_Y, a_Z, a_Volume, a_Pitch); } // for itr - LoadedByClient[] } diff --git a/src/Chunk.h b/src/Chunk.h index 7664a7afd..dfcfdab0f 100644 --- a/src/Chunk.h +++ b/src/Chunk.h @@ -200,11 +200,16 @@ public: void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client); /** Adds a client to the chunk; returns true if added, false if already there */ - bool AddClient (cClientHandle* a_Client ); + bool AddClient(cClientHandle * a_Client); - void RemoveClient (cClientHandle* a_Client ); - bool HasClient (cClientHandle* a_Client ); - bool HasAnyClients(void); // Returns true if theres any client in the chunk; false otherwise + /** Removes the specified client from the chunk; ignored if client not in chunk. */ + void RemoveClient(cClientHandle * a_Client); + + /** Returns true if the specified client is present in this chunk. */ + bool HasClient(cClientHandle * a_Client); + + /** Returns true if theres any client in the chunk; false otherwise */ + bool HasAnyClients(void) const; void AddEntity(cEntity * a_Entity); void RemoveEntity(cEntity * a_Entity); @@ -279,7 +284,7 @@ public: void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL); void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); void BroadcastChunkData (cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); - void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); + void BroadcastCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); void BroadcastEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude = NULL); void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); @@ -293,7 +298,7 @@ public: void BroadcastEntityAnimation (const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = NULL); void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude = NULL); void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = NULL); - void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 + void BroadcastSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); @@ -390,6 +395,17 @@ public: 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); } + + /** Returns true if the chunk should be ticked in the tick-thread. + Checks if there are any clients and if the always-tick flag is set */ + bool ShouldBeTicked(void) const; + + /** Increments (a_AlwaysTicked == true) or decrements (false) the m_AlwaysTicked counter. + If the m_AlwaysTicked counter is greater than zero, the chunk is ticked in the tick-thread regardless of + whether it has any clients or not. + This function allows nesting and task-concurrency (multiple separate tasks can request ticking and as long + as at least one requests is active the chunk will be ticked). */ + void SetAlwaysTicked(bool a_AlwaysTicked); private: @@ -462,10 +478,16 @@ private: /** Indicates if simulate-once blocks should be updated by the redstone simulator */ bool m_IsRedstoneDirty; + + /** If greater than zero, the chunk is ticked even if it has no clients. + Manipulated by the SetAlwaysTicked() function, allows for nested calls of the function. + This is the support for plugin-accessible chunk tick forcing. */ + int m_AlwaysTicked; + // Pick up a random block of this chunk - void getRandomBlockCoords(int& a_X, int& a_Y, int& a_Z); - void getThreeRandomNumber(int& a_X, int& a_Y, int& a_Z,int a_MaxX, int a_MaxY, int a_MaxZ); + void GetRandomBlockCoords(int & a_X, int & a_Y, int & a_Z); + void GetThreeRandomNumbers(int & a_X, int & a_Y, int & a_Z, int a_MaxX, int a_MaxY, int a_MaxZ); void RemoveBlockEntity(cBlockEntity * a_BlockEntity); void AddBlockEntity (cBlockEntity * a_BlockEntity); diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 164b7d37a..a12be04cf 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -419,16 +419,16 @@ void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSeriali -void cChunkMap::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +void cChunkMap::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); - cChunkPtr Chunk = GetChunkNoGen(a_Pickup.GetChunkX(), ZERO_CHUNK_Y, a_Pickup.GetChunkZ()); + cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ()); if (Chunk == NULL) { return; } // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); + Chunk->BroadcastCollectEntity(a_Entity, a_Player, a_Exclude); } @@ -647,19 +647,19 @@ void cChunkMap::BroadcastRemoveEntityEffect(const cEntity & a_Entity, int a_Effe -void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(a_SrcX / 8, a_SrcZ / 8, ChunkX, ChunkZ); + cChunkDef::BlockToChunk((int)std::floor(a_X), (int)std::floor(a_Z), ChunkX, ChunkZ); cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ); if (Chunk == NULL) { return; } // It's perfectly legal to broadcast packets even to invalid chunks! - Chunk->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); + Chunk->BroadcastSoundEffect(a_SoundName, a_X, a_Y, a_Z, a_Volume, a_Pitch, a_Exclude); } @@ -847,7 +847,22 @@ void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_M -void cChunkMap::MarkChunkDirty (int a_ChunkX, int a_ChunkZ) +void cChunkMap::MarkRedstoneDirty(int a_ChunkX, int a_ChunkZ) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) || !Chunk->IsValid()) + { + return; + } + Chunk->SetIsRedstoneDirty(true); +} + + + + + +void cChunkMap::MarkChunkDirty(int a_ChunkX, int a_ChunkZ, bool a_MarkRedstoneDirty) { cCSLock Lock(m_CSLayers); cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); @@ -856,6 +871,10 @@ void cChunkMap::MarkChunkDirty (int a_ChunkX, int a_ChunkZ) return; } Chunk->MarkDirty(); + if (a_MarkRedstoneDirty) + { + Chunk->SetIsRedstoneDirty(true); + } } @@ -1259,7 +1278,7 @@ void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYP -void cChunkMap::SetBlock(cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients) +void cChunkMap::SetBlock(cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, bool a_SendToClients) { cChunkInterface ChunkInterface(this); if (a_BlockType == E_BLOCK_AIR) @@ -1284,7 +1303,7 @@ void cChunkMap::SetBlock(cWorldInterface & a_WorldInterface, int a_BlockX, int a -void cChunkMap::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, Int64 a_Tick, BLOCKTYPE a_PreviousBlockType) +void cChunkMap::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick, BLOCKTYPE a_PreviousBlockType) { int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ); @@ -2674,6 +2693,20 @@ void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ) +void cChunkMap::SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if (Chunk != NULL) + { + Chunk->SetAlwaysTicked(a_AlwaysTicked); + } +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cChunkMap::cChunkLayer: @@ -2788,12 +2821,14 @@ void cChunkMap::cChunkLayer::SpawnMobs(cMobSpawner& a_MobSpawner) + + void cChunkMap::cChunkLayer::Tick(float a_Dt) { for (size_t i = 0; i < ARRAYCOUNT(m_Chunks); i++) { - // Only tick chunks that are valid and have clients: - if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->HasAnyClients()) + // Only tick chunks that are valid and should be ticked: + if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->ShouldBeTicked()) { m_Chunks[i]->Tick(a_Dt); } diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 3ee0bab3c..ef9b76e3f 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -70,6 +70,7 @@ public: void BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a_blockY, int a_blockZ, char a_stage, const cClientHandle * a_Exclude = NULL); void BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude); void BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); + void BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); void BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); void BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); void BroadcastEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude = NULL); @@ -84,7 +85,7 @@ public: void BroadcastEntityAnimation(const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude = NULL); void BroadcastParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude = NULL); void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = NULL); - void BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // a_Src coords are Block * 8 + void BroadcastSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); void BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); void BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); @@ -105,7 +106,8 @@ public: /** Wakes up the simulators for the specified area of blocks */ void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ); - void MarkChunkDirty (int a_ChunkX, int a_ChunkZ); + void MarkRedstoneDirty (int a_ChunkX, int a_ChunkZ); + void MarkChunkDirty (int a_ChunkX, int a_ChunkZ, bool a_MarkRedstoneDirty = false); void MarkChunkSaving (int a_ChunkX, int a_ChunkZ); void MarkChunkSaved (int a_ChunkX, int a_ChunkZ); @@ -152,9 +154,9 @@ public: NIBBLETYPE GetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ); NIBBLETYPE GetBlockSkyLight (int a_BlockX, int a_BlockY, int a_BlockZ); NIBBLETYPE GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_BlockZ); - void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockMeta); - void SetBlock (cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients = true); - void QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, Int64 a_Tick, BLOCKTYPE a_PreviousBlockType = E_BLOCK_AIR); + void SetBlockMeta (int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_BlockMeta); + void SetBlock (cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, bool a_SendToClients = true); + void QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick, BLOCKTYPE a_PreviousBlockType = E_BLOCK_AIR); bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta); bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight); @@ -338,6 +340,13 @@ public: /** Returns the CS for locking the chunkmap; only cWorld::cLock may use this function! */ cCriticalSection & GetCS(void) { return m_CSLayers; } + + /** Increments (a_AlwaysTicked == true) or decrements (false) the m_AlwaysTicked counter for the specified chunk. + If the m_AlwaysTicked counter is greater than zero, the chunk is ticked in the tick-thread regardless of + whether it has any clients or not. + This function allows nesting and task-concurrency (multiple separate tasks can request ticking and as long + as at least one requests is active the chunk will be ticked). */ + void SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked); private: diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 46083a8f1..340db6394 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -30,7 +30,7 @@ #include "CompositeChat.h" #include "Items/ItemSword.h" -#include "md5/md5.h" +#include "polarssl/md5.h" @@ -40,9 +40,6 @@ /** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick */ #define MAX_BLOCK_CHANGE_INTERACTIONS 20 -/** How many ticks before the socket is closed after the client is destroyed (#31) */ -static const int TICKS_BEFORE_CLOSE = 20; - @@ -79,7 +76,6 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) : m_PingID(1), m_BlockDigAnimStage(-1), m_HasStartedDigging(false), - m_TicksSinceDestruction(0), m_State(csConnected), m_ShouldCheckDownloaded(false), m_NumExplosionsThisTick(0), @@ -104,7 +100,7 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) : cClientHandle::~cClientHandle() { - ASSERT(m_State >= csDestroyedWaiting); // Has Destroy() been called? + ASSERT(m_State == csDestroyed); // Has Destroy() been called? LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), this); @@ -169,7 +165,7 @@ void cClientHandle::Destroy(void) RemoveFromAllChunks(); m_Player->GetWorld()->RemoveClientFromChunkSender(this); } - m_State = csDestroyedWaiting; + m_State = csDestroyed; } @@ -239,18 +235,16 @@ AString cClientHandle::GenerateOfflineUUID(const AString & a_Username) // xxxxxxxx-xxxx-3xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal digit and y is one of 8, 9, A, or B // Generate an md5 checksum, and use it as base for the ID: - MD5 Checksum(a_Username); - AString UUID = Checksum.hexdigest(); - UUID[12] = '3'; // Version 3 UUID - UUID[16] = '8'; // Variant 1 UUID - - // Now the digest doesn't have the UUID slashes, but the client requires them, so add them into the appropriate positions: - UUID.insert(8, "-"); - UUID.insert(13, "-"); - UUID.insert(18, "-"); - UUID.insert(23, "-"); - - return UUID; + unsigned char MD5[16]; + md5((const unsigned char *)a_Username.c_str(), a_Username.length(), MD5); + MD5[6] &= 0x0f; // Need to trim to 4 bits only... + MD5[8] &= 0x0f; // ... otherwise %01x overflows into two chars + return Printf("%02x%02x%02x%02x-%02x%02x-3%01x%02x-8%01x%02x-%02x%02x%02x%02x%02x%02x", + MD5[0], MD5[1], MD5[2], MD5[3], + MD5[4], MD5[5], MD5[6], MD5[7], + MD5[8], MD5[9], MD5[10], MD5[11], + MD5[12], MD5[13], MD5[14], MD5[15] + ); } @@ -365,6 +359,9 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID) // Send scoreboard data World->GetScoreBoard().SendTo(*this); + // Send statistics + SendStatistics(m_Player->GetStatManager()); + // Delay the first ping until the client "settles down" // This should fix #889, "BadCast exception, cannot convert bit to fm" error in client cTimer t1; @@ -1085,12 +1082,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo void cClientHandle::FinishDigAnimation() { - if ( - !m_HasStartedDigging || // Hasn't received the DIG_STARTED packet - (m_LastDigBlockX == -1) || - (m_LastDigBlockY == -1) || - (m_LastDigBlockZ == -1) - ) + if (!m_HasStartedDigging) // Hasn't received the DIG_STARTED packet { return; } @@ -1227,9 +1219,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e { // A plugin won't let us eat, abort (send the proper packets to the client, too): m_Player->AbortEating(); - return; } - return; } else { @@ -1371,7 +1361,7 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, e NewBlock->OnPlacedByPlayer(ChunkInterface,*World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); // Step sound with 0.8f pitch is used as block placement sound - World->BroadcastSoundEffect(NewBlock->GetStepSound(), a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.8f); + World->BroadcastSoundEffect(NewBlock->GetStepSound(), (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 1.0f, 0.8f); cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); } @@ -1829,18 +1819,7 @@ bool cClientHandle::CheckBlockInteractionsRate(void) void cClientHandle::Tick(float a_Dt) -{ - // Handle clients that are waiting for final close while destroyed: - if (m_State == csDestroyedWaiting) - { - m_TicksSinceDestruction += 1; // This field is misused for the timeout counting - if (m_TicksSinceDestruction > TICKS_BEFORE_CLOSE) - { - m_State = csDestroyed; - } - return; - } - +{ // Process received network data: AString IncomingData; { @@ -1906,15 +1885,7 @@ void cClientHandle::Tick(float a_Dt) void cClientHandle::ServerTick(float a_Dt) -{ - // Handle clients that are waiting for final close while destroyed: - if (m_State == csDestroyedWaiting) - { - // Do not wait while the client is not in the world, simply cut them off. - m_State = csDestroyed; - return; - } - +{ // Process received network data: AString IncomingData; { @@ -2077,9 +2048,9 @@ void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializ -void cClientHandle::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +void cClientHandle::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player) { - m_Protocol->SendCollectPickup(a_Pickup, a_Player); + m_Protocol->SendCollectEntity(a_Entity, a_Player); } @@ -2401,9 +2372,9 @@ void cClientHandle::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effec -void cClientHandle::SendRespawn(const cWorld & a_World) +void cClientHandle::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks) { - m_Protocol->SendRespawn(a_World); + m_Protocol->SendRespawn(a_World, a_ShouldIgnoreDimensionChecks); } @@ -2455,9 +2426,9 @@ void cClientHandle::SendDisplayObjective(const AString & a_Objective, cScoreboar -void cClientHandle::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +void cClientHandle::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) { - m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); + m_Protocol->SendSoundEffect(a_SoundName, a_X, a_Y, a_Z, a_Volume, a_Pitch); } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index 3e18cbdad..922740b11 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -123,7 +123,7 @@ public: void SendChat (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = ""); void SendChat (const cCompositeChat & a_Message); void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer); - void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player); + void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player); void SendDestroyEntity (const cEntity & a_Entity); void SendDisconnect (const AString & a_Reason); void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ); @@ -156,13 +156,13 @@ public: void SendPlayerSpawn (const cPlayer & a_Player); void SendPluginMessage (const AString & a_Channel, const AString & a_Message); // Exported in ManualBindings.cpp void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID); - void SendRespawn (const cWorld & a_World); + void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false); void SendExperience (void); void SendExperienceOrb (const cExpOrb & a_ExpOrb); void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode); void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode); void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display); - void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8 + void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch); // tolua_export void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data); void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock); void SendSpawnMob (const cMonster & a_Mob); @@ -325,9 +325,6 @@ private: int m_LastDigBlockX; int m_LastDigBlockY; int m_LastDigBlockZ; - - /** Used while csDestroyedWaiting for counting the ticks until the connection is closed */ - int m_TicksSinceDestruction; enum eState { @@ -338,7 +335,6 @@ private: csConfirmingPos, ///< The client has been sent the position packet, waiting for them to repeat the position back csPlaying, ///< Normal gameplay csDestroying, ///< The client is being destroyed, don't queue any more packets / don't add to chunks - csDestroyedWaiting, ///< The client has been destroyed, but is still kept so that the Kick packet is delivered (#31) csDestroyed, ///< The client has been destroyed, the destructor is to be called from the owner thread // TODO: Add Kicking here as well diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp index db9dc781a..d5e41bd46 100644 --- a/src/Entities/ArrowEntity.cpp +++ b/src/Entities/ArrowEntity.cpp @@ -3,6 +3,7 @@ #include "Player.h" #include "ArrowEntity.h" #include "../Chunk.h" +#include "FastRandom.h" @@ -24,9 +25,9 @@ cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a SetYawFromSpeed(); SetPitchFromSpeed(); LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}", - m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(), - GetYaw(), GetPitch() - ); + m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(), + GetYaw(), GetPitch() + ); } @@ -44,6 +45,10 @@ cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) : m_bIsCollected(false), m_HitBlockPos(0, 0, 0) { + if (a_Player.IsGameModeCreative()) + { + m_PickupState = psInCreative; + } } @@ -67,17 +72,24 @@ bool cArrowEntity::CanPickup(const cPlayer & a_Player) const void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) -{ +{ + if (GetSpeed().EqualsEps(Vector3d(0, 0, 0), 0.0000001)) + { + SetSpeed(GetLookVector().NormalizeCopy() * 0.1); // Ensure that no division by zero happens later + } + Vector3d Hit = a_HitPos; - Hit += GetSpeed() / 700; // Make arrow sink into block a little + Vector3d SinkMovement = (GetSpeed() / 800); + Hit += (SinkMovement * 0.01) / SinkMovement.Length(); // Make arrow sink into block a centimetre so it lodges (but not to far so it goes black clientside) super::OnHitSolidBlock(Hit, a_HitFace); - int X = (int)floor(Hit.x), Y = (int)floor(Hit.y), Z = (int)floor(Hit.z); - + Vector3i BlockHit = Hit.Floor(); + + int X = BlockHit.x, Y = BlockHit.y, Z = BlockHit.z; m_HitBlockPos = Vector3i(X, Y, Z); // Broadcast arrow hit sound - m_World->BroadcastSoundEffect("random.bowhit", X * 8, Y * 8, Z * 8, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + m_World->BroadcastSoundEffect("random.bowhit", (double)X, (double)Y, (double)Z, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); } @@ -94,7 +106,7 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1); // Broadcast successful hit sound - m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + GetWorld()->BroadcastSoundEffect("random.successful_hit", GetPosX(), GetPosY(), GetPosZ(), 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); Destroy(); } @@ -105,16 +117,22 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) void cArrowEntity::CollectedBy(cPlayer * a_Dest) { - if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest))) + if (m_IsInGround && !m_bIsCollected && CanPickup(*a_Dest)) { - int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW); - if (NumAdded > 0) // Only play effects if there was space in inventory + // Do not add the arrow to the inventory when the player is in creative: + if (!a_Dest->IsGameModeCreative()) { - m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest); - // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) - m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); - m_bIsCollected = true; + int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW); + if (NumAdded == 0) + { + // No space in the inventory + return; + } } + + GetWorld()->BroadcastCollectEntity(*this, *a_Dest); + GetWorld()->BroadcastSoundEffect("random.pop", GetPosX(), GetPosY(), GetPosZ(), 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + m_bIsCollected = true; } } diff --git a/src/Entities/ArrowEntity.h b/src/Entities/ArrowEntity.h index 1fe3032ee..76cb24449 100644 --- a/src/Entities/ArrowEntity.h +++ b/src/Entities/ArrowEntity.h @@ -58,8 +58,14 @@ public: /// Sets the IsCritical flag void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; } + + /** Gets the block arrow is in */ + Vector3i GetBlockHit(void) const { return m_HitBlockPos; } // tolua_end + + /** Sets the block arrow is in. To be used by the MCA loader only! */ + void SetBlockHit(const Vector3i & a_BlockHit) { m_HitBlockPos = a_BlockHit; } protected: diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index f4e89367b..26823924f 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -255,7 +255,8 @@ void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_R void cEntity::SetYawFromSpeed(void) { - if ((abs(m_Speed.x) < std::numeric_limits<double>::epsilon()) && (abs(m_Speed.z) < std::numeric_limits<double>::epsilon())) + const double EPS = 0.0000001; + if ((abs(m_Speed.x) < EPS) && (abs(m_Speed.z) < EPS)) { // atan2() may overflow or is undefined, pick any number SetYaw(0); @@ -1089,7 +1090,10 @@ void cEntity::HandleAir(void) if (IsSubmerged()) { - SetSpeedY(1); // Float in the water + if (!IsPlayer()) // Players control themselves + { + SetSpeedY(1); // Float in the water + } // Either reduce air level or damage player if (m_AirLevel < 1) @@ -1235,7 +1239,7 @@ void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) if (GetWorld()->GetWorldAge() % 2 == 0) { double SpeedSqr = GetSpeed().SqrLength(); - if (SpeedSqr < std::numeric_limits<double>::epsilon()) + if (SpeedSqr == 0.0) { // Speed is zero, send this to clients once only as well as an absolute position if (!m_bHasSentNoSpeed) diff --git a/src/Entities/ExpOrb.cpp b/src/Entities/ExpOrb.cpp index 10f79aedc..e437c24ef 100644 --- a/src/Entities/ExpOrb.cpp +++ b/src/Entities/ExpOrb.cpp @@ -56,7 +56,7 @@ void cExpOrb::Tick(float a_Dt, cChunk & a_Chunk) LOGD("Player %s picked up an ExpOrb. His reward is %i", a_ClosestPlayer->GetName().c_str(), m_Reward); a_ClosestPlayer->DeltaExperience(m_Reward); - m_World->BroadcastSoundEffect("random.orb", (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + m_World->BroadcastSoundEffect("random.orb", GetPosX(), GetPosY(), GetPosZ(), 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); Destroy(); } diff --git a/src/Entities/ExpOrb.h b/src/Entities/ExpOrb.h index e76274ac9..2cd4ef31f 100644 --- a/src/Entities/ExpOrb.h +++ b/src/Entities/ExpOrb.h @@ -11,7 +11,7 @@ class cExpOrb : public cEntity { - typedef cExpOrb super; + typedef cEntity super; public: // tolua_end diff --git a/src/Entities/Floater.cpp b/src/Entities/Floater.cpp index b910c3769..d49893020 100644 --- a/src/Entities/Floater.cpp +++ b/src/Entities/Floater.cpp @@ -134,7 +134,7 @@ void cFloater::Tick(float a_Dt, cChunk & a_Chunk) { if (m_CountDownTime <= 0) { - m_World->BroadcastSoundEffect("random.splash", (int) floor(GetPosX() * 8), (int) floor(GetPosY() * 8), (int) floor(GetPosZ() * 8), 1, 1); + m_World->BroadcastSoundEffect("random.splash", GetPosX(), GetPosY(), GetPosZ(), 1, 1); SetPosY(GetPosY() - 1); m_CanPickupItem = true; m_PickupCountDown = 20; diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index 0fd006485..bae1485d4 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -30,7 +30,7 @@ public: virtual bool Item(cEntity * a_Entity) override { - if (!a_Entity->IsPickup() || (a_Entity->GetUniqueID() == m_Pickup->GetUniqueID()) || a_Entity->IsDestroyed()) + if (!a_Entity->IsPickup() || (a_Entity->GetUniqueID() <= m_Pickup->GetUniqueID()) || a_Entity->IsDestroyed()) { return false; } @@ -38,10 +38,31 @@ public: Vector3d EntityPos = a_Entity->GetPosition(); double Distance = (EntityPos - m_Position).Length(); - if ((Distance < 1.2) && ((cPickup *)a_Entity)->GetItem().IsEqual(m_Pickup->GetItem())) + cItem & Item = ((cPickup *)a_Entity)->GetItem(); + if ((Distance < 1.2) && Item.IsEqual(m_Pickup->GetItem())) { - m_Pickup->GetItem().AddCount(((cPickup *)a_Entity)->GetItem().m_ItemCount); - a_Entity->Destroy(); + short CombineCount = Item.m_ItemCount; + if ((CombineCount + m_Pickup->GetItem().m_ItemCount) > Item.GetMaxStackSize()) + { + CombineCount = Item.GetMaxStackSize() - m_Pickup->GetItem().m_ItemCount; + } + + if (CombineCount <= 0) + { + return false; + } + + m_Pickup->GetItem().AddCount((char)CombineCount); + Item.m_ItemCount -= CombineCount; + + if (Item.m_ItemCount <= 0) + { + a_Entity->Destroy(); + } + else + { + a_Entity->GetWorld()->BroadcastEntityMetadata(*a_Entity); + } m_FoundMatchingPickup = true; } return false; @@ -129,7 +150,7 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk) } } - if (!IsDestroyed()) // Don't try to combine if someone has tried to combine me + if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine into an already full pickup { cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this); m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries @@ -203,10 +224,10 @@ bool cPickup::CollectedBy(cPlayer * a_Dest) } m_Item.m_ItemCount -= NumAdded; - m_World->BroadcastCollectPickup(*this, *a_Dest); + m_World->BroadcastCollectEntity(*this, *a_Dest); // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) - m_World->BroadcastSoundEffect("random.pop",(int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); - if (m_Item.m_ItemCount == 0) + m_World->BroadcastSoundEffect("random.pop", GetPosX(), GetPosY(), GetPosZ(), 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + if (m_Item.m_ItemCount <= 0) { // All of the pickup has been collected, schedule the pickup for destroying m_bCollected = true; diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index e1e03fded..b1b7fc74e 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1,4 +1,4 @@ - + #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Player.h" @@ -8,6 +8,7 @@ #include "../World.h" #include "../Bindings/PluginManager.h" #include "../BlockEntities/BlockEntity.h" +#include "../BlockEntities/EnderChestEntity.h" #include "../GroupManager.h" #include "../Group.h" #include "../Root.h" @@ -33,50 +34,48 @@ -cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) - : super(etPlayer, 0.6, 1.8) - , m_bVisible(true) - , m_FoodLevel(MAX_FOOD_LEVEL) - , m_FoodSaturationLevel(5) - , m_FoodTickTimer(0) - , m_FoodExhaustionLevel(0) - , m_FoodPoisonedTicksRemaining(0) - , m_LastJumpHeight(0) - , m_LastGroundHeight(0) - , m_bTouchGround(false) - , m_Stance(0.0) - , m_Inventory(*this) - , m_CurrentWindow(NULL) - , m_InventoryWindow(NULL) - , m_Color('-') - , m_GameMode(eGameMode_NotSet) - , m_IP("") - , m_ClientHandle(a_Client) - , m_NormalMaxSpeed(1.0) - , m_SprintingMaxSpeed(1.3) - , m_FlyingMaxSpeed(1.0) - , m_IsCrouched(false) - , m_IsSprinting(false) - , m_IsFlying(false) - , m_IsSwimming(false) - , m_IsSubmerged(false) - , m_IsFishing(false) - , m_CanFly(false) - , m_EatingFinishTick(-1) - , m_LifetimeTotalXp(0) - , m_CurrentXp(0) - , m_bDirtyExperience(false) - , m_IsChargingBow(false) - , m_BowCharge(0) - , m_FloaterID(-1) - , m_Team(NULL) - , m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL) -{ - LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", - a_PlayerName.c_str(), a_Client->GetIPString().c_str(), - this, GetUniqueID() - ); - +cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) : + super(etPlayer, 0.6, 1.8), + m_bVisible(true), + m_FoodLevel(MAX_FOOD_LEVEL), + m_FoodSaturationLevel(5.0), + m_FoodTickTimer(0), + m_FoodExhaustionLevel(0.0), + m_FoodPoisonedTicksRemaining(0), + m_LastJumpHeight(0), + m_LastGroundHeight(0), + m_bTouchGround(false), + m_Stance(0.0), + m_Inventory(*this), + m_EnderChestContents(9, 3), + m_CurrentWindow(NULL), + m_InventoryWindow(NULL), + m_Color('-'), + m_GameMode(eGameMode_NotSet), + m_IP(""), + m_ClientHandle(a_Client), + m_NormalMaxSpeed(1.0), + m_SprintingMaxSpeed(1.3), + m_FlyingMaxSpeed(1.0), + m_IsCrouched(false), + m_IsSprinting(false), + m_IsFlying(false), + m_IsSwimming(false), + m_IsSubmerged(false), + m_IsFishing(false), + m_CanFly(false), + m_EatingFinishTick(-1), + m_LifetimeTotalXp(0), + m_CurrentXp(0), + m_bDirtyExperience(false), + m_IsChargingBow(false), + m_BowCharge(0), + m_FloaterID(-1), + m_Team(NULL), + m_TicksUntilNextSave(PLAYER_INVENTORY_SAVE_INTERVAL), + m_bIsTeleporting(false), + m_UUID((a_Client != NULL) ? a_Client->GetUUID() : "") +{ m_InventoryWindow = new cInventoryWindow(*this); m_CurrentWindow = m_InventoryWindow; m_InventoryWindow->OpenedByPlayer(*this); @@ -225,7 +224,7 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) SendExperience(); } - if (GetPosition() != m_LastPos) // Change in position from last tick? + if (!GetPosition().EqualsEps(m_LastPos, 0.01)) // Non negligible change in position from last tick? { // Apply food exhaustion from movement: ApplyFoodExhaustionFromMovement(); @@ -412,6 +411,7 @@ void cPlayer::StartChargingBow(void) LOGD("Player \"%s\" started charging their bow", GetName().c_str()); m_IsChargingBow = true; m_BowCharge = 0; + m_World->BroadcastEntityMetadata(*this, m_ClientHandle); } @@ -424,6 +424,8 @@ int cPlayer::FinishChargingBow(void) int res = m_BowCharge; m_IsChargingBow = false; m_BowCharge = 0; + m_World->BroadcastEntityMetadata(*this, m_ClientHandle); + return res; } @@ -436,6 +438,7 @@ void cPlayer::CancelChargingBow(void) LOGD("Player \"%s\" cancelled charging their bow at a charge of %d", GetName().c_str(), m_BowCharge); m_IsChargingBow = false; m_BowCharge = 0; + m_World->BroadcastEntityMetadata(*this, m_ClientHandle); } @@ -517,7 +520,15 @@ void cPlayer::Heal(int a_Health) void cPlayer::SetFoodLevel(int a_FoodLevel) { - m_FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL)); + int FoodLevel = std::max(0, std::min(a_FoodLevel, (int)MAX_FOOD_LEVEL)); + + if (cRoot::Get()->GetPluginManager()->CallHookPlayerFoodLevelChange(*this, FoodLevel)) + { + m_FoodSaturationLevel = 5.0; + return; + } + + m_FoodLevel = FoodLevel; SendHealth(); } @@ -567,11 +578,9 @@ bool cPlayer::Feed(int a_Food, double a_Saturation) { return false; } - - m_FoodLevel = std::min(a_Food + m_FoodLevel, (int)MAX_FOOD_LEVEL); - m_FoodSaturationLevel = std::min(m_FoodSaturationLevel + a_Saturation, (double)m_FoodLevel); - - SendHealth(); + + SetFoodSaturationLevel(m_FoodSaturationLevel + a_Saturation); + SetFoodLevel(m_FoodLevel + a_Food); return true; } @@ -965,14 +974,15 @@ void cPlayer::Respawn(void) // Reset food level: m_FoodLevel = MAX_FOOD_LEVEL; - m_FoodSaturationLevel = 5; + m_FoodSaturationLevel = 5.0; + m_FoodExhaustionLevel = 0.0; // Reset Experience m_CurrentXp = 0; m_LifetimeTotalXp = 0; // ToDo: send score to client? How? - m_ClientHandle->SendRespawn(*m_World); + m_ClientHandle->SendRespawn(*m_World, true); // Extinguish the fire: StopBurning(); @@ -1222,6 +1232,7 @@ void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) SetPosition(a_PosX, a_PosY, a_PosZ); m_LastGroundHeight = (float)a_PosY; m_LastJumpHeight = (float)a_PosY; + m_bIsTeleporting = true; m_World->BroadcastTeleportEntity(*this, GetClientHandle()); m_ClientHandle->SendPlayerMoveLook(); @@ -1675,53 +1686,98 @@ void cPlayer::LoadPermissionsFromDisk() -bool cPlayer::LoadFromDisk() + +bool cPlayer::LoadFromDisk(void) { LoadPermissionsFromDisk(); - AString SourceFile; - Printf(SourceFile, "players/%s.json", GetName().c_str() ); + // Load from the UUID file: + if (LoadFromFile(GetUUIDFileName(m_UUID))) + { + return true; + } + + // Load from the offline UUID file, if allowed: + AString OfflineUUID = cClientHandle::GenerateOfflineUUID(GetName()); + if (cRoot::Get()->GetServer()->ShouldLoadOfflinePlayerData()) + { + if (LoadFromFile(GetUUIDFileName(OfflineUUID))) + { + return true; + } + } + + // Load from the old-style name-based file, if allowed: + if (cRoot::Get()->GetServer()->ShouldLoadNamedPlayerData()) + { + AString OldStyleFileName = Printf("players/%s.json", GetName().c_str()); + if (LoadFromFile(OldStyleFileName)) + { + // Save in new format and remove the old file + if (SaveToDisk()) + { + cFile::Delete(OldStyleFileName); + } + return true; + } + } + + // None of the files loaded successfully + LOG("Player data file not found for %s (%s, offline %s), will be reset to defaults.", + GetName().c_str(), m_UUID.c_str(), OfflineUUID.c_str() + ); + return false; +} + + + + +bool cPlayer::LoadFromFile(const AString & a_FileName) +{ + // Load the data from the file: cFile f; - if (!f.Open(SourceFile, cFile::fmRead)) + if (!f.Open(a_FileName, cFile::fmRead)) { // This is a new player whom we haven't seen yet, bail out, let them have the defaults return false; } - AString buffer; if (f.ReadRestOfFile(buffer) != f.GetSize()) { - LOGWARNING("Cannot read player data from file \"%s\"", SourceFile.c_str()); + LOGWARNING("Cannot read player data from file \"%s\"", a_FileName.c_str()); return false; } - f.Close(); //cool kids play nice + f.Close(); + // Parse the JSON format: Json::Value root; Json::Reader reader; if (!reader.parse(buffer, root, false)) { - LOGWARNING("Cannot parse player data in file \"%s\", player will be reset", SourceFile.c_str()); + LOGWARNING("Cannot parse player data in file \"%s\"", a_FileName.c_str()); + return false; } + // Load the player data: Json::Value & JSON_PlayerPosition = root["position"]; if (JSON_PlayerPosition.size() == 3) { - SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble()); - SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble()); - SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble()); + SetPosX(JSON_PlayerPosition[(unsigned)0].asDouble()); + SetPosY(JSON_PlayerPosition[(unsigned)1].asDouble()); + SetPosZ(JSON_PlayerPosition[(unsigned)2].asDouble()); m_LastPos = GetPosition(); } Json::Value & JSON_PlayerRotation = root["rotation"]; if (JSON_PlayerRotation.size() == 3) { - SetYaw ((float)JSON_PlayerRotation[(unsigned int)0].asDouble()); - SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble()); - SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble()); + SetYaw ((float)JSON_PlayerRotation[(unsigned)0].asDouble()); + SetPitch ((float)JSON_PlayerRotation[(unsigned)1].asDouble()); + SetRoll ((float)JSON_PlayerRotation[(unsigned)2].asDouble()); } - m_Health = root.get("health", 0).asInt(); + m_Health = root.get("health", 0).asInt(); m_AirLevel = root.get("air", MAX_AIR_LEVEL).asInt(); m_FoodLevel = root.get("food", MAX_FOOD_LEVEL).asInt(); m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); @@ -1739,6 +1795,7 @@ bool cPlayer::LoadFromDisk() } m_Inventory.LoadFromJson(root["inventory"]); + cEnderChestEntity::LoadFromJson(root["enderchestinventory"], m_EnderChestContents); m_LoadedWorldName = root.get("world", "world").asString(); @@ -1747,8 +1804,8 @@ bool cPlayer::LoadFromDisk() cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); StatSerializer.Load(); - LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", - GetName().c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() + LOGD("Player \"%s\" was read from file \"%s\", spawning at {%.2f, %.2f, %.2f} in world \"%s\"", + GetName().c_str(), a_FileName.c_str(), GetPosX(), GetPosY(), GetPosZ(), m_LoadedWorldName.c_str() ); return true; @@ -1761,6 +1818,7 @@ bool cPlayer::LoadFromDisk() bool cPlayer::SaveToDisk() { cFile::CreateFolder(FILE_IO_PREFIX + AString("players")); + cFile::CreateFolder(FILE_IO_PREFIX + AString("players/") + m_UUID.substr(0, 2)); // create the JSON data Json::Value JSON_PlayerPosition; @@ -1776,45 +1834,61 @@ bool cPlayer::SaveToDisk() Json::Value JSON_Inventory; m_Inventory.SaveToJson(JSON_Inventory); + Json::Value JSON_EnderChestInventory; + cEnderChestEntity::SaveToJson(JSON_EnderChestInventory, m_EnderChestContents); + Json::Value root; - root["position"] = JSON_PlayerPosition; - root["rotation"] = JSON_PlayerRotation; - root["inventory"] = JSON_Inventory; - root["health"] = m_Health; - root["xpTotal"] = m_LifetimeTotalXp; - root["xpCurrent"] = m_CurrentXp; - root["air"] = m_AirLevel; - root["food"] = m_FoodLevel; - root["foodSaturation"] = m_FoodSaturationLevel; - root["foodTickTimer"] = m_FoodTickTimer; - root["foodExhaustion"] = m_FoodExhaustionLevel; - root["world"] = GetWorld()->GetName(); - root["isflying"] = IsFlying(); - - if (m_GameMode == GetWorld()->GetGameMode()) - { - root["gamemode"] = (int) eGameMode_NotSet; + root["position"] = JSON_PlayerPosition; + root["rotation"] = JSON_PlayerRotation; + root["inventory"] = JSON_Inventory; + root["enderchestinventory"] = JSON_EnderChestInventory; + root["health"] = m_Health; + root["xpTotal"] = m_LifetimeTotalXp; + root["xpCurrent"] = m_CurrentXp; + root["air"] = m_AirLevel; + root["food"] = m_FoodLevel; + root["foodSaturation"] = m_FoodSaturationLevel; + root["foodTickTimer"] = m_FoodTickTimer; + root["foodExhaustion"] = m_FoodExhaustionLevel; + root["isflying"] = IsFlying(); + root["lastknownname"] = GetName(); + if (m_World != NULL) + { + root["world"] = m_World->GetName(); + if (m_GameMode == m_World->GetGameMode()) + { + root["gamemode"] = (int) eGameMode_NotSet; + } + else + { + root["gamemode"] = (int) m_GameMode; + } } else { - root["gamemode"] = (int) m_GameMode; + // This happens if the player is saved to new format after loading from the old format + root["world"] = m_LoadedWorldName; + root["gamemode"] = (int) eGameMode_NotSet; } Json::StyledWriter writer; std::string JsonData = writer.write(root); - AString SourceFile; - Printf(SourceFile, "players/%s.json", GetName().c_str() ); + AString SourceFile = GetUUIDFileName(m_UUID); cFile f; if (!f.Open(SourceFile, cFile::fmWrite)) { - LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", GetName().c_str(), SourceFile.c_str()); + LOGWARNING("Error writing player \"%s\" to file \"%s\" - cannot open file. Player will lose their progress.", + GetName().c_str(), SourceFile.c_str() + ); return false; } if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) { - LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); + LOGWARNING("Error writing player \"%s\" to file \"%s\" - cannot save data. Player will lose their progress. ", + GetName().c_str(), SourceFile.c_str() + ); return false; } @@ -1823,7 +1897,7 @@ bool cPlayer::SaveToDisk() cStatSerializer StatSerializer(cRoot::Get()->GetDefaultWorld()->GetName(), GetName(), &m_Stats); if (!StatSerializer.Save()) { - LOGERROR("Could not save stats for player %s", GetName().c_str()); + LOGWARNING("Could not save stats for player %s", GetName().c_str()); return false; } @@ -1863,7 +1937,7 @@ void cPlayer::UseEquippedItem(void) if (GetInventory().DamageEquippedItem()) { - m_World->BroadcastSoundEffect("random.break", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); + m_World->BroadcastSoundEffect("random.break", GetPosX(), GetPosY(), GetPosZ(), 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); } } @@ -1891,16 +1965,13 @@ void cPlayer::TickBurning(cChunk & a_Chunk) void cPlayer::HandleFood(void) { // Ref.: http://www.minecraftwiki.net/wiki/Hunger - + if (IsGameModeCreative()) { // Hunger is disabled for Creative return; } - - // Remember the food level before processing, for later comparison - int LastFoodLevel = m_FoodLevel; - + // Heal or damage, based on the food level, using the m_FoodTickTimer: if ((m_FoodLevel > 17) || (m_FoodLevel <= 0)) { @@ -1909,11 +1980,11 @@ void cPlayer::HandleFood(void) { m_FoodTickTimer = 0; - if (m_FoodLevel >= 17) + if ((m_FoodLevel > 17) && (GetHealth() < GetMaxHealth())) { // Regenerate health from food, incur 3 pts of food exhaustion: Heal(1); - m_FoodExhaustionLevel += 3; + m_FoodExhaustionLevel += 3.0; } else if ((m_FoodLevel <= 0) && (m_Health > 1)) { @@ -1922,7 +1993,7 @@ void cPlayer::HandleFood(void) } } } - + // Apply food poisoning food exhaustion: if (m_FoodPoisonedTicksRemaining > 0) { @@ -1935,24 +2006,19 @@ void cPlayer::HandleFood(void) } // Apply food exhaustion that has accumulated: - if (m_FoodExhaustionLevel >= 4) + if (m_FoodExhaustionLevel >= 4.0) { - m_FoodExhaustionLevel -= 4; + m_FoodExhaustionLevel -= 4.0; - if (m_FoodSaturationLevel >= 1) + if (m_FoodSaturationLevel >= 1.0) { - m_FoodSaturationLevel -= 1; + m_FoodSaturationLevel -= 1.0; } else { - m_FoodLevel = std::max(m_FoodLevel - 1, 0); + SetFoodLevel(m_FoodLevel - 1); } } - - if (m_FoodLevel != LastFoodLevel) - { - SendHealth(); - } } @@ -2075,6 +2141,11 @@ void cPlayer::ApplyFoodExhaustionFromMovement() { return; } + if (m_bIsTeleporting) + { + m_bIsTeleporting = false; + return; + } // If riding anything, apply no food exhaustion if (m_AttachedTo != NULL) @@ -2139,3 +2210,19 @@ void cPlayer::Detach() + +AString cPlayer::GetUUIDFileName(const AString & a_UUID) +{ + ASSERT(a_UUID.size() == 36); + + AString res("players/"); + res.append(a_UUID, 0, 2); + res.push_back('/'); + res.append(a_UUID, 2, AString::npos); + res.append(".json"); + return res; +} + + + + diff --git a/src/Entities/Player.h b/src/Entities/Player.h index b2142a18b..8f9b46e0f 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -41,6 +41,7 @@ public: cPlayer(cClientHandle * a_Client, const AString & a_PlayerName); + virtual ~cPlayer(); virtual void SpawnOn(cClientHandle & a_Client) override; @@ -124,6 +125,9 @@ public: inline double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc. inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export inline const cInventory & GetInventory(void) const { return m_Inventory; } + + /** Gets the contents of the player's associated enderchest */ + cItemGrid & GetEnderChestContents(void) { return m_EnderChestContents; } inline const cItem & GetEquippedItem(void) const { return GetInventory().GetEquippedItem(); } // tolua_export @@ -334,7 +338,15 @@ public: bool MoveToWorld(const char * a_WorldName); // tolua_export bool SaveToDisk(void); + + /** Loads the player data from the disk file. + Returns true on success, false on failure. */ bool LoadFromDisk(void); + + /** Loads the player data from the specified file. + Returns true on success, false on failure. */ + bool LoadFromFile(const AString & a_FileName); + void LoadPermissionsFromDisk(void); // tolua_export const AString & GetLoadedWorldName() { return m_LoadedWorldName; } @@ -404,7 +416,7 @@ public: // cEntity overrides: virtual bool IsCrouched (void) const { return m_IsCrouched; } virtual bool IsSprinting(void) const { return m_IsSprinting; } - virtual bool IsRclking (void) const { return IsEating(); } + virtual bool IsRclking (void) const { return IsEating() || IsChargingBow(); } virtual void Detach(void); @@ -449,7 +461,13 @@ protected: float m_LastGroundHeight; bool m_bTouchGround; double m_Stance; + + /** Stores the player's inventory, consisting of crafting grid, hotbar, and main slots */ cInventory m_Inventory; + + /** An item grid that stores the player specific enderchest contents */ + cItemGrid m_EnderChestContents; + cWindow * m_CurrentWindow; cWindow * m_InventoryWindow; @@ -510,6 +528,22 @@ protected: cStatManager m_Stats; + /** Flag representing whether the player is currently in a bed + Set by a right click on unoccupied bed, unset by a time fast forward or teleport */ + bool m_bIsInBed; + + /** How long till the player's inventory will be saved + Default save interval is #defined in PLAYER_INVENTORY_SAVE_INTERVAL */ + unsigned int m_TicksUntilNextSave; + + /** Flag used by food handling system to determine whether a teleport has just happened + Will not apply food penalties if found to be true; will set to false after processing + */ + bool m_bIsTeleporting; + + /** The UUID of the player, as read from the ClientHandle. + If no ClientHandle is given, the UUID is initialized to empty. */ + AString m_UUID; /** Sets the speed and sends it to the client, so that they are forced to move so. */ @@ -538,14 +572,9 @@ protected: /** Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block) */ void ApplyFoodExhaustionFromMovement(); - /** Flag representing whether the player is currently in a bed - Set by a right click on unoccupied bed, unset by a time fast forward or teleport */ - bool m_bIsInBed; - - /** How long till the player's inventory will be saved - Default save interval is #defined in PLAYER_INVENTORY_SAVE_INTERVAL */ - unsigned int m_TicksUntilNextSave; - + /** Returns the filename for the player data based on the UUID given. + This can be used both for online and offline UUIDs. */ + AString GetUUIDFileName(const AString & a_UUID); } ; // tolua_export diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index d45f1d212..b5ef5c90a 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -21,6 +21,7 @@ #include "FireChargeEntity.h" #include "FireworkEntity.h" #include "GhastFireballEntity.h" +#include "Player.h" @@ -68,15 +69,15 @@ protected: if (cBlockInfo::IsSolid(a_BlockType)) { // The projectile hit a solid block, calculate the exact hit coords: - cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1); // Bounding box of the block hit - const Vector3d LineStart = m_Projectile->GetPosition(); // Start point for the imaginary line that goes through the block hit - const Vector3d LineEnd = LineStart + m_Projectile->GetSpeed(); // End point for the imaginary line that goes through the block hit - double LineCoeff = 0; // Used to calculate where along the line an intersection with the bounding box occurs - eBlockFace Face; // Face hit + cBoundingBox bb(a_BlockX, a_BlockX + 1, a_BlockY, a_BlockY + 1, a_BlockZ, a_BlockZ + 1); // Bounding box of the block hit + const Vector3d LineStart = m_Projectile->GetPosition(); // Start point for the imaginary line that goes through the block hit + const Vector3d LineEnd = LineStart + m_Projectile->GetSpeed(); // End point for the imaginary line that goes through the block hit + double LineCoeff = 0; // Used to calculate where along the line an intersection with the bounding box occurs + eBlockFace Face; // Face hit if (bb.CalcLineIntersection(LineStart, LineEnd, LineCoeff, Face)) { - Vector3d Intersection = LineStart + m_Projectile->GetSpeed() * LineCoeff; // Point where projectile goes into the hit block + Vector3d Intersection = LineStart + m_Projectile->GetSpeed() * LineCoeff; // Point where projectile goes into the hit block if (cPluginManager::Get()->CallHookProjectileHitBlock(*m_Projectile, a_BlockX, a_BlockY, a_BlockZ, Face, &Intersection)) { @@ -141,7 +142,7 @@ public: { if ( (a_Entity == m_Projectile) || // Do not check collisions with self - (a_Entity == m_Projectile->GetCreator()) // Do not check whoever shot the projectile + (a_Entity->GetUniqueID() == m_Projectile->GetCreatorUniqueID()) // Do not check whoever shot the projectile ) { // TODO: Don't check creator only for the first 5 ticks @@ -215,7 +216,10 @@ protected: cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height) : super(etProjectile, a_X, a_Y, a_Z, a_Width, a_Height), m_ProjectileKind(a_Kind), - m_Creator(a_Creator), + m_CreatorData( + ((a_Creator != NULL) ? a_Creator->GetUniqueID() : -1), + ((a_Creator != NULL) ? (a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "") : "") + ), m_IsInGround(false) { } @@ -227,7 +231,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) : super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height), m_ProjectileKind(a_Kind), - m_Creator(a_Creator), + m_CreatorData(a_Creator->GetUniqueID(), a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : ""), m_IsInGround(false) { SetSpeed(a_Speed); @@ -239,7 +243,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve -cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed) +cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem * a_Item, const Vector3d * a_Speed) { Vector3d Speed; if (a_Speed != NULL) @@ -258,12 +262,13 @@ cProjectileEntity * cProjectileEntity::Create(eKind a_Kind, cEntity * a_Creator, case pkExpBottle: return new cExpBottleEntity (a_Creator, a_X, a_Y, a_Z, Speed); case pkFirework: { - if (a_Item.m_FireworkItem.m_Colours.empty()) + ASSERT(a_Item != NULL); + if (a_Item->m_FireworkItem.m_Colours.empty()) { return NULL; } - return new cFireworkEntity(a_Creator, a_X, a_Y, a_Z, a_Item); + return new cFireworkEntity(a_Creator, a_X, a_Y, a_Z, *a_Item); } } @@ -304,7 +309,7 @@ AString cProjectileEntity::GetMCAClassName(void) const case pkEgg: return "Egg"; case pkGhastFireball: return "Fireball"; case pkFireCharge: return "SmallFireball"; - case pkEnderPearl: return "ThrownEnderPearl"; + case pkEnderPearl: return "ThrownEnderpearl"; case pkExpBottle: return "ThrownExpBottle"; case pkSplashPotion: return "ThrownPotion"; case pkWitherSkull: return "WitherSkull"; @@ -361,7 +366,7 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) EntityCollisionCallback.GetHitEntity()->GetClass(), HitPos.x, HitPos.y, HitPos.z, EntityCollisionCallback.GetMinCoeff() - ); + ); OnHitEntity(*(EntityCollisionCallback.GetHitEntity()), HitPos); } diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h index ae06b072f..14cee1272 100644 --- a/src/Entities/ProjectileEntity.h +++ b/src/Entities/ProjectileEntity.h @@ -46,7 +46,7 @@ public: cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, double a_Width, double a_Height); cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height); - static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item, const Vector3d * a_Speed = NULL); + static cProjectileEntity * Create(eKind a_Kind, cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem * a_Item, const Vector3d * a_Speed = NULL); /// Called by the physics blocktracer when the entity hits a solid block, the hit position and the face hit (BLOCK_FACE_) is given virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace); @@ -66,8 +66,15 @@ public: /// Returns the kind of the projectile (fast class identification) eKind GetProjectileKind(void) const { return m_ProjectileKind; } - /// Returns the entity who created this projectile; may be NULL - cEntity * GetCreator(void) { return m_Creator; } + /** Returns the unique ID of the entity who created this projectile + May return an ID <0 + */ + int GetCreatorUniqueID(void) { return m_CreatorData.m_UniqueID; } + + /** Returns the name of the player that created the projectile + Will be empty for non-player creators + */ + AString GetCreatorName(void) const { return m_CreatorData.m_Name; } /// Returns the string that is used as the entity type (class name) in MCA files AString GetMCAClassName(void) const; @@ -81,10 +88,29 @@ public: void SetIsInGround(bool a_IsInGround) { m_IsInGround = a_IsInGround; } protected: + + /** A structure that stores the Entity ID and Playername of the projectile's creator + Used to migitate invalid pointers caused by the creator being destroyed + */ + struct CreatorData + { + CreatorData(int a_UniqueID, const AString & a_Name) : + m_UniqueID(a_UniqueID), + m_Name(a_Name) + { + } + + const int m_UniqueID; + AString m_Name; + }; + + /** The type of projectile I am */ eKind m_ProjectileKind; - /// The entity who has created this projectile; may be NULL (e. g. for dispensers) - cEntity * m_Creator; + /** The structure for containing the entity ID and name who has created this projectile + The ID and/or name may be NULL (e.g. for dispensers/mobs) + */ + CreatorData m_CreatorData; /// True if the projectile has hit the ground and is stuck there bool m_IsInGround; diff --git a/src/Entities/ThrownEggEntity.cpp b/src/Entities/ThrownEggEntity.cpp index d7eed41e3..456083108 100644 --- a/src/Entities/ThrownEggEntity.cpp +++ b/src/Entities/ThrownEggEntity.cpp @@ -22,7 +22,7 @@ void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_H { TrySpawnChicken(a_HitPos); - m_DestroyTimer = 5; + m_DestroyTimer = 2; } diff --git a/src/Entities/ThrownEggEntity.h b/src/Entities/ThrownEggEntity.h index 894665428..dc72c279f 100644 --- a/src/Entities/ThrownEggEntity.h +++ b/src/Entities/ThrownEggEntity.h @@ -41,7 +41,10 @@ protected: return; } } - else { super::Tick(a_Dt, a_Chunk); } + else + { + super::Tick(a_Dt, a_Chunk); + } } // Randomly decides whether to spawn a chicken where the egg lands. diff --git a/src/Entities/ThrownEnderPearlEntity.cpp b/src/Entities/ThrownEnderPearlEntity.cpp index a3ee23389..c7407e6ae 100644 --- a/src/Entities/ThrownEnderPearlEntity.cpp +++ b/src/Entities/ThrownEnderPearlEntity.cpp @@ -1,6 +1,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "ThrownEnderPearlEntity.h" +#include "Player.h" @@ -22,7 +23,7 @@ void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockF // TODO: Tweak a_HitPos based on block face. TeleportCreator(a_HitPos); - m_DestroyTimer = 5; + m_DestroyTimer = 2; } @@ -46,10 +47,34 @@ void cThrownEnderPearlEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d void cThrownEnderPearlEntity::TeleportCreator(const Vector3d & a_HitPos) { - // Teleport the creator here, make them take 5 damage: - if (m_Creator != NULL) + if (m_CreatorData.m_Name.empty()) { - m_Creator->TeleportToCoords(a_HitPos.x, a_HitPos.y + 0.2, a_HitPos.z); - m_Creator->TakeDamage(dtEnderPearl, this, 5, 0); + return; } + + class cProjectileCreatorCallbackForPlayers : public cPlayerListCallback + { + public: + cProjectileCreatorCallbackForPlayers(cEntity * a_Attacker, Vector3i a_HitPos) : + m_Attacker(a_Attacker), + m_HitPos(a_HitPos) + { + } + + virtual bool Item(cPlayer * a_Entity) override + { + // Teleport the creator here, make them take 5 damage: + a_Entity->TeleportToCoords(m_HitPos.x, m_HitPos.y + 0.2, m_HitPos.z); + a_Entity->TakeDamage(dtEnderPearl, m_Attacker, 5, 0); + return true; + } + + private: + + cEntity * m_Attacker; + Vector3i m_HitPos; + }; + + cProjectileCreatorCallbackForPlayers PCCFP(this, a_HitPos); + GetWorld()->FindAndDoWithPlayer(m_CreatorData.m_Name, PCCFP); } diff --git a/src/Entities/ThrownEnderPearlEntity.h b/src/Entities/ThrownEnderPearlEntity.h index bfd9bd70d..1cea5f7d9 100644 --- a/src/Entities/ThrownEnderPearlEntity.h +++ b/src/Entities/ThrownEnderPearlEntity.h @@ -41,7 +41,10 @@ protected: return; } } - else { super::Tick(a_Dt, a_Chunk); } + else + { + super::Tick(a_Dt, a_Chunk); + } } /** Teleports the creator where the ender pearl lands */ diff --git a/src/Entities/ThrownSnowballEntity.cpp b/src/Entities/ThrownSnowballEntity.cpp index b82cd56db..d94e75898 100644 --- a/src/Entities/ThrownSnowballEntity.cpp +++ b/src/Entities/ThrownSnowballEntity.cpp @@ -20,7 +20,7 @@ cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, do void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) { - m_DestroyTimer = 5; + m_DestroyTimer = 2; } diff --git a/src/Entities/ThrownSnowballEntity.h b/src/Entities/ThrownSnowballEntity.h index e30971f0a..9a8770379 100644 --- a/src/Entities/ThrownSnowballEntity.h +++ b/src/Entities/ThrownSnowballEntity.h @@ -41,7 +41,10 @@ protected: return; } } - else { super::Tick(a_Dt, a_Chunk); } + else + { + super::Tick(a_Dt, a_Chunk); + } } private: diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp index 2cb204ccf..8add9610c 100644 --- a/src/FurnaceRecipe.cpp +++ b/src/FurnaceRecipe.cpp @@ -5,7 +5,8 @@ #include "Item.h" #include <fstream> -#include <sstream> + +#define FURNACE_RECIPE_FILE "furnace.txt" @@ -54,128 +55,207 @@ void cFurnaceRecipe::ReloadRecipes(void) ClearRecipes(); LOGD("Loading furnace recipes..."); - std::ifstream f; - char a_File[] = "furnace.txt"; - f.open(a_File, std::ios::in); - + std::ifstream f(FURNACE_RECIPE_FILE, std::ios::in); if (!f.good()) { - f.close(); - LOG("Could not open the furnace recipes file \"%s\"", a_File); + LOG("Could not open the furnace recipes file \"%s\". No furnace recipes are available.", FURNACE_RECIPE_FILE); return; } + + unsigned int LineNum = 0; + AString ParsingLine; - // TODO: Replace this messy parse with a high-level-structured one (ReadLine / ProcessLine) - bool bSyntaxError = false; - while (f.good()) + while (std::getline(f, ParsingLine)) { - char c; + LineNum++; + ParsingLine.erase(std::remove_if(ParsingLine.begin(), ParsingLine.end(), isspace), ParsingLine.end()); // Remove ALL whitespace from the line + if (ParsingLine.empty()) + { + continue; + } - ////////////////////////////////////////////////////////////////////////// - // comments - f >> c; - f.unget(); - if( c == '#' ) + switch (ParsingLine[0]) { - while( f.good() && c != '\n' ) + case '#': { - f.get( c ); + // Comment + break; } - continue; - } + + case '!': + { + AddFuelFromLine(ParsingLine, LineNum); + break; + } + + default: + { + AddRecipeFromLine(ParsingLine, LineNum); + break; + } + } // switch (ParsingLine[0]) + } // while (getline(ParsingLine)) + + LOG("Loaded " SIZE_T_FMT " furnace recipes and " SIZE_T_FMT " fuels", m_pState->Recipes.size(), m_pState->Fuel.size()); +} + + + + + +void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, int a_LineNum) +{ + // Fuel + int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0; + AString::size_type BeginPos = 1; // Begin at one after exclamation mark (bang) + + if ( + !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID + !ReadOptionalNumbers(BeginPos, ":", "=", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health) + !ReadMandatoryNumber(BeginPos, "0123456789", a_Line, a_LineNum, IBurnTime, true) // Read item burn time - last value + ) + { + return; + } + + // Add to fuel list: + Fuel F; + F.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth); + F.BurnTime = IBurnTime; + m_pState->Fuel.push_back(F); +} + + + + + +void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, int a_LineNum) +{ + int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0; + int OItemID = 0, OItemCount = 0, OItemHealth = 0; + AString::size_type BeginPos = 0; // Begin at start of line + + if ( + !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID + !ReadOptionalNumbers(BeginPos, ":", "@", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health) + !ReadMandatoryNumber(BeginPos, "=", a_Line, a_LineNum, IBurnTime) || // Read item burn time + !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, OItemID) || // Read result ID + !ReadOptionalNumbers(BeginPos, ":", "012456789", a_Line, a_LineNum, OItemCount, OItemHealth, true) // Read result count (and optionally health) - last value + ) + { + return; + } + + // Add to recipe list + Recipe R; + R.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth); + R.Out = new cItem((ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth); + R.CookTime = IBurnTime; + m_pState->Recipes.push_back(R); +} + + + - ////////////////////////////////////////////////////////////////////////// - // Line breaks - f.get( c ); - while( f.good() && ( c == '\n' || c == '\r' ) ) { f.get( c ); } - if (f.eof()) +void cFurnaceRecipe::PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing) +{ + LOGWARN("Error parsing furnace recipes at line %i pos " SIZE_T_FMT ": missing '%s'", a_Line, a_Position, a_CharactersMissing.c_str()); +} + + + + + +bool cFurnaceRecipe::ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue) +{ + // TODO: replace atoi with std::stoi + AString::size_type End; + if (a_IsLastValue) + { + End = a_Text.find_first_not_of(a_Delimiter, a_Begin); + } + else + { + End = a_Text.find_first_of(a_Delimiter, a_Begin); + if (End == AString::npos) { - break; + PrintParseError(a_Line, a_Begin, a_Delimiter); + return false; } - f.unget(); + } + + // stoi won't throw an exception if the string is alphanumeric, we should check for this + if (!DoesStringContainOnlyNumbers(a_Text.substr(a_Begin, End - a_Begin))) + { + PrintParseError(a_Line, a_Begin, "number"); + return false; + } + a_Value = atoi(a_Text.substr(a_Begin, End - a_Begin).c_str()); + + a_Begin = End + 1; // Jump over delimiter + return true; +} + + + + - ////////////////////////////////////////////////////////////////////////// - // Check for fuel - f >> c; - if( c == '!' ) // It's fuel :) +bool cFurnaceRecipe::ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue) +{ + // TODO: replace atoi with std::stoi + AString::size_type End, Begin = a_Begin; + + End = a_Text.find_first_of(a_DelimiterOne, Begin); + if (End != AString::npos) + { + if (DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin))) { - // Read item - int IItemID = 0, IItemCount = 0, IItemHealth = 0; - f >> IItemID; - f >> c; if( c != ':' ) { bSyntaxError = true; break; } - f >> IItemCount; - - // Optional health - f >> c; - if( c != ':' ) - f.unget(); + a_ValueOne = std::atoi(a_Text.substr(Begin, End - Begin).c_str()); + Begin = End + 1; + + if (a_IsLastValue) + { + End = a_Text.find_first_not_of(a_DelimiterTwo, Begin); + } else { - f >> IItemHealth; + End = a_Text.find_first_of(a_DelimiterTwo, Begin); + if (End == AString::npos) + { + PrintParseError(a_Line, Begin, a_DelimiterTwo); + return false; + } } - // Burn time - int BurnTime; - f >> c; if( c != '=' ) { bSyntaxError = true; break; } - f >> BurnTime; + // stoi won't throw an exception if the string is alphanumeric, we should check for this + if (!DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin))) + { + PrintParseError(a_Line, Begin, "number"); + return false; + } + a_ValueTwo = atoi(a_Text.substr(Begin, End - Begin).c_str()); - // Add to fuel list - Fuel F; - F.In = new cItem( (ENUM_ITEM_ID) IItemID, (char)IItemCount, (short)IItemHealth ); - F.BurnTime = BurnTime; - m_pState->Fuel.push_back( F ); - continue; + a_Begin = End + 1; // Jump over delimiter + return true; } - f.unget(); - - ////////////////////////////////////////////////////////////////////////// - // Read items - int IItemID = 0, IItemCount = 0, IItemHealth = 0; - f >> IItemID; - f >> c; if( c != ':' ) { bSyntaxError = true; break; } - f >> IItemCount; - - // Optional health - f >> c; - if( c != ':' ) - f.unget(); else { - f >> IItemHealth; + return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue); } + } + + return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue); +} - int CookTime; - f >> c; if( c != '@' ) { bSyntaxError = true; break; } - f >> CookTime; - int OItemID = 0, OItemCount = 0, OItemHealth = 0; - f >> c; if( c != '=' ) { bSyntaxError = true; break; } - f >> OItemID; - f >> c; if( c != ':' ) { bSyntaxError = true; break; } - f >> OItemCount; - // Optional health - f >> c; - if( c != ':' ) - f.unget(); - else - { - f >> OItemHealth; - } - // Add to recipe list - Recipe R; - R.In = new cItem( (ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth ); - R.Out = new cItem( (ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth ); - R.CookTime = CookTime; - m_pState->Recipes.push_back( R ); - } - if (bSyntaxError) - { - LOGERROR("ERROR: FurnaceRecipe, syntax error" ); - } - LOG("Loaded " SIZE_T_FMT " furnace recipes and " SIZE_T_FMT " fuels", m_pState->Recipes.size(), m_pState->Fuel.size()); + +bool cFurnaceRecipe::DoesStringContainOnlyNumbers(const AString & a_String) +{ + // TODO: replace this with std::all_of(a_String.begin(), a_String.end(), isdigit) + return (a_String.find_first_not_of("0123456789") == AString::npos); } diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h index 2f91e9bcb..77ed35a57 100644 --- a/src/FurnaceRecipe.h +++ b/src/FurnaceRecipe.h @@ -41,6 +41,36 @@ public: private: void ClearRecipes(void); + /** Parses the fuel contained in the line, adds it to m_pState's fuels. + Logs a warning to the console on input error. */ + void AddFuelFromLine(const AString & a_Line, int a_LineNum); + + /** Parses the recipe contained in the line, adds it to m_pState's recipes. + Logs a warning to the console on input error. */ + void AddRecipeFromLine(const AString & a_Line, int a_LineNum); + + /** Calls LOGWARN with the line, position, and error */ + static void PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing); + + /** Reads a number from a string given, starting at a given position and ending at a delimiter given + Updates beginning position to the delimiter found + 1, and updates the value to the one read + If it encounters a substring that is not fully numeric, it will call SetParseError() and return false; the caller should abort processing + Otherwise, the function will return true + */ + static bool ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue = false); + + /** Reads two numbers from a string given, starting at a given position and ending at the first delimiter given, then again (with an updated position) until the second delimiter given + Updates beginning position to the second delimiter found + 1, and updates the values to the ones read + If it encounters a substring that is not fully numeric whilst reading the second value, it will call SetParseError() and return false; the caller should abort processing + If this happens whilst reading the first value, it will call ReadMandatoryNumber() with the appropriate position, as this may legitimately occur with the optional value and AString::find_first_of finding the incorrect delimiter. It will return the result of ReadMandatoryNumber() + True will be returned definitively for an optional value that is valid + */ + static bool ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue = false); + + /** Uses std::all_of to determine if a string contains only digits */ + static bool DoesStringContainOnlyNumbers(const AString & a_String); + + struct sFurnaceRecipeState; sFurnaceRecipeState * m_pState; }; diff --git a/src/Generating/Prefab.cpp b/src/Generating/Prefab.cpp index 2ab1455b9..7d876909a 100644 --- a/src/Generating/Prefab.cpp +++ b/src/Generating/Prefab.cpp @@ -211,6 +211,17 @@ void cPrefab::Draw(cChunkDesc & a_Dest, const Vector3i & a_Placement, int a_NumR int ChunkStartZ = a_Dest.GetChunkZ() * cChunkDef::Width; Placement.Move(-ChunkStartX, 0, -ChunkStartZ); const cBlockArea & Image = m_BlockArea[a_NumRotations]; + + // If the placement is outside this chunk, bail out: + if ( + (Placement.x > cChunkDef::Width) || (Placement.x + Image.GetSizeX() < 0) || + (Placement.z > cChunkDef::Width) || (Placement.z + Image.GetSizeZ() < 0) + ) + { + return; + } + + // Write the image: a_Dest.WriteBlockArea(Image, Placement.x, Placement.y, Placement.z, m_MergeStrategy); // If requested, draw the floor (from the bottom of the prefab down to the nearest non-air) diff --git a/src/Generating/Prefabs/AlchemistVillagePrefabs.cpp b/src/Generating/Prefabs/AlchemistVillagePrefabs.cpp index 5cb864d53..976f8490e 100644 --- a/src/Generating/Prefabs/AlchemistVillagePrefabs.cpp +++ b/src/Generating/Prefabs/AlchemistVillagePrefabs.cpp @@ -58,9 +58,9 @@ const cPrefab::sDef g_AlchemistVillagePrefabs[] = "o: 50: 4\n" /* torch */ "p: 13: 0\n" /* gravel */ "q: 5: 0\n" /* wood */ - "r: 96:12\n" /* trapdoor */ + "r: 96: 8\n" /* trapdoor */ "s:128: 5\n" /* sandstonestairs */ - "t:107: 0\n" /* fencegate */ + "t:107: 2\n" /* fencegate */ "u:128: 4\n" /* sandstonestairs */ "v:134: 3\n" /* 134 */ "w: 85: 0\n" /* fence */ @@ -288,7 +288,7 @@ const cPrefab::sDef g_AlchemistVillagePrefabs[] = "d: 13: 0\n" /* gravel */ "e: 5: 0\n" /* wood */ "f:128: 5\n" /* sandstonestairs */ - "g:107: 0\n" /* fencegate */ + "g:107: 2\n" /* fencegate */ "h:128: 4\n" /* sandstonestairs */ "i:134: 1\n" /* 134 */ "j:134: 3\n" /* 134 */ @@ -298,7 +298,7 @@ const cPrefab::sDef g_AlchemistVillagePrefabs[] = "n:134: 5\n" /* 134 */ "o:134: 7\n" /* 134 */ "p:134: 4\n" /* 134 */ - "q:107: 5\n" /* fencegate */ + "q:107: 1\n" /* fencegate */ "r: 64: 1\n" /* wooddoorblock */ "s: 65: 3\n" /* ladder */ "t: 50: 3\n" /* torch */ @@ -2195,7 +2195,7 @@ const cPrefab::sDef g_AlchemistVillagePrefabs[] = 0, // MoveToGround: - false, + true, }, // LittleHouse8 diff --git a/src/Generating/Prefabs/JapaneseVillagePrefabs.cpp b/src/Generating/Prefabs/JapaneseVillagePrefabs.cpp index 79f7a5272..d22153d87 100644 --- a/src/Generating/Prefabs/JapaneseVillagePrefabs.cpp +++ b/src/Generating/Prefabs/JapaneseVillagePrefabs.cpp @@ -294,7 +294,7 @@ const cPrefab::sDef g_JapaneseVillagePrefabs[] = 0, // MoveToGround: - false, + true, }, // Farm diff --git a/src/Generating/Prefabs/PlainsVillagePrefabs.cpp b/src/Generating/Prefabs/PlainsVillagePrefabs.cpp index 9e5e06769..bad8dae74 100644 --- a/src/Generating/Prefabs/PlainsVillagePrefabs.cpp +++ b/src/Generating/Prefabs/PlainsVillagePrefabs.cpp @@ -356,6 +356,8 @@ const cPrefab::sDef g_PlainsVillagePrefabs[] = "e: 8: 0\n" /* water */ "f: 50: 5\n" /* torch */ "g: 59: 7\n" /* crops */ + "h: 59: 0\n" /* crops */ + "i: 59: 1\n" /* crops */ "m: 19: 0\n" /* sponge */, // Block data: @@ -403,12 +405,12 @@ const cPrefab::sDef g_PlainsVillagePrefabs[] = /* * 012345678901234 */ /* 0 */ "f.....f.f.....f" /* 1 */ ".gg.gg...gg.gg." - /* 2 */ ".g...g...gg.gg." - /* 3 */ ".g.......gg.gg." - /* 4 */ ".gg..g...gg.gg." - /* 5 */ ".gg..g...gg.gg." - /* 6 */ "..g..g...gg.gg." - /* 7 */ "..g.g....gg.gg." + /* 2 */ ".gh.hg...gg.gg." + /* 3 */ ".gh.ih...gg.gg." + /* 4 */ ".gg.hg...gg.gg." + /* 5 */ ".gg.hg...gg.gg." + /* 6 */ ".ig.hg...gg.gg." + /* 7 */ ".hg.gh...gg.gg." /* 8 */ "f.....f.f.....f" // Level 4 diff --git a/src/Globals.h b/src/Globals.h index c5768facf..0c11429bd 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -71,9 +71,24 @@ #define FORMATSTRING(formatIndex, va_argsIndex) __attribute__((format (printf, formatIndex, va_argsIndex))) - #define SIZE_T_FMT "%zu" - #define SIZE_T_FMT_PRECISION(x) "%" #x "zu" - #define SIZE_T_FMT_HEX "%zx" + #if defined(_WIN32) + // We're compiling on MinGW, which uses an old MSVCRT library that has no support for size_t printfing. + // We need direct size formats: + #if defined(_WIN64) + #define SIZE_T_FMT "%I64u" + #define SIZE_T_FMT_PRECISION(x) "%" #x "I64u" + #define SIZE_T_FMT_HEX "%I64x" + #else + #define SIZE_T_FMT "%u" + #define SIZE_T_FMT_PRECISION(x) "%" #x "u" + #define SIZE_T_FMT_HEX "%x" + #endif + #else + // We're compiling on Linux, so we can use libc's size_t printf format: + #define SIZE_T_FMT "%zu" + #define SIZE_T_FMT_PRECISION(x) "%" #x "zu" + #define SIZE_T_FMT_HEX "%zx" + #endif #define NORETURN __attribute((__noreturn__)) diff --git a/src/HTTPServer/HTTPServer.cpp b/src/HTTPServer/HTTPServer.cpp index d288c83c9..036b2e042 100644 --- a/src/HTTPServer/HTTPServer.cpp +++ b/src/HTTPServer/HTTPServer.cpp @@ -179,6 +179,8 @@ bool cHTTPServer::Initialize(const AString & a_PortsIPv4, const AString & a_Port // Open up requested ports: bool HasAnyPort; + m_ListenThreadIPv4.SetReuseAddr(true); + m_ListenThreadIPv6.SetReuseAddr(true); HasAnyPort = m_ListenThreadIPv4.Initialize(a_PortsIPv4); HasAnyPort = m_ListenThreadIPv6.Initialize(a_PortsIPv6) || HasAnyPort; if (!HasAnyPort) diff --git a/src/Item.cpp b/src/Item.cpp index d6e8b224a..56ceae0b7 100644 --- a/src/Item.cpp +++ b/src/Item.cpp @@ -1,4 +1,4 @@ - + #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Item.h" diff --git a/src/Items/ItemBow.h b/src/Items/ItemBow.h index 821e2ab26..e7a77dcbc 100644 --- a/src/Items/ItemBow.h +++ b/src/Items/ItemBow.h @@ -46,20 +46,17 @@ public: { // Actual shot - produce the arrow with speed based on the ticks that the bow was charged ASSERT(a_Player != NULL); - + int BowCharge = a_Player->FinishChargingBow(); - double Force = (double)BowCharge / 20; - Force = (Force * Force + 2 * Force) / 3; // This formula is used by the 1.6.2 client + double Force = (double)BowCharge / 20.0; + Force = (Force * Force + 2.0 * Force) / 3.0; // This formula is used by the 1.6.2 client if (Force < 0.1) { // Too little force, ignore the shot return; } - if (Force > 1) - { - Force = 1; - } - + Force = std::min(Force, 1.0); + // Create the arrow entity: cArrowEntity * Arrow = new cArrowEntity(*a_Player, Force * 2); if (Arrow == NULL) @@ -72,9 +69,8 @@ public: Arrow = NULL; return; } - a_Player->GetWorld()->BroadcastSpawnEntity(*Arrow); - a_Player->GetWorld()->BroadcastSoundEffect("random.bow", (int)a_Player->GetPosX() * 8, (int)a_Player->GetPosY() * 8, (int)a_Player->GetPosZ() * 8, 0.5, (float)Force); + a_Player->GetWorld()->BroadcastSoundEffect("random.bow", a_Player->GetPosX(), a_Player->GetPosY(), a_Player->GetPosZ(), 0.5, (float)Force); if (!a_Player->IsGameModeCreative()) { a_Player->UseEquippedItem(); diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h index 68c89dd85..5529b4e36 100644 --- a/src/Items/ItemBucket.h +++ b/src/Items/ItemBucket.h @@ -41,7 +41,7 @@ public: bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) { - if (a_BlockFace > 0) + if (a_BlockFace != BLOCK_FACE_NONE) { return false; } @@ -95,29 +95,15 @@ public: bool PlaceFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_FluidBlock) { - if (a_BlockFace < 0) + if (a_BlockFace != BLOCK_FACE_NONE) { return false; } - BLOCKTYPE CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - bool CanWashAway = cFluidSimulator::CanWashAway(CurrentBlock); - if (!CanWashAway) + BLOCKTYPE CurrentBlock; + Vector3i BlockPos; + if (!GetPlacementCoordsFromTrace(a_World, a_Player, BlockPos, CurrentBlock)) { - // The block pointed at cannot be washed away, so put fluid on top of it / on its sides - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - CurrentBlock = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - } - if ( - !CanWashAway && - (CurrentBlock != E_BLOCK_AIR) && - (CurrentBlock != E_BLOCK_WATER) && - (CurrentBlock != E_BLOCK_STATIONARY_WATER) && - (CurrentBlock != E_BLOCK_LAVA) && - (CurrentBlock != E_BLOCK_STATIONARY_LAVA) - ) - { - // Cannot place water here return false; } @@ -138,7 +124,7 @@ public: } // Wash away anything that was there prior to placing: - if (CanWashAway) + if (cFluidSimulator::CanWashAway(CurrentBlock)) { cBlockHandler * Handler = BlockHandler(CurrentBlock); if (Handler->DoesDropOnUnsuitable()) @@ -149,13 +135,13 @@ public: } } - a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, a_FluidBlock, 0); + a_World->SetBlock(BlockPos.x, BlockPos.y, BlockPos.z, a_FluidBlock, 0); return true; } - bool GetBlockFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & BlockPos) + bool GetBlockFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos) { class cCallbacks : public cBlockTracer::cCallbacks @@ -198,8 +184,49 @@ public: } - BlockPos.Set(Callbacks.m_Pos.x, Callbacks.m_Pos.y, Callbacks.m_Pos.z); + a_BlockPos = Callbacks.m_Pos; return true; } + + + bool GetPlacementCoordsFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos, BLOCKTYPE & a_BlockType) + { + class cCallbacks : + public cBlockTracer::cCallbacks + { + public: + Vector3i m_Pos; + BLOCKTYPE m_ReplacedBlock; + + virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override + { + if (a_BlockType != E_BLOCK_AIR) + { + m_ReplacedBlock = a_BlockType; + if (!cFluidSimulator::CanWashAway(a_BlockType)) + { + AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, (eBlockFace)a_EntryFace); // Was an unwashawayable block, can't overwrite it! + } + m_Pos.Set(a_BlockX, a_BlockY, a_BlockZ); // (Block could be washed away, replace it) + return true; // Abort tracing + } + return false; + } + } Callbacks; + + cLineBlockTracer Tracer(*a_World, Callbacks); + Vector3d Start(a_Player->GetEyePosition() + a_Player->GetLookVector()); + Vector3d End(a_Player->GetEyePosition() + a_Player->GetLookVector() * 5); + + // cTracer::Trace returns true when whole line was traversed. By returning true when we hit something, we ensure that this never happens if liquid could be placed + // Use this to judge whether the position is valid + if (!Tracer.Trace(Start.x, Start.y, Start.z, End.x, End.y, End.z)) + { + a_BlockPos = Callbacks.m_Pos; + a_BlockType = Callbacks.m_ReplacedBlock; + return true; + } + return false; + } }; diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index f639423ae..423039cf4 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -44,6 +44,7 @@ #include "ItemSign.h" #include "ItemMobHead.h" #include "ItemSpawnEgg.h" +#include "ItemString.h" #include "ItemSugarcane.h" #include "ItemSword.h" @@ -62,7 +63,7 @@ cItemHandler * cItemHandler::m_ItemHandler[2268]; cItemHandler * cItemHandler::GetItemHandler(int a_ItemType) { - if (a_ItemType < 0) + if ((a_ItemType < 0) || ((unsigned long)a_ItemType >= ARRAYCOUNT(m_ItemHandler))) { // Either nothing (-1), or bad value, both cases should return the air handler if (a_ItemType < -1) @@ -129,6 +130,7 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) case E_ITEM_HEAD: return new cItemMobHeadHandler(a_ItemType); case E_ITEM_SNOWBALL: return new cItemSnowballHandler(); case E_ITEM_SPAWN_EGG: return new cItemSpawnEggHandler(a_ItemType); + case E_ITEM_STRING: return new cItemStringHandler(a_ItemType); case E_ITEM_SUGARCANE: return new cItemSugarcaneHandler(a_ItemType); case E_ITEM_WOODEN_HOE: diff --git a/src/Items/ItemLighter.h b/src/Items/ItemLighter.h index 32f49cab6..9f98bf85f 100644 --- a/src/Items/ItemLighter.h +++ b/src/Items/ItemLighter.h @@ -52,8 +52,8 @@ public: case E_BLOCK_TNT: { // Activate the TNT: - a_World->BroadcastSoundEffect("game.tnt.primed", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 1.0f); - a_World->SetBlock(a_BlockX,a_BlockY,a_BlockZ, E_BLOCK_AIR, 0); + a_World->BroadcastSoundEffect("game.tnt.primed", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 1.0f, 1.0f); + a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); a_World->SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5); // 80 ticks to boom break; } @@ -68,7 +68,7 @@ public: if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) { a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 0); - a_World->BroadcastSoundEffect("fire.ignite", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0F, 1.04F); + a_World->BroadcastSoundEffect("fire.ignite", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 1.0F, 1.04F); break; } } diff --git a/src/Items/ItemString.h b/src/Items/ItemString.h new file mode 100644 index 000000000..a97fbe0ce --- /dev/null +++ b/src/Items/ItemString.h @@ -0,0 +1,39 @@ + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemStringHandler : + public cItemHandler +{ +public: + cItemStringHandler(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_TRIPWIRE; + a_BlockMeta = 0; + return true; + } +}; + + + + diff --git a/src/Items/ItemThrowable.h b/src/Items/ItemThrowable.h index 25935a1bc..c151c5d3a 100644 --- a/src/Items/ItemThrowable.h +++ b/src/Items/ItemThrowable.h @@ -33,16 +33,9 @@ public: // Play sound cFastRandom Random; - a_World->BroadcastSoundEffect( - "random.bow", - (int)std::floor(a_Player->GetPosX() * 8.0), - (int)std::floor((a_Player->GetPosY() - a_Player->GetHeight()) * 8.0), - (int)std::floor(a_Player->GetPosZ() * 8.0), - 0.5F, - 0.4F / (Random.NextFloat(1.0F) * 0.4F + 0.8F) - ); - - if (a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, a_Player->GetEquippedItem(), &Speed) < 0) + a_World->BroadcastSoundEffect("random.bow", a_Player->GetPosX(), a_Player->GetPosY() - a_Player->GetHeight(), a_Player->GetPosZ(), 0.5f, 0.4f / (Random.NextFloat(1.0f) * 0.4f + 0.8f)); + + if (a_World->CreateProjectile(Pos.x, Pos.y, Pos.z, m_ProjectileKind, a_Player, &a_Player->GetEquippedItem(), &Speed) < 0) { return false; } @@ -142,7 +135,7 @@ public: return false; } - if (a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, a_Player->GetEquippedItem()) < 0) + if (a_World->CreateProjectile(a_BlockX + 0.5, a_BlockY + 1, a_BlockZ + 0.5, m_ProjectileKind, a_Player, &a_Player->GetEquippedItem()) < 0) { return false; } diff --git a/src/Map.cpp b/src/Map.cpp index 7721baa70..8f205a606 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -200,8 +200,8 @@ bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z) int BlockX = m_CenterX + ((a_X - (m_Width / 2)) * PixelWidth); int BlockZ = m_CenterZ + ((a_Z - (m_Height / 2)) * PixelWidth); - int ChunkX, ChunkY, ChunkZ; - m_World->BlockToChunk(BlockX, 0, BlockZ, ChunkX, ChunkY, ChunkZ); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); int RelX = BlockX - (ChunkX * cChunkDef::Width); int RelZ = BlockZ - (ChunkZ * cChunkDef::Width); diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp index a7b97f604..8ab09a4c5 100644 --- a/src/Mobs/Creeper.cpp +++ b/src/Mobs/Creeper.cpp @@ -67,9 +67,27 @@ void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer) } AddRandomDropItem(a_Drops, 0, 2 + LootingLevel, E_ITEM_GUNPOWDER); - if ((a_Killer != NULL) && (a_Killer->IsProjectile())) + if ((a_Killer != NULL) && a_Killer->IsProjectile() && (((cProjectileEntity *)a_Killer)->GetCreatorUniqueID() >= 0)) { - if (((cMonster *)((cProjectileEntity *)a_Killer)->GetCreator())->GetMobType() == mtSkeleton) + class cProjectileCreatorCallback : public cEntityCallback + { + public: + cProjectileCreatorCallback(void) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + if (a_Entity->IsMob() && ((cMonster *)a_Entity)->GetMobType() == mtSkeleton) + { + return true; + } + return false; + } + }; + + cProjectileCreatorCallback PCC; + if (GetWorld()->DoWithEntityByID(((cProjectileEntity *)a_Killer)->GetCreatorUniqueID(), PCC)) { // 12 music discs. TickRand starts from 0 to 11. Disk IDs start at 2256, so add that. There. AddRandomDropItem(a_Drops, 1, 1, (short)m_World->GetTickRandomNumber(11) + 2256); @@ -107,7 +125,7 @@ void cCreeper::Attack(float a_Dt) if (!m_bIsBlowing) { - 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_World->BroadcastSoundEffect("game.tnt.primed", GetPosX(), GetPosY(), GetPosZ(), 1.f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); m_bIsBlowing = true; m_World->BroadcastEntityMetadata(*this); } @@ -125,7 +143,7 @@ void cCreeper::OnRightClicked(cPlayer & a_Player) { a_Player.UseEquippedItem(); } - 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_World->BroadcastSoundEffect("game.tnt.primed", GetPosX(), GetPosY(), GetPosZ(), 1.f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64)); m_bIsBlowing = true; m_World->BroadcastEntityMetadata(*this); m_BurnedWithFlintAndSteel = true; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 5843ca5a6..16f75db67 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -46,8 +46,8 @@ static const struct {cMonster::mtSheep, "sheep"}, {cMonster::mtSilverfish, "silverfish"}, {cMonster::mtSkeleton, "skeleton"}, - {cMonster::mtSnowGolem, "snowgolem"}, {cMonster::mtSlime, "slime"}, + {cMonster::mtSnowGolem, "snowgolem"}, {cMonster::mtSpider, "spider"}, {cMonster::mtSquid, "squid"}, {cMonster::mtVillager, "villager"}, @@ -478,7 +478,7 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI) if (!m_SoundHurt.empty() && (m_Health > 0)) { - m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); + m_World->BroadcastSoundEffect(m_SoundHurt, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); } if (a_TDI.Attacker != NULL) @@ -497,7 +497,7 @@ void cMonster::KilledBy(cEntity * a_Killer) super::KilledBy(a_Killer); if (m_SoundHurt != "") { - m_World->BroadcastSoundEffect(m_SoundDeath, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f); + m_World->BroadcastSoundEffect(m_SoundDeath, GetPosX(), GetPosY(), GetPosZ(), 1.0f, 0.8f); } int Reward; switch (m_MobType) @@ -900,7 +900,7 @@ cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType) case mtMooshroom: toReturn = new cMooshroom(); break; case mtOcelot: toReturn = new cOcelot(); break; case mtPig: toReturn = new cPig(); break; - case mtSheep: toReturn = new cSheep (Random.NextInt(15)); break; // Colour parameter + case mtSheep: toReturn = new cSheep(); break; case mtSilverfish: toReturn = new cSilverfish(); break; case mtSnowGolem: toReturn = new cSnowGolem(); break; case mtSpider: toReturn = new cSpider(); break; diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp index e862f5aaa..1f77cf613 100644 --- a/src/Mobs/Pig.cpp +++ b/src/Mobs/Pig.cpp @@ -78,3 +78,23 @@ void cPig::OnRightClicked(cPlayer & a_Player) + + +void cPig::Tick(float a_Dt, cChunk & a_Chunk) +{ + super::Tick(a_Dt, a_Chunk); + + // If the attachee player is holding a carrot-on-stick, let them drive this pig: + if (m_bIsSaddled && (m_Attachee != NULL)) + { + if (m_Attachee->IsPlayer() && (m_Attachee->GetEquippedWeapon().m_ItemType == E_ITEM_CARROT_ON_STICK)) + { + MoveToPosition((m_Attachee->GetPosition()) + (m_Attachee->GetLookVector()*10)); + m_bMovingToDestination = true; + } + } +} + + + + diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h index d434324c1..313af2f44 100644 --- a/src/Mobs/Pig.h +++ b/src/Mobs/Pig.h @@ -19,6 +19,7 @@ public: virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void OnRightClicked(cPlayer & a_Player) override; + virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); } diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp index 5a6b760af..019f9e6a2 100644 --- a/src/Mobs/Sheep.cpp +++ b/src/Mobs/Sheep.cpp @@ -5,6 +5,7 @@ #include "../BlockID.h" #include "../Entities/Player.h" #include "../World.h" +#include "FastRandom.h" @@ -16,6 +17,16 @@ cSheep::cSheep(int a_Color) : m_WoolColor(a_Color), m_TimeToStopEating(-1) { + // Generate random wool color. + if (m_WoolColor == -1) + { + m_WoolColor = GenerateNaturalRandomColor(); + } + + if ((m_WoolColor < 0) || (m_WoolColor > 15)) + { + m_WoolColor = 0; + } } @@ -37,7 +48,7 @@ void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer) void cSheep::OnRightClicked(cPlayer & a_Player) { const cItem & EquippedItem = a_Player.GetEquippedItem(); - if ((EquippedItem.m_ItemType == E_ITEM_SHEARS) && (!m_IsSheared)) + if ((EquippedItem.m_ItemType == E_ITEM_SHEARS) && !IsSheared() && !IsBaby()) { m_IsSheared = true; m_World->BroadcastEntityMetadata(*this); @@ -51,6 +62,7 @@ void cSheep::OnRightClicked(cPlayer & a_Player) int NumDrops = m_World->GetTickRandomNumber(2) + 1; Drops.push_back(cItem(E_BLOCK_WOOL, NumDrops, m_WoolColor)); m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ(), 10); + m_World->BroadcastSoundEffect("mob.sheep.shear", GetPosX(), GetPosY(), GetPosZ(), 1.0f, 1.0f); } else if ((EquippedItem.m_ItemType == E_ITEM_DYE) && (m_WoolColor != 15 - EquippedItem.m_ItemDamage)) { @@ -109,3 +121,38 @@ void cSheep::Tick(float a_Dt, cChunk & a_Chunk) } } + + + + +NIBBLETYPE cSheep::GenerateNaturalRandomColor(void) +{ + cFastRandom Random; + int Chance = Random.NextInt(101); + + if (Chance <= 81) + { + return E_META_WOOL_WHITE; + } + else if (Chance <= 86) + { + return E_META_WOOL_BLACK; + } + else if (Chance <= 91) + { + return E_META_WOOL_GRAY; + } + else if (Chance <= 96) + { + return E_META_WOOL_LIGHTGRAY; + } + else if (Chance <= 99) + { + return E_META_WOOL_BROWN; + } + else + { + return E_META_WOOL_PINK; + } +} + diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h index 402e8e61c..5ffd3e4fe 100644 --- a/src/Mobs/Sheep.h +++ b/src/Mobs/Sheep.h @@ -13,21 +13,32 @@ class cSheep : typedef cPassiveMonster super; public: - cSheep(int a_Color); - + + /** The number is the color of the sheep. + Use E_META_WOOL_* constants for the wool color. + If you type -1, the server will generate a random color + with the GenerateNaturalRandomColor() function. */ + cSheep(int a_Color = -1); + CLASS_PROTODEF(cSheep); - + virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override; virtual void OnRightClicked(cPlayer & a_Player) override; virtual void Tick(float a_Dt, cChunk & a_Chunk) override; virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); } + /** Generates a random color for the sheep like the vanilla server. + The percent's where used are from the wiki: http://minecraft.gamepedia.com/Sheep#Breeding */ + static NIBBLETYPE GenerateNaturalRandomColor(void); + bool IsSheared(void) const { return m_IsSheared; } + void SetSheared(bool a_IsSheared) { m_IsSheared = a_IsSheared; } + int GetFurColor(void) const { return m_WoolColor; } + void SetFurColor(int a_WoolColor) { m_WoolColor = a_WoolColor; } private: - bool m_IsSheared; int m_WoolColor; int m_TimeToStopEating; diff --git a/src/OSSupport/Event.cpp b/src/OSSupport/Event.cpp index 6b8fccde2..72bce4c3c 100644 --- a/src/OSSupport/Event.cpp +++ b/src/OSSupport/Event.cpp @@ -18,7 +18,7 @@ cEvent::cEvent(void) m_Event = CreateEvent(NULL, FALSE, FALSE, NULL); if (m_Event == NULL) { - LOGERROR("cEvent: cannot create event, GLE = %d. Aborting server.", GetLastError()); + LOGERROR("cEvent: cannot create event, GLE = %u. Aborting server.", (unsigned)GetLastError()); abort(); } #else // *nix @@ -86,7 +86,7 @@ void cEvent::Wait(void) DWORD res = WaitForSingleObject(m_Event, INFINITE); if (res != WAIT_OBJECT_0) { - LOGWARN("cEvent: waiting for the event failed: %d, GLE = %d. Continuing, but server may be unstable.", res, GetLastError()); + LOGWARN("cEvent: waiting for the event failed: %u, GLE = %u. Continuing, but server may be unstable.", (unsigned)res, (unsigned)GetLastError()); } #else int res = sem_wait(m_Event); @@ -107,7 +107,7 @@ void cEvent::Set(void) #ifdef _WIN32 if (!SetEvent(m_Event)) { - LOGWARN("cEvent: Could not set cEvent: GLE = %d", GetLastError()); + LOGWARN("cEvent: Could not set cEvent: GLE = %u", (unsigned)GetLastError()); } #else int res = sem_post(m_Event); diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp index 8c24fa541..addf8f928 100644 --- a/src/OSSupport/File.cpp +++ b/src/OSSupport/File.cpp @@ -7,6 +7,9 @@ #include "File.h" #include <fstream> +#ifdef _WIN32 + #include <share.h> // for _SH_DENYWRITE +#endif // _WIN32 diff --git a/src/OSSupport/IsThread.cpp b/src/OSSupport/IsThread.cpp index 67f336c97..1a436623a 100644 --- a/src/OSSupport/IsThread.cpp +++ b/src/OSSupport/IsThread.cpp @@ -90,7 +90,7 @@ bool cIsThread::Start(void) m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &m_ThreadID); if (m_Handle == NULL) { - LOGERROR("ERROR: Could not create thread \"%s\", GLE = %d!", m_ThreadName.c_str(), GetLastError()); + LOGERROR("ERROR: Could not create thread \"%s\", GLE = %u!", m_ThreadName.c_str(), (unsigned)GetLastError()); return false; } ResumeThread(m_Handle); diff --git a/src/Protocol/Authenticator.cpp b/src/Protocol/Authenticator.cpp index 2050393c2..54b823e0f 100644 --- a/src/Protocol/Authenticator.cpp +++ b/src/Protocol/Authenticator.cpp @@ -262,7 +262,7 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S AString HexDump; if (Response.compare(0, prefix.size(), prefix)) { - LOGINFO("User \"%s\" failed to auth, bad http status line received", a_UserName.c_str()); + LOGINFO("User %s failed to auth, bad http status line received", a_UserName.c_str()); LOG("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return false; } @@ -271,7 +271,7 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S size_t idxHeadersEnd = Response.find("\r\n\r\n"); if (idxHeadersEnd == AString::npos) { - LOGINFO("User \"%s\" failed to authenticate, bad http response header received", a_UserName.c_str()); + LOGINFO("User %s failed to authenticate, bad http response header received", a_UserName.c_str()); LOG("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return false; } diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index c6e569919..dc35a6653 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -64,7 +64,7 @@ public: virtual void SendChat (const AString & a_Message) = 0; virtual void SendChat (const cCompositeChat & a_Message) = 0; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) = 0; + virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) = 0; virtual void SendDestroyEntity (const cEntity & a_Entity) = 0; virtual void SendDisconnect (const AString & a_Reason) = 0; virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) = 0; ///< Request the client to open up the sign editor for the sign (1.6+) @@ -100,13 +100,13 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0; virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0; - virtual void SendRespawn (const cWorld & a_World) = 0; + virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) = 0; virtual void SendExperience (void) = 0; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0; virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0; virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) = 0; virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) = 0; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8 + virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) = 0; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0; virtual void SendSpawnMob (const cMonster & a_Mob) = 0; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index 491058919..28fdcf23a 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -274,11 +274,11 @@ void cProtocol125::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerialize -void cProtocol125::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +void cProtocol125::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player) { cCSLock Lock(m_CSPacket); WriteByte(PACKET_COLLECT_PICKUP); - WriteInt (a_Pickup.GetUniqueID()); + WriteInt (a_Entity.GetUniqueID()); WriteInt (a_Player.GetUniqueID()); Flush(); } @@ -833,12 +833,12 @@ void cProtocol125::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect -void cProtocol125::SendRespawn(const cWorld & a_World) +void cProtocol125::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks) { cCSLock Lock(m_CSPacket); - if (m_LastSentDimension == a_World.GetDimension()) + if ((m_LastSentDimension == a_World.GetDimension()) && !a_ShouldIgnoreDimensionChecks) { - // Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do + // Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death) return; } cPlayer * Player = m_Client->GetPlayer(); @@ -899,7 +899,7 @@ void cProtocol125::SendScoreboardObjective(const AString & a_Name, const AString -void cProtocol125::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +void cProtocol125::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) { // Not needed in this protocol version } diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index 85418f71f..86a49f3f6 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -36,7 +36,7 @@ public: virtual void SendChat (const AString & a_Message) override; virtual void SendChat (const cCompositeChat & a_Message) override; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; virtual void SendDisconnect (const AString & a_Reason) override; virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) @@ -72,13 +72,13 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; - virtual void SendRespawn (const cWorld & a_World) override; + virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override; virtual void SendExperience (void) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override {} // This protocol doesn't support such message virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override {} // This protocol doesn't support such message - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; virtual void SendSpawnMob (const cMonster & a_Mob) override; diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp index 1e3fc8de8..7a8c2221e 100644 --- a/src/Protocol/Protocol132.cpp +++ b/src/Protocol/Protocol132.cpp @@ -188,20 +188,13 @@ void cProtocol132::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerialize -void cProtocol132::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +void cProtocol132::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player) { cCSLock Lock(m_CSPacket); WriteByte(PACKET_COLLECT_PICKUP); - WriteInt (a_Pickup.GetUniqueID()); + WriteInt (a_Entity.GetUniqueID()); WriteInt (a_Player.GetUniqueID()); Flush(); - - // Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;) - SendSoundEffect( - "random.pop", - (int)(a_Pickup.GetPosX() * 8), (int)(a_Pickup.GetPosY() * 8), (int)(a_Pickup.GetPosZ() * 8), - 0.5, (float)(0.75 + ((float)((a_Pickup.GetUniqueID() * 23) % 32)) / 64) - ); } @@ -285,14 +278,14 @@ void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player) -void cProtocol132::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +void cProtocol132::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) { cCSLock Lock(m_CSPacket); WriteByte (PACKET_SOUND_EFFECT); WriteString (a_SoundName); - WriteInt (a_SrcX); - WriteInt (a_SrcY); - WriteInt (a_SrcZ); + WriteInt ((int)(a_X * 8.0)); + WriteInt ((int)(a_Y * 8.0)); + WriteInt ((int)(a_Z * 8.0)); WriteFloat (a_Volume); WriteChar ((char)(a_Pitch * 63.0f)); Flush(); diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h index 32bc7d581..1124a7253 100644 --- a/src/Protocol/Protocol132.h +++ b/src/Protocol/Protocol132.h @@ -48,12 +48,12 @@ public: 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 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 SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; virtual void SendEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item) override; virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override; virtual void SendPlayerSpawn (const cPlayer & a_Player) override; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnMob (const cMonster & a_Mob) override; virtual void SendTabCompletionResults(const AStringVector & a_Results) override; diff --git a/src/Protocol/Protocol16x.cpp b/src/Protocol/Protocol16x.cpp index 9e0f3f852..eba795f61 100644 --- a/src/Protocol/Protocol16x.cpp +++ b/src/Protocol/Protocol16x.cpp @@ -158,10 +158,10 @@ void cProtocol161::SendPlayerMaxSpeed(void) -void cProtocol161::SendRespawn(const cWorld & a_World) +void cProtocol161::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks) { // Besides sending the respawn, we need to also send the player max speed, otherwise the client reverts to super-fast - super::SendRespawn(a_World); + super::SendRespawn(a_World, a_ShouldIgnoreDimensionChecks); SendPlayerMaxSpeed(); } diff --git a/src/Protocol/Protocol16x.h b/src/Protocol/Protocol16x.h index e91dc8a1c..e6e79027e 100644 --- a/src/Protocol/Protocol16x.h +++ b/src/Protocol/Protocol16x.h @@ -42,7 +42,7 @@ protected: virtual void SendGameMode (eGameMode a_GameMode) override; virtual void SendHealth (void) override; virtual void SendPlayerMaxSpeed(void) override; - virtual void SendRespawn (const cWorld & a_World) override; + virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override; virtual void SendWindowOpen (const cWindow & a_Window) override; virtual int ParseEntityAction (void) override; diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 02c577dc8..dc6a817a3 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -351,12 +351,12 @@ void cProtocol172::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerialize -void cProtocol172::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +void cProtocol172::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player) { ASSERT(m_State == 3); // In game mode? cPacketizer Pkt(*this, 0x0d); // Collect Item packet - Pkt.WriteInt(a_Pickup.GetUniqueID()); + Pkt.WriteInt(a_Entity.GetUniqueID()); Pkt.WriteInt(a_Player.GetUniqueID()); } @@ -986,11 +986,11 @@ void cProtocol172::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effect -void cProtocol172::SendRespawn(const cWorld & a_World) +void cProtocol172::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks) { - if (m_LastSentDimension == a_World.GetDimension()) + if ((m_LastSentDimension == a_World.GetDimension()) && !a_ShouldIgnoreDimensionChecks) { - // Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do + // Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death) return; } @@ -1084,15 +1084,15 @@ void cProtocol172::SendDisplayObjective(const AString & a_Objective, cScoreboard -void cProtocol172::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) // a_Src coords are Block * 8 +void cProtocol172::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) { ASSERT(m_State == 3); // In game mode? - + cPacketizer Pkt(*this, 0x29); // Sound Effect packet Pkt.WriteString(a_SoundName); - Pkt.WriteInt(a_SrcX); - Pkt.WriteInt(a_SrcY); - Pkt.WriteInt(a_SrcZ); + Pkt.WriteInt((int)(a_X * 8.0)); + Pkt.WriteInt((int)(a_Y * 8.0)); + Pkt.WriteInt((int)(a_Z * 8.0)); Pkt.WriteFloat(a_Volume); Pkt.WriteByte((Byte)(a_Pitch * 63)); } @@ -1221,10 +1221,9 @@ void cProtocol172::SendStatistics(const cStatManager & a_Manager) cPacketizer Pkt(*this, 0x37); Pkt.WriteVarInt(statCount); // TODO 2014-05-11 xdot: Optimization: Send "dirty" statistics only - for (unsigned int i = 0; i < (unsigned int)statCount; ++i) + for (size_t i = 0; i < (size_t)statCount; ++i) { StatValue Value = a_Manager.GetValue((eStatistic) i); - const AString & StatName = cStatInfo::GetName((eStatistic) i); Pkt.WriteString(StatName); @@ -2334,7 +2333,7 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata) for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings { - AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a newline, used internally by MCS to display a new line in the client; don't forget to c_str ;) + AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a grave accent/backtick, used internally by MCS to display a new line in the client; don't forget to c_str ;) } a_Item.m_Lore = Lore; diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 8be1d9211..e635a62c1 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -68,7 +68,7 @@ public: virtual void SendChat (const AString & a_Message) override; virtual void SendChat (const cCompositeChat & a_Message) override; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; virtual void SendDisconnect (const AString & a_Reason) override; virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) @@ -104,8 +104,8 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; - virtual void SendRespawn (const cWorld & a_World) override; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8 + virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override; + virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; virtual void SendExperience (void) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 80f5da25a..29f4576cd 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -181,10 +181,10 @@ void cProtocolRecognizer::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSe -void cProtocolRecognizer::SendCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player) +void cProtocolRecognizer::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player) { ASSERT(m_Protocol != NULL); - m_Protocol->SendCollectPickup(a_Pickup, a_Player); + m_Protocol->SendCollectEntity(a_Entity, a_Player); } @@ -556,10 +556,10 @@ void cProtocolRecognizer::SendRemoveEntityEffect(const cEntity & a_Entity, int a -void cProtocolRecognizer::SendRespawn(const cWorld & a_World) +void cProtocolRecognizer::SendRespawn(const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks) { ASSERT(m_Protocol != NULL); - m_Protocol->SendRespawn(a_World); + m_Protocol->SendRespawn(a_World, a_ShouldIgnoreDimensionChecks); } @@ -616,10 +616,10 @@ void cProtocolRecognizer::SendDisplayObjective(const AString & a_Objective, cSco -void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) +void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) { ASSERT(m_Protocol != NULL); - m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch); + m_Protocol->SendSoundEffect(a_SoundName, a_X, a_Y, a_Z, a_Volume, a_Pitch); } diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index 5e178447c..e5ec3ece2 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -71,7 +71,7 @@ public: virtual void SendChat (const AString & a_Message) override; virtual void SendChat (const cCompositeChat & a_Message) override; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override; - virtual void SendCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player) override; + virtual void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player) override; virtual void SendDestroyEntity (const cEntity & a_Entity) override; virtual void SendDisconnect (const AString & a_Reason) override; virtual void SendEditSign (int a_BlockX, int a_BlockY, int a_BlockZ) override; ///< Request the client to open up the sign editor for the sign (1.6+) @@ -107,13 +107,13 @@ public: virtual void SendPlayerSpawn (const cPlayer & a_Player) override; virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override; virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override; - virtual void SendRespawn (const cWorld & a_World) override; + virtual void SendRespawn (const cWorld & a_World, bool a_ShouldIgnoreDimensionChecks = false) override; virtual void SendExperience (void) override; virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override; virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override; virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override; virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override; - virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; + virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override; virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override; virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override; virtual void SendSpawnMob (const cMonster & a_Mob) override; diff --git a/src/Server.cpp b/src/Server.cpp index 2c695cc70..9220731eb 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -221,14 +221,12 @@ bool cServer::InitServer(cIniFile & a_SettingsIni) bool HasAnyPorts = false; AString Ports = a_SettingsIni.GetValueSet("Server", "Port", "25565"); - m_ListenThreadIPv4.SetReuseAddr(true); if (m_ListenThreadIPv4.Initialize(Ports)) { HasAnyPorts = true; } Ports = a_SettingsIni.GetValueSet("Server", "PortsIPv6", "25565"); - m_ListenThreadIPv6.SetReuseAddr(true); if (m_ListenThreadIPv6.Initialize(Ports)) { HasAnyPorts = true; @@ -258,6 +256,9 @@ bool cServer::InitServer(cIniFile & a_SettingsIni) m_ServerID.resize(16, '0'); } + m_ShouldLoadOfflinePlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadOfflinePlayerData", false); + m_ShouldLoadNamedPlayerData = a_SettingsIni.GetValueSetB("PlayerData", "LoadNamedPlayerData", true); + m_ClientViewDistance = a_SettingsIni.GetValueSetI("Server", "DefaultViewDistance", cClientHandle::DEFAULT_VIEW_DISTANCE); if (m_ClientViewDistance < cClientHandle::MIN_VIEW_DISTANCE) { diff --git a/src/Server.h b/src/Server.h index 3d76c8ccf..5227799e8 100644 --- a/src/Server.h +++ b/src/Server.h @@ -112,8 +112,18 @@ public: // tolua_export cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; } const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; } + /** Returns true if authentication has been turned on in server settings. */ bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; } + /** Returns true if offline UUIDs should be used to load data for players whose normal UUIDs cannot be found. + Loaded from the settings.ini [PlayerData].LoadOfflinePlayerData setting. */ + bool ShouldLoadOfflinePlayerData(void) const { return m_ShouldLoadOfflinePlayerData; } + + /** Returns true if old-style playernames should be used to load data for players whose regular datafiles cannot be found. + This allows a seamless transition from name-based to UUID-based player storage. + Loaded from the settings.ini [PlayerData].LoadNamedPlayerData setting. */ + bool ShouldLoadNamedPlayerData(void) const { return m_ShouldLoadNamedPlayerData; } + private: friend class cRoot; // so cRoot can create and destroy cServer @@ -204,6 +214,16 @@ private: This setting is the same as the "online-mode" setting in Vanilla. */ bool m_ShouldAuthenticate; + /** True if offline UUIDs should be used to load data for players whose normal UUIDs cannot be found. + This allows transitions from an offline (no-auth) server to an online one. + Loaded from the settings.ini [PlayerData].LoadOfflinePlayerData setting. */ + bool m_ShouldLoadOfflinePlayerData; + + /** True if old-style playernames should be used to load data for players whose regular datafiles cannot be found. + This allows a seamless transition from name-based to UUID-based player storage. + Loaded from the settings.ini [PlayerData].LoadNamedPlayerData setting. */ + bool m_ShouldLoadNamedPlayerData; + cServer(void); diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp index e95af3a1c..d8de19871 100644 --- a/src/Simulator/FloodyFluidSimulator.cpp +++ b/src/Simulator/FloodyFluidSimulator.cpp @@ -217,14 +217,20 @@ void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, i { ASSERT(a_NewMeta <= 8); // Invalid meta values ASSERT(a_NewMeta > 0); // Source blocks aren't spread - - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - if (!a_NearChunk->UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta)) + + a_NearChunk = a_NearChunk->GetRelNeighborChunkAdjustCoords(a_RelX, a_RelZ); + if ((a_NearChunk == NULL) || (!a_NearChunk->IsValid())) { // Chunk not available return; } + + const int BlockX = a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX; + const int BlockZ = a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ; + + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + a_NearChunk->GetBlockTypeMeta(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta); if (IsAllowedBlock(BlockType)) { @@ -246,15 +252,9 @@ void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, i a_RelX, a_RelY, a_RelZ, ItemTypeToString(NewBlock).c_str() ); - a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); + a_NearChunk->SetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); - int BaseX = a_NearChunk->GetPosX() * cChunkDef::Width; - int BaseZ = a_NearChunk->GetPosZ() * cChunkDef::Width; - - BaseX += a_RelX; - BaseZ += a_RelZ; - - a_NearChunk->BroadcastSoundEffect("random.fizz", BaseX * 8, a_RelY * 8, BaseZ * 8, 0.5f, 1.5f); + a_NearChunk->BroadcastSoundEffect("random.fizz", (double)BlockX, (double)a_RelY, (double)BlockZ, 0.5f, 1.5f); return; } } @@ -267,15 +267,9 @@ void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, i FLOG(" Water flowing into lava, turning lava at rel {%d, %d, %d} into %s", a_RelX, a_RelY, a_RelZ, ItemTypeToString(NewBlock).c_str() ); - a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); - - int BaseX = a_NearChunk->GetPosX() * cChunkDef::Width; - int BaseZ = a_NearChunk->GetPosZ() * cChunkDef::Width; - - BaseX += a_RelX; - BaseZ += a_RelZ; + a_NearChunk->SetBlock(a_RelX, a_RelY, a_RelZ, NewBlock, 0); - a_NearChunk->BroadcastSoundEffect("random.fizz", BaseX * 8, a_RelY * 8, BaseZ * 8, 0.5f, 1.5f); + a_NearChunk->BroadcastSoundEffect("random.fizz", (double)BlockX, (double)a_RelY, (double)BlockZ, 0.5f, 1.5f); return; } } @@ -303,21 +297,17 @@ void cFloodyFluidSimulator::SpreadToNeighbor(cChunk * a_NearChunk, int a_RelX, i m_World, PluginInterface, NULL, - a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX, + BlockX, a_RelY, - a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ + BlockZ ); } } // if (CanWashAway) - + // Spread: - FLOG(" Spreading to {%d, %d, %d} with meta %d", - a_NearChunk->GetPosX() * cChunkDef::Width + a_RelX, - a_RelY, - a_NearChunk->GetPosZ() * cChunkDef::Width + a_RelZ, - a_NewMeta - ); - a_NearChunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); + FLOG(" Spreading to {%d, %d, %d} with meta %d", BlockX, a_RelY, BlockZ, a_NewMeta); + a_NearChunk->SetBlock(a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); + m_World.GetSimulatorManager()->WakeUp(BlockX, a_RelY, BlockZ, a_NearChunk); HardenBlock(a_NearChunk, a_RelX, a_RelY, a_RelZ, m_FluidBlock, a_NewMeta); } @@ -409,13 +399,13 @@ bool cFloodyFluidSimulator::HardenBlock(cChunk * a_Chunk, int a_RelX, int a_RelY if (a_Meta == 0) { // Source lava block - a_Chunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_OBSIDIAN, 0); + a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_OBSIDIAN, 0); return true; } // Ignore last lava level else if (a_Meta <= 4) { - a_Chunk->UnboundedRelSetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_COBBLESTONE, 0); + a_Chunk->SetBlock(a_RelX, a_RelY, a_RelZ, E_BLOCK_COBBLESTONE, 0); return true; } } diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 1de0d2c34..8b9d830cd 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -2,8 +2,10 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "IncrementalRedstoneSimulator.h" +#include "BoundingBox.h" #include "../BlockEntities/DropSpenserEntity.h" #include "../BlockEntities/NoteEntity.h" +#include "../BlockEntities/ChestEntity.h" #include "../BlockEntities/CommandBlockEntity.h" #include "../Entities/TNTEntity.h" #include "../Entities/Pickup.h" @@ -12,10 +14,11 @@ #include "../Blocks/BlockButton.h" #include "../Blocks/BlockLever.h" #include "../Blocks/BlockPiston.h" +#include "../Blocks/BlockTripwireHook.h" + - cIncrementalRedstoneSimulator::cIncrementalRedstoneSimulator(cWorld & a_World) : super(a_World), @@ -59,14 +62,16 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, int RelZ = 0; BLOCKTYPE Block; NIBBLETYPE Meta; - cChunk * OtherChunk = a_Chunk; 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); - OtherChunk = a_OtherChunk; + + // If a_OtherChunk is passed (not NULL), it is the chunk that had a block change, and a_Chunk will be the neighbouring chunk of that block + // Because said neighbouring chunk does not know of this change but still needs to update its redstone, we set it to dirty + a_Chunk->SetIsRedstoneDirty(true); } else { @@ -91,43 +96,21 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, { 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); itr = PoweredBlocks->erase(itr); - a_Chunk->SetIsRedstoneDirty(true); - OtherChunk->SetIsRedstoneDirty(true); continue; } else if ( // Changeable sources ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || - ((Block == E_BLOCK_DETECTOR_RAIL) && (Meta & 0x08) == 0) || + ((Block == E_BLOCK_DETECTOR_RAIL) && ((Meta & 0x08) == 0)) || (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) || - (((Block == E_BLOCK_STONE_PRESSURE_PLATE) || (Block == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (Meta == 0)) || - (((Block == E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE) || (Block == E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE)) && (Meta == 0)) + ((Block == E_BLOCK_TRIPWIRE_HOOK) && ((Meta & 0x08) == 0)) ) { 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); itr = PoweredBlocks->erase(itr); - a_Chunk->SetIsRedstoneDirty(true); - OtherChunk->SetIsRedstoneDirty(true); continue; } - else if (Block == E_BLOCK_DAYLIGHT_SENSOR) - { - if (!m_World.IsChunkLighted(OtherChunk->GetPosX(), OtherChunk->GetPosZ())) - { - m_World.QueueLightChunk(OtherChunk->GetPosX(), OtherChunk->GetPosZ()); - } - else - { - if (OtherChunk->GetTimeAlteredLight(OtherChunk->GetSkyLight(RelX, a_BlockY + 1, RelZ)) <= 7) - { - itr = PoweredBlocks->erase(itr); - a_Chunk->SetIsRedstoneDirty(true); - OtherChunk->SetIsRedstoneDirty(true); - continue; - } - } - } ++itr; } @@ -141,23 +124,17 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, { 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); itr = LinkedPoweredBlocks->erase(itr); - a_Chunk->SetIsRedstoneDirty(true); - OtherChunk->SetIsRedstoneDirty(true); continue; } else if ( // Things that can send power through a block but which depends on meta ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) || ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) || - (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) || - (((Block == E_BLOCK_STONE_PRESSURE_PLATE) || (Block == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (Meta == 0)) || - (((Block == E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE) || (Block == E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE)) && (Meta == 0)) + (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) ) { 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); itr = LinkedPoweredBlocks->erase(itr); - a_Chunk->SetIsRedstoneDirty(true); - OtherChunk->SetIsRedstoneDirty(true); continue; } } @@ -167,8 +144,6 @@ void cIncrementalRedstoneSimulator::RedstoneAddBlock(int a_BlockX, int a_BlockY, { 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); itr = LinkedPoweredBlocks->erase(itr); - a_Chunk->SetIsRedstoneDirty(true); - OtherChunk->SetIsRedstoneDirty(true); continue; } } @@ -289,6 +264,9 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int switch (dataitr->Data) { case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_TRIPWIRE: HandleTripwire(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_TRIPWIRE_HOOK: HandleTripwireHook(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_WOODEN_PRESSURE_PLATE: case E_BLOCK_STONE_PRESSURE_PLATE: case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: @@ -312,6 +290,7 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int case E_BLOCK_FENCE_GATE: HandleFenceGate(dataitr->x, dataitr->y, dataitr->z); break; case E_BLOCK_TNT: HandleTNT(dataitr->x, dataitr->y, dataitr->z); break; case E_BLOCK_TRAPDOOR: HandleTrapdoor(dataitr->x, dataitr->y, dataitr->z); break; + case E_BLOCK_TRAPPED_CHEST: HandleTrappedChest(dataitr->x, dataitr->y, dataitr->z); break; case E_BLOCK_ACTIVATOR_RAIL: case E_BLOCK_DETECTOR_RAIL: @@ -374,18 +353,13 @@ 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 + if (AreCoordsOnChunkBoundary(a_BlockX, a_BlockY, a_BlockZ)) { // 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 to pass both the neighbouring chunk and the chunk which the coordinates 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); @@ -440,7 +414,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_RelBlockX, int a_R if (i + 1 < ARRAYCOUNT(gCrossCoords)) // Sides of torch, not top (top is last) { if ( - ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) && // Is it a mechanism or wire? Not block/other torch etc. + IsMechanism(Type) && // Is it a mechanism? Not block/other torch etc. (!Vector3i(a_RelBlockX + gCrossCoords[i].x, a_RelBlockY + gCrossCoords[i].y, a_RelBlockZ + gCrossCoords[i].z).Equals(Vector3i(X, Y, Z))) // CAN'T power block is that it is on ) { @@ -460,7 +434,7 @@ void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_RelBlockX, int a_R { BLOCKTYPE Type = m_Chunk->GetBlock(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ); - if ((IsMechanism(Type)) || (Type == E_BLOCK_REDSTONE_WIRE)) // Still can't make a normal block powered though! + if (IsMechanism(Type)) // Still can't make a normal block powered though! { SetBlockPowered(a_RelBlockX, a_RelBlockY - 1, a_RelBlockZ, a_RelBlockX, a_RelBlockY, a_RelBlockZ); } @@ -526,8 +500,11 @@ void cIncrementalRedstoneSimulator::HandleFenceGate(int a_RelBlockX, int a_RelBl { if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true)) { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData | 0x4); - m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + if ((MetaData & 0x4) == 0) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData | 0x4); + m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + } SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, true); } } @@ -535,8 +512,11 @@ void cIncrementalRedstoneSimulator::HandleFenceGate(int a_RelBlockX, int a_RelBl { if (!AreCoordsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false)) { - m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData & 0xFFFFFFFB); - m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + if ((MetaData & 0x4) != 0) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, MetaData & ~0x04); + m_Chunk->BroadcastSoundParticleEffect(1003, BlockX, a_RelBlockY, BlockZ, 0); + } SetPlayerToggleableBlockAsSimulated(a_RelBlockX, a_RelBlockY, a_RelBlockZ, false); } } @@ -766,17 +746,20 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeaterDelays() { for (RepeatersDelayList::iterator itr = m_RepeatersDelayList->begin(); itr != m_RepeatersDelayList->end();) { - if (itr->a_ElapsedTicks >= itr->a_DelayTicks) // Has the elapsed ticks reached the target ticks? { int RelBlockX = itr->a_RelBlockPos.x; int RelBlockY = itr->a_RelBlockPos.y; int RelBlockZ = itr->a_RelBlockPos.z; - NIBBLETYPE Meta = m_Chunk->GetMeta(RelBlockX, RelBlockY, RelBlockZ); + BLOCKTYPE Block; + NIBBLETYPE Meta; + m_Chunk->GetBlockTypeMeta(RelBlockX, RelBlockY, RelBlockZ, Block, Meta); if (itr->ShouldPowerOn) { - - m_Chunk->SetBlock(itr->a_RelBlockPos, E_BLOCK_REDSTONE_REPEATER_ON, Meta); // For performance + if (Block != E_BLOCK_REDSTONE_REPEATER_ON) // For performance + { + m_Chunk->SetBlock(itr->a_RelBlockPos, E_BLOCK_REDSTONE_REPEATER_ON, Meta); + } switch (Meta & 0x3) // We only want the direction (bottom) bits { @@ -806,17 +789,14 @@ void cIncrementalRedstoneSimulator::HandleRedstoneRepeaterDelays() } } } - else + else if (Block != E_BLOCK_REDSTONE_REPEATER_OFF) { - m_Chunk->SetBlock(RelBlockX, RelBlockY, RelBlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, Meta); + m_Chunk->SetBlock(RelBlockX, RelBlockY, RelBlockZ, E_BLOCK_REDSTONE_REPEATER_OFF, Meta); } itr = m_RepeatersDelayList->erase(itr); } else { - // Apparently, incrementing ticks only works reliably here, and not in SimChunk; - // With a world with lots of redstone, the repeaters simply do not delay - // I am confounded to say why. Perhaps optimisation failure. LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_RelBlockPos.x, itr->a_RelBlockPos.y, itr->a_RelBlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks); itr->a_ElapsedTicks++; itr++; @@ -901,7 +881,7 @@ void cIncrementalRedstoneSimulator::HandleTNT(int a_RelBlockX, int a_RelBlockY, if (AreCoordsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ)) { - m_Chunk->BroadcastSoundEffect("game.tnt.primed", BlockX * 8, a_RelBlockY * 8, BlockZ * 8, 0.5f, 0.6f); + m_Chunk->BroadcastSoundEffect("game.tnt.primed", (double)BlockX, (double)a_RelBlockY, (double)BlockZ, 0.5f, 0.6f); m_Chunk->SetBlock(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_BLOCK_AIR, 0); m_World.SpawnPrimedTNT(BlockX + 0.5, a_RelBlockY + 0.5, BlockZ + 0.5); // 80 ticks to boom } @@ -1114,7 +1094,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R case E_BLOCK_STONE_PRESSURE_PLATE: { // MCS feature - stone pressure plates can only be triggered by players :D - cPlayer * a_Player = m_World.FindClosestPlayer(Vector3f(BlockX + 0.5f, (float)a_RelBlockY, BlockZ + 0.5f), 0.7f, false); + cPlayer * a_Player = m_World.FindClosestPlayer(Vector3f(BlockX + 0.5f, (float)a_RelBlockY, BlockZ + 0.5f), 0.5f, false); if (a_Player != NULL) { @@ -1125,7 +1105,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R else { m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x0); - m_World.WakeUpSimulators(BlockX, a_RelBlockY, BlockZ); + SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); } break; } @@ -1149,7 +1129,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); double Distance = (EntityPos - BlockPos).Length(); - if (Distance <= 0.7) + if (Distance <= 0.5) { m_NumberOfEntities++; } @@ -1171,7 +1151,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R }; cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ); - m_World.ForEachEntity(PressurePlateCallback); + m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback); unsigned char Power; NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); @@ -1179,7 +1159,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R { if (Meta == E_META_PRESSURE_PLATE_RAISED) { - m_Chunk->BroadcastSoundEffect("random.click", (int)((BlockX + 0.5) * 8.0), (int)((a_RelBlockY + 0.1) * 8.0), (int)((BlockZ + 0.5) * 8.0), 0.3F, 0.5F); + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F); } m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Power); @@ -1189,10 +1169,10 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R { if (Meta == E_META_PRESSURE_PLATE_DEPRESSED) { - m_Chunk->BroadcastSoundEffect("random.click", (int)((BlockX + 0.5) * 8.0), (int)((a_RelBlockY + 0.1) * 8.0), (int)((BlockZ + 0.5) * 8.0), 0.3F, 0.6F); + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F); } m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED); - m_World.WakeUpSimulators(BlockX, a_RelBlockY, BlockZ); + SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); } break; @@ -1217,7 +1197,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); double Distance = (EntityPos - BlockPos).Length(); - if (Distance <= 0.7) + if (Distance <= 0.5) { m_NumberOfEntities++; } @@ -1226,7 +1206,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R bool GetPowerLevel(unsigned char & a_PowerLevel) const { - a_PowerLevel = std::min((int)ceil(m_NumberOfEntities / (float)10), MAX_POWER_LEVEL); + a_PowerLevel = std::min((int)ceil(m_NumberOfEntities / 10.f), MAX_POWER_LEVEL); return (a_PowerLevel > 0); } @@ -1239,7 +1219,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R }; cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ); - m_World.ForEachEntity(PressurePlateCallback); + m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback); unsigned char Power; NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); @@ -1247,7 +1227,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R { if (Meta == E_META_PRESSURE_PLATE_RAISED) { - m_Chunk->BroadcastSoundEffect("random.click", (int)((BlockX + 0.5) * 8.0), (int)((a_RelBlockY + 0.1) * 8.0), (int)((BlockZ + 0.5) * 8.0), 0.3F, 0.5F); + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F); } m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Power); @@ -1257,10 +1237,10 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R { if (Meta == E_META_PRESSURE_PLATE_DEPRESSED) { - m_Chunk->BroadcastSoundEffect("random.click", (int)((BlockX + 0.5) * 8.0), (int)((a_RelBlockY + 0.1) * 8.0), (int)((BlockZ + 0.5) * 8.0), 0.3F, 0.6F); + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F); } m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED); - m_World.WakeUpSimulators(BlockX, a_RelBlockY, BlockZ); + SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); } break; @@ -1285,7 +1265,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); double Distance = (EntityPos - BlockPos).Length(); - if (Distance <= 0.7) + if (Distance <= 0.5) { m_FoundEntity = true; return true; // Break out, we only need to know for plates that at least one entity is on top @@ -1307,14 +1287,14 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R } ; cPressurePlateCallback PressurePlateCallback(BlockX, a_RelBlockY, BlockZ); - m_World.ForEachEntity(PressurePlateCallback); + m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), PressurePlateCallback); NIBBLETYPE Meta = m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ); if (PressurePlateCallback.FoundEntity()) { if (Meta == E_META_PRESSURE_PLATE_RAISED) { - m_Chunk->BroadcastSoundEffect("random.click", (int)((BlockX + 0.5) * 8.0), (int)((a_RelBlockY + 0.1) * 8.0), (int)((BlockZ + 0.5) * 8.0), 0.3F, 0.5F); + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.5F); } m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_DEPRESSED); SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); @@ -1324,10 +1304,10 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R { if (Meta == E_META_PRESSURE_PLATE_DEPRESSED) { - m_Chunk->BroadcastSoundEffect("random.click", (int)((BlockX + 0.5) * 8.0), (int)((a_RelBlockY + 0.1) * 8.0), (int)((BlockZ + 0.5) * 8.0), 0.3F, 0.6F); + m_Chunk->BroadcastSoundEffect("random.click", (double)BlockX + 0.5, (double)a_RelBlockY + 0.1, (double)BlockZ + 0.5, 0.3F, 0.6F); } m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, E_META_PRESSURE_PLATE_RAISED); - m_World.WakeUpSimulators(BlockX, a_RelBlockY, BlockZ); + SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); } break; } @@ -1343,6 +1323,175 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_RelBlockX, int a_R +void cIncrementalRedstoneSimulator::HandleTripwireHook(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX; + int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ; + int RelX = a_RelBlockX, RelZ = a_RelBlockZ; + bool FoundActivated = false; + eBlockFace FaceToGoTowards = cBlockTripwireHookHandler::MetadataToDirection(m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ)); + + for (int i = 0; i < 40; ++i) // Tripwires can be connected up to 40 blocks + { + BLOCKTYPE Type; + NIBBLETYPE Meta; + + AddFaceDirection(RelX, a_RelBlockY, RelZ, FaceToGoTowards); + m_Chunk->UnboundedRelGetBlock(RelX, a_RelBlockY, RelZ, Type, Meta); + + if (Type == E_BLOCK_TRIPWIRE) + { + if (Meta == 0x1) + { + FoundActivated = true; + } + } + else if (Type == E_BLOCK_TRIPWIRE_HOOK) + { + if (ReverseBlockFace(cBlockTripwireHookHandler::MetadataToDirection(Meta)) == FaceToGoTowards) + { + // Other hook facing in opposite direction - circuit completed! + break; + } + else + { + // Tripwire hook not connected at all, AND away all the power state bits + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x3); + SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); + return; + } + } + else + { + // Tripwire hook not connected at all, AND away all the power state bits + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x3); + SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); + return; + } + } + + if (FoundActivated) + { + // Connected and activated, set the 3rd and 4th highest bits + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) | 0xC); + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); + } + else + { + // Connected but not activated, AND away the highest bit + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, (m_Chunk->GetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ) & 0x7) | 0x4); + SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleTrappedChest(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + class cGetTrappedChestPlayers : + public cChestCallback + { + public: + cGetTrappedChestPlayers(void) : + m_NumberOfPlayers(0) + { + } + + virtual bool Item(cChestEntity * a_Chest) override + { + ASSERT(a_Chest->GetBlockType() == E_BLOCK_TRAPPED_CHEST); + m_NumberOfPlayers = a_Chest->GetNumberOfPlayers(); + return (m_NumberOfPlayers <= 0); + } + + unsigned char GetPowerLevel(void) const + { + return std::min(m_NumberOfPlayers, MAX_POWER_LEVEL); + } + + private: + int m_NumberOfPlayers; + + } GTCP; + + int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX; + int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ; + if (m_Chunk->DoWithChestAt(BlockX, a_RelBlockY, BlockZ, GTCP)) + { + SetAllDirsAsPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ, GTCP.GetPowerLevel()); + } + else + { + SetSourceUnpowered(BlockX, a_RelBlockY, BlockZ, m_Chunk); + } +} + + + + + +void cIncrementalRedstoneSimulator::HandleTripwire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) +{ + int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX; + int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ; + + class cTripwireCallback : + public cEntityCallback + { + public: + cTripwireCallback(int a_BlockX, int a_BlockY, int a_BlockZ) : + m_FoundEntity(false), + m_X(a_BlockX), + m_Y(a_BlockY), + m_Z(a_BlockZ) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + cBoundingBox bbWire(m_X, m_X + 1, m_Y, m_Y + 0.1, m_Z, m_Z + 1); + cBoundingBox bbEntity(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight()); + + if (bbEntity.DoesIntersect(bbWire)) + { + m_FoundEntity = true; + return true; // One entity is sufficient to trigger the wire + } + return false; + } + + bool FoundEntity(void) const + { + return m_FoundEntity; + } + + protected: + bool m_FoundEntity; + + int m_X; + int m_Y; + int m_Z; + }; + + cTripwireCallback TripwireCallback(BlockX, a_RelBlockY, BlockZ); + m_World.ForEachEntityInChunk(m_Chunk->GetPosX(), m_Chunk->GetPosZ(), TripwireCallback); + + if (TripwireCallback.FoundEntity()) + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x1); + } + else + { + m_Chunk->SetMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, 0x0); + } +} + + + + + bool cIncrementalRedstoneSimulator::AreCoordsDirectlyPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) { int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; @@ -1552,8 +1701,8 @@ bool cIncrementalRedstoneSimulator::IsPistonPowered(int a_RelBlockX, int a_RelBl bool cIncrementalRedstoneSimulator::IsWirePowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char & a_PowerLevel) { a_PowerLevel = 0; - int BlockX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelBlockX; - int BlockZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelBlockZ; + int BlockX = m_Chunk->GetPosX() * cChunkDef::Width + a_RelBlockX; + int BlockZ = m_Chunk->GetPosZ() * cChunkDef::Width + a_RelBlockZ; for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) // Check powered list { @@ -1561,7 +1710,7 @@ bool cIncrementalRedstoneSimulator::IsWirePowered(int a_RelBlockX, int a_RelBloc { continue; } - a_PowerLevel = itr->a_PowerLevel; + a_PowerLevel = std::max(itr->a_PowerLevel , a_PowerLevel); // Get the highest power level (a_PowerLevel is initialised already and there CAN be multiple levels for one block) } for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks->begin(); itr != m_LinkedPoweredBlocks->end(); ++itr) // Check linked powered list @@ -1570,7 +1719,15 @@ bool cIncrementalRedstoneSimulator::IsWirePowered(int a_RelBlockX, int a_RelBloc { continue; } - a_PowerLevel = itr->a_PowerLevel; + + BLOCKTYPE Type = E_BLOCK_AIR; + int RelSourceX = itr->a_SourcePos.x - m_Chunk->GetPosX() * cChunkDef::Width; + int RelSourceZ = itr->a_SourcePos.z - m_Chunk->GetPosZ() * cChunkDef::Width; + if (!m_Chunk->UnboundedRelGetBlockType(RelSourceX, itr->a_SourcePos.y, RelSourceZ, Type) || (Type == E_BLOCK_REDSTONE_WIRE)) + { + continue; + } + a_PowerLevel = std::max(itr->a_PowerLevel, a_PowerLevel); } return (a_PowerLevel != 0); // Answer the inital question: is the wire powered? @@ -1742,20 +1899,9 @@ void cIncrementalRedstoneSimulator::SetBlockPowered(int a_RelBlockX, int a_RelBl int SourceX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelSourceX; int SourceZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelSourceZ; - BLOCKTYPE Block = 0; - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Block)) - { - return; - } - if (Block == E_BLOCK_AIR) - { - // Don't set air, fixes some bugs (wires powering themselves) - return; - } - - cChunk * Neighbour = m_Chunk->GetNeighborChunk(BlockX, BlockZ); - PoweredBlocksList * Powered = Neighbour->GetRedstoneSimulatorPoweredBlocksList(); - for (PoweredBlocksList::iterator itr = Powered->begin(); itr != Powered->end(); ++itr) // Check powered list + cChunk * Neighbour = m_Chunk->GetRelNeighborChunkAdjustCoords(a_RelBlockX, a_RelBlockZ); // Adjust coordinates for the later call using these values + PoweredBlocksList * Powered = Neighbour->GetRedstoneSimulatorPoweredBlocksList(); // We need to insert the value into the chunk who owns the block position + for (PoweredBlocksList::iterator itr = Powered->begin(); itr != Powered->end(); ++itr) { if ( itr->a_BlockPos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)) && @@ -1768,16 +1914,33 @@ void cIncrementalRedstoneSimulator::SetBlockPowered(int a_RelBlockX, int a_RelBl } } - PoweredBlocksList * OtherPowered = m_Chunk->GetNeighborChunk(SourceX, SourceZ)->GetRedstoneSimulatorPoweredBlocksList(); - for (PoweredBlocksList::const_iterator itr = OtherPowered->begin(); itr != OtherPowered->end(); ++itr) // Check powered list + // No need to get neighbouring chunk as we can guarantee that when something is powering us, the entry will be in our chunk + // TODO: on C++11 support, change this to a llama function pased to a std::remove_if + for (PoweredBlocksList::iterator itr = m_PoweredBlocks->begin(); itr != m_PoweredBlocks->end(); ++itr) { if ( itr->a_BlockPos.Equals(Vector3i(SourceX, a_RelSourceY, SourceZ)) && - itr->a_SourcePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)) + itr->a_SourcePos.Equals(Vector3i(BlockX, a_RelBlockY, BlockZ)) && + (m_Chunk->GetBlock(a_RelSourceX, a_RelSourceY, a_RelSourceZ) == E_BLOCK_REDSTONE_WIRE) ) { - // Powered wires try to power their source - don't let them! - return; + BLOCKTYPE Block; + NIBBLETYPE Meta; + Neighbour->GetBlockTypeMeta(a_RelBlockX, a_RelBlockY, a_RelBlockZ, Block, Meta); + + if (Block == E_BLOCK_REDSTONE_WIRE) + { + if (Meta < a_PowerLevel) + { + m_PoweredBlocks->erase(itr); // Powering source with higher power level, allow it + break; + } + else + { + // Powered wires try to power their source - don't let them! + return; + } + } } } @@ -1808,20 +1971,6 @@ void cIncrementalRedstoneSimulator::SetBlockLinkedPowered( int SourceX = (m_Chunk->GetPosX() * cChunkDef::Width) + a_RelSourceX; int SourceZ = (m_Chunk->GetPosZ() * cChunkDef::Width) + a_RelSourceZ; - BLOCKTYPE DestBlock = 0; - if (!m_Chunk->UnboundedRelGetBlockType(a_RelBlockX, a_RelBlockY, a_RelBlockZ, DestBlock)) - { - return; - } - if (DestBlock == E_BLOCK_AIR) - { - // Don't set air, fixes some bugs (wires powering themselves) - return; - } - if ((DestBlock == E_BLOCK_REDSTONE_WIRE) && (m_Chunk->GetBlock(a_RelSourceX, a_RelSourceY, a_RelSourceZ) == E_BLOCK_REDSTONE_WIRE)) - { - return; - } if (!IsViableMiddleBlock(a_MiddleBlock)) { return; @@ -1927,6 +2076,45 @@ bool cIncrementalRedstoneSimulator::QueueRepeaterPowerChange(int a_RelBlockX, in +void cIncrementalRedstoneSimulator::SetSourceUnpowered(int a_SourceX, int a_SourceY, int a_SourceZ, cChunk * a_Chunk, bool a_IsFirstCall) +{ + // TODO: on C++11 support, change both of these to llama functions pased to a std::remove_if + + for (PoweredBlocksList::iterator itr = a_Chunk->GetRedstoneSimulatorPoweredBlocksList()->begin(); itr != a_Chunk->GetRedstoneSimulatorPoweredBlocksList()->end();) + { + if (itr->a_SourcePos.Equals(Vector3i(a_SourceX, a_SourceY, a_SourceZ))) + { + itr = a_Chunk->GetRedstoneSimulatorPoweredBlocksList()->erase(itr); + a_Chunk->SetIsRedstoneDirty(true); + continue; + } + ++itr; + } + for (LinkedBlocksList::iterator itr = a_Chunk->GetRedstoneSimulatorLinkedBlocksList()->begin(); itr != a_Chunk->GetRedstoneSimulatorLinkedBlocksList()->end();) + { + if (itr->a_SourcePos.Equals(Vector3i(a_SourceX, a_SourceY, a_SourceZ))) + { + itr = a_Chunk->GetRedstoneSimulatorLinkedBlocksList()->erase(itr); + a_Chunk->SetIsRedstoneDirty(true); + continue; + } + ++itr; + } + + if (a_IsFirstCall && AreCoordsOnChunkBoundary(a_SourceX, a_SourceY, a_SourceZ)) + { + // +- 2 to accomodate linked powered blocks + SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX - 2, a_SourceZ), false); + SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX + 2, a_SourceZ), false); + SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX, a_SourceZ - 2), false); + SetSourceUnpowered(a_SourceX, a_SourceY, a_SourceZ, a_Chunk->GetNeighborChunk(a_SourceX, a_SourceZ + 2), false); + } +} + + + + + cIncrementalRedstoneSimulator::eRedstoneDirection cIncrementalRedstoneSimulator::GetWireDirection(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) { int Dir = REDSTONE_NONE; diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h index 9c1f9460c..ce987a60f 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.h +++ b/src/Simulator/IncrementalRedstoneSimulator.h @@ -102,6 +102,13 @@ private: void HandleDaylightSensor(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); /** Handles pressure plates */ void HandlePressurePlate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyType); + /** Handles tripwire hooks + Performs correct meta and power setting for self by going in the direction it faces and looking for a continous line of tripwire bounded by another oppositely facing hook + If this line is complete, it verifies that at least on wire reports an entity is on top (via its meta), and performs its task + */ + void HandleTripwireHook(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles trapped chests */ + void HandleTrappedChest(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); /* ==================== */ /* ====== CARRIERS ====== */ @@ -109,8 +116,6 @@ private: void HandleRedstoneWire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); /** Handles repeaters */ void HandleRedstoneRepeater(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, BLOCKTYPE a_MyState); - /** Handles delayed updates to Repeaters **/ - void HandleRedstoneRepeaterDelays(); /* ====================== */ /* ====== DEVICES ====== */ @@ -134,6 +139,8 @@ private: void HandleFenceGate(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); /** Handles noteblocks */ void HandleNoteBlock(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); + /** Handles tripwires */ + void HandleTripwire(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ); /* ===================== */ /* ====== Helper functions ====== */ @@ -149,6 +156,10 @@ private: void SetAllDirsAsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char a_PowerLevel = MAX_POWER_LEVEL); /** Queues a repeater to be powered or unpowered and returns if the m_RepeatersDelayList iterators were invalidated */ bool QueueRepeaterPowerChange(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, NIBBLETYPE a_Meta, bool ShouldPowerOn); + /** Removes a block from the Powered and LinkedPowered lists + Used for variable sources such as tripwire hooks, daylight sensors, and trapped chests + */ + void SetSourceUnpowered(int a_RelSourceX, int a_RelSourceY, int a_RelSourceZ, cChunk * a_Chunk, bool a_IsFirstCall = true); /** Returns if a coordinate is powered or linked powered */ bool AreCoordsPowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ) { return AreCoordsDirectlyPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ) || AreCoordsLinkedPowered(a_RelBlockX, a_RelBlockY, a_RelBlockZ); } @@ -167,7 +178,8 @@ private: /** Returns if a wire is powered The only diffence between this and a normal AreCoordsPowered is that this function checks for a wire powering another wire */ bool IsWirePowered(int a_RelBlockX, int a_RelBlockY, int a_RelBlockZ, unsigned char & a_PowerLevel); - + /** Handles delayed updates to repeaters **/ + void HandleRedstoneRepeaterDelays(void); /** Returns if lever metadata marks it as emitting power */ bool IsLeverOn(NIBBLETYPE a_BlockMeta); @@ -179,7 +191,9 @@ private: /** Returns if a block is viable to be the MiddleBlock of a SetLinkedPowered operation */ inline static bool IsViableMiddleBlock(BLOCKTYPE Block) { return cBlockInfo::FullyOccupiesVoxel(Block); } - /** Returns if a block is a mechanism (something that accepts power and does something) */ + /** Returns if a block is a mechanism (something that accepts power and does something) + Used by torches to determine if they power a block whilst not standing on the ground + */ inline static bool IsMechanism(BLOCKTYPE Block) { switch (Block) @@ -202,6 +216,7 @@ private: case E_BLOCK_REDSTONE_REPEATER_OFF: case E_BLOCK_REDSTONE_REPEATER_ON: case E_BLOCK_POWERED_RAIL: + case E_BLOCK_REDSTONE_WIRE: { return true; } @@ -228,6 +243,7 @@ private: case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: case E_BLOCK_STONE_PRESSURE_PLATE: case E_BLOCK_WOODEN_PRESSURE_PLATE: + case E_BLOCK_TRAPPED_CHEST: { return true; } @@ -270,7 +286,9 @@ private: case E_BLOCK_STONE_PRESSURE_PLATE: case E_BLOCK_TNT: case E_BLOCK_TRAPDOOR: + case E_BLOCK_TRAPPED_CHEST: case E_BLOCK_TRIPWIRE_HOOK: + case E_BLOCK_TRIPWIRE: case E_BLOCK_WOODEN_BUTTON: case E_BLOCK_WOODEN_DOOR: case E_BLOCK_WOODEN_PRESSURE_PLATE: @@ -281,6 +299,16 @@ private: default: return false; } } + + inline static bool AreCoordsOnChunkBoundary(int a_BlockX, int a_BlockY, int a_BlockZ) + { + return ( // Are we on a chunk boundary? +- 2 because of LinkedPowered blocks + ((a_BlockX % cChunkDef::Width) <= 1) || + ((a_BlockX % cChunkDef::Width) >= 14) || + ((a_BlockZ % cChunkDef::Width) <= 1) || + ((a_BlockZ % cChunkDef::Width) >= 14) + ); + } }; diff --git a/src/Simulator/VaporizeFluidSimulator.cpp b/src/Simulator/VaporizeFluidSimulator.cpp index 4206c64d1..191770273 100644 --- a/src/Simulator/VaporizeFluidSimulator.cpp +++ b/src/Simulator/VaporizeFluidSimulator.cpp @@ -35,7 +35,7 @@ void cVaporizeFluidSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, ) { a_Chunk->SetBlock(RelX, a_BlockY, RelZ, E_BLOCK_AIR, 0); - a_Chunk->BroadcastSoundEffect("random.fizz", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 1.0f, 0.6f); + a_Chunk->BroadcastSoundEffect("random.fizz", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 1.0f, 0.6f); } } diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 728692f2a..e220960ff 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -60,12 +60,35 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickA ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); return; } - case caDblClick: { DblClicked(a_Player, a_SlotNum); return; } + case caMiddleClick: + { + MiddleClicked(a_Player, a_SlotNum); + return; + } + case caDropKey: + case caCtrlDropKey: + { + DropClicked(a_Player, a_SlotNum, (a_ClickAction == caCtrlDropKey)); + return; + } + case caNumber1: + case caNumber2: + case caNumber3: + case caNumber4: + case caNumber5: + case caNumber6: + case caNumber7: + case caNumber8: + case caNumber9: + { + NumberClicked(a_Player, a_SlotNum, a_ClickAction); + return; + } default: { break; @@ -226,6 +249,77 @@ void cSlotArea::DblClicked(cPlayer & a_Player, int a_SlotNum) +void cSlotArea::MiddleClicked(cPlayer & a_Player, int a_SlotNum) +{ + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + cItem & DraggingItem = a_Player.GetDraggingItem(); + + if (!a_Player.IsGameModeCreative() || Slot.IsEmpty() || !DraggingItem.IsEmpty()) + { + return; + } + + DraggingItem = Slot; + DraggingItem.m_ItemCount = DraggingItem.GetMaxStackSize(); +} + + + + + +void cSlotArea::DropClicked(cPlayer & a_Player, int a_SlotNum, bool a_DropStack) +{ + cItem Slot(*GetSlot(a_SlotNum, a_Player)); + if (Slot.IsEmpty()) + { + return; + } + + cItem ItemToDrop = Slot.CopyOne(); + if (a_DropStack) + { + ItemToDrop.m_ItemCount = Slot.m_ItemCount; + } + + Slot.m_ItemCount -= ItemToDrop.m_ItemCount; + if (Slot.m_ItemCount <= 0) + { + Slot.Empty(); + } + SetSlot(a_SlotNum, a_Player, Slot); + + a_Player.TossPickup(ItemToDrop); +} + + + + + +void cSlotArea::NumberClicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction) +{ + if ((a_ClickAction < caNumber1) || (a_ClickAction > caNumber9)) + { + return; + } + + int HotbarSlot = (int)a_ClickAction - (int)caNumber1; + cItem ItemInHotbar(a_Player.GetInventory().GetHotbarSlot(HotbarSlot)); + cItem ItemInSlot(*GetSlot(a_SlotNum, a_Player)); + + // The items are equal. Do nothing. + if (ItemInHotbar.IsEqual(ItemInSlot)) + { + return; + } + + a_Player.GetInventory().SetHotbarSlot(HotbarSlot, ItemInSlot); + SetSlot(a_SlotNum, a_Player, ItemInHotbar); +} + + + + + void cSlotArea::OnPlayerAdded(cPlayer & a_Player) { UNUSED(a_Player); @@ -410,6 +504,12 @@ cSlotAreaCrafting::cSlotAreaCrafting(int a_GridSize, cWindow & a_ParentWindow) : void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) { + if (a_ClickAction == caMiddleClick) + { + MiddleClicked(a_Player, a_SlotNum); + return; + } + // Override for craft result slot if (a_SlotNum == 0) { @@ -417,12 +517,17 @@ void cSlotAreaCrafting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction { ShiftClickedResult(a_Player); } + else if ((a_ClickAction == caDropKey) || (a_ClickAction == caCtrlDropKey)) + { + DropClickedResult(a_Player); + } else { ClickedResult(a_Player); } return; } + super::Clicked(a_Player, a_SlotNum, a_ClickAction, a_ClickedItem); UpdateRecipe(a_Player); } @@ -468,6 +573,20 @@ void cSlotAreaCrafting::OnPlayerRemoved(cPlayer & a_Player) +void cSlotAreaCrafting::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) +{ + // Update the recipe after setting the slot, if the slot is not the result slot: + super::SetSlot(a_SlotNum, a_Player, a_Item); + if (a_SlotNum != 0) + { + UpdateRecipe(a_Player); + } +} + + + + + void cSlotAreaCrafting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) { UNUSED(a_ItemStack); @@ -545,16 +664,20 @@ void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player) // Distribute the result, this time for real: ResultCopy = Result; m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true); - + // Remove the ingredients from the crafting grid and update the recipe: cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize); Recipe.ConsumeIngredients(Grid); Grid.CopyToItems(PlayerSlots); UpdateRecipe(a_Player); + + // Broadcast the window, we sometimes move items to different locations than Vanilla, causing needless desyncs: + m_ParentWindow.BroadcastWholeWindow(); + + // If the recipe has changed, bail out: if (!Recipe.GetResult().IsEqual(Result)) { - // The recipe has changed, bail out return; } } @@ -564,6 +687,27 @@ void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player) +void cSlotAreaCrafting::DropClickedResult(cPlayer & a_Player) +{ + // Get the current recipe: + cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); + const cItem & Result = Recipe.GetResult(); + + cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1; + cCraftingGrid Grid(PlayerSlots, m_GridSize, m_GridSize); + + a_Player.TossPickup(Result); + Recipe.ConsumeIngredients(Grid); + Grid.CopyToItems(PlayerSlots); + + HandleCraftItem(Result, a_Player); + UpdateRecipe(a_Player); +} + + + + + void cSlotAreaCrafting::UpdateRecipe(cPlayer & a_Player) { cCraftingGrid Grid(GetPlayerSlots(a_Player) + 1, m_GridSize, m_GridSize); @@ -651,15 +795,37 @@ void cSlotAreaAnvil::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_C return; } - if (a_ClickAction == caDblClick) - { - return; - } - - if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick)) + switch (a_ClickAction) { - ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); - return; + case caDblClick: + { + return; + } + case caShiftLeftClick: + case caShiftRightClick: + { + ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); + return; + } + case caMiddleClick: + { + MiddleClicked(a_Player, a_SlotNum); + return; + } + case caDropKey: + case caCtrlDropKey: + { + if (CanTakeResultItem(a_Player)) + { + DropClicked(a_Player, a_SlotNum, true); + OnTakeResult(a_Player); + } + return; + } + default: + { + break; + } } cItem Slot(*GetSlot(a_SlotNum, a_Player)); @@ -1057,12 +1223,16 @@ void cSlotAreaEnchanting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickActio ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); return; } - case caDblClick: { DblClicked(a_Player, a_SlotNum); return; } + case caMiddleClick: + { + MiddleClicked(a_Player, a_SlotNum); + return; + } default: { break; @@ -1341,8 +1511,7 @@ cSlotAreaEnderChest::cSlotAreaEnderChest(cEnderChestEntity * a_EnderChest, cWind const cItem * cSlotAreaEnderChest::GetSlot(int a_SlotNum, cPlayer & a_Player) const { - // a_SlotNum ranges from 0 to 26, use that to index the chest entity's inventory directly: - return &(m_EnderChest->GetSlot(a_SlotNum)); + return &(a_Player.GetEnderChestContents().GetSlot(a_SlotNum)); } @@ -1351,7 +1520,7 @@ const cItem * cSlotAreaEnderChest::GetSlot(int a_SlotNum, cPlayer & a_Player) co void cSlotAreaEnderChest::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) { - m_EnderChest->SetSlot(a_SlotNum, a_Item); + a_Player.GetEnderChestContents().SetSlot(a_SlotNum, a_Item); } @@ -1408,11 +1577,32 @@ void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a bAsync = true; } - if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick)) + switch (a_ClickAction) { - HandleSmeltItem(Slot, a_Player); - ShiftClicked(a_Player, a_SlotNum, Slot); - return; + case caShiftLeftClick: + case caShiftRightClick: + { + HandleSmeltItem(Slot, a_Player); + ShiftClicked(a_Player, a_SlotNum, Slot); + return; + } + case caMiddleClick: + { + MiddleClicked(a_Player, a_SlotNum); + return; + } + case caDropKey: + case caCtrlDropKey: + { + DropClicked(a_Player, a_SlotNum, (a_SlotNum == caCtrlDropKey)); + Slot.m_ItemCount = Slot.m_ItemCount - GetSlot(a_SlotNum, a_Player)->m_ItemCount; + HandleSmeltItem(Slot, a_Player); + return; + } + default: + { + break; + } } cItem & DraggingItem = a_Player.GetDraggingItem(); @@ -1590,6 +1780,12 @@ void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAc { if (a_Player.IsGameModeCreative() && (m_ParentWindow.GetWindowType() == cWindow::wtInventory)) { + if ((a_ClickAction == caDropKey) || (a_ClickAction == caCtrlDropKey)) + { + DropClicked(a_Player, a_SlotNum, (a_ClickAction == caCtrlDropKey)); + return; + } + // Creative inventory must treat a_ClickedItem as a DraggedItem instead, replacing the inventory slot with it SetSlot(a_SlotNum, a_Player, a_ClickedItem); return; @@ -1677,16 +1873,28 @@ void cSlotAreaArmor::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_C return; } - if ((a_ClickAction == caShiftLeftClick) || (a_ClickAction == caShiftRightClick)) - { - ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); - return; - } - - // Armors haven't a dbl click - if (a_ClickAction == caDblClick) + switch (a_ClickAction) { - return; + case caDblClick: + { + // Armors haven't a dbl click + return; + } + case caShiftLeftClick: + case caShiftRightClick: + { + ShiftClicked(a_Player, a_SlotNum, a_ClickedItem); + return; + } + case caMiddleClick: + { + MiddleClicked(a_Player, a_SlotNum); + return; + } + default: + { + break; + } } cItem Slot(*GetSlot(a_SlotNum, a_Player)); diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index b4b693cf6..3dc5c3849 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -46,10 +46,19 @@ public: /// Called from Clicked when the action is a shiftclick (left or right) virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem); - + /// Called from Clicked when the action is a caDblClick virtual void DblClicked(cPlayer & a_Player, int a_SlotNum); - + + /** Called from Clicked when the action is a middleclick */ + virtual void MiddleClicked(cPlayer & a_Player, int a_SlotNum); + + /** Called from Clicked when the action is a drop click. */ + virtual void DropClicked(cPlayer & a_Player, int a_SlotNum, bool a_DropStack); + + /** Called from Clicked when the action is a number click. */ + virtual void NumberClicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction); + /// Called when a new player opens the same parent window. The window already tracks the player. CS-locked. virtual void OnPlayerAdded(cPlayer & a_Player); @@ -232,10 +241,12 @@ public: virtual void Clicked (cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; virtual void DblClicked (cPlayer & a_Player, int a_SlotNum); virtual void OnPlayerRemoved(cPlayer & a_Player) override; + virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; // Distributing items into this area is completely disabled virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; + protected: /// Maps player's EntityID -> current recipe; not a std::map because cCraftingGrid needs proper constructor params typedef std::list<std::pair<int, cCraftingRecipe> > cRecipeMap; @@ -248,7 +259,10 @@ protected: /// Handles a shift-click in the result slot. Crafts using the current recipe until it changes or no more space for result. void ShiftClickedResult(cPlayer & a_Player); - + + /** Handles a drop-click in the result slot. */ + void DropClickedResult(cPlayer & a_Player); + /// Updates the current recipe and result slot based on the ingredients currently in the crafting grid of the specified player void UpdateRecipe(cPlayer & a_Player); diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp index 98a9a0cec..19db01b7a 100644 --- a/src/UI/Window.cpp +++ b/src/UI/Window.cpp @@ -145,8 +145,7 @@ void cWindow::GetSlots(cPlayer & a_Player, cItems & a_Slots) const { int NumSlots = (*itr)->GetNumSlots(); for (int i = 0; i < NumSlots; i++) - { - + { const cItem * Item = (*itr)->GetSlot(i, a_Player); if (Item == NULL) { @@ -170,7 +169,7 @@ void cWindow::Clicked( const cItem & a_ClickedItem ) { - cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); + cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager(); if (a_WindowID != m_WindowID) { LOGWARNING("%s: Wrong window ID (exp %d, got %d) received from \"%s\"; ignoring click.", __FUNCTION__, m_WindowID, a_WindowID, a_Player.GetName().c_str()); @@ -179,6 +178,7 @@ void cWindow::Clicked( switch (a_ClickAction) { + case caLeftClickOutside: case caRightClickOutside: { if (PlgMgr->CallHookPlayerTossingItem(a_Player)) @@ -191,25 +191,16 @@ void cWindow::Clicked( a_Player.TossPickup(a_ClickedItem); } - // Toss one of the dragged items: - a_Player.TossHeldItem(); - return; - } - case caLeftClickOutside: - { - if (PlgMgr->CallHookPlayerTossingItem(a_Player)) + if (a_ClickAction == caLeftClickOutside) { - // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch) - return; + // Toss all dragged items: + a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount); } - - if (a_Player.IsGameModeCreative()) + else { - a_Player.TossPickup(a_ClickedItem); + // Toss one of the dragged items: + a_Player.TossHeldItem(); } - - // Toss all dragged items: - a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount); return; } case caLeftClickOutsideHoldNothing: @@ -914,21 +905,23 @@ void cEnchantingWindow::GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ) // cChestWindow: cChestWindow::cChestWindow(cChestEntity * a_Chest) : - cWindow(wtChest, "Chest"), + cWindow(wtChest, (a_Chest->GetBlockType() == E_BLOCK_CHEST) ? "Chest" : "Trapped Chest"), m_World(a_Chest->GetWorld()), m_BlockX(a_Chest->GetPosX()), m_BlockY(a_Chest->GetPosY()), - m_BlockZ(a_Chest->GetPosZ()) + m_BlockZ(a_Chest->GetPosZ()), + m_PrimaryChest(a_Chest), + m_SecondaryChest(NULL) { m_SlotAreas.push_back(new cSlotAreaChest(a_Chest, *this)); m_SlotAreas.push_back(new cSlotAreaInventory(*this)); m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); // Play the opening sound: - m_World->BroadcastSoundEffect("random.chestopen", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); + m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); // Send out the chest-open packet: - m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST); + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_Chest->GetBlockType()); } @@ -936,11 +929,13 @@ cChestWindow::cChestWindow(cChestEntity * a_Chest) : cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest) : - cWindow(wtChest, "Double Chest"), + cWindow(wtChest, (a_PrimaryChest->GetBlockType() == E_BLOCK_CHEST) ? "Double Chest" : "Double Trapped Chest"), m_World(a_PrimaryChest->GetWorld()), m_BlockX(a_PrimaryChest->GetPosX()), m_BlockY(a_PrimaryChest->GetPosY()), - m_BlockZ(a_PrimaryChest->GetPosZ()) + m_BlockZ(a_PrimaryChest->GetPosZ()), + m_PrimaryChest(a_PrimaryChest), + m_SecondaryChest(a_SecondaryChest) { m_SlotAreas.push_back(new cSlotAreaDoubleChest(a_PrimaryChest, a_SecondaryChest, *this)); m_SlotAreas.push_back(new cSlotAreaInventory(*this)); @@ -949,10 +944,55 @@ cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_Secon m_ShouldDistributeToHotbarFirst = false; // Play the opening sound: - m_World->BroadcastSoundEffect("random.chestopen", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); + m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); // Send out the chest-open packet: - m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_CHEST); + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_PrimaryChest->GetBlockType()); +} + + + + + +void cChestWindow::OpenedByPlayer(cPlayer & a_Player) +{ + int ChunkX, ChunkZ; + + m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() + 1); + cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ); + m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + + if (m_SecondaryChest != NULL) + { + m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() + 1); + cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ); + m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + } + + cWindow::OpenedByPlayer(a_Player); +} + + + + + +bool cChestWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) +{ + int ChunkX, ChunkZ; + + m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() - 1); + cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ); + m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + + if (m_SecondaryChest != NULL) + { + m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() - 1); + cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ); + m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + } + + cWindow::ClosedByPlayer(a_Player, a_CanRefuse); + return true; } @@ -962,9 +1002,9 @@ cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_Secon cChestWindow::~cChestWindow() { // Send out the chest-close packet: - m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_CHEST); + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, m_PrimaryChest->GetBlockType()); - m_World->BroadcastSoundEffect("random.chestclosed", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); + m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); } @@ -975,7 +1015,7 @@ cChestWindow::~cChestWindow() // cDropSpenserWindow: cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser) : - cWindow(wtDropSpenser, "Dropspenser") + cWindow(wtDropSpenser, (a_DropSpenser->GetBlockType() == E_BLOCK_DISPENSER) ? "Dispenser" : "Dropper") { m_ShouldDistributeToHotbarFirst = false; m_SlotAreas.push_back(new cSlotAreaItemGrid(a_DropSpenser->GetContents(), *this)); @@ -1002,7 +1042,7 @@ cEnderChestWindow::cEnderChestWindow(cEnderChestEntity * a_EnderChest) : m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); // Play the opening sound: - m_World->BroadcastSoundEffect("random.chestopen", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); + m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); // Send out the chest-open packet: m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_ENDER_CHEST); @@ -1017,7 +1057,8 @@ cEnderChestWindow::~cEnderChestWindow() // Send out the chest-close packet: m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_ENDER_CHEST); - m_World->BroadcastSoundEffect("random.chestclosed", m_BlockX * 8, m_BlockY * 8, m_BlockZ * 8, 1, 1); + // Play the closing sound + m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); } diff --git a/src/UI/Window.h b/src/UI/Window.h index 542dccb88..b170a9d78 100644 --- a/src/UI/Window.h +++ b/src/UI/Window.h @@ -114,7 +114,7 @@ public: const cItem & a_ClickedItem ); - void OpenedByPlayer(cPlayer & a_Player); + virtual void OpenedByPlayer(cPlayer & a_Player); /// Called when a player closes this window; notifies all slot areas. Returns true if close accepted virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse); @@ -327,10 +327,15 @@ public: cChestWindow(cChestEntity * a_Chest); cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest); ~cChestWindow(); + + virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; + virtual void OpenedByPlayer(cPlayer & a_Player) override; protected: cWorld * m_World; int m_BlockX, m_BlockY, m_BlockZ; // Position of the chest, for the window-close packet + cChestEntity * m_PrimaryChest; + cChestEntity * m_SecondaryChest; } ; diff --git a/src/Vector3.h b/src/Vector3.h index 5faac1457..c175bf135 100644 --- a/src/Vector3.h +++ b/src/Vector3.h @@ -134,6 +134,16 @@ public: z += a_Diff.z; } + /** Runs each value of the vector through std::floor() */ + inline Vector3<int> Floor(void) const + { + return Vector3<int>( + (int)floor(x), + (int)floor(y), + (int)floor(z) + ); + } + // tolua_end inline bool operator != (const Vector3<T> & a_Rhs) const @@ -146,6 +156,16 @@ public: return Equals(a_Rhs); } + inline bool operator > (const Vector3<T> & a_Rhs) const + { + return (SqrLength() > a_Rhs.SqrLength()); + } + + inline bool operator < (const Vector3<T> & a_Rhs) const + { + return (SqrLength() < a_Rhs.SqrLength()); + } + inline void operator += (const Vector3<T> & a_Rhs) { x += a_Rhs.x; @@ -288,6 +308,7 @@ protected: { return (a_Value < 0) ? -a_Value : a_Value; } + }; // tolua_end @@ -295,6 +316,15 @@ protected: +template <> inline Vector3<int> Vector3<int>::Floor(void) const +{ + return *this; +} + + + + + template <typename T> const double Vector3<T>::EPS = 0.000001; diff --git a/src/VoronoiMap.cpp b/src/VoronoiMap.cpp index 9c3ca7e65..5efd09c01 100644 --- a/src/VoronoiMap.cpp +++ b/src/VoronoiMap.cpp @@ -14,7 +14,9 @@ cVoronoiMap::cVoronoiMap(int a_Seed, int a_CellSize) : m_Noise1(a_Seed + 1), m_Noise2(a_Seed + 2), m_Noise3(a_Seed + 3), - m_CellSize(a_CellSize) + m_CellSize(a_CellSize), + m_CurrentCellX(9999999), // Cell coords that are definitely out of the range for normal generator, so that the first query will overwrite them + m_CurrentCellZ(9999999) { } diff --git a/src/World.cpp b/src/World.cpp index 5b067b386..ba8add8f0 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -389,8 +389,8 @@ void cWorld::InitializeSpawn(void) IniFile.WriteFile(m_IniFileName); } - int ChunkX = 0, ChunkY = 0, ChunkZ = 0; - BlockToChunk((int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ); + int ChunkX = 0, ChunkZ = 0; + cChunkDef::BlockToChunk((int)m_SpawnX, (int)m_SpawnZ, ChunkX, ChunkZ); // For the debugging builds, don't make the server build too much world upon start: #if defined(_DEBUG) || defined(ANDROID_NDK) @@ -1088,7 +1088,7 @@ void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_Blo Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ); cVector3iArray BlocksAffected; m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected); - BroadcastSoundEffect("random.explode", (int)floor(a_BlockX * 8), (int)floor(a_BlockY * 8), (int)floor(a_BlockZ * 8), 1.0f, 0.6f); + BroadcastSoundEffect("random.explode", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 1.0f, 0.6f); { cCSLock Lock(m_CSPlayers); for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) @@ -1877,9 +1877,9 @@ void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer -void cWorld::BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude) +void cWorld::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude) { - m_ChunkMap->BroadcastCollectPickup(a_Pickup, a_Player, a_Exclude); + m_ChunkMap->BroadcastCollectEntity(a_Entity, a_Player, a_Exclude); } @@ -2074,9 +2074,9 @@ void cWorld::BroadcastDisplayObjective(const AString & a_Objective, cScoreboard: -void cWorld::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) +void cWorld::BroadcastSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude) { - m_ChunkMap->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude); + m_ChunkMap->BroadcastSoundEffect(a_SoundName, a_X, a_Y, a_Z, a_Volume, a_Pitch, a_Exclude); } @@ -2182,9 +2182,18 @@ void cWorld::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHa -void cWorld::MarkChunkDirty (int a_ChunkX, int a_ChunkZ) +void cWorld::MarkRedstoneDirty(int a_ChunkX, int a_ChunkZ) { - m_ChunkMap->MarkChunkDirty (a_ChunkX, a_ChunkZ); + m_ChunkMap->MarkRedstoneDirty(a_ChunkX, a_ChunkZ); +} + + + + + +void cWorld::MarkChunkDirty(int a_ChunkX, int a_ChunkZ, bool a_MarkRedstoneDirty) +{ + m_ChunkMap->MarkChunkDirty(a_ChunkX, a_ChunkZ, a_MarkRedstoneDirty); } @@ -2767,10 +2776,8 @@ bool cWorld::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunk void cWorld::SaveAllChunks(void) { - LOGINFO("Saving all chunks..."); m_LastSave = m_WorldAge; m_ChunkMap->SaveAllChunks(); - m_Storage.QueueSavedMessage(); } @@ -2991,7 +2998,7 @@ int cWorld::SpawnMobFinalize(cMonster * a_Monster) -int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem & a_Item, const Vector3d * a_Speed) +int cWorld::CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem * a_Item, const Vector3d * a_Speed) { cProjectileEntity * Projectile = cProjectileEntity::Create(a_Kind, a_Creator, a_PosX, a_PosY, a_PosZ, a_Item, a_Speed); if (Projectile == NULL) @@ -3035,6 +3042,15 @@ void cWorld::TabCompleteUserName(const AString & a_Text, AStringVector & a_Resul +void cWorld::SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked) +{ + m_ChunkMap->SetChunkAlwaysTicked(a_ChunkX, a_ChunkZ, a_AlwaysTicked); +} + + + + + cRedstoneSimulator * cWorld::InitializeRedstoneSimulator(cIniFile & a_IniFile) { AString SimulatorName = a_IniFile.GetValueSet("Physics", "RedstoneSimulator", ""); diff --git a/src/World.h b/src/World.h index 2b7f78760..5bb0e640f 100644 --- a/src/World.h +++ b/src/World.h @@ -206,7 +206,7 @@ public: // tolua_end void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); - void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); + void BroadcastCollectEntity (const cEntity & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); void BroadcastEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude = NULL); void BroadcastEntityEquipment (const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude = NULL); @@ -224,7 +224,7 @@ public: void BroadcastScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode); void BroadcastScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode); void BroadcastDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display); - void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // tolua_export a_Src coords are Block * 8 + void BroadcastSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // tolua_export void BroadcastSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); // tolua_export void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); void BroadcastTeleportEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = NULL); @@ -241,7 +241,8 @@ public: /** If there is a block entity at the specified coords, sends it to the client specified */ void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); - void MarkChunkDirty (int a_ChunkX, int a_ChunkZ); + void MarkRedstoneDirty(int a_ChunkX, int a_ChunkZ); + void MarkChunkDirty (int a_ChunkX, int a_ChunkZ, bool a_MarkRedstoneDirty = false); void MarkChunkSaving(int a_ChunkX, int a_ChunkZ); void MarkChunkSaved (int a_ChunkX, int a_ChunkZ); @@ -485,7 +486,7 @@ public: double GetSpawnZ(void) const { return m_SpawnZ; } /** Wakes up the simulators for the specified block */ - void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ); + virtual void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ) override; /** Wakes up the simulators for the specified area of blocks */ void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ); @@ -634,18 +635,6 @@ public: // tolua_end - inline static void BlockToChunk( int a_X, int a_Y, int a_Z, int & a_ChunkX, int & a_ChunkY, int & a_ChunkZ ) - { - // TODO: Use floor() instead of weird if statements - // Also fix Y - (void)a_Y; // not unused anymore - a_ChunkX = a_X/cChunkDef::Width; - if(a_X < 0 && a_X % cChunkDef::Width != 0) a_ChunkX--; - a_ChunkY = 0; - a_ChunkZ = a_Z/cChunkDef::Width; - if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--; - } - /** Saves all chunks immediately. Dangerous interface, may deadlock, use QueueSaveAllChunks() instead */ void SaveAllChunks(void); @@ -762,7 +751,7 @@ public: /** Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise Item parameter used currently for Fireworks to correctly set entity metadata based on item metadata */ - int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem & a_Item, const Vector3d * a_Speed = NULL); // tolua_export + int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const cItem * a_Item, const Vector3d * a_Speed = NULL); // tolua_export /** Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! */ int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); } @@ -772,6 +761,13 @@ public: /** Get the current darkness level based on the time */ NIBBLETYPE GetSkyDarkness() { return m_SkyDarkness; } + + /** Increments (a_AlwaysTicked == true) or decrements (false) the m_AlwaysTicked counter for the specified chunk. + If the m_AlwaysTicked counter is greater than zero, the chunk is ticked in the tick-thread regardless of + whether it has any clients or not. + This function allows nesting and task-concurrency (multiple separate tasks can request ticking and as long + as at least one requests is active the chunk will be ticked). */ + void SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked = true); // tolua_export private: diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 8294d4a00..3ccbceb7a 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -175,10 +175,10 @@ void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char -void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity) +void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity, BLOCKTYPE a_ChestType) { m_Writer.BeginCompound(""); - AddBasicTileEntity(a_Entity, "Chest"); + AddBasicTileEntity(a_Entity, (a_ChestType == E_BLOCK_CHEST) ? "Chest" : "TrappedChest"); m_Writer.BeginList("Items", TAG_Compound); AddItemGrid(a_Entity->GetContents()); m_Writer.EndList(); @@ -345,6 +345,7 @@ void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_C m_Writer.AddDouble("", a_Entity->GetYaw()); m_Writer.AddDouble("", a_Entity->GetPitch()); m_Writer.EndList(); + m_Writer.AddShort("Health", a_Entity->GetHealth()); } @@ -575,7 +576,6 @@ void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup) m_Writer.BeginCompound(""); AddBasicEntity(a_Pickup, "Item"); AddItem(a_Pickup->GetItem(), -1, "Item"); - m_Writer.AddShort("Health", (Int16)(unsigned char)a_Pickup->GetHealth()); m_Writer.AddShort("Age", (Int16)a_Pickup->GetAge()); m_Writer.EndCompound(); } @@ -589,20 +589,19 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile) m_Writer.BeginCompound(""); AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName()); Vector3d Pos = a_Projectile->GetPosition(); - m_Writer.AddShort("xTile", (Int16)floor(Pos.x)); - m_Writer.AddShort("yTile", (Int16)floor(Pos.y)); - m_Writer.AddShort("zTile", (Int16)floor(Pos.z)); - m_Writer.AddShort("inTile", 0); // TODO: Query the block type - m_Writer.AddShort("shake", 0); // TODO: Any shake? - m_Writer.AddByte ("inGround", a_Projectile->IsInGround() ? 1 : 0); + m_Writer.AddByte("inGround", a_Projectile->IsInGround() ? 1 : 0); switch (a_Projectile->GetProjectileKind()) { case cProjectileEntity::pkArrow: { - m_Writer.AddByte("inData", 0); // TODO: Query the block meta (is it needed?) - m_Writer.AddByte("pickup", ((cArrowEntity *)a_Projectile)->GetPickupState()); - m_Writer.AddDouble("damage", ((cArrowEntity *)a_Projectile)->GetDamageCoeff()); + cArrowEntity * Arrow = (cArrowEntity *)a_Projectile; + + m_Writer.AddInt("xTile", (Int16)Arrow->GetBlockHit().x); + m_Writer.AddInt("yTile", (Int16)Arrow->GetBlockHit().y); + m_Writer.AddInt("zTile", (Int16)Arrow->GetBlockHit().z); + m_Writer.AddByte("pickup", Arrow->GetPickupState()); + m_Writer.AddDouble("damage", Arrow->GetDamageCoeff()); break; } case cProjectileEntity::pkGhastFireball: @@ -620,14 +619,11 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile) { ASSERT(!"Unsaved projectile entity!"); } - } // switch (ProjectileKind) - cEntity * Creator = a_Projectile->GetCreator(); - if (Creator != NULL) + } // switch (ProjectileKind) + + if (!a_Projectile->GetCreatorName().empty()) { - if (Creator->GetEntityType() == cEntity::etPlayer) - { - m_Writer.AddString("ownerName", ((cPlayer *)Creator)->GetName()); - } + m_Writer.AddString("ownerName", a_Projectile->GetCreatorName()); } m_Writer.EndCompound(); } @@ -678,7 +674,6 @@ void cNBTChunkSerializer::AddExpOrbEntity(cExpOrb * a_ExpOrb) { m_Writer.BeginCompound(""); AddBasicEntity(a_ExpOrb, "XPOrb"); - m_Writer.AddShort("Health", (Int16)(unsigned char)a_ExpOrb->GetHealth()); m_Writer.AddShort("Age", (Int16)a_ExpOrb->GetAge()); m_Writer.AddShort("Value", (Int16)a_ExpOrb->GetReward()); m_Writer.EndCompound(); @@ -818,18 +813,21 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity) // Add tile-entity into NBT: switch (a_Entity->GetBlockType()) { - case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break; - case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break; - case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break; - case E_BLOCK_FLOWER_POT: AddFlowerPotEntity ((cFlowerPotEntity *) a_Entity); break; - case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break; - case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break; - case E_BLOCK_SIGN_POST: - case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break; - case E_BLOCK_HEAD: AddMobHeadEntity ((cMobHeadEntity *) a_Entity); break; - case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break; - case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break; - case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity((cCommandBlockEntity *) a_Entity); break; + case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity, a_Entity->GetBlockType()); break; + case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity((cCommandBlockEntity *)a_Entity); break; + case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break; + case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break; + case E_BLOCK_ENDER_CHEST: /* No data to be saved */ break; + case E_BLOCK_FLOWER_POT: AddFlowerPotEntity ((cFlowerPotEntity *) a_Entity); break; + case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break; + case E_BLOCK_HEAD: AddMobHeadEntity ((cMobHeadEntity *) a_Entity); break; + case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break; + case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break; + case E_BLOCK_LIT_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break; + case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break; + case E_BLOCK_SIGN_POST: AddSignEntity ((cSignEntity *) a_Entity); break; + case E_BLOCK_TRAPPED_CHEST: AddChestEntity ((cChestEntity *) a_Entity, a_Entity->GetBlockType()); break; + case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break; default: { diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h index 112afc27e..f73f4ad7d 100644 --- a/src/WorldStorage/NBTChunkSerializer.h +++ b/src/WorldStorage/NBTChunkSerializer.h @@ -92,7 +92,7 @@ protected: // Block entities: void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID); - void AddChestEntity (cChestEntity * a_Entity); + void AddChestEntity (cChestEntity * a_Entity, BLOCKTYPE a_ChestType); void AddDispenserEntity(cDispenserEntity * a_Entity); void AddDropperEntity (cDropperEntity * a_Entity); void AddFurnaceEntity (cFurnaceEntity * a_Furnace); diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 0f84a0eb1..0b2a4c053 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -582,7 +582,7 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con } if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0) { - LoadChestFromNBT(a_BlockEntities, a_NBT, Child); + LoadChestFromNBT(a_BlockEntities, a_NBT, Child, E_BLOCK_CHEST); } else if (strncmp(a_NBT.GetData(sID), "Control", a_NBT.GetDataLength(sID)) == 0) { @@ -624,6 +624,10 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con { LoadDispenserFromNBT(a_BlockEntities, a_NBT, Child); } + else if (strncmp(a_NBT.GetData(sID), "TrappedChest", a_NBT.GetDataLength(sID)) == 0) + { + LoadChestFromNBT(a_BlockEntities, a_NBT, Child, E_BLOCK_TRAPPED_CHEST); + } // TODO: Other block entities } // for Child - tag children } @@ -740,7 +744,7 @@ void cWSSAnvil::LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a -void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) +void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType) { ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); int x, y, z; @@ -753,7 +757,7 @@ void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cPars { return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this } - std::auto_ptr<cChestEntity> Chest(new cChestEntity(x, y, z, m_World)); + std::auto_ptr<cChestEntity> Chest(new cChestEntity(x, y, z, m_World, a_ChestType)); LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items); a_BlockEntities.push_back(Chest.release()); } @@ -1461,13 +1465,6 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a { return; } - - // Load health: - int Health = a_NBT.FindChildByName(a_TagIdx, "Health"); - if (Health > 0) - { - Pickup->SetHealth((int) (a_NBT.GetShort(Health) & 0xFF)); - } // Load age: int Age = a_NBT.FindChildByName(a_TagIdx, "Age"); @@ -1513,13 +1510,6 @@ void cWSSAnvil::LoadExpOrbFromNBT(cEntityList & a_Entities, const cParsedNBT & a return; } - // Load Health: - int Health = a_NBT.FindChildByName(a_TagIdx, "Health"); - if (Health > 0) - { - ExpOrb->SetHealth((int) (a_NBT.GetShort(Health) & 0xFF)); - } - // Load Age: int Age = a_NBT.FindChildByName(a_TagIdx, "Age"); if (Age > 0) @@ -1656,6 +1646,15 @@ void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ Arrow->SetDamageCoeff(a_NBT.GetDouble(DamageIdx)); } + // Load block hit: + int InBlockXIdx = a_NBT.FindChildByName(a_TagIdx, "xTile"); + int InBlockYIdx = a_NBT.FindChildByName(a_TagIdx, "yTile"); + int InBlockZIdx = a_NBT.FindChildByName(a_TagIdx, "zTile"); + if ((InBlockXIdx > 0) && (InBlockYIdx > 0) && (InBlockZIdx > 0)) + { + Arrow->SetBlockHit(Vector3i(a_NBT.GetInt(InBlockXIdx), a_NBT.GetInt(InBlockYIdx), a_NBT.GetInt(InBlockZIdx))); + } + // Store the new arrow in the entities list: a_Entities.push_back(Arrow.release()); } @@ -2087,10 +2086,11 @@ void cWSSAnvil::LoadPigFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { int ColorIdx = a_NBT.FindChildByName(a_TagIdx, "Color"); - - if (ColorIdx < 0) { return; } - - int Color = (int)a_NBT.GetByte(ColorIdx); + int Color = -1; + if (ColorIdx > 0) + { + Color = (int)a_NBT.GetByte(ColorIdx); + } std::auto_ptr<cSheep> Monster(new cSheep(Color)); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) @@ -2103,6 +2103,12 @@ void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ return; } + int ShearedIdx = a_NBT.FindChildByName(a_TagIdx, "Sheared"); + if (ShearedIdx > 0) + { + Monster->SetSheared(a_NBT.GetByte(ShearedIdx) != 0); + } + a_Entities.push_back(Monster.release()); } @@ -2437,6 +2443,13 @@ bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_N } a_Entity.SetYaw(Rotation[0]); a_Entity.SetRoll(Rotation[1]); + + // Load health: + int Health = a_NBT.FindChildByName(a_TagIdx, "Health"); + if (Health > 0) + { + a_Entity.SetHealth(a_NBT.GetShort(Health)); + } return true; } @@ -2481,8 +2494,6 @@ bool cWSSAnvil::LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cP } a_Entity.SetIsInGround(IsInGround); - // TODO: Load inTile, TileCoords - return true; } diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 7542a828a..6f619fdb8 100644 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -133,7 +133,7 @@ protected: */ void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0); - void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType); void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadFlowerPotFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp index a853f6ec9..a3ba443fd 100644 --- a/src/WorldStorage/WSSCompact.cpp +++ b/src/WorldStorage/WSSCompact.cpp @@ -273,7 +273,7 @@ void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_En { for (Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr ) { - std::auto_ptr<cChestEntity> ChestEntity(new cChestEntity(0, 0, 0, a_World)); + std::auto_ptr<cChestEntity> ChestEntity(new cChestEntity(0, 0, 0, a_World, E_BLOCK_CHEST)); if (!ChestEntity->LoadFromJson(*itr)) { LOGWARNING("ERROR READING CHEST FROM JSON!" ); diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp index 6867ad5bc..d3f35384b 100644 --- a/src/WorldStorage/WorldStorage.cpp +++ b/src/WorldStorage/WorldStorage.cpp @@ -17,13 +17,6 @@ -/// If a chunk with this Y coord is de-queued, it is a signal to emit the saved-all message (cWorldStorage::QueueSavedMessage()) -#define CHUNK_Y_MESSAGE 2 - - - - - /// Example storage schema - forgets all chunks ;) class cWSSForgetful : public cWSSchema @@ -168,17 +161,6 @@ void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) -void cWorldStorage::QueueSavedMessage(void) -{ - // Pushes a special coord pair into the queue, signalizing a message instead - m_SaveQueue.EnqueueItem(cChunkCoords(0, CHUNK_Y_MESSAGE, 0)); - m_Event.Set(); -} - - - - - void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ) { m_LoadQueue.Remove(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ,true)); @@ -286,19 +268,12 @@ bool cWorldStorage::SaveOneChunk(void) { cChunkCoords ToSave(0, 0, 0); bool ShouldSave = m_SaveQueue.TryDequeueItem(ToSave); - if(ShouldSave) { - if (ToSave.m_ChunkY == CHUNK_Y_MESSAGE) - { - LOGINFO("Saved all chunks in world %s", m_World->GetName().c_str()); - return ShouldSave; - } - if (ShouldSave && m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ)) + if (ShouldSave && m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ)) + { + m_World->MarkChunkSaving(ToSave.m_ChunkX, ToSave.m_ChunkZ); + if (m_SaveSchema->SaveChunk(ToSave)) { - m_World->MarkChunkSaving(ToSave.m_ChunkX, ToSave.m_ChunkZ); - if (m_SaveSchema->SaveChunk(ToSave)) - { - m_World->MarkChunkSaved(ToSave.m_ChunkX, ToSave.m_ChunkZ); - } + m_World->MarkChunkSaved(ToSave.m_ChunkX, ToSave.m_ChunkZ); } } return ShouldSave; diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h index bb189b6c9..1204b4310 100644 --- a/src/WorldStorage/WorldStorage.h +++ b/src/WorldStorage/WorldStorage.h @@ -67,9 +67,6 @@ public: void QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate); // Queues the chunk for loading; if not loaded, the chunk will be generated if a_Generate is true void QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); - /// Signals that a message should be output to the console when all the chunks have been saved - void QueueSavedMessage(void); - /// Loads the chunk specified; returns true on success, false on failure bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); |