diff options
104 files changed, 2182 insertions, 1751 deletions
diff --git a/.travis.yml b/.travis.yml index 5260cfd17..4e8377748 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,15 @@ compiler: before_install: - if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi + # g++4.8 + - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test + - sudo apt-get update -qq + +install: + # g++4.8 + - sudo apt-get install -qq g++-4.8 + - export CXX="g++-4.8" + # Build MCServer script: ./CIbuild.sh diff --git a/CONTRIBUTORS b/CONTRIBUTORS index fffc55b2a..9a0a675e7 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -18,6 +18,7 @@ mgueydan MikeHunsinger mtilden nesco +p-mcgowan rs2k SamJBarney Sofapriester diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 72dcce5e4..ba3763724 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -2564,7 +2564,9 @@ World:ForEachEntity( if not(a_Entity:IsMob()) then return; end - local Monster = tolua.cast(a_Entity, "cMonster"); -- Get the cMonster out of cEntity, now that we know the entity represents one. + + -- Get the cMonster out of cEntity, now that we know the entity represents one. + local Monster = tolua.cast(a_Entity, "cMonster"); if (Monster:GetMobType() == mtSpider) then Monster:TeleportToCoords(Monster:GetPosX(), Monster:GetPosY() + 100, Monster:GetPosZ()); end diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core -Subproject 4183d6cfb2049d0757d811a65bd4e75ed78b9f4 +Subproject 39d980e3a3447ac23f61c7d65426b33ee6c0718 diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 3dcd4ebee..a047488b5 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -35,39 +35,15 @@ function Initialize(Plugin) -- _X: Disabled so that the normal operation doesn't interfere with anything -- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); - PM:BindCommand("/nick", "debuggers", HandleNickCmd, "- Gives you a custom name"); - PM:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities"); - PM:BindCommand("/ke", "debuggers", HandleKillEntitiesCmd, "- Kills all the loaded entities"); - PM:BindCommand("/wool", "debuggers", HandleWoolCmd, "- Sets all your armor to blue wool"); - PM:BindCommand("/testwnd", "debuggers", HandleTestWndCmd, "- Opens up a window using plugin API"); - PM:BindCommand("/gc", "debuggers", HandleGCCmd, "- Activates the Lua garbage collector"); - PM:BindCommand("/fast", "debuggers", HandleFastCmd, "- Switches between fast and normal movement speed"); - PM:BindCommand("/dash", "debuggers", HandleDashCmd, "- Switches between fast and normal sprinting speed"); - PM:BindCommand("/hunger", "debuggers", HandleHungerCmd, "- Lists the current hunger-related variables"); - PM:BindCommand("/poison", "debuggers", HandlePoisonCmd, "- Sets food-poisoning for 15 seconds"); - PM:BindCommand("/starve", "debuggers", HandleStarveCmd, "- Sets the food level to zero"); - PM:BindCommand("/fl", "debuggers", HandleFoodLevelCmd, "- Sets the food level to the given value"); - PM:BindCommand("/spidey", "debuggers", HandleSpideyCmd, "- Shoots a line of web blocks until it hits non-air"); - PM:BindCommand("/ench", "debuggers", HandleEnchCmd, "- Provides an instant dummy enchantment window"); - PM:BindCommand("/fs", "debuggers", HandleFoodStatsCmd, "- Turns regular foodstats message on or off"); - PM:BindCommand("/arr", "debuggers", HandleArrowCmd, "- Creates an arrow going away from the player"); - PM:BindCommand("/fb", "debuggers", HandleFireballCmd, "- Creates a ghast fireball as if shot by the player"); - PM:BindCommand("/xpa", "debuggers", HandleAddExperience, "- Adds 200 experience to the player"); - PM:BindCommand("/xpr", "debuggers", HandleRemoveXp, "- Remove all xp"); - PM:BindCommand("/fill", "debuggers", HandleFill, "- Fills all block entities in current chunk with junk"); - PM:BindCommand("/fr", "debuggers", HandleFurnaceRecipe, "- Shows the furnace recipe for the currently held item"); - PM:BindCommand("/ff", "debuggers", HandleFurnaceFuel, "- Shows how long the currently held item would burn in a furnace"); - PM:BindCommand("/sched", "debuggers", HandleSched, "- Schedules a simple countdown using cWorld:ScheduleTask()"); - PM:BindCommand("/cs", "debuggers", HandleChunkStay, "- Tests the ChunkStay Lua integration for the specified chunk coords"); - PM:BindCommand("/compo", "debuggers", HandleCompo, "- Tests the cCompositeChat bindings"); - PM:BindCommand("/sb", "debuggers", HandleSetBiome, "- Sets the biome around you to the specified one"); - PM:BindCommand("/wesel", "debuggers", HandleWESel, "- Expands the current WE selection by 1 block in X/Z"); - PM:BindCommand("/rmitem", "debuggers", HandleRMItem, "- Remove the specified item from the inventory."); - PM:BindCommand("/pickups", "debuggers", HandlePickups, "- Spawns random pickups around you"); - PM:BindCommand("/poof", "debuggers", HandlePoof, "- Nudges pickups close to you away from you"); - - PM:BindConsoleCommand("sched", HandleConsoleSchedule, "Tests the world scheduling"); - + -- Load the InfoReg shared library: + dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua") + + -- Bind all the commands: + RegisterPluginInfoCommands(); + + -- Bind all the console commands: + RegisterPluginInfoConsoleCommands(); + Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers) Plugin:AddWebTab("StressTest", HandleRequest_StressTest) @@ -1643,3 +1619,85 @@ end + +function HandleConsoleLoadChunk(a_Split) + -- Check params: + local numParams = #a_Split + if (numParams ~= 3) and (numParams ~= 4) then + return true, "Usage: " .. a_Split[1] .. " <ChunkX> <ChunkZ> [<WorldName>]" + end + + -- Get the chunk coords: + local chunkX = tonumber(a_Split[2]) + if (chunkX == nil) then + return true, "Not a number: '" .. a_Split[2] .. "'" + end + local chunkZ = tonumber(a_Split[3]) + if (chunkZ == nil) then + return true, "Not a number: '" .. a_Split[3] .. "'" + end + + -- Get the world: + local world + if (a_Split[4] == nil) then + world = cRoot:Get():GetDefaultWorld() + else + world = cRoot:Get():GetWorld(a_Split[4]) + if (world == nil) then + return true, "There's no world named '" .. a_Split[4] .. "'." + end + end + + -- Queue a ChunkStay for the chunk, log a message when the chunk is loaded: + world:ChunkStay({{chunkX, chunkZ}}, nil, + function() + LOG("Chunk [" .. chunkX .. ", " .. chunkZ .. "] is loaded") + end + ) + return true +end + + + + + +function HandleConsolePrepareChunk(a_Split) + -- Check params: + local numParams = #a_Split + if (numParams ~= 3) and (numParams ~= 4) then + return true, "Usage: " .. a_Split[1] .. " <ChunkX> <ChunkZ> [<WorldName>]" + end + + -- Get the chunk coords: + local chunkX = tonumber(a_Split[2]) + if (chunkX == nil) then + return true, "Not a number: '" .. a_Split[2] .. "'" + end + local chunkZ = tonumber(a_Split[3]) + if (chunkZ == nil) then + return true, "Not a number: '" .. a_Split[3] .. "'" + end + + -- Get the world: + local world + if (a_Split[4] == nil) then + world = cRoot:Get():GetDefaultWorld() + else + world = cRoot:Get():GetWorld(a_Split[4]) + if (world == nil) then + return true, "There's no world named '" .. a_Split[4] .. "'." + end + end + + -- Queue the chunk for preparing, log a message when prepared: + world:PrepareChunk(chunkX, chunkZ, + function(a_CBChunkX, a_CBChunkZ) + LOG("Chunk [" .. chunkX .. ", " .. chunkZ .. "] has been prepared") + end + ) + return true +end + + + + diff --git a/MCServer/Plugins/Debuggers/Info.lua b/MCServer/Plugins/Debuggers/Info.lua new file mode 100644 index 000000000..b96ef3de5 --- /dev/null +++ b/MCServer/Plugins/Debuggers/Info.lua @@ -0,0 +1,223 @@ + +-- Info.lua + +-- Implements the g_PluginInfo standard plugin description + + + + + +g_PluginInfo = +{ + Name = "Debuggers", + Version = "14", + Date = "2014-12-11", + Description = [[Contains code for testing and debugging the server. Should not be enabled on a production server!]], + + Commands = + { + ["/arr"] = + { + Permission = "debuggers", + Handler = HandleArrowCmd, + HelpString = "Creates an arrow going away from the player" + }, + ["/compo"] = + { + Permission = "debuggers", + Handler = HandleCompo, + HelpString = "Tests the cCompositeChat bindings" + }, + ["/cs"] = + { + Permission = "debuggers", + Handler = HandleChunkStay, + HelpString = "Tests the ChunkStay Lua integration for the specified chunk coords" + }, + ["/dash"] = + { + Permission = "debuggers", + Handler = HandleDashCmd, + HelpString = "Switches between fast and normal sprinting speed" + }, + ["/ench"] = + { + Permission = "debuggers", + Handler = HandleEnchCmd, + HelpString = "Provides an instant dummy enchantment window" + }, + ["/fast"] = + { + Permission = "debuggers", + Handler = HandleFastCmd, + HelpString = "Switches between fast and normal movement speed" + }, + ["/fb"] = + { + Permission = "debuggers", + Handler = HandleFireballCmd, + HelpString = "Creates a ghast fireball as if shot by the player" + }, + ["/ff"] = + { + Permission = "debuggers", + Handler = HandleFurnaceFuel, + HelpString = "Shows how long the currently held item would burn in a furnace" + }, + ["/fill"] = + { + Permission = "debuggers", + Handler = HandleFill, + HelpString = "Fills all block entities in current chunk with junk" + }, + ["/fl"] = + { + Permission = "debuggers", + Handler = HandleFoodLevelCmd, + HelpString = "Sets the food level to the given value" + }, + ["/fr"] = + { + Permission = "debuggers", + Handler = HandleFurnaceRecipe, + HelpString = "Shows the furnace recipe for the currently held item" + }, + ["/fs"] = + { + Permission = "debuggers", + Handler = HandleFoodStatsCmd, + HelpString = "Turns regular foodstats message on or off" + }, + ["/gc"] = + { + Permission = "debuggers", + Handler = HandleGCCmd, + HelpString = "Activates the Lua garbage collector" + }, + ["/hunger"] = + { + Permission = "debuggers", + Handler = HandleHungerCmd, + HelpString = "Lists the current hunger-related variables" + }, + ["/ke"] = + { + Permission = "debuggers", + Handler = HandleKillEntitiesCmd, + HelpString = "Kills all the loaded entities" + }, + ["/le"] = + { + Permission = "debuggers", + Handler = HandleListEntitiesCmd, + HelpString = "Shows a list of all the loaded entities" + }, + ["/nick"] = + { + Permission = "debuggers", + Handler = HandleNickCmd, + HelpString = "Gives you a custom name", + }, + ["/pickups"] = + { + Permission = "debuggers", + Handler = HandlePickups, + HelpString = "Spawns random pickups around you" + }, + ["/poison"] = + { + Permission = "debuggers", + Handler = HandlePoisonCmd, + HelpString = "Sets food-poisoning for 15 seconds" + }, + ["/poof"] = + { + Permission = "debuggers", + Handler = HandlePoof, + HelpString = "Nudges pickups close to you away from you" + }, + ["/rmitem"] = + { + Permission = "debuggers", + Handler = HandleRMItem, + HelpString = "Remove the specified item from the inventory." + }, + ["/sb"] = + { + Permission = "debuggers", + Handler = HandleSetBiome, + HelpString = "Sets the biome around you to the specified one" + }, + ["/sched"] = + { + Permission = "debuggers", + Handler = HandleSched, + HelpString = "Schedules a simple countdown using cWorld:ScheduleTask()" + }, + ["/spidey"] = + { + Permission = "debuggers", + Handler = HandleSpideyCmd, + HelpString = "Shoots a line of web blocks until it hits non-air" + }, + ["/starve"] = + { + Permission = "debuggers", + Handler = HandleStarveCmd, + HelpString = "Sets the food level to zero" + }, + ["/testwnd"] = + { + Permission = "debuggers", + Handler = HandleTestWndCmd, + HelpString = "Opens up a window using plugin API" + }, + ["/wesel"] = + { + Permission = "debuggers", + Handler = HandleWESel, + HelpString = "Expands the current WE selection by 1 block in X/Z" + }, + ["/wool"] = + { + Permission = "debuggers", + Handler = HandleWoolCmd, + HelpString = "Sets all your armor to blue wool" + }, + ["/xpa"] = + { + Permission = "debuggers", + Handler = HandleAddExperience, + HelpString = "Adds 200 experience to the player" + }, + ["/xpr"] = + { + Permission = "debuggers", + Handler = HandleRemoveXp, + HelpString = "Remove all xp" + }, + }, -- Commands + + ConsoleCommands = + { + ["sched"] = + { + Handler = HandleConsoleSchedule, + HelpString = "Tests the world scheduling", + }, + ["loadchunk"] = + { + Handler = HandleConsoleLoadChunk, + HelpString = "Loads the specified chunk into memory", + }, + ["preparechunk"] = + { + Handler = HandleConsolePrepareChunk, + HelpString = "Prepares the specified chunk completely (load / gen / light)", + } + }, -- ConsoleCommands +} -- g_PluginInfo + + + + diff --git a/MCServer/Plugins/InfoDump.lua b/MCServer/Plugins/InfoDump.lua index de1d1f451..07a534b88 100644 --- a/MCServer/Plugins/InfoDump.lua +++ b/MCServer/Plugins/InfoDump.lua @@ -444,7 +444,18 @@ local function BuildPermissions(a_PluginInfo) Permissions[info.Permission] = Permission -- Add the command to the list of commands using this permission: Permission.CommandsAffected = Permission.CommandsAffected or {} - table.insert(Permission.CommandsAffected, CommandString) + -- First, make sure that we don't already have this command in the list, + -- it may have already been present in a_PluginInfo + local NewCommand = true + for _, existCmd in ipairs(Permission.CommandsAffected) do + if CommandString == existCmd then + NewCommand = false + break + end + end + if NewCommand then + table.insert(Permission.CommandsAffected, CommandString) + end end -- Process the command param combinations for permissions: diff --git a/SetFlags.cmake b/SetFlags.cmake index c0a021c25..0d1bd99b3 100644 --- a/SetFlags.cmake +++ b/SetFlags.cmake @@ -64,18 +64,12 @@ macro(set_flags) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) endif() - - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND (NOT GCC_VERSION VERSION_GREATER 4.6)) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++0x") - set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++0x") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++0x") - else() - 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") - endif() + + 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") + #on os x clang adds pthread for us but we need to add it for gcc if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_flags_cxx("-stdlib=libc++") @@ -94,18 +88,11 @@ macro(set_flags) execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion OUTPUT_VARIABLE GCC_VERSION) endif() - - if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND (NOT GCC_VERSION VERSION_GREATER 4.6)) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++0x") - set(CMAKE_CXX_FLAGS_COVERAGE "${CMAKE_CXX_FLAGS_COVERAGE} -std=c++0x") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++0x") - else() - 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") - endif() + + 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") # We use a signed char (fixes #640 on RasPi) add_flags_cxx("-fsigned-char") diff --git a/Tools/AnvilStats/CMakeLists.txt b/Tools/AnvilStats/CMakeLists.txt index 557a4c17a..688fbe493 100644 --- a/Tools/AnvilStats/CMakeLists.txt +++ b/Tools/AnvilStats/CMakeLists.txt @@ -53,7 +53,6 @@ set(SHARED_OSS_SRC ../../src/OSSupport/File.cpp ../../src/OSSupport/GZipFile.cpp ../../src/OSSupport/IsThread.cpp - ../../src/OSSupport/Timer.cpp ) set(SHARED_OSS_HDR @@ -62,7 +61,6 @@ set(SHARED_OSS_HDR ../../src/OSSupport/File.h ../../src/OSSupport/GZipFile.h ../../src/OSSupport/IsThread.h - ../../src/OSSupport/Timer.h ) flatten_files(SHARED_SRC) diff --git a/Tools/MCADefrag/CMakeLists.txt b/Tools/MCADefrag/CMakeLists.txt index 618719d68..82e048671 100644 --- a/Tools/MCADefrag/CMakeLists.txt +++ b/Tools/MCADefrag/CMakeLists.txt @@ -56,14 +56,13 @@ set(SHARED_OSS_SRC ../../src/OSSupport/File.cpp ../../src/OSSupport/IsThread.cpp ../../src/OSSupport/StackTrace.cpp - ../../src/OSSupport/Timer.cpp ) + set(SHARED_OSS_HDR ../../src/OSSupport/CriticalSection.h ../../src/OSSupport/File.h ../../src/OSSupport/IsThread.h ../../src/OSSupport/StackTrace.h - ../../src/OSSupport/Timer.h ) if(WIN32) diff --git a/Tools/ProtoProxy/CMakeLists.txt b/Tools/ProtoProxy/CMakeLists.txt index fc8721da0..cca0c1b8b 100644 --- a/Tools/ProtoProxy/CMakeLists.txt +++ b/Tools/ProtoProxy/CMakeLists.txt @@ -58,14 +58,12 @@ set(SHARED_OSS_SRC ../../src/OSSupport/File.cpp ../../src/OSSupport/IsThread.cpp ../../src/OSSupport/StackTrace.cpp - ../../src/OSSupport/Timer.cpp ) set(SHARED_OSS_HDR ../../src/OSSupport/CriticalSection.h ../../src/OSSupport/File.h ../../src/OSSupport/IsThread.h ../../src/OSSupport/StackTrace.h - ../../src/OSSupport/Timer.h ) if(WIN32) diff --git a/Tools/ProtoProxy/Connection.cpp b/Tools/ProtoProxy/Connection.cpp index eaf4fab02..fb2d40e5b 100644 --- a/Tools/ProtoProxy/Connection.cpp +++ b/Tools/ProtoProxy/Connection.cpp @@ -189,7 +189,7 @@ cConnection::cConnection(SOCKET a_ClientSocket, cServer & a_Server) : m_Server(a_Server), m_ClientSocket(a_ClientSocket), m_ServerSocket(-1), - m_BeginTick(m_Timer.GetNowTime()), + m_BeginTick(std::chrono::steady_clock::now()), m_ClientState(csUnencrypted), m_ServerState(csUnencrypted), m_Nonce(0), @@ -436,7 +436,8 @@ bool cConnection::RelayFromClient(void) double cConnection::GetRelativeTime(void) { - return (double)(m_Timer.GetNowTime() - m_BeginTick) / 1000; + Int64 msec = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - m_BeginTick).count(); + return static_cast<double>(msec) / 1000; } diff --git a/Tools/ProtoProxy/Connection.h b/Tools/ProtoProxy/Connection.h index 1fc9536de..c79273f8a 100644 --- a/Tools/ProtoProxy/Connection.h +++ b/Tools/ProtoProxy/Connection.h @@ -10,7 +10,6 @@ #pragma once #include "ByteBuffer.h" -#include "OSSupport/Timer.h" #include "PolarSSL++/AesCfb128Decryptor.h" #include "PolarSSL++/AesCfb128Encryptor.h" @@ -37,8 +36,7 @@ class cConnection SOCKET m_ClientSocket; SOCKET m_ServerSocket; - cTimer m_Timer; - long long m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime()) + std::chrono::steady_clock::time_point m_BeginTick; // Tick when the relative time was first retrieved (used for GetRelativeTime()) enum eConnectionState { diff --git a/src/Bindings/DeprecatedBindings.cpp b/src/Bindings/DeprecatedBindings.cpp index 0b2539fde..6dd6a4e59 100644 --- a/src/Bindings/DeprecatedBindings.cpp +++ b/src/Bindings/DeprecatedBindings.cpp @@ -52,7 +52,9 @@ static int tolua_get_AllToLua_g_BlockSpreadLightFalloff(lua_State* tolua_S) { tolua_Error tolua_err; if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) + { tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); + } } #endif BlockType = (int)tolua_tonumber(tolua_S, 2, 0); @@ -78,7 +80,9 @@ static int tolua_get_AllToLua_g_BlockTransparent(lua_State* tolua_S) { tolua_Error tolua_err; if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) + { tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); + } } #endif BlockType = (int)tolua_tonumber(tolua_S, 2, 0); @@ -104,7 +108,9 @@ static int tolua_get_AllToLua_g_BlockOneHitDig(lua_State* tolua_S) { tolua_Error tolua_err; if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) + { tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); + } } #endif BlockType = (int)tolua_tonumber(tolua_S, 2, 0); @@ -130,7 +136,9 @@ static int tolua_get_AllToLua_g_BlockPistonBreakable(lua_State* tolua_S) { tolua_Error tolua_err; if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) + { tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); + } } #endif BlockType = (int)tolua_tonumber(tolua_S, 2, 0); @@ -156,7 +164,9 @@ static int tolua_get_AllToLua_g_BlockIsSnowable(lua_State* tolua_S) { tolua_Error tolua_err; if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) + { tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); + } } #endif BlockType = (int)tolua_tonumber(tolua_S, 2, 0); @@ -182,7 +192,9 @@ static int tolua_get_AllToLua_g_BlockIsSolid(lua_State* tolua_S) { tolua_Error tolua_err; if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) + { tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); + } } #endif BlockType = (int)tolua_tonumber(tolua_S, 2, 0); @@ -208,7 +220,9 @@ static int tolua_get_AllToLua_g_BlockFullyOccupiesVoxel(lua_State* tolua_S) { tolua_Error tolua_err; if (!tolua_isnumber(tolua_S, 2, 0, &tolua_err)) + { tolua_error(tolua_S, "#vinvalid type in array indexing.", &tolua_err); + } } #endif BlockType = (int)tolua_tonumber(tolua_S, 2, 0); @@ -233,8 +247,8 @@ static int tolua_AllToLua_StringToMobType00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_iscppstring(tolua_S,1,0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) + !tolua_iscppstring(tolua_S, 1, 0, &tolua_err) || + !tolua_isnoobj(tolua_S, 2, &tolua_err) ) goto tolua_lerror; else diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 750f7c65a..56f2e73bc 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -1874,6 +1874,72 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S) +static int tolua_cWorld_PrepareChunk(lua_State * tolua_S) +{ + /* Function signature: + World:PrepareChunk(ChunkX, ChunkZ, Callback) + */ + + // Check the param types: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType (1, "cWorld") || + !L.CheckParamNumber (2, 3) || + !L.CheckParamFunctionOrNil(4) + ) + { + return 0; + } + + // Read the params: + cWorld * world = nullptr; + int chunkX = 0, chunkZ = 0; + L.GetStackValues(1, world, chunkX, chunkZ); + if (world == nullptr) + { + LOGWARNING("World:PrepareChunk(): invalid world parameter"); + L.LogStackTrace(); + return 0; + } + + // Wrap the Lua callback inside a C++ callback class: + class cCallback: + public cChunkCoordCallback + { + public: + cCallback(lua_State * a_LuaState): + m_LuaState(a_LuaState), + m_Callback(m_LuaState, 4) + { + } + + // cChunkCoordCallback override: + virtual void Call(int a_CBChunkX, int a_CBChunkZ) override + { + if (m_Callback.IsValid()) + { + m_LuaState.Call(m_Callback, a_CBChunkX, a_CBChunkZ); + } + + // This is the last reference of this object, we must delete it so that it doesn't leak: + delete this; + } + + protected: + cLuaState m_LuaState; + cLuaState::cRef m_Callback; + }; + cCallback * callback = new cCallback(tolua_S); + + // Call the chunk preparation: + world->PrepareChunk(chunkX, chunkZ, callback); + return 0; +} + + + + + static int tolua_cPlayer_GetPermissions(lua_State * tolua_S) { // Function signature: cPlayer:GetPermissions() -> {permissions-array} @@ -3401,6 +3467,7 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "GetBlockInfo", tolua_cWorld_GetBlockInfo); tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta); tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines); + tolua_function(tolua_S, "PrepareChunk", tolua_cWorld_PrepareChunk); tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask); tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask); tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines); diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 8cc9ff0cd..08677553c 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -54,7 +54,7 @@ public: virtual bool OnChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ) = 0; virtual bool OnChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ) = 0; virtual bool OnCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup) = 0; - virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe) = 0; + virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0; virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0; virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0; diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 391d8bcbe..ea782ea3f 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -382,7 +382,7 @@ bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup) -bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe) +bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { cCSLock Lock(m_CriticalSection); bool res = false; diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index 6bb134efc..7de5ffec4 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -78,7 +78,7 @@ public: virtual bool OnChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ) override; virtual bool OnChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ) override; virtual bool OnCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup) override; - virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe) override; + virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override; virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override; virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index f63578885..406a540f4 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -448,7 +448,7 @@ bool cPluginManager::CallHookCollectingPickup(cPlayer & a_Player, cPickup & a_Pi -bool cPluginManager::CallHookCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe) +bool cPluginManager::CallHookCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { FIND_HOOK(HOOK_CRAFTING_NO_RECIPE); VERIFY_HOOK; @@ -1459,11 +1459,16 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player, -cPlugin * cPluginManager::GetPlugin( const AString & a_Plugin) const +cPlugin * cPluginManager::GetPlugin(const AString & a_Plugin) const { for (PluginMap::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) { - if (itr->second == nullptr) continue; + if (itr->second == nullptr) + { + // The plugin is currently unloaded + continue; + } + if (itr->second->GetName().compare(a_Plugin) == 0) { return itr->second; diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index bc8c1f5e6..3a2aecc92 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -187,7 +187,7 @@ public: bool CallHookChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ); bool CallHookChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ); bool CallHookCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup); - bool CallHookCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe * a_Recipe); + bool CallHookCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe); bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason); bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier); bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == nullptr, it is a console cmd diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h index 9fd45644b..a8b5be899 100644 --- a/src/Blocks/BlockBed.h +++ b/src/Blocks/BlockBed.h @@ -52,7 +52,10 @@ public: static NIBBLETYPE RotationToMetaData(double a_Rotation) { a_Rotation += 180 + (180 / 4); // So its not aligned with axis - if (a_Rotation > 360) a_Rotation -= 360; + if (a_Rotation > 360) + { + a_Rotation -= 360; + } a_Rotation = (a_Rotation / 360) * 4; diff --git a/src/Blocks/BlockDoor.cpp b/src/Blocks/BlockDoor.cpp index 96345a2df..90b7b15c2 100644 --- a/src/Blocks/BlockDoor.cpp +++ b/src/Blocks/BlockDoor.cpp @@ -145,7 +145,10 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta) // in only the bottom tile while the hinge position is in the top tile. This function only operates on one tile at a time, // so the function can only see either the hinge position or orientation, but not both, at any given time. The class itself // needs extra datamembers. - if (a_Meta & 0x08) return a_Meta; + if (a_Meta & 0x08) + { + return a_Meta; + } // Holds open/closed meta data. 0x0C == 1100. NIBBLETYPE OtherMeta = a_Meta & 0x0C; @@ -173,7 +176,10 @@ NIBBLETYPE cBlockDoorHandler::MetaMirrorYZ(NIBBLETYPE a_Meta) // so the function can only see either the hinge position or orientation, but not both, at any given time.The class itself // needs extra datamembers. - if (a_Meta & 0x08) return a_Meta; + if (a_Meta & 0x08) + { + return a_Meta; + } // Holds open/closed meta data. 0x0C == 1100. NIBBLETYPE OtherMeta = a_Meta & 0x0C; diff --git a/src/Blocks/BlockRail.h b/src/Blocks/BlockRail.h index 87ce069ab..21a34d8ce 100644 --- a/src/Blocks/BlockRail.h +++ b/src/Blocks/BlockRail.h @@ -151,13 +151,21 @@ public: Neighbors[6] = (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ - 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_NORTH, E_PURE_NONE)); Neighbors[7] = (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ + 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY + 1, a_BlockZ, BLOCK_FACE_SOUTH, E_PURE_NONE)); if (IsUnstable(a_ChunkInterface, a_BlockX + 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_EAST)) + { Neighbors[0] = true; + } if (IsUnstable(a_ChunkInterface, a_BlockX - 1, a_BlockY - 1, a_BlockZ) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_WEST)) + { Neighbors[1] = true; + } if (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ - 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_NORTH)) + { Neighbors[2] = true; + } if (IsUnstable(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ + 1) || !IsNotConnected(a_ChunkInterface, a_BlockX, a_BlockY - 1, a_BlockZ, BLOCK_FACE_SOUTH)) + { Neighbors[3] = true; + } for (int i = 0; i < 8; i++) { if (Neighbors[i]) @@ -167,12 +175,30 @@ public: } if (RailsCnt == 1) { - if (Neighbors[7]) return E_META_RAIL_ASCEND_ZP; - else if (Neighbors[6]) return E_META_RAIL_ASCEND_ZM; - else if (Neighbors[5]) return E_META_RAIL_ASCEND_XM; - else if (Neighbors[4]) return E_META_RAIL_ASCEND_XP; - else if (Neighbors[0] || Neighbors[1]) return E_META_RAIL_XM_XP; - else if (Neighbors[2] || Neighbors[3]) return E_META_RAIL_ZM_ZP; + if (Neighbors[7]) + { + return E_META_RAIL_ASCEND_ZP; + } + else if (Neighbors[6]) + { + return E_META_RAIL_ASCEND_ZM; + } + else if (Neighbors[5]) + { + return E_META_RAIL_ASCEND_XM; + } + else if (Neighbors[4]) + { + return E_META_RAIL_ASCEND_XP; + } + else if (Neighbors[0] || Neighbors[1]) + { + return E_META_RAIL_XM_XP; + } + else if (Neighbors[2] || Neighbors[3]) + { + return E_META_RAIL_ZM_ZP; + } ASSERT(!"Weird neighbor count"); return Meta; } @@ -185,16 +211,46 @@ public: } if (RailsCnt > 1) { - if (Neighbors[3] && Neighbors[0] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZP_XP; - else if (Neighbors[3] && Neighbors[1] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZP_XM; - else if (Neighbors[2] && Neighbors[0] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZM_XP; - else if (Neighbors[2] && Neighbors[1] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZM_XM; - else if (Neighbors[7] && Neighbors[2]) return E_META_RAIL_ASCEND_ZP; - else if (Neighbors[3] && Neighbors[6]) return E_META_RAIL_ASCEND_ZM; - else if (Neighbors[5] && Neighbors[0]) return E_META_RAIL_ASCEND_XM; - else if (Neighbors[4] && Neighbors[1]) return E_META_RAIL_ASCEND_XP; - else if (Neighbors[0] && Neighbors[1]) return E_META_RAIL_XM_XP; - else if (Neighbors[2] && Neighbors[3]) return E_META_RAIL_ZM_ZP; + if (Neighbors[3] && Neighbors[0] && CanThisRailCurve()) + { + return E_META_RAIL_CURVED_ZP_XP; + } + else if (Neighbors[3] && Neighbors[1] && CanThisRailCurve()) + { + return E_META_RAIL_CURVED_ZP_XM; + } + else if (Neighbors[2] && Neighbors[0] && CanThisRailCurve()) + { + return E_META_RAIL_CURVED_ZM_XP; + } + else if (Neighbors[2] && Neighbors[1] && CanThisRailCurve()) + { + return E_META_RAIL_CURVED_ZM_XM; + } + else if (Neighbors[7] && Neighbors[2]) + { + return E_META_RAIL_ASCEND_ZP; + } + else if (Neighbors[3] && Neighbors[6]) + { + return E_META_RAIL_ASCEND_ZM; + } + else if (Neighbors[5] && Neighbors[0]) + { + return E_META_RAIL_ASCEND_XM; + } + else if (Neighbors[4] && Neighbors[1]) + { + return E_META_RAIL_ASCEND_XP; + } + else if (Neighbors[0] && Neighbors[1]) + { + return E_META_RAIL_XM_XP; + } + else if (Neighbors[2] && Neighbors[3]) + { + return E_META_RAIL_ZM_ZP; + } if (CanThisRailCurve()) { diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp index 012105ca1..080176dcd 100644 --- a/src/ByteBuffer.cpp +++ b/src/ByteBuffer.cpp @@ -111,26 +111,36 @@ public: #ifdef _DEBUG -/// Simple RAII class that uses one internal unsigned long for checking if two threads are using an object simultanously -class cSingleThreadAccessChecker -{ -public: - cSingleThreadAccessChecker(unsigned long * a_ThreadID) : - m_ThreadID(a_ThreadID) + /** Simple RAII class that is used for checking that no two threads are using an object simultanously. + It requires the monitored object to provide the storage for a thread ID. + It uses that storage to check if the thread ID of consecutive calls is the same all the time. */ + class cSingleThreadAccessChecker { - ASSERT((*a_ThreadID == 0) || (*a_ThreadID == cIsThread::GetCurrentID())); - } - - ~cSingleThreadAccessChecker() - { - *m_ThreadID = 0; - } - -protected: - unsigned long * m_ThreadID; -} ; + public: + cSingleThreadAccessChecker(std::thread::id * a_ThreadID) : + m_ThreadID(a_ThreadID) + { + ASSERT( + (*a_ThreadID == std::this_thread::get_id()) || // Either the object is used by current thread... + (*a_ThreadID == std::thread::id()) // ... or by no thread at all + ); + + // Mark as being used by this thread: + *m_ThreadID = std::this_thread::get_id(); + } + + ~cSingleThreadAccessChecker() + { + // Mark as not being used by any thread: + *m_ThreadID = std::thread::id(); + } + + protected: + /** Points to the storage used for ID of the thread using the object. */ + std::thread::id * m_ThreadID; + }; -#define CHECK_THREAD cSingleThreadAccessChecker Checker(const_cast<unsigned long *>(&m_ThreadID)) + #define CHECK_THREAD cSingleThreadAccessChecker Checker(&m_ThreadID); #else #define CHECK_THREAD @@ -146,9 +156,6 @@ protected: cByteBuffer::cByteBuffer(size_t a_BufferSize) : m_Buffer(new char[a_BufferSize + 1]), m_BufferSize(a_BufferSize + 1), - #ifdef _DEBUG - m_ThreadID(0), - #endif // _DEBUG m_DataStart(0), m_WritePos(0), m_ReadPos(0) @@ -174,7 +181,7 @@ cByteBuffer::~cByteBuffer() bool cByteBuffer::Write(const void * a_Bytes, size_t a_Count) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); // Store the current free space for a check after writing: @@ -221,7 +228,7 @@ bool cByteBuffer::Write(const void * a_Bytes, size_t a_Count) size_t cByteBuffer::GetFreeSpace(void) const { - CHECK_THREAD; + CHECK_THREAD CheckValid(); if (m_WritePos >= m_DataStart) { @@ -243,7 +250,7 @@ size_t cByteBuffer::GetFreeSpace(void) const /// Returns the number of bytes that are currently in the ringbuffer. Note GetReadableBytes() size_t cByteBuffer::GetUsedSpace(void) const { - CHECK_THREAD; + CHECK_THREAD CheckValid(); ASSERT(m_BufferSize >= GetFreeSpace()); ASSERT((m_BufferSize - GetFreeSpace()) >= 1); @@ -257,7 +264,7 @@ size_t cByteBuffer::GetUsedSpace(void) const /// Returns the number of bytes that are currently available for reading (may be less than UsedSpace due to some data having been read already) size_t cByteBuffer::GetReadableSpace(void) const { - CHECK_THREAD; + CHECK_THREAD CheckValid(); if (m_ReadPos > m_WritePos) { @@ -276,7 +283,7 @@ size_t cByteBuffer::GetReadableSpace(void) const bool cByteBuffer::CanReadBytes(size_t a_Count) const { - CHECK_THREAD; + CHECK_THREAD CheckValid(); return (a_Count <= GetReadableSpace()); } @@ -287,7 +294,7 @@ bool cByteBuffer::CanReadBytes(size_t a_Count) const bool cByteBuffer::CanWriteBytes(size_t a_Count) const { - CHECK_THREAD; + CHECK_THREAD CheckValid(); return (a_Count <= GetFreeSpace()); } @@ -298,7 +305,7 @@ bool cByteBuffer::CanWriteBytes(size_t a_Count) const bool cByteBuffer::ReadChar(char & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); NEEDBYTES(1); ReadBuf(&a_Value, 1); @@ -311,7 +318,7 @@ bool cByteBuffer::ReadChar(char & a_Value) bool cByteBuffer::ReadByte(unsigned char & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); NEEDBYTES(1); ReadBuf(&a_Value, 1); @@ -324,7 +331,7 @@ bool cByteBuffer::ReadByte(unsigned char & a_Value) bool cByteBuffer::ReadBEShort(short & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); NEEDBYTES(2); ReadBuf(&a_Value, 2); @@ -338,7 +345,7 @@ bool cByteBuffer::ReadBEShort(short & a_Value) bool cByteBuffer::ReadBEInt(int & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); NEEDBYTES(4); ReadBuf(&a_Value, 4); @@ -352,7 +359,7 @@ bool cByteBuffer::ReadBEInt(int & a_Value) bool cByteBuffer::ReadBEInt64(Int64 & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); NEEDBYTES(8); ReadBuf(&a_Value, 8); @@ -366,7 +373,7 @@ bool cByteBuffer::ReadBEInt64(Int64 & a_Value) bool cByteBuffer::ReadBEFloat(float & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); NEEDBYTES(4); ReadBuf(&a_Value, 4); @@ -380,7 +387,7 @@ bool cByteBuffer::ReadBEFloat(float & a_Value) bool cByteBuffer::ReadBEDouble(double & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); NEEDBYTES(8); ReadBuf(&a_Value, 8); @@ -394,7 +401,7 @@ bool cByteBuffer::ReadBEDouble(double & a_Value) bool cByteBuffer::ReadBool(bool & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); NEEDBYTES(1); char Value = 0; @@ -409,7 +416,7 @@ bool cByteBuffer::ReadBool(bool & a_Value) bool cByteBuffer::ReadBEUTF16String16(AString & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); short Length; if (!ReadBEShort(Length)) @@ -430,7 +437,7 @@ bool cByteBuffer::ReadBEUTF16String16(AString & a_Value) bool cByteBuffer::ReadVarInt(UInt32 & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); UInt32 Value = 0; int Shift = 0; @@ -452,7 +459,7 @@ bool cByteBuffer::ReadVarInt(UInt32 & a_Value) bool cByteBuffer::ReadVarUTF8String(AString & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); UInt32 Size = 0; if (!ReadVarInt(Size)) @@ -472,7 +479,7 @@ bool cByteBuffer::ReadVarUTF8String(AString & a_Value) bool cByteBuffer::ReadLEInt(int & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); NEEDBYTES(4); ReadBuf(&a_Value, 4); @@ -491,6 +498,7 @@ bool cByteBuffer::ReadLEInt(int & a_Value) bool cByteBuffer::ReadPosition(int & a_BlockX, int & a_BlockY, int & a_BlockZ) { + CHECK_THREAD Int64 Value; if (!ReadBEInt64(Value)) { @@ -515,7 +523,7 @@ bool cByteBuffer::ReadPosition(int & a_BlockX, int & a_BlockY, int & a_BlockZ) bool cByteBuffer::WriteChar(char a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); PUTBYTES(1); return WriteBuf(&a_Value, 1); @@ -527,7 +535,7 @@ bool cByteBuffer::WriteChar(char a_Value) bool cByteBuffer::WriteByte(unsigned char a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); PUTBYTES(1); return WriteBuf(&a_Value, 1); @@ -539,7 +547,7 @@ bool cByteBuffer::WriteByte(unsigned char a_Value) bool cByteBuffer::WriteBEShort(short a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); PUTBYTES(2); u_short Converted = htons((u_short)a_Value); @@ -552,7 +560,7 @@ bool cByteBuffer::WriteBEShort(short a_Value) bool cByteBuffer::WriteBEUShort(unsigned short a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); PUTBYTES(2); u_short Converted = htons((u_short)a_Value); @@ -565,7 +573,7 @@ bool cByteBuffer::WriteBEUShort(unsigned short a_Value) bool cByteBuffer::WriteBEInt(int a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); PUTBYTES(4); UInt32 Converted = HostToNetwork4(&a_Value); @@ -578,7 +586,7 @@ bool cByteBuffer::WriteBEInt(int a_Value) bool cByteBuffer::WriteBEInt64(Int64 a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); PUTBYTES(8); UInt64 Converted = HostToNetwork8(&a_Value); @@ -591,7 +599,7 @@ bool cByteBuffer::WriteBEInt64(Int64 a_Value) bool cByteBuffer::WriteBEFloat(float a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); PUTBYTES(4); UInt32 Converted = HostToNetwork4(&a_Value); @@ -604,7 +612,7 @@ bool cByteBuffer::WriteBEFloat(float a_Value) bool cByteBuffer::WriteBEDouble(double a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); PUTBYTES(8); UInt64 Converted = HostToNetwork8(&a_Value); @@ -618,7 +626,7 @@ bool cByteBuffer::WriteBEDouble(double a_Value) bool cByteBuffer::WriteBool(bool a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); return WriteChar(a_Value ? 1 : 0); } @@ -629,7 +637,7 @@ bool cByteBuffer::WriteBool(bool a_Value) bool cByteBuffer::WriteVarInt(UInt32 a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); // A 32-bit integer can be encoded by at most 5 bytes: @@ -650,7 +658,7 @@ bool cByteBuffer::WriteVarInt(UInt32 a_Value) bool cByteBuffer::WriteVarUTF8String(const AString & a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early. bool res = WriteVarInt((UInt32)(a_Value.size())); @@ -667,7 +675,7 @@ bool cByteBuffer::WriteVarUTF8String(const AString & a_Value) bool cByteBuffer::WriteLEInt(int a_Value) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); #ifdef IS_LITTLE_ENDIAN return WriteBuf((const char *)&a_Value, 4); @@ -683,6 +691,7 @@ bool cByteBuffer::WriteLEInt(int a_Value) bool cByteBuffer::WritePosition(int a_BlockX, int a_BlockY, int a_BlockZ) { + CHECK_THREAD return WriteBEInt64(((Int64)a_BlockX & 0x3FFFFFF) << 38 | ((Int64)a_BlockY & 0xFFF) << 26 | ((Int64)a_BlockZ & 0x3FFFFFF)); } @@ -692,7 +701,7 @@ bool cByteBuffer::WritePosition(int a_BlockX, int a_BlockY, int a_BlockZ) bool cByteBuffer::ReadBuf(void * a_Buffer, size_t a_Count) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); NEEDBYTES(a_Count); char * Dst = (char *)a_Buffer; // So that we can do byte math @@ -725,7 +734,7 @@ bool cByteBuffer::ReadBuf(void * a_Buffer, size_t a_Count) bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); PUTBYTES(a_Count); char * Src = (char *)a_Buffer; // So that we can do byte math @@ -755,7 +764,7 @@ bool cByteBuffer::WriteBuf(const void * a_Buffer, size_t a_Count) bool cByteBuffer::ReadString(AString & a_String, size_t a_Count) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); NEEDBYTES(a_Count); a_String.clear(); @@ -790,7 +799,7 @@ bool cByteBuffer::ReadString(AString & a_String, size_t a_Count) bool cByteBuffer::ReadUTF16String(AString & a_String, size_t a_NumChars) { // Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String - CHECK_THREAD; + CHECK_THREAD CheckValid(); AString RawData; if (!ReadString(RawData, a_NumChars * 2)) @@ -807,7 +816,7 @@ bool cByteBuffer::ReadUTF16String(AString & a_String, size_t a_NumChars) bool cByteBuffer::SkipRead(size_t a_Count) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); if (!CanReadBytes(a_Count)) { @@ -823,7 +832,7 @@ bool cByteBuffer::SkipRead(size_t a_Count) void cByteBuffer::ReadAll(AString & a_Data) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); ReadString(a_Data, GetReadableSpace()); } @@ -834,6 +843,7 @@ void cByteBuffer::ReadAll(AString & a_Data) bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes) { + CHECK_THREAD if (!a_Dst.CanWriteBytes(a_NumBytes) || !CanReadBytes(a_NumBytes)) { // There's not enough source bytes or space in the dest BB @@ -858,7 +868,7 @@ bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes) void cByteBuffer::CommitRead(void) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); m_DataStart = m_ReadPos; } @@ -869,7 +879,7 @@ void cByteBuffer::CommitRead(void) void cByteBuffer::ResetRead(void) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); m_ReadPos = m_DataStart; } @@ -882,7 +892,7 @@ void cByteBuffer::ReadAgain(AString & a_Out) { // Return the data between m_DataStart and m_ReadPos (the data that has been read but not committed) // Used by ProtoProxy to repeat communication twice, once for parsing and the other time for the remote party - CHECK_THREAD; + CHECK_THREAD CheckValid(); size_t DataStart = m_DataStart; if (m_ReadPos < m_DataStart) @@ -902,7 +912,7 @@ void cByteBuffer::ReadAgain(AString & a_Out) void cByteBuffer::AdvanceReadPos(size_t a_Count) { - CHECK_THREAD; + CHECK_THREAD CheckValid(); m_ReadPos += a_Count; if (m_ReadPos >= m_BufferSize) diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h index 70de419f0..2a316fa32 100644 --- a/src/ByteBuffer.h +++ b/src/ByteBuffer.h @@ -130,13 +130,15 @@ protected: char * m_Buffer; size_t m_BufferSize; // Total size of the ringbuffer - #ifdef _DEBUG - volatile unsigned long m_ThreadID; // Thread that is currently accessing the object, checked via cSingleThreadAccessChecker - #endif // _DEBUG - size_t m_DataStart; // Where the data starts in the ringbuffer size_t m_WritePos; // Where the data ends in the ringbuffer size_t m_ReadPos; // Where the next read will start in the ringbuffer + + #ifdef _DEBUG + /** The ID of the thread currently accessing the object. + Used for checking that only one thread accesses the object at a time, via cSingleThreadAccessChecker. */ + mutable std::thread::id m_ThreadID; + #endif /** Advances the m_ReadPos by a_Count bytes */ void AdvanceReadPos(size_t a_Count); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d6218ff42..997326cc7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -113,7 +113,6 @@ SET (HDRS Map.h MapManager.h Matrix4.h - MersenneTwister.h MobCensus.h MobFamilyCollecter.h MobProximityCounter.h diff --git a/src/CheckBasicStyle.lua b/src/CheckBasicStyle.lua index 76ae8c325..0c7b05d6d 100644 --- a/src/CheckBasicStyle.lua +++ b/src/CheckBasicStyle.lua @@ -92,6 +92,25 @@ end local g_ViolationPatterns = { + -- Parenthesis around comparisons: + {"==[^)]+&&", "Add parenthesis around comparison"}, + {"&&[^(]+==", "Add parenthesis around comparison"}, + {"==[^)]+||", "Add parenthesis around comparison"}, + {"||[^(]+==", "Add parenthesis around comparison"}, + {"!=[^)]+&&", "Add parenthesis around comparison"}, + {"&&[^(]+!=", "Add parenthesis around comparison"}, + {"!=[^)]+||", "Add parenthesis around comparison"}, + {"||[^(]+!=", "Add parenthesis around comparison"}, + {"<[^)T][^)]*&&", "Add parenthesis around comparison"}, -- Must take special care of templates: "template <T> fn(Args && ...)" + {"&&[^(]+<", "Add parenthesis around comparison"}, + {"<[^)T][^)]*||", "Add parenthesis around comparison"}, -- Must take special care of templates: "template <T> fn(Args && ...)" + {"||[^(]+<", "Add parenthesis around comparison"}, + -- Cannot check ">" because of "obj->m_Flag &&". Check at least ">=": + {">=[^)]+&&", "Add parenthesis around comparison"}, + {"&&[^(]+>=", "Add parenthesis around comparison"}, + {">=[^)]+||", "Add parenthesis around comparison"}, + {"||[^(]+>=", "Add parenthesis around comparison"}, + -- Check against indenting using spaces: {"^\t* +", "Indenting with a space"}, @@ -116,11 +135,11 @@ local g_ViolationPatterns = -- Space after keywords: {"[^_]if%(", "Needs a space after \"if\""}, - {"for%(", "Needs a space after \"for\""}, - {"while%(", "Needs a space after \"while\""}, - {"switch%(", "Needs a space after \"switch\""}, - {"catch%(", "Needs a space after \"catch\""}, - {"template<", "Needs a space after \"template\""}, + {"%sfor%(", "Needs a space after \"for\""}, + {"%swhile%(", "Needs a space after \"while\""}, + {"%sswitch%(", "Needs a space after \"switch\""}, + {"%scatch%(", "Needs a space after \"catch\""}, + {"%stemplate<", "Needs a space after \"template\""}, -- No space after keyword's parenthesis: {"[^%a#]if %( ", "Remove the space after \"(\""}, @@ -162,6 +181,7 @@ local function ProcessFile(a_FileName) -- Ref.: http://stackoverflow.com/questions/10416869/iterate-over-possibly-empty-lines-in-a-way-that-matches-the-expectations-of-exis local lineCounter = 1 local lastIndentLevel = 0 + local isLastLineControl = false all:gsub("\r\n", "\n") -- normalize CRLF into LF-only string.gsub(all .. "\n", "[^\n]*\n", -- Iterate over each line, while preserving empty lines function(a_Line) @@ -198,6 +218,24 @@ local function ProcessFile(a_FileName) end lastIndentLevel = indentLevel end + + -- Check that control statements have braces on separate lines after them: + -- Note that if statements can be broken into multiple lines, in which case this test is not taken + if (isLastLineControl) then + if not(a_Line:find("^%s*{") or a_Line:find("^%s*#")) then + -- Not followed by a brace, not followed by a preprocessor + ReportViolation(a_FileName, lineCounter - 1, 1, 1, "Control statement needs a brace on separate line") + end + end + local lineWithSpace = " " .. a_Line + isLastLineControl = + lineWithSpace:find("^%s+if %b()") or + lineWithSpace:find("^%s+else if %b()") or + lineWithSpace:find("^%s+for %b()") or + lineWithSpace:find("^%s+switch %b()") or + lineWithSpace:find("^%s+else\n") or + lineWithSpace:find("^%s+else //") or + lineWithSpace:find("^%s+do %b()") lineCounter = lineCounter + 1 end @@ -227,6 +265,9 @@ end +-- Remove buffering from stdout, so that the output appears immediately in IDEs: +io.stdout:setvbuf("no") + -- Process all files in the AllFiles.lst file (generated by cmake): for fnam in io.lines("AllFiles.lst") do ProcessItem(fnam) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index d271b4cb4..017ceda26 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -28,7 +28,6 @@ #include "Item.h" #include "Noise/Noise.h" #include "Root.h" -#include "MersenneTwister.h" #include "Entities/Player.h" #include "BlockArea.h" #include "Bindings/PluginManager.h" diff --git a/src/ChunkDef.h b/src/ChunkDef.h index 260aace58..8f1d416ad 100644 --- a/src/ChunkDef.h +++ b/src/ChunkDef.h @@ -410,7 +410,7 @@ typedef std::list<cChunkCoordsWithBool> cChunkCoordsWithBoolList; -/// Interface class used as a callback for operations that involve chunk coords +/** Interface class used as a callback for operations that involve chunk coords */ class cChunkCoordCallback { public: @@ -424,6 +424,27 @@ public: +/** Provides storage for a set of chunk coords together with a callback. +Used for chunk queues that notify about processed items. */ +class cChunkCoordsWithCallback +{ +public: + cChunkCoordsWithCallback(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback): + m_ChunkX(a_ChunkX), + m_ChunkZ(a_ChunkZ), + m_Callback(a_Callback) + { + } + + int m_ChunkX; + int m_ChunkZ; + cChunkCoordCallback * m_Callback; +}; + + + + + /** Generic template that can store any kind of data together with a triplet of 3 coords*/ template <typename X> class cCoordWithData { diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 8a8f17a1b..222a756b1 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -2349,6 +2349,103 @@ void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkZ) +void cChunkMap::PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); + + // If the chunk is not prepared, queue it in the lighting thread, that will do all the needed processing: + if ((Chunk == nullptr) || !Chunk->IsValid() || !Chunk->IsLightValid()) + { + m_World->GetLightingThread().QueueChunk(a_ChunkX, a_ChunkZ, a_Callback); + return; + } + + // The chunk is present and lit, just call the callback: + if (a_Callback != nullptr) + { + a_Callback->Call(a_ChunkX, a_ChunkZ); + } +} + + + + + +bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ); + if (Chunk == nullptr) + { + // Generic error while getting the chunk - out of memory? + return false; + } + + // Try loading the chunk: + if ((Chunk == nullptr) || (!Chunk->IsValid())) + { + class cPrepareLoadCallback: public cChunkCoordCallback + { + public: + cPrepareLoadCallback(cWorld & a_World, cChunkMap & a_ChunkMap, cChunkCoordCallback * a_Callback): + m_World(a_World), + m_ChunkMap(a_ChunkMap), + m_Callback(a_Callback) + { + } + + // cChunkCoordCallback override: + virtual void Call(int a_CBChunkX, int a_CBChunkZ) override + { + // The chunk has been loaded or an error occurred, check if it's valid now: + cChunkPtr CBChunk = m_ChunkMap.GetChunkNoLoad(a_CBChunkX, a_CBChunkZ); + + if (CBChunk == nullptr) + { + // An error occurred, but we promised to call the callback, so call it even when there's no real chunk data: + if (m_Callback != nullptr) + { + m_Callback->Call(a_CBChunkX, a_CBChunkZ); + } + return; + } + + // If the chunk is not valid, queue it in the generator: + if (!CBChunk->IsValid()) + { + m_World.GetGenerator().QueueGenerateChunk(a_CBChunkX, a_CBChunkZ, false, m_Callback); + return; + } + + // The chunk was loaded, call the callback: + if (m_Callback != nullptr) + { + m_Callback->Call(a_CBChunkX, a_CBChunkZ); + } + } + + protected: + cWorld & m_World; + cChunkMap & m_ChunkMap; + cChunkCoordCallback * m_Callback; + }; + m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkZ, new cPrepareLoadCallback(*m_World, *this, a_Callback)); + return true; + } + + // The chunk is valid, just call the callback: + if (a_Callback != nullptr) + { + a_Callback->Call(a_ChunkX, a_ChunkZ); + } + return true; +} + + + + + void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ) { cCSLock Lock(m_CSLayers); diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 51be5cb3d..6a858b06d 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -280,6 +280,20 @@ public: /** Touches the chunk, causing it to be loaded or generated */ void TouchChunk(int a_ChunkX, int a_ChunkZ); + + /** Queues the chunk for preparing - making sure that it's generated and lit. + The specified chunk is queued to be loaded or generated, and lit if needed. + The specified callback is called after the chunk has been prepared. If there's no preparation to do, only the callback is called. + It is legal to call without the callback. */ + void PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr); // Lua-accessible + + /** Queues the chunk for generating. + First attempts to load the chunk from the storage. If that fails, queues the chunk for generating. + The specified callback is called after the chunk has been loaded / generated. + It is legal to call without the callback. + Returns true if successful, false if not (possibly an out-of-memory error). + If the return value is true, the callback was / will be called. */ + bool GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr); // Lua-accessible /** Marks the chunk as failed-to-load */ void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ); diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index a6cbad32a..cb9d34c84 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -16,7 +16,6 @@ #include "Mobs/Monster.h" #include "ChatColor.h" #include "OSSupport/Socket.h" -#include "OSSupport/Timer.h" #include "Items/ItemHandler.h" #include "Blocks/BlockHandler.h" #include "Blocks/BlockSlab.h" @@ -25,8 +24,6 @@ #include "Root.h" #include "Protocol/Authenticator.h" -#include "MersenneTwister.h" - #include "Protocol/ProtocolRecognizer.h" #include "CompositeChat.h" #include "Items/ItemSword.h" @@ -41,18 +38,13 @@ /** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick */ #define MAX_BLOCK_CHANGE_INTERACTIONS 20 +/** The interval for sending pings to clients. +Vanilla sends one ping every 1 second. */ +static const std::chrono::milliseconds PING_TIME_MS = std::chrono::milliseconds(1000); -#define RECI_RAND_MAX (1.f/RAND_MAX) -inline int fRadRand(MTRand & r1, int a_BlockCoord) -{ - return a_BlockCoord * 32 + (int)(16 * ((float)r1.rand() * RECI_RAND_MAX) * 16 - 8); -} - - - int cClientHandle::s_ClientCount = 0; @@ -76,8 +68,6 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) : m_TimeSinceLastPacket(0), m_Ping(1000), m_PingID(1), - m_PingStartTime(0), - m_LastPingTime(1000), m_BlockDigAnimStage(-1), m_BlockDigAnimSpeed(0), m_BlockDigAnimX(0), @@ -101,9 +91,7 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) : s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread m_UniqueID = s_ClientCount; - - cTimer t1; - m_LastPingTime = t1.GetNowTime(); + m_PingStartTime = std::chrono::steady_clock::now(); LOGD("New ClientHandle created at %p", this); } @@ -199,9 +187,13 @@ void cClientHandle::GenerateOfflineUUID(void) AString cClientHandle::FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2) { if (ShouldAppendChatPrefixes) + { return Printf("%s[%s] %s", m_Color1.c_str(), a_ChatPrefixS.c_str(), m_Color2.c_str()); + } else + { return Printf("%s", m_Color1.c_str()); + } } @@ -397,8 +389,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, // Delay the first ping until the client "settles down" // This should fix #889, "BadCast exception, cannot convert bit to fm" error in client - cTimer t1; - m_LastPingTime = t1.GetNowTime() + 3000; // Send the first KeepAlive packet in 3 seconds + m_PingStartTime = std::chrono::steady_clock::now() + std::chrono::seconds(3); // Send the first KeepAlive packet in 3 seconds cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player); } @@ -1779,8 +1770,7 @@ void cClientHandle::HandleKeepAlive(int a_KeepAliveID) { if (a_KeepAliveID == m_PingID) { - cTimer t1; - m_Ping = (short)((t1.GetNowTime() - m_PingStartTime) / 2); + m_Ping = std::chrono::steady_clock::now() - m_PingStartTime; } } @@ -1788,6 +1778,43 @@ void cClientHandle::HandleKeepAlive(int a_KeepAliveID) +bool cClientHandle::CheckMultiLogin(const AString & a_Username) +{ + // If the multilogin is allowed, skip this check entirely: + if ((cRoot::Get()->GetServer()->DoesAllowMultiLogin())) + { + return true; + } + + // Check if the player is waiting to be transferred to the World. + if (cRoot::Get()->GetServer()->IsPlayerInQueue(a_Username)) + { + Kick("A player of the username is already logged in"); + return false; + } + + class cCallback : + public cPlayerListCallback + { + virtual bool Item(cPlayer * a_Player) override + { + return true; + } + } Callback; + + // Check if the player is in any World. + if (cRoot::Get()->DoWithPlayer(a_Username, Callback)) + { + Kick("A player of the username is already logged in"); + return false; + } + return true; +} + + + + + bool cClientHandle::HandleHandshake(const AString & a_Username) { if (!cRoot::Get()->GetPluginManager()->CallHookHandshake(*this, a_Username)) @@ -1798,7 +1825,8 @@ bool cClientHandle::HandleHandshake(const AString & a_Username) return false; } } - return true; + + return CheckMultiLogin(a_Username); } @@ -2005,13 +2033,11 @@ void cClientHandle::Tick(float a_Dt) // Send a ping packet: if (m_State == csPlaying) { - cTimer t1; - if ((m_LastPingTime + cClientHandle::PING_TIME_MS <= t1.GetNowTime())) + if ((m_PingStartTime + PING_TIME_MS <= std::chrono::steady_clock::now())) { m_PingID++; - m_PingStartTime = t1.GetNowTime(); + m_PingStartTime = std::chrono::steady_clock::now(); m_Protocol->SendKeepAlive(m_PingID); - m_LastPingTime = m_PingStartTime; } } diff --git a/src/ClientHandle.h b/src/ClientHandle.h index f195b6be7..25dd250d9 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -215,7 +215,7 @@ public: const AString & GetUsername(void) const; void SetUsername( const AString & a_Username); - inline short GetPing(void) const { return m_Ping; } + inline short GetPing(void) const { return static_cast<short>(std::chrono::duration_cast<std::chrono::milliseconds>(m_Ping).count()); } /** Sets the maximal view distance. */ void SetViewDistance(int a_ViewDistance); @@ -280,6 +280,10 @@ public: void HandleEntityLeaveBed (int a_EntityID); void HandleEntitySprinting (int a_EntityID, bool a_IsSprinting); + /** Kicks the client if the same username is already logged in. + Returns false if the client has been kicked, true otherwise. */ + bool CheckMultiLogin(const AString & a_Username); + /** Called when the protocol handshake has been received (for protocol versions that support it; otherwise the first instant when a username is received). Returns true if the player is to be let in, false if they were disconnected @@ -378,12 +382,15 @@ private: /** Seconds since the last packet data was received (updated in Tick(), reset in DataReceived()) */ float m_TimeSinceLastPacket; - short m_Ping; - int m_PingID; - long long m_PingStartTime; - long long m_LastPingTime; - static const unsigned short PING_TIME_MS = 1000; // Vanilla sends 1 per 20 ticks (1 second or every 1000 ms) - + /** Duration of the last completed client ping. */ + std::chrono::steady_clock::duration m_Ping; + + /** ID of the last ping request sent to the client. */ + int m_PingID; + + /** Time of the last ping request sent to the client. */ + std::chrono::steady_clock::time_point m_PingStartTime; + // Values required for block dig animation int m_BlockDigAnimStage; // Current stage of the animation; -1 if not digging int m_BlockDigAnimSpeed; // Current speed of the animation (units ???) diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp index 2c2b02a69..202fb900e 100644 --- a/src/CraftingRecipes.cpp +++ b/src/CraftingRecipes.cpp @@ -294,7 +294,7 @@ void cCraftingRecipes::GetRecipe(cPlayer & a_Player, cCraftingGrid & a_CraftingG if (Recipe.get() == nullptr) { // Allow plugins to intercept a no-recipe-found situation: - cRoot::Get()->GetPluginManager()->CallHookCraftingNoRecipe(a_Player, a_CraftingGrid, &a_Recipe); + cRoot::Get()->GetPluginManager()->CallHookCraftingNoRecipe(a_Player, a_CraftingGrid, a_Recipe); return; } for (cRecipeSlots::const_iterator itr = Recipe->m_Ingredients.begin(); itr != Recipe->m_Ingredients.end(); ++itr) diff --git a/src/DeadlockDetect.cpp b/src/DeadlockDetect.cpp index 7f703416c..3bb897221 100644 --- a/src/DeadlockDetect.cpp +++ b/src/DeadlockDetect.cpp @@ -13,7 +13,7 @@ -/// Number of milliseconds per cycle +/** Number of milliseconds per cycle */ const int CYCLE_MILLISECONDS = 100; @@ -87,7 +87,7 @@ void cDeadlockDetect::Execute(void) } Checker(this); cRoot::Get()->ForEachWorld(Checker); - cSleep::MilliSleep(CYCLE_MILLISECONDS); + std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_MILLISECONDS)); } // while (should run) } @@ -119,7 +119,7 @@ void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age) if (WorldAge.m_Age == a_Age) { WorldAge.m_NumCyclesSame += 1; - if (WorldAge.m_NumCyclesSame > (1000 * m_IntervalSec) / CYCLE_MILLISECONDS) + if (WorldAge.m_NumCyclesSame > (m_IntervalSec * 1000) / CYCLE_MILLISECONDS) { DeadlockDetected(); } diff --git a/src/DeadlockDetect.h b/src/DeadlockDetect.h index 6aa98acbb..57027e923 100644 --- a/src/DeadlockDetect.h +++ b/src/DeadlockDetect.h @@ -28,7 +28,7 @@ class cDeadlockDetect : public: cDeadlockDetect(void); - /// Starts the detection. Hides cIsThread's Start, because we need some initialization + /** Starts the detection. Hides cIsThread's Start, because we need some initialization */ bool Start(int a_IntervalSec); protected: diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index afc61c73f..54b9f2a20 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -665,7 +665,10 @@ int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_Dama // Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover // Filter out damage types that are not protected by armor: - if (!ArmorCoversAgainst(a_DamageType)) return 0; + if (!ArmorCoversAgainst(a_DamageType)) + { + return 0; + } // Add up all armor points: // Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20 @@ -1011,9 +1014,18 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk) // Block hit was within our projected path // Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1. // For example: HitNormal.y = -1 : BLOCK_FACE_YM; HitNormal.y = 1 : BLOCK_FACE_YP - if (Tracer.HitNormal.x != 0.f) NextSpeed.x = 0.f; - if (Tracer.HitNormal.y != 0.f) NextSpeed.y = 0.f; - if (Tracer.HitNormal.z != 0.f) NextSpeed.z = 0.f; + if (Tracer.HitNormal.x != 0.f) + { + NextSpeed.x = 0.f; + } + if (Tracer.HitNormal.y != 0.f) + { + NextSpeed.y = 0.f; + } + if (Tracer.HitNormal.z != 0.f) + { + NextSpeed.z = 0.f; + } if (Tracer.HitNormal.y == 1.f) // Hit BLOCK_FACE_YP, we are on the ground { @@ -1276,7 +1288,7 @@ bool cEntity::DetectPortal() return false; } - if (IsPlayer() && !((cPlayer *)this)->IsGameModeCreative() && m_PortalCooldownData.m_TicksDelayed != 80) + if (IsPlayer() && !((cPlayer *)this)->IsGameModeCreative() && (m_PortalCooldownData.m_TicksDelayed != 80)) { // Delay teleportation for four seconds if the entity is a non-creative player m_PortalCooldownData.m_TicksDelayed++; diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index 22e046800..fac4f0714 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -142,8 +142,13 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk) if (!IsBlockRail(InsideType)) { - Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta); // When an descending minecart hits a flat rail, it goes through the ground; check for this - if (IsBlockRail(InsideType)) AddPosY(1); // Push cart upwards + // When a descending minecart hits a flat rail, it goes through the ground; check for this + Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta); + if (IsBlockRail(InsideType)) + { + // Push cart upwards + AddPosY(1); + } } bool WasDetectorRail = false; @@ -218,7 +223,10 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) // Execute both the entity and block collision checks bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); - if (EntCol || BlckCol) return; + if (EntCol || BlckCol) + { + return; + } if (GetSpeedZ() != NO_SPEED) // Don't do anything if cart is stationary { @@ -243,7 +251,10 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt) SetSpeedZ(NO_SPEED); bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); - if (EntCol || BlckCol) return; + if (EntCol || BlckCol) + { + return; + } if (GetSpeedX() != NO_SPEED) { @@ -422,7 +433,10 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta) SetSpeedX(0); bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); - if (EntCol || BlckCol) return; + if (EntCol || BlckCol) + { + return; + } if (GetSpeedZ() != NO_SPEED) { @@ -445,7 +459,10 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta) SetSpeedZ(NO_SPEED); bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta); - if (EntCol || BlckCol) return; + if (EntCol || BlckCol) + { + return; + } if (GetSpeedX() != NO_SPEED) { diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index edcdb4799..8d2eb1e5f 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -11,7 +11,6 @@ #include "../BlockEntities/BlockEntity.h" #include "../BlockEntities/EnderChestEntity.h" #include "../Root.h" -#include "../OSSupport/Timer.h" #include "../Chunk.h" #include "../Items/ItemHandler.h" #include "../Vector3.h" @@ -27,7 +26,7 @@ #define PLAYER_INVENTORY_SAVE_INTERVAL 6000 // 1000 = once per second -#define PLAYER_LIST_TIME_MS 1000 +#define PLAYER_LIST_TIME_MS std::chrono::milliseconds(1000) @@ -91,9 +90,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) : SetMaxHealth(MAX_HEALTH); m_Health = MAX_HEALTH; - cTimer t1; - m_LastPlayerListTime = t1.GetNowTime(); - + m_LastPlayerListTime = std::chrono::steady_clock::now(); m_PlayerName = a_PlayerName; cWorld * World = nullptr; @@ -120,6 +117,11 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) : { m_CanFly = true; } + if (World->IsGameModeSpectator()) // Otherwise Player will fall out of the world on join + { + m_CanFly = true; + m_IsFlying = true; + } } cRoot::Get()->GetServer()->PlayerCreated(this); @@ -263,11 +265,10 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk) m_Inventory.UpdateItems(); // Send Player List (Once per m_LastPlayerListTime/1000 ms) - cTimer t1; - if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= t1.GetNowTime()) + if (m_LastPlayerListTime + PLAYER_LIST_TIME_MS <= std::chrono::steady_clock::now()) { m_World->BroadcastPlayerListUpdatePing(*this); - m_LastPlayerListTime = t1.GetNowTime(); + m_LastPlayerListTime = std::chrono::steady_clock::now(); } if (IsFlying()) @@ -358,7 +359,7 @@ float cPlayer::GetXpPercentage() bool cPlayer::SetCurrentExperience(short int a_CurrentXp) { - if (!(a_CurrentXp >= 0) || (a_CurrentXp > (SHRT_MAX - m_LifetimeTotalXp))) + if (!(a_CurrentXp >= 0) || (a_CurrentXp > (std::numeric_limits<short>().max() - m_LifetimeTotalXp))) { LOGWARNING("Tried to update experiece with an invalid Xp value: %d", a_CurrentXp); return false; // oops, they gave us a dodgey number @@ -378,18 +379,17 @@ bool cPlayer::SetCurrentExperience(short int a_CurrentXp) short cPlayer::DeltaExperience(short a_Xp_delta) { - if (a_Xp_delta > (SHRT_MAX - m_CurrentXp)) + if (a_Xp_delta > (std::numeric_limits<short>().max() - m_CurrentXp)) { // Value was bad, abort and report - LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.", - a_Xp_delta); + LOGWARNING("Attempt was made to increment Xp by %d, which overflowed the short datatype. Ignoring.", a_Xp_delta); return -1; // Should we instead just return the current Xp? } m_CurrentXp += a_Xp_delta; // Make sure they didn't subtract too much - m_CurrentXp = std::max<short int>(m_CurrentXp, 0); + m_CurrentXp = std::max<short>(m_CurrentXp, 0); // Update total for score calculation if (a_Xp_delta > 0) @@ -397,8 +397,7 @@ short cPlayer::DeltaExperience(short a_Xp_delta) m_LifetimeTotalXp += a_Xp_delta; } - LOGD("Player \"%s\" gained/lost %d experience, total is now: %d", - GetName().c_str(), a_Xp_delta, m_CurrentXp); + LOGD("Player \"%s\" gained/lost %d experience, total is now: %d", GetName().c_str(), a_Xp_delta, m_CurrentXp); // Set experience to be updated m_bDirtyExperience = true; @@ -1074,7 +1073,7 @@ bool cPlayer::IsGameModeAdventure(void) const bool cPlayer::IsGameModeSpectator(void) const { return (m_GameMode == gmSpectator) || // Either the player is explicitly in Spectator - ((m_GameMode == gmNotSet) && m_World->IsGameModeSpectator()); // or they inherit from the world and the world is Adventure + ((m_GameMode == gmNotSet) && m_World->IsGameModeSpectator()); // or they inherit from the world and the world is Spectator } @@ -1893,8 +1892,8 @@ void cPlayer::UseEquippedItem(int a_Amount) void cPlayer::TickBurning(cChunk & a_Chunk) { - // Don't burn in creative and stop burning in creative if necessary - if (!IsGameModeCreative()) + // Don't burn in creative or spectator and stop burning in creative if necessary + if (!IsGameModeCreative() && !IsGameModeSpectator()) { super::TickBurning(a_Chunk); } @@ -1913,9 +1912,9 @@ void cPlayer::HandleFood(void) { // Ref.: http://www.minecraftwiki.net/wiki/Hunger - if (IsGameModeCreative()) + if (IsGameModeCreative() || IsGameModeSpectator()) { - // Hunger is disabled for Creative + // Hunger is disabled for Creative and Spectator return; } @@ -2080,7 +2079,7 @@ void cPlayer::UpdateMovementStats(const Vector3d & a_DeltaPos) void cPlayer::ApplyFoodExhaustionFromMovement() { - if (IsGameModeCreative()) + if (IsGameModeCreative() || IsGameModeSpectator()) { return; } diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 4bb51b556..c643aaa8e 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -516,7 +516,7 @@ protected: /** The item being dragged by the cursor while in a UI window */ cItem m_DraggingItem; - long long m_LastPlayerListTime; + std::chrono::steady_clock::time_point m_LastPlayerListTime; cClientHandle * m_ClientHandle; diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp index 42bf5f3f9..515dc25ea 100644 --- a/src/FastRandom.cpp +++ b/src/FastRandom.cpp @@ -4,13 +4,15 @@ // Implements the cFastRandom class representing a fast random number generator #include "Globals.h" -#include <time.h> #include "FastRandom.h" +//////////////////////////////////////////////////////////////////////////////// +// cFastRandom: + #if 0 && defined(_DEBUG) // Self-test // Both ints and floats are quick-tested to see if the random is calculated correctly, checking the range in ASSERTs, @@ -83,16 +85,8 @@ public: - -int cFastRandom::m_SeedCounter = 0; - - - - - cFastRandom::cFastRandom(void) : - m_Seed(m_SeedCounter++), - m_Counter(0) + m_LinearRand(static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count())) { } @@ -102,82 +96,96 @@ cFastRandom::cFastRandom(void) : int cFastRandom::NextInt(int a_Range) { - ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges - ASSERT(a_Range > 0); - - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = a_Range + Counter * 57 + m_Seed * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - return ((n / 11) % a_Range); + std::uniform_int_distribution<> distribution(0, a_Range - 1); + return distribution(m_LinearRand); } + int cFastRandom::NextInt(int a_Range, int a_Salt) { - ASSERT(a_Range <= 1000000); // The random is not sufficiently linearly distributed with bigger ranges - ASSERT(a_Range > 0); - - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = a_Range + Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - return ((n / 11) % a_Range); + m_LinearRand.seed(a_Salt); + std::uniform_int_distribution<> distribution(0, a_Range - 1); + return distribution(m_LinearRand); } + float cFastRandom::NextFloat(float a_Range) { - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = (int)a_Range + Counter * 57 + m_Seed * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - - // Convert the integer into float with the specified range: - return (((float)n / (float)0x7fffffff) * a_Range); + std::uniform_real_distribution<float> distribution(0, a_Range); + return distribution(m_LinearRand); } + float cFastRandom::NextFloat(float a_Range, int a_Salt) { - // Make the m_Counter operations as minimal as possible, to emulate atomicity - int Counter = m_Counter++; - - // Use a_Range, a_Salt, m_Counter and m_Seed as inputs to the pseudorandom function: - int n = (int)a_Range + Counter * 57 + m_Seed * 57 * 57 + a_Salt * 57 * 57 * 57; - n = (n << 13) ^ n; - n = ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff); - - // Convert the integer into float with the specified range: - return (((float)n / (float)0x7fffffff) * a_Range); + m_LinearRand.seed(a_Salt); + std::uniform_real_distribution<float> distribution(0, a_Range); + return distribution(m_LinearRand); } + int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End) { - cFastRandom Random; - return Random.NextInt(a_End - a_Begin + 1) + a_Begin; + std::uniform_int_distribution<> distribution(a_Begin, a_End); + return distribution(m_LinearRand); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// MTRand: + +MTRand::MTRand() : + m_MersenneRand(static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count())) +{ +} + + + + + +int MTRand::randInt(int a_Range) +{ + std::uniform_int_distribution<> distribution(0, a_Range); + return distribution(m_MersenneRand); +} + + + + + +int MTRand::randInt() +{ + std::uniform_int_distribution<> distribution(0, std::numeric_limits<int>::max()); + return distribution(m_MersenneRand); +} + + + + + +double MTRand::rand(double a_Range) +{ + std::uniform_real_distribution<> distribution(0, a_Range); + return distribution(m_MersenneRand); } diff --git a/src/FastRandom.h b/src/FastRandom.h index cebebad96..64a087c97 100644 --- a/src/FastRandom.h +++ b/src/FastRandom.h @@ -22,6 +22,7 @@ salts, the values they get will be different. #pragma once +#include <random> @@ -30,18 +31,19 @@ salts, the values they get will be different. class cFastRandom { public: + cFastRandom(void); - /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M + /** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M */ int NextInt(int a_Range); - /// Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness + /** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness */ int NextInt(int a_Range, int a_Salt); - /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M + /** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M */ float NextFloat(float a_Range); - /// Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness + /** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness */ float NextFloat(float a_Range, int a_Salt); /** Returns a random float between 0 and 1. */ @@ -49,14 +51,35 @@ public: /** Returns a random int in the range [a_Begin .. a_End] */ int GenerateRandomInteger(int a_Begin, int a_End); - -protected: - int m_Seed; - int m_Counter; - - /// Counter that is used to initialize the seed, incremented for each object created - static int m_SeedCounter; -} ; + +private: + + std::minstd_rand m_LinearRand; +}; + + + + + +class MTRand +{ +public: + + MTRand(void); + + /** Returns a random integer in the range [0 .. a_Range]. */ + int randInt(int a_Range); + + /** Returns a random integer in the range [0 .. MAX_INT]. */ + int randInt(void); + + /** Returns a random floating point number in the range [0 .. a_Range]. */ + double rand(double a_Range); + +private: + + std::mt19937 m_MersenneRand; +}; diff --git a/src/Generating/Caves.cpp b/src/Generating/Caves.cpp index fc925a150..e4735cb83 100644 --- a/src/Generating/Caves.cpp +++ b/src/Generating/Caves.cpp @@ -692,8 +692,14 @@ static float GetMarbleNoise( float x, float y, float z, cNoise & a_Noise) float oct1 = (a_Noise.CubicNoise3D(x * 0.1f, y * 0.1f, z * 0.1f)) * 4; oct1 = oct1 * oct1 * oct1; - if (oct1 < 0.f) oct1 = PI_2; - if (oct1 > PI_2) oct1 = PI_2; + if (oct1 < 0.f) + { + oct1 = PI_2; + } + if (oct1 > PI_2) + { + oct1 = PI_2; + } return oct1; } diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp index 3ee02c767..854aac60d 100644 --- a/src/Generating/ChunkGenerator.cpp +++ b/src/Generating/ChunkGenerator.cpp @@ -6,7 +6,7 @@ #include "ChunkDesc.h" #include "ComposableGenerator.h" #include "Noise3DGenerator.h" -#include "../MersenneTwister.h" +#include "FastRandom.h" @@ -110,29 +110,19 @@ void cChunkGenerator::Stop(void) -void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate) +void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback) { ASSERT(m_ChunkSink->IsChunkQueued(a_ChunkX, a_ChunkZ)); { cCSLock Lock(m_CS); - // Check if it is already in the queue: - for (cChunkCoordsWithBoolList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr) - { - if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ)) - { - // Already in the queue, bail out - return; - } - } // for itr - m_Queue[] - // Add to queue, issue a warning if too many: if (m_Queue.size() >= QUEUE_WARNING_LIMIT) { LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (" SIZE_T_FMT ")", a_ChunkX, a_ChunkZ, m_Queue.size()); } - m_Queue.push_back(cChunkCoordsWithBool(a_ChunkX, a_ChunkZ, a_ForceGenerate)); + m_Queue.push_back(cQueueItem{a_ChunkX, a_ChunkZ, a_ForceGenerate, a_Callback}); } m_Event.Set(); @@ -242,9 +232,9 @@ void cChunkGenerator::Execute(void) continue; } - cChunkCoordsWithBool coords = m_Queue.front(); // Get next coord from queue + cQueueItem item = m_Queue.front(); // Get next chunk from the queue bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT); - m_Queue.erase(m_Queue.begin()); // Remove coordinate from queue + m_Queue.erase(m_Queue.begin()); // Remove the item from the queue Lock.Unlock(); // Unlock ASAP m_evtRemoved.Set(); @@ -258,22 +248,35 @@ void cChunkGenerator::Execute(void) LastReportTick = clock(); } - if (!coords.m_ForceGenerate && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ)) + // Skip the chunk if it's already generated and regeneration is not forced: + if (!item.m_ForceGenerate && m_ChunkSink->IsChunkValid(item.m_ChunkX, item.m_ChunkZ)) { - LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ); - // Already generated, ignore request + LOGD("Chunk [%d, %d] already generated, skipping generation", item.m_ChunkX, item.m_ChunkZ); + if (item.m_Callback != nullptr) + { + item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ); + } continue; } - if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ)) + // Skip the chunk if the generator is overloaded: + if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(item.m_ChunkX, item.m_ChunkZ)) { - LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ); + LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ); + if (item.m_Callback != nullptr) + { + item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ); + } continue; } - LOGD("Generating chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ); - DoGenerate(coords.m_ChunkX, coords.m_ChunkZ); - + // Generate the chunk: + LOGD("Generating chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ); + DoGenerate(item.m_ChunkX, item.m_ChunkZ); + if (item.m_Callback != nullptr) + { + item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ); + } NumChunksGenerated++; } // while (!bStop) } diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h index 190d9e616..4af486ae1 100644 --- a/src/Generating/ChunkGenerator.h +++ b/src/Generating/ChunkGenerator.h @@ -119,8 +119,12 @@ public: bool Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile); void Stop(void); - /// Queues the chunk for generation; removes duplicate requests - void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate); + /** Queues the chunk for generation + If a-ForceGenerate is set, the chunk is regenerated even if the data is already present in the chunksink. + a_Callback is called after the chunk is generated. If the chunk was already present, the callback is still called, even if not regenerating. + It is legal to set the callback to nullptr, no callback is called then. + If the generator becomes overloaded and skips this chunk, the callback is still called. */ + void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback = nullptr); /// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap); @@ -131,22 +135,46 @@ public: int GetSeed(void) const { return m_Seed; } - /// Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome + /** Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome */ EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ); - /// Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. + /** Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. */ static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default); private: + struct cQueueItem + { + /** The chunk coords */ + int m_ChunkX, m_ChunkZ; + + /** Force the regeneration of an already existing chunk */ + bool m_ForceGenerate; + + /** Callback to call after generating.*/ + cChunkCoordCallback * m_Callback; + }; + + typedef std::list<cQueueItem> cGenQueue; + + + /** Seed used for the generator. */ int m_Seed; - cCriticalSection m_CS; - cChunkCoordsWithBoolList m_Queue; - cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate - cEvent m_evtRemoved; ///< Set when an item is removed from the queue + /** CS protecting access to the queue. */ + cCriticalSection m_CS; + + /** Queue of the chunks to be generated. Protected against multithreaded access by m_CS. */ + cGenQueue m_Queue; + + /** Set when an item is added to the queue or the thread should terminate. */ + cEvent m_Event; + + /** Set when an item is removed from the queue. */ + cEvent m_evtRemoved; - cGenerator * m_Generator; ///< The actual generator engine used to generate chunks + /** The actual generator engine used to generate chunks. */ + cGenerator * m_Generator; /** The plugin interface that may modify the generated chunks */ cPluginInterface * m_PluginInterface; @@ -158,6 +186,7 @@ private: // cIsThread override: virtual void Execute(void) override; + /** Generates the specified chunk and sets it into the chunksink. */ void DoGenerate(int a_ChunkX, int a_ChunkZ); }; diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp index 6c3b50b4e..cb9c04fd7 100644 --- a/src/Generating/CompoGen.cpp +++ b/src/Generating/CompoGen.cpp @@ -290,17 +290,7 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc, const cChunkDesc: BLOCKTYPE Block = E_BLOCK_AIR; if (Val < m_Threshold) // Don't calculate if the block should be Netherrack or Soulsand when it's already decided that it's air. { - NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(BaseX + x)) / 8; - NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(BaseZ + z)) / 8; - NOISE_DATATYPE CompBlock = m_Noise1.CubicNoise3D(NoiseX, (float) (y + Segment) / 2, NoiseY); - if (CompBlock < -0.5) - { - Block = E_BLOCK_SOULSAND; - } - else - { - Block = E_BLOCK_NETHERRACK; - } + Block = E_BLOCK_NETHERRACK; } a_ChunkDesc.SetBlockType(x, y + Segment, z, Block); } diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index 4192dfa72..bda45ad92 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -294,7 +294,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr) { // Finishers, alpha-sorted: - if (NoCaseCompare(*itr, "BottomLava") == 0) + if (NoCaseCompare(*itr, "Animals") == 0) + { + m_FinishGens.push_back(cFinishGenPtr(new cFinishGenPassiveMobs(Seed, a_IniFile, Dimension))); + } + else if (NoCaseCompare(*itr, "BottomLava") == 0) { int DefaultBottomLavaLevel = (Dimension == dimNether) ? 30 : 10; int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel); @@ -570,6 +574,10 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) GridSize, MaxOffset ))); } + else if (NoCaseCompare(*itr, "SoulsandRims") == 0) + { + m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSoulsandRims(Seed))); + } else if (NoCaseCompare(*itr, "Snow") == 0) { m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSnow)); diff --git a/src/Generating/DungeonRoomsFinisher.cpp b/src/Generating/DungeonRoomsFinisher.cpp index 7ab22c2c5..c4bf8839e 100644 --- a/src/Generating/DungeonRoomsFinisher.cpp +++ b/src/Generating/DungeonRoomsFinisher.cpp @@ -7,6 +7,7 @@ #include "DungeonRoomsFinisher.h" #include "../FastRandom.h" #include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/MobSpawnerEntity.h" @@ -57,6 +58,22 @@ public: int SecondChestPos = (FirstChestPos + 2 + (rnd % (NumPositions - 3))) % NumPositions; m_Chest1 = DecodeChestCoords(FirstChestPos, SizeX, SizeZ); m_Chest2 = DecodeChestCoords(SecondChestPos, SizeX, SizeZ); + + // Choose what the mobspawner will spawn. + // 25% chance for a spider, 25% for a skeleton and 50% chance to get a zombie spawer. + int MobType = (a_Noise.IntNoise3DInt(a_OriginX, m_FloorHeight, a_OriginZ) / 7) % 100; + if (MobType <= 25) + { + m_MonsterType = mtSkeleton; + } + else if (MobType <= 50) + { + m_MonsterType = mtSpider; + } + else + { + m_MonsterType = mtZombie; + } } protected: @@ -76,6 +93,8 @@ protected: /** The (absolute) coords of the second chest. The Y coord represents the chest's Meta value (facing). */ Vector3i m_Chest2; + /** The monster type for the mobspawner entity. */ + eMonsterType m_MonsterType; /** Decodes the position index along the room walls into a proper 2D position for a chest. @@ -112,8 +131,8 @@ protected: { int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; - int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width - 1); - int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1); + int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width); + int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width); int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width); int RelEndZ = Clamp(a_EndZ - BlockZ, 0, cChunkDef::Width); for (int y = a_StartY; y < a_EndY; y++) @@ -246,7 +265,9 @@ protected: ) { a_ChunkDesc.SetBlockTypeMeta(CenterX, b, CenterZ, E_BLOCK_MOB_SPAWNER, 0); - // TODO: Set the spawned mob + cMobSpawnerEntity * MobSpawner = static_cast<cMobSpawnerEntity *>(a_ChunkDesc.GetBlockEntity(CenterX, b, CenterZ)); + ASSERT((MobSpawner != nullptr) && (MobSpawner->GetBlockType() == E_BLOCK_MOB_SPAWNER)); + MobSpawner->SetEntity(m_MonsterType); } } } ; diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index 1e8a67dd0..e10cb00f8 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -26,6 +26,8 @@ #define DEF_OVERWORLD_LAVA_SPRINGS "0, 0; 10, 5; 11, 45; 48, 2; 64, 1; 255, 0" #define DEF_END_WATER_SPRINGS "0, 1; 255, 1" #define DEF_END_LAVA_SPRINGS "0, 1; 255, 1" +#define DEF_ANIMAL_SPAWN_PERCENT 10 +#define DEF_NO_ANIMALS 0 @@ -65,7 +67,7 @@ void cFinishGenNetherClumpFoliage::GenFinish(cChunkDesc & a_ChunkDesc) { continue; } - + // Choose what block to use. NOISE_DATATYPE BlockType = m_Noise.IntNoise3D((int) ChunkX, y, (int) ChunkZ); if (BlockType < -0.7) @@ -195,10 +197,10 @@ void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc) { continue; } - + // Get the top block + 1. This is the place where the grass would finaly be placed: int y = a_ChunkDesc.GetHeight(x, z) + 1; - + if (y >= 255) { continue; @@ -281,7 +283,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_ { return false; } - + // All conditions met, place a sugarcane here: a_ChunkDesc.SetBlockType(a_RelX, a_RelY + 1, a_RelZ, E_BLOCK_SUGARCANE); return true; @@ -294,7 +296,7 @@ bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc) { // Generate small foliage (1-block): - + // TODO: Update heightmap with 1-block-tall foliage for (int z = 0; z < cChunkDef::Width; z++) { @@ -319,7 +321,7 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc) // WEIRD, since we're using heightmap, so there should NOT be anything above it continue; } - + const float xx = (float)BlockX; float val1 = m_Noise.CubicNoise2D(xx * 0.1f, zz * 0.1f); float val2 = m_Noise.CubicNoise2D(xx * 0.01f, zz * 0.01f); @@ -359,7 +361,7 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc) } break; } // case E_BLOCK_GRASS - + case E_BLOCK_SAND: { int y = Top + 1; @@ -370,7 +372,8 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc) (a_ChunkDesc.GetBlockType(x + 1, y, z) == E_BLOCK_AIR) && (a_ChunkDesc.GetBlockType(x - 1, y, z) == E_BLOCK_AIR) && (a_ChunkDesc.GetBlockType(x, y, z + 1) == E_BLOCK_AIR) && - (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR) + (a_ChunkDesc.GetBlockType(x, y, z - 1) == E_BLOCK_AIR) && + IsDesertVariant(a_ChunkDesc.GetBiome(x, z)) ) { a_ChunkDesc.SetBlockType(x, ++Top, z, E_BLOCK_CACTUS); @@ -391,6 +394,72 @@ void cFinishGenSprinkleFoliage::GenFinish(cChunkDesc & a_ChunkDesc) +bool cFinishGenSprinkleFoliage::IsDesertVariant(EMCSBiome a_Biome) +{ + return + ( + (a_Biome == biDesertHills) || + (a_Biome == biDesert) || + (a_Biome == biDesertM) + ); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cFinishGenSoulsandRims + +void cFinishGenSoulsandRims::GenFinish(cChunkDesc & a_ChunkDesc) +{ + int ChunkX = a_ChunkDesc.GetChunkX() * cChunkDef::Width; + int ChunkZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + HEIGHTTYPE MaxHeight = a_ChunkDesc.GetMaxHeight(); + + for (int x = 0; x < 16; x++) + { + int xx = ChunkX + x; + for (int z = 0; z < 16; z++) + { + int zz = ChunkZ + z; + + // Place soulsand rims when netherrack gets thin + for (int y = 2; y < MaxHeight - 2; y++) + { + // The current block is air. Let's bail ut. + BLOCKTYPE Block = a_ChunkDesc.GetBlockType(x, y, z); + if (Block == E_BLOCK_AIR) + { + continue; + } + + if ( + ((a_ChunkDesc.GetBlockType(x, y + 1, z) != E_BLOCK_AIR) && + ( a_ChunkDesc.GetBlockType(x, y + 2, z) != E_BLOCK_AIR)) || + ((a_ChunkDesc.GetBlockType(x, y - 1, z) != E_BLOCK_AIR) && + ( a_ChunkDesc.GetBlockType(x, y - 2, z) != E_BLOCK_AIR)) + ) + { + continue; + } + + NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(xx)) / 32; + NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(zz)) / 32; + NOISE_DATATYPE CompBlock = m_Noise.CubicNoise3D(NoiseX, (float) (y) / 4, NoiseY); + if (CompBlock < 0) + { + a_ChunkDesc.SetBlockType(x, y, z, E_BLOCK_SOULSAND); + } + } + } + } +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cFinishGenSnow: @@ -716,7 +785,7 @@ void cFinishGenPreSimulator::StationarizeFluid( } // for y } // for x } // for z - + // Turn fluid at the chunk edges into non-stationary fluid: for (int y = 0; y < cChunkDef::Height; y++) { @@ -808,12 +877,12 @@ void cFinishGenFluidSprings::GenFinish(cChunkDesc & a_ChunkDesc) // Not in this chunk return; } - + // Get the height at which to try: int Height = m_Noise.IntNoise3DInt(128 * a_ChunkDesc.GetChunkX(), 1024, 256 * a_ChunkDesc.GetChunkZ()) / 11; Height %= m_HeightDistribution.GetSum(); Height = m_HeightDistribution.MapValue(Height); - + // Try adding the spring at the height, if unsuccessful, move lower: for (int y = Height; y > 1; y--) { @@ -851,7 +920,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int { return false; } - + static const struct { int x, y, z; @@ -882,7 +951,7 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int { return false; } - + // Has exactly one air neighbor, place a spring: a_ChunkDesc.SetBlockTypeMeta(x, y, z, m_Fluid, 0); return true; @@ -891,3 +960,236 @@ bool cFinishGenFluidSprings::TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int + +//////////////////////////////////////////////////////////////////////////////// +// cFinishGenPassiveMobs: + +cFinishGenPassiveMobs::cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension) : + m_Noise(a_Seed) +{ + AString SectionName = "Animals"; + int DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT; + switch (a_Dimension) + { + case dimOverworld: + { + DefaultAnimalSpawnChunkPercentage = DEF_ANIMAL_SPAWN_PERCENT; + break; + } + case dimNether: + case dimEnd: // No nether or end animals (currently) + { + DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS; + break; + } + default: + { + ASSERT(!"Unhandled world dimension"); + DefaultAnimalSpawnChunkPercentage = DEF_NO_ANIMALS; + break; + } + } // switch (dimension) + m_AnimalProbability = a_IniFile.GetValueSetI(SectionName, "AnimalSpawnChunkPercentage", DefaultAnimalSpawnChunkPercentage); + if ((m_AnimalProbability < 0) || (m_AnimalProbability > 100)) + { + LOGWARNING("[Animals]: AnimalSpawnChunkPercentage is invalid, using the default of \"%d\".", DefaultAnimalSpawnChunkPercentage); + m_AnimalProbability = DefaultAnimalSpawnChunkPercentage; + } +} + + + + + +void cFinishGenPassiveMobs::GenFinish(cChunkDesc & a_ChunkDesc) +{ + int chunkX = a_ChunkDesc.GetChunkX(); + int chunkZ = a_ChunkDesc.GetChunkZ(); + int ChanceRnd = (m_Noise.IntNoise2DInt(chunkX, chunkZ) / 7) % 100; + if (ChanceRnd > m_AnimalProbability) + { + return; + } + + eMonsterType RandomMob = GetRandomMob(a_ChunkDesc); + if (RandomMob == mtInvalidType) + { + // No mobs here. Don't send an error, because if the biome was a desert it would return mtInvalidType as well. + return; + } + + // Try spawning a pack center 10 times, should get roughly the same probability + for (int Tries = 0; Tries < 10; Tries++) + { + int PackCenterX = (m_Noise.IntNoise2DInt(chunkX + chunkZ, Tries) / 7) % cChunkDef::Width; + int PackCenterZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries) / 7) % cChunkDef::Width; + if (TrySpawnAnimals(a_ChunkDesc, PackCenterX, a_ChunkDesc.GetHeight(PackCenterX, PackCenterZ), PackCenterZ, RandomMob)) + { + for (int i = 0; i < 3; i++) + { + int OffsetX = (m_Noise.IntNoise2DInt(chunkX + chunkZ + i, Tries) / 7) % cChunkDef::Width; + int OffsetZ = (m_Noise.IntNoise2DInt(chunkX, chunkZ + Tries + i) / 7) % cChunkDef::Width; + TrySpawnAnimals(a_ChunkDesc, OffsetX, a_ChunkDesc.GetHeight(OffsetX, OffsetZ), OffsetZ, RandomMob); + } + return; + + } // if pack center spawn successful + } // for tries +} + + + + + +bool cFinishGenPassiveMobs::TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, eMonsterType AnimalToSpawn) +{ + if ((a_RelY >= cChunkDef::Height - 1) || (a_RelY <= 0)) + { + return false; + } + + BLOCKTYPE BlockAtHead = a_ChunkDesc.GetBlockType(a_RelX, a_RelY + 1, a_RelZ); + BLOCKTYPE BlockAtFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY, a_RelZ); + BLOCKTYPE BlockUnderFeet = a_ChunkDesc.GetBlockType(a_RelX, a_RelY - 1, a_RelZ); + + // Check block below (opaque, grass, water), and above (air) + if ((AnimalToSpawn == mtSquid) && (BlockAtFeet != E_BLOCK_WATER)) + { + return false; + } + if ( + (AnimalToSpawn != mtSquid) && + (BlockAtHead != E_BLOCK_AIR) && + (BlockAtFeet != E_BLOCK_AIR) && + (!cBlockInfo::IsTransparent(BlockUnderFeet)) + ) + { + return false; + } + if ( + (BlockUnderFeet != E_BLOCK_GRASS) && + ((AnimalToSpawn == mtSheep) || (AnimalToSpawn == mtChicken) || (AnimalToSpawn == mtPig)) + ) + { + return false; + } + if ((AnimalToSpawn == mtMooshroom) && (BlockUnderFeet != E_BLOCK_MYCELIUM)) + { + return false; + } + + double AnimalX = static_cast<double>(a_ChunkDesc.GetChunkX() * cChunkDef::Width + a_RelX + 0.5); + double AnimalY = a_RelY; + double AnimalZ = static_cast<double>(a_ChunkDesc.GetChunkZ() * cChunkDef::Width + a_RelZ + 0.5); + + cMonster * NewMob = cMonster::NewMonsterFromType(AnimalToSpawn); + NewMob->SetPosition(AnimalX, AnimalY, AnimalZ); + a_ChunkDesc.GetEntities().push_back(NewMob); + LOGD("Spawning %s #%i at {%.02f, %.02f, %.02f}", NewMob->GetClass(), NewMob->GetUniqueID(), AnimalX, AnimalY, AnimalZ); + + return true; +} + + + + + +eMonsterType cFinishGenPassiveMobs::GetRandomMob(cChunkDesc & a_ChunkDesc) +{ + + std::set<eMonsterType> ListOfSpawnables; + int chunkX = a_ChunkDesc.GetChunkX(); + int chunkZ = a_ChunkDesc.GetChunkZ(); + int x = (m_Noise.IntNoise2DInt(chunkX, chunkZ + 10) / 7) % cChunkDef::Width; + int z = (m_Noise.IntNoise2DInt(chunkX + chunkZ, chunkZ) / 7) % cChunkDef::Width; + + // Check biomes first to get a list of animals + switch (a_ChunkDesc.GetBiome(x, z)) + { + // No animals in deserts or non-overworld dimensions + case biNether: + case biEnd: + case biDesertHills: + case biDesert: + case biDesertM: + { + return mtInvalidType; + } + + // Mooshroom only - no other mobs on mushroom islands + case biMushroomIsland: + case biMushroomShore: + { + return mtMooshroom; + } + + // Add squid in ocean biomes + case biOcean: + case biFrozenOcean: + case biFrozenRiver: + case biRiver: + case biDeepOcean: + { + ListOfSpawnables.insert(mtSquid); + break; + } + + // Add ocelots in jungle biomes + case biJungle: + case biJungleHills: + case biJungleEdge: + case biJungleM: + case biJungleEdgeM: + { + ListOfSpawnables.insert(mtOcelot); + break; + } + + // Add horses in plains-like biomes + case biPlains: + case biSunflowerPlains: + case biSavanna: + case biSavannaPlateau: + case biSavannaM: + case biSavannaPlateauM: + { + ListOfSpawnables.insert(mtHorse); + break; + } + + // Add wolves in forest and spruce forests + case biForest: + case biTaiga: + case biMegaTaiga: + case biColdTaiga: + case biColdTaigaM: + { + ListOfSpawnables.insert(mtWolf); + break; + } + // Nothing special about this biome + default: + { + break; + } + } + ListOfSpawnables.insert(mtChicken); + ListOfSpawnables.insert(mtCow); + ListOfSpawnables.insert(mtPig); + ListOfSpawnables.insert(mtSheep); + + if (ListOfSpawnables.empty()) + { + return mtInvalidType; + } + + int RandMob = (m_Noise.IntNoise2DInt(chunkX - chunkZ + 2, chunkX + 5) / 7) % ListOfSpawnables.size(); + auto MobIter = ListOfSpawnables.begin(); + std::advance(MobIter, RandMob); + + return *MobIter; +} + + + + diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h index 991a85787..ae6dee590 100644 --- a/src/Generating/FinishGen.h +++ b/src/Generating/FinishGen.h @@ -18,6 +18,7 @@ #include "ComposableGenerator.h" #include "../Noise/Noise.h" #include "../ProbabDistrib.h" +#include "../Mobs/Monster.h" @@ -117,19 +118,41 @@ protected: +class cFinishGenSoulsandRims : + public cFinishGen +{ +public: + cFinishGenSoulsandRims(int a_Seed) : + m_Noise(a_Seed) + { + } + +protected: + cNoise m_Noise; + + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +} ; + + + + + class cFinishGenSprinkleFoliage : public cFinishGen { public: cFinishGenSprinkleFoliage(int a_Seed) : m_Noise(a_Seed), m_Seed(a_Seed) {} - + protected: cNoise m_Noise; int m_Seed; - + /// Tries to place sugarcane at the coords specified, returns true if successful bool TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ); - + + // Returns true is the specified biome is a desert or its variant + static bool IsDesertVariant(EMCSBiome a_biome); + // cFinishGen override: virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; } ; @@ -167,31 +190,31 @@ public: { m_IsAllowedBelow[idx] = false; } - + // Load the allowed blocks into m_IsAllowedBelow for (BlockList::iterator itr = a_AllowedBelow.begin(); itr != a_AllowedBelow.end(); ++itr) { m_IsAllowedBelow[*itr] = true; } - + // Initialize all the biome types. for (size_t idx = 0; idx < ARRAYCOUNT(m_IsBiomeAllowed); ++idx) { m_IsBiomeAllowed[idx] = false; } - + // Load the allowed biomes into m_IsBiomeAllowed for (BiomeList::iterator itr = a_Biomes.begin(); itr != a_Biomes.end(); ++itr) { m_IsBiomeAllowed[*itr] = true; } } - + protected: cNoise m_Noise; BLOCKTYPE m_BlockType; int m_Amount; ///< Relative amount of blocks to try adding. 1 = one block per 256 biome columns. - + int GetNumToGen(const cChunkDef::BiomeMap & a_BiomeMap); // Returns true if the given biome is a biome that is allowed. @@ -206,7 +229,7 @@ protected: return m_IsAllowedBelow[a_BlockBelow]; } - + // cFinishGen override: virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; } ; @@ -223,11 +246,11 @@ public: m_Level(a_Level) { } - + int GetLevel(void) const { return m_Level; } protected: int m_Level; - + // cFinishGen override: virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; } ; @@ -241,7 +264,7 @@ class cFinishGenPreSimulator : { public: cFinishGenPreSimulator(bool a_PreSimulateFallingBlocks, bool a_PreSimulateWater, bool a_PreSimulateLava); - + protected: bool m_PreSimulateFallingBlocks; @@ -253,7 +276,7 @@ protected: cChunkDef::BlockTypes & a_BlockTypes, // Block types to read and change cChunkDef::HeightMap & a_HeightMap // Height map to update by the current data ); - + /** For each fluid block: - if all surroundings are of the same fluid, makes it stationary; otherwise makes it flowing (excl. top) - all fluid on the chunk's edge is made flowing @@ -278,7 +301,7 @@ class cFinishGenFluidSprings : { public: cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension); - + protected: cNoise m_Noise; @@ -289,10 +312,43 @@ protected: // cFinishGen override: virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; - /// Tries to place a spring at the specified coords, checks neighbors. Returns true if successful + /** Tries to place a spring at the specified coords, checks neighbors. Returns true if successful. */ bool TryPlaceSpring(cChunkDesc & a_ChunkDesc, int x, int y, int z); } ; + +/** This class populates generated chunks with packs of biome-dependant animals +Animals: cows, sheep, pigs, mooshrooms, squid, horses, wolves, ocelots */ +class cFinishGenPassiveMobs : + public cFinishGen +{ +public: + + cFinishGenPassiveMobs(int a_Seed, cIniFile & a_IniFile, eDimension a_Dimension); + +protected: + + /** The noise used as the source of randomness */ + cNoise m_Noise; + + /** Chance, [0..100], that an animal pack will be generated in a chunk */ + int m_AnimalProbability; + + + // cFinishGen override: + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; + + /** Returns false if an animal cannot spawn at given coords, else adds it to the chunk's entity list and returns true */ + bool TrySpawnAnimals(cChunkDesc & a_ChunkDesc, int x, int y, int z, eMonsterType AnimalToSpawn); + + /** Picks a random animal from biome-dependant list for a random position in the chunk. + Returns the chosen mob type, or mtInvalid if no mob chosen. */ + eMonsterType GetRandomMob(cChunkDesc & a_ChunkDesc); +} ; + + + + diff --git a/src/Generating/MineShafts.cpp b/src/Generating/MineShafts.cpp index 55b3b64dd..65588ce4b 100644 --- a/src/Generating/MineShafts.cpp +++ b/src/Generating/MineShafts.cpp @@ -20,6 +20,7 @@ in a depth-first processing. Each of the descendants will branch randomly, if no #include "MineShafts.h" #include "../Cuboid.h" #include "../BlockEntities/ChestEntity.h" +#include "../BlockEntities/MobSpawnerEntity.h" @@ -875,7 +876,9 @@ void cMineShaftCorridor::PlaceSpawner(cChunkDesc & a_ChunkDesc) ) { a_ChunkDesc.SetBlockTypeMeta(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ, E_BLOCK_MOB_SPAWNER, 0); - // TODO: The spawner needs its accompanying cMobSpawnerEntity, when implemented + cMobSpawnerEntity * MobSpawner = static_cast<cMobSpawnerEntity *>(a_ChunkDesc.GetBlockEntity(SpawnerRelX, m_BoundingBox.p1.y + 1, SpawnerRelZ)); + ASSERT((MobSpawner != nullptr) && (MobSpawner->GetBlockType() == E_BLOCK_MOB_SPAWNER)); + MobSpawner->SetEntity(mtCaveSpider); } } diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp index 57e23809e..b43a1a6de 100644 --- a/src/Generating/Noise3DGenerator.cpp +++ b/src/Generating/Noise3DGenerator.cpp @@ -6,7 +6,6 @@ #include "Globals.h" #include "Noise3DGenerator.h" #include "../OSSupport/File.h" -#include "../OSSupport/Timer.h" #include "../IniFile.h" #include "../LinearInterpolation.h" #include "../LinearUpscale.h" @@ -739,7 +738,7 @@ void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE case biFlowerForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break; case biForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break; case biForestHills: a_HeightAmp = 0.075f; a_MidPoint = 68; break; - case biFrozenRiver: a_HeightAmp = 0.4f; a_MidPoint = 57; break; + case biFrozenRiver: a_HeightAmp = 0.4f; a_MidPoint = 54; break; case biFrozenOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break; case biIceMountains: a_HeightAmp = 0.075f; a_MidPoint = 68; break; case biIcePlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break; @@ -764,7 +763,7 @@ void cBiomalNoise3DComposable::GetBiomeParams(EMCSBiome a_Biome, NOISE_DATATYPE case biNether: a_HeightAmp = 0.01f; a_MidPoint = 64; break; case biOcean: a_HeightAmp = 0.12f; a_MidPoint = 45; break; case biPlains: a_HeightAmp = 0.3f; a_MidPoint = 62; break; - case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 57; break; + case biRiver: a_HeightAmp = 0.4f; a_MidPoint = 54; break; case biRoofedForest: a_HeightAmp = 0.1f; a_MidPoint = 64; break; case biRoofedForestM: a_HeightAmp = 0.1f; a_MidPoint = 64; break; case biSavanna: a_HeightAmp = 0.3f; a_MidPoint = 62; break; diff --git a/src/Globals.h b/src/Globals.h index d75ae0093..61f500db9 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -184,9 +184,9 @@ template class SizeChecker<UInt16, 2>; // OS-dependent stuff: #ifdef _WIN32 - #define WIN32_LEAN_AND_MEAN - #define _WIN32_WINNT 0x501 // We want to target WinXP and higher + #define WIN32_LEAN_AND_MEAN + #define _WIN32_WINNT _WIN32_WINNT_WS03 // We want to target Windows XP with Service Pack 2 & Windows Server 2003 with Service Pack 1 and higher #include <Windows.h> #include <winsock2.h> @@ -241,6 +241,7 @@ template class SizeChecker<UInt16, 2>; // STL stuff: +#include <chrono> #include <vector> #include <list> #include <deque> @@ -251,17 +252,17 @@ template class SizeChecker<UInt16, 2>; #include <set> #include <queue> #include <limits> +#include <chrono> + #ifndef TEST_GLOBALS // Common headers (part 1, without macros): #include "StringUtils.h" - #include "OSSupport/Sleep.h" #include "OSSupport/CriticalSection.h" #include "OSSupport/Semaphore.h" #include "OSSupport/Event.h" - #include "OSSupport/Thread.h" #include "OSSupport/File.h" #include "Logger.h" #include "OSSupport/StackTrace.h" diff --git a/src/HTTPServer/HTTPFormParser.h b/src/HTTPServer/HTTPFormParser.h index edc6d2471..d9d29d9bc 100644 --- a/src/HTTPServer/HTTPFormParser.h +++ b/src/HTTPServer/HTTPFormParser.h @@ -82,7 +82,7 @@ protected: bool m_IsValid; /// The parser for the multipart data, if used - std::auto_ptr<cMultipartParser> m_MultipartParser; + std::unique_ptr<cMultipartParser> m_MultipartParser; /// Name of the currently parsed part in multipart data AString m_CurrentPartName; diff --git a/src/Inventory.cpp b/src/Inventory.cpp index 51ac32da9..fba6f4aea 100644 --- a/src/Inventory.cpp +++ b/src/Inventory.cpp @@ -507,7 +507,11 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, int MaxStackSize = cItemHandler::GetItemHandler(a_Item.m_ItemType)->GetMaxStackSize(); for (int i = 0; i < a_Size; i++) { - if (m_Slots[i + a_Offset].m_ItemType == a_Item.m_ItemType && m_Slots[i + a_Offset].m_ItemCount < MaxStackSize && m_Slots[i + a_Offset].m_ItemDamage == a_Item.m_ItemDamage) + if ( + (m_Slots[i + a_Offset].m_ItemType == a_Item.m_ItemType) && + (m_Slots[i + a_Offset].m_ItemCount < MaxStackSize) && + (m_Slots[i + a_Offset].m_ItemDamage == a_Item.m_ItemDamage) + ) { int NumFree = MaxStackSize - m_Slots[i + a_Offset].m_ItemCount; if (NumFree >= a_Item.m_ItemCount) @@ -533,7 +537,7 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, if (a_Mode > 0) { // If we got more left, find first empty slot - for (int i = 0; i < a_Size && a_Item.m_ItemCount > 0; i++) + for (int i = 0; (i < a_Size) && (a_Item.m_ItemCount > 0); i++) { if (m_Slots[i + a_Offset].m_ItemType == -1) { diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp index ae5e746e7..ced95d4e1 100644 --- a/src/LightingThread.cpp +++ b/src/LightingThread.cpp @@ -226,6 +226,8 @@ void cLightingThread::Execute(void) } // CSLock(m_CS) LightChunk(*Item); + Item->Disable(); + delete Item; } } @@ -236,6 +238,16 @@ void cLightingThread::Execute(void) void cLightingThread::LightChunk(cLightingChunkStay & a_Item) { + // If the chunk is already lit, skip it: + if (m_World->IsChunkLighted(a_Item.m_ChunkX, a_Item.m_ChunkZ)) + { + if (a_Item.m_CallbackAfter != nullptr) + { + a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ); + } + return; + } + cChunkDef::BlockNibbles BlockLight, SkyLight; ReadChunks(a_Item.m_ChunkX, a_Item.m_ChunkZ); @@ -314,8 +326,6 @@ void cLightingThread::LightChunk(cLightingChunkStay & a_Item) { a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ); } - a_Item.Disable(); - delete &a_Item; } diff --git a/src/Logger.cpp b/src/Logger.cpp index cb528e8ab..292622a46 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -45,12 +45,12 @@ void cLogger::LogSimple(AString a_Message, eLogLevel a_LogLevel) AString Line; #ifdef _DEBUG - Printf(Line, "[%04lx|%02d:%02d:%02d] %s\n", cIsThread::GetCurrentID(), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, a_Message.c_str()); + Printf(Line, "[%04llx|%02d:%02d:%02d] %s\n", static_cast<UInt64>(std::hash<std::thread::id>()(std::this_thread::get_id())), timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, a_Message.c_str()); #else Printf(Line, "[%02d:%02d:%02d] %s\n", timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec, a_Message.c_str()); #endif - + cCSLock Lock(m_CriticalSection); for (size_t i = 0; i < m_LogListeners.size(); i++) { diff --git a/src/MersenneTwister.h b/src/MersenneTwister.h deleted file mode 100644 index e83a470e2..000000000 --- a/src/MersenneTwister.h +++ /dev/null @@ -1,456 +0,0 @@ -// MersenneTwister.h -// Mersenne Twister random number generator -- a C++ class MTRand -// Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus -// Richard J. Wagner v1.1 28 September 2009 wagnerr@umich.edu - -// The Mersenne Twister is an algorithm for generating random numbers. It -// was designed with consideration of the flaws in various other generators. -// The period, 2^19937-1, and the order of equidistribution, 623 dimensions, -// are far greater. The generator is also fast; it avoids multiplication and -// division, and it benefits from caches and pipelines. For more information -// see the inventors' web page at -// http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html - -// Reference -// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally -// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on -// Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30. - -// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, -// Copyright (C) 2000 - 2009, Richard J. Wagner -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// -// 3. The names of its contributors may not be used to endorse or promote -// products derived from this software without specific prior written -// permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -#ifndef MERSENNETWISTER_H -#define MERSENNETWISTER_H - -// Not thread safe (unless auto-initialization is avoided and each thread has -// its own MTRand object) - -#include <iostream> -#include <climits> -#include <cstdio> -#include <ctime> -#include <cmath> - -class MTRand { -// Data -public: - typedef UInt32 uint32; // unsigned integer type, at least 32 bits - - enum { N = 624 }; // length of state vector - enum { SAVE = N + 1 }; // length of array for save() - -protected: - enum { M = 397 }; // period parameter - - uint32 state[N]; // internal state - uint32 *pNext; // next value to get from state - uint32 left; // number of values left before reload needed - -// Methods -public: - MTRand( const uint32 oneSeed ); // initialize with a simple uint32 - MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or array - MTRand(); // auto-initialize with /dev/urandom or time() and clock() - MTRand( const MTRand& o ); // copy - - // Do NOT use for CRYPTOGRAPHY without securely hashing several returned - // values together, otherwise the generator state can be learned after - // reading 624 consecutive values. - - // Access to 32-bit random numbers - uint32 randInt(); // integer in [0,2^32-1] - uint32 randInt( const uint32 n ); // integer in [0,n] for n < 2^32 - double rand(); // real number in [0,1] - double rand( const double n ); // real number in [0,n] - double randExc(); // real number in [0,1) - double randExc( const double n ); // real number in [0,n) - double randDblExc(); // real number in (0,1) - double randDblExc( const double n ); // real number in (0,n) - double operator()(); // same as rand() - - // Access to 53-bit random numbers (capacity of IEEE double precision) - double rand53(); // real number in [0,1) - - // Access to nonuniform random number distributions - double randNorm( const double mean = 0.0, const double stddev = 1.0 ); - - // Re-seeding functions with same behavior as initializers - void seed( const uint32 oneSeed ); - void seed( uint32 *const bigSeed, const uint32 seedLength = N ); - void seed(); - - // Saving and loading generator state - void save( uint32* saveArray ) const; // to array of size SAVE - void load( uint32 *const loadArray ); // from such array - friend std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ); - friend std::istream& operator>>( std::istream& is, MTRand& mtrand ); - MTRand& operator=( const MTRand& o ); - -protected: - void initialize( const uint32 oneSeed ); - void reload(); - uint32 hiBit( const uint32 u ) const { return u & 0x80000000UL; } - uint32 loBit( const uint32 u ) const { return u & 0x00000001UL; } - uint32 loBits( const uint32 u ) const { return u & 0x7fffffffUL; } - uint32 mixBits( const uint32 u, const uint32 v ) const - { return hiBit(u) | loBits(v); } - uint32 magic( const uint32 u ) const - { return loBit(u) ? 0x9908b0dfUL : 0x0UL; } - uint32 twist( const uint32 m, const uint32 s0, const uint32 s1 ) const - { return m ^ (mixBits(s0,s1)>>1) ^ magic(s1); } - static uint32 hash( time_t t, clock_t c ); -}; - -// Functions are defined in order of usage to assist inlining - -inline MTRand::uint32 MTRand::hash( time_t t, clock_t c ) -{ - // Get a uint32 from t and c - // Better than uint32(x) in case x is floating point in [0,1] - // Based on code by Lawrence Kirby (fred@genesis.demon.co.uk) - - static uint32 differ = 0; // guarantee time-based seeds will change - - uint32 h1 = 0; - unsigned char *p = (unsigned char *) &t; - for( size_t i = 0; i < sizeof(t); ++i ) - { - h1 *= UCHAR_MAX + 2U; - h1 += p[i]; - } - uint32 h2 = 0; - p = (unsigned char *) &c; - for( size_t j = 0; j < sizeof(c); ++j ) - { - h2 *= UCHAR_MAX + 2U; - h2 += p[j]; - } - return ( h1 + differ++ ) ^ h2; -} - -inline void MTRand::initialize( const uint32 seed ) -{ - // Initialize generator state with seed - // See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier. - // In previous versions, most significant bits (MSBs) of the seed affect - // only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. - uint32 *s = state; - uint32 *r = state; - uint32 i = 1; - *s++ = seed & 0xffffffffUL; - for( ; i < N; ++i ) - { - *s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL; - r++; - } -} - -inline void MTRand::reload() -{ - // Generate N new values in state - // Made clearer and faster by Matthew Bellew (matthew.bellew@home.com) - static const int MmN = int(M) - int(N); // in case enums are unsigned - uint32 *p = state; - int i; - for( i = N - M; i--; ++p ) - *p = twist( p[M], p[0], p[1] ); - for( i = M; --i; ++p ) - *p = twist( p[MmN], p[0], p[1] ); - *p = twist( p[MmN], p[0], state[0] ); - - left = N, pNext = state; -} - -inline void MTRand::seed( const uint32 oneSeed ) -{ - // Seed the generator with a simple uint32 - initialize(oneSeed); - reload(); -} - -inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength ) -{ - // Seed the generator with an array of uint32's - // There are 2^19937-1 possible initial states. This function allows - // all of those to be accessed by providing at least 19937 bits (with a - // default seed length of N = 624 uint32's). Any bits above the lower 32 - // in each element are discarded. - // Just call seed() if you want to get array from /dev/urandom - initialize(19650218UL); - uint32 i = 1; - uint32 j = 0; - uint32 k = ( static_cast<uint32>(N) > seedLength ? static_cast<uint32>(N) : seedLength ); - for( ; k; --k ) - { - state[i] = - state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL ); - state[i] += ( bigSeed[j] & 0xffffffffUL ) + j; - state[i] &= 0xffffffffUL; - ++i; ++j; - if( i >= N ) { state[0] = state[N-1]; i = 1; } - if( j >= seedLength ) j = 0; - } - for( k = N - 1; k; --k ) - { - state[i] = - state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL ); - state[i] -= i; - state[i] &= 0xffffffffUL; - ++i; - if( i >= N ) { state[0] = state[N-1]; i = 1; } - } - state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array - reload(); -} - -inline void MTRand::seed() -{ - // Seed the generator with an array from /dev/urandom if available - // Otherwise use a hash of time() and clock() values - - // First try getting an array from /dev/urandom - - /* // Commented out by FakeTruth because doing this 200 times a tick is SUUUUPEERRR SLOW!!~~!\D5Ne - FILE* urandom = fopen( "/dev/urandom", "rb" ); - if( urandom ) - { - uint32 bigSeed[N]; - register uint32 *s = bigSeed; - register int i = N; - register bool success = true; - while( success && i-- ) - success = fread( s++, sizeof(uint32), 1, urandom ); - fclose(urandom); - if( success ) { seed( bigSeed, N ); return; } - } - */ - - // Was not successful, so use time() and clock() instead - seed( hash( time(NULL), clock() ) ); -} - -inline MTRand::MTRand( const uint32 oneSeed ) - { seed(oneSeed); } - -inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength ) - { seed(bigSeed,seedLength); } - -inline MTRand::MTRand() - { seed(); } - -inline MTRand::MTRand( const MTRand& o ) -{ - const uint32 *t = o.state; - uint32 *s = state; - int i = N; - for( ; i--; *s++ = *t++ ) {} - left = o.left; - pNext = &state[N-left]; -} - -inline MTRand::uint32 MTRand::randInt() -{ - // Pull a 32-bit integer from the generator state - // Every other access function simply transforms the numbers extracted here - - if( left == 0 ) reload(); - --left; - - uint32 s1; - s1 = *pNext++; - s1 ^= (s1 >> 11); - s1 ^= (s1 << 7) & 0x9d2c5680UL; - s1 ^= (s1 << 15) & 0xefc60000UL; - return ( s1 ^ (s1 >> 18) ); -} - -inline MTRand::uint32 MTRand::randInt( const uint32 n ) -{ - // Find which bits are used in n - // Optimized by Magnus Jonsson (magnus@smartelectronix.com) - uint32 used = n; - used |= used >> 1; - used |= used >> 2; - used |= used >> 4; - used |= used >> 8; - used |= used >> 16; - - // Draw numbers until one is found in [0,n] - uint32 i; - do - i = randInt() & used; // toss unused bits to shorten search - while( i > n ); - return i; -} - -inline double MTRand::rand() - { return double(randInt()) * (1.0/4294967295.0); } - -inline double MTRand::rand( const double n ) - { return rand() * n; } - -inline double MTRand::randExc() - { return double(randInt()) * (1.0/4294967296.0); } - -inline double MTRand::randExc( const double n ) - { return randExc() * n; } - -inline double MTRand::randDblExc() - { return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); } - -inline double MTRand::randDblExc( const double n ) - { return randDblExc() * n; } - -inline double MTRand::rand53() -{ - uint32 a = randInt() >> 5, b = randInt() >> 6; - return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada -} - -inline double MTRand::randNorm( const double mean, const double stddev ) -{ - // Return a real number from a normal (Gaussian) distribution with given - // mean and standard deviation by polar form of Box-Muller transformation - double x, y, r; - do - { - x = 2.0 * rand() - 1.0; - y = 2.0 * rand() - 1.0; - r = x * x + y * y; - } - while ( r >= 1.0 || r == 0.0 ); - double s = sqrt( -2.0 * log(r) / r ); - return mean + x * s * stddev; -} - -inline double MTRand::operator()() -{ - return rand(); -} - -inline void MTRand::save( uint32* saveArray ) const -{ - const uint32 *s = state; - uint32 *sa = saveArray; - int i = N; - for( ; i--; *sa++ = *s++ ) {} - *sa = left; -} - -inline void MTRand::load( uint32 *const loadArray ) -{ - uint32 *s = state; - uint32 *la = loadArray; - int i = N; - for( ; i--; *s++ = *la++ ) {} - left = *la; - pNext = &state[N-left]; -} - -inline std::ostream& operator<<( std::ostream& os, const MTRand& mtrand ) -{ - const MTRand::uint32 *s = mtrand.state; - int i = mtrand.N; - for( ; i--; os << *s++ << "\t" ) {} - return os << mtrand.left; -} - -inline std::istream& operator>>( std::istream& is, MTRand& mtrand ) -{ - MTRand::uint32 *s = mtrand.state; - int i = mtrand.N; - for( ; i--; is >> *s++ ) {} - is >> mtrand.left; - mtrand.pNext = &mtrand.state[mtrand.N-mtrand.left]; - return is; -} - -inline MTRand& MTRand::operator=( const MTRand& o ) -{ - if( this == &o ) return (*this); - const uint32 *t = o.state; - uint32 *s = state; - int i = N; - for( ; i--; *s++ = *t++ ) {} - left = o.left; - pNext = &state[N-left]; - return (*this); -} - -#endif // MERSENNETWISTER_H - -// Change log: -// -// v0.1 - First release on 15 May 2000 -// - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus -// - Translated from C to C++ -// - Made completely ANSI compliant -// - Designed convenient interface for initialization, seeding, and -// obtaining numbers in default or user-defined ranges -// - Added automatic seeding from /dev/urandom or time() and clock() -// - Provided functions for saving and loading generator state -// -// v0.2 - Fixed bug which reloaded generator one step too late -// -// v0.3 - Switched to clearer, faster reload() code from Matthew Bellew -// -// v0.4 - Removed trailing newline in saved generator format to be consistent -// with output format of built-in types -// -// v0.5 - Improved portability by replacing static const int's with enum's and -// clarifying return values in seed(); suggested by Eric Heimburg -// - Removed MAXINT constant; use 0xffffffffUL instead -// -// v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits -// - Changed integer [0,n] generator to give better uniformity -// -// v0.7 - Fixed operator precedence ambiguity in reload() -// - Added access for real numbers in (0,1) and (0,n) -// -// v0.8 - Included time.h header to properly support time_t and clock_t -// -// v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto -// - Allowed for seeding with arrays of any length -// - Added access for real numbers in [0,1) with 53-bit resolution -// - Added access for real numbers from normal (Gaussian) distributions -// - Increased overall speed by optimizing twist() -// - Doubled speed of integer [0,n] generation -// - Fixed out-of-range number generation on 64-bit machines -// - Improved portability by substituting literal constants for long enum's -// - Changed license from GNU LGPL to BSD -// -// v1.1 - Corrected parameter label in randNorm from "variance" to "stddev" -// - Changed randNorm algorithm from basic to polar form for efficiency -// - Updated includes from deprecated <xxxx.h> to standard <cxxxx> forms -// - Cleaned declarations and definitions to please Intel compiler -// - Revised twist() operator to work on ones'-complement machines -// - Fixed reload() function to work when N and M are unsigned -// - Added copy constructor and copy operator from Salvador Espana diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index a1d375e9e..ee9e569a7 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -61,7 +61,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) { std::set<eMonsterType> allowedMobs; - if (a_Biome == biMushroomIsland || a_Biome == biMushroomShore) + if ((a_Biome == biMushroomIsland) || (a_Biome == biMushroomShore)) { addIfAllowed(mtMooshroom, allowedMobs); } @@ -84,7 +84,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) addIfAllowed(mtCreeper, allowedMobs); addIfAllowed(mtSquid, allowedMobs); - if (a_Biome != biDesert && a_Biome != biBeach && a_Biome != biOcean) + if ((a_Biome != biDesert) && (a_Biome != biBeach) && (a_Biome != biOcean)) { addIfAllowed(mtSheep, allowedMobs); addIfAllowed(mtPig, allowedMobs); @@ -93,11 +93,11 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) addIfAllowed(mtEnderman, allowedMobs); addIfAllowed(mtSlime, allowedMobs); // MG TODO : much more complicated rule - if (a_Biome == biForest || a_Biome == biForestHills || a_Biome == biTaiga || a_Biome == biTaigaHills) + if ((a_Biome == biForest) || (a_Biome == biForestHills) || (a_Biome == biTaiga) || (a_Biome == biTaigaHills)) { addIfAllowed(mtWolf, allowedMobs); } - else if (a_Biome == biJungle || a_Biome == biJungleHills) + else if ((a_Biome == biJungle) || (a_Biome == biJungleHills)) { addIfAllowed(mtOcelot, allowedMobs); } @@ -285,6 +285,19 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R ) ); } + + case mtMooshroom: + { + return ( + (TargetBlock == E_BLOCK_AIR) && + (BlockAbove == E_BLOCK_AIR) && + (BlockBelow == E_BLOCK_MYCELIUM) && + ( + (a_Biome == biMushroomShore) || + (a_Biome == biMushroomIsland) + ) + ); + } default: { diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 41ef26e2a..7ca7a9d66 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -75,7 +75,9 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) } if (m_Target == nullptr) + { return; + } cTracer LineOfSight(GetWorld()); Vector3d AttackDirection(m_Target->GetPosition() - GetPosition()); diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index 16869c79d..1fa9d2c37 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -34,7 +34,7 @@ void cBlaze::Attack(float a_Dt) { m_AttackInterval += a_Dt * m_AttackRate; - if (m_Target != nullptr && m_AttackInterval > 3.0) + if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { // Setting this higher gives us more wiggle room for attackrate Vector3d Speed = GetLookVector() * 20; diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp index c65c0d29a..fc8de8362 100644 --- a/src/Mobs/Ghast.cpp +++ b/src/Mobs/Ghast.cpp @@ -36,7 +36,7 @@ void cGhast::Attack(float a_Dt) { m_AttackInterval += a_Dt * m_AttackRate; - if (m_Target != nullptr && m_AttackInterval > 3.0) + if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { // Setting this higher gives us more wiggle room for attackrate Vector3d Speed = GetLookVector() * 20; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index b8926e31d..7b8f763af 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -9,7 +9,6 @@ #include "../Entities/Player.h" #include "../Entities/ExpOrb.h" #include "../MonsterConfig.h" -#include "../MersenneTwister.h" #include "../Chunk.h" #include "../FastRandom.h" @@ -267,7 +266,9 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) } if ((m_Target != nullptr) && m_Target->IsDestroyed()) + { m_Target = nullptr; + } // Burning in daylight HandleDaylightBurning(a_Chunk); @@ -1028,22 +1029,34 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel) MTRand r1; if (r1.randInt() % 200 < ((m_DropChanceHelmet * 200) + (a_LootingLevel * 2))) { - if (!GetEquippedHelmet().IsEmpty()) a_Drops.push_back(GetEquippedHelmet()); + if (!GetEquippedHelmet().IsEmpty()) + { + a_Drops.push_back(GetEquippedHelmet()); + } } if (r1.randInt() % 200 < ((m_DropChanceChestplate * 200) + (a_LootingLevel * 2))) { - if (!GetEquippedChestplate().IsEmpty()) a_Drops.push_back(GetEquippedChestplate()); + if (!GetEquippedChestplate().IsEmpty()) + { + a_Drops.push_back(GetEquippedChestplate()); + } } if (r1.randInt() % 200 < ((m_DropChanceLeggings * 200) + (a_LootingLevel * 2))) { - if (!GetEquippedLeggings().IsEmpty()) a_Drops.push_back(GetEquippedLeggings()); + if (!GetEquippedLeggings().IsEmpty()) + { + a_Drops.push_back(GetEquippedLeggings()); + } } if (r1.randInt() % 200 < ((m_DropChanceBoots * 200) + (a_LootingLevel * 2))) { - if (!GetEquippedBoots().IsEmpty()) a_Drops.push_back(GetEquippedBoots()); + if (!GetEquippedBoots().IsEmpty()) + { + a_Drops.push_back(GetEquippedBoots()); + } } } @@ -1056,7 +1069,10 @@ void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel) MTRand r1; if (r1.randInt() % 200 < ((m_DropChanceWeapon * 200) + (a_LootingLevel * 2))) { - if (!GetEquippedWeapon().IsEmpty()) a_Drops.push_back(GetEquippedWeapon()); + if (!GetEquippedWeapon().IsEmpty()) + { + a_Drops.push_back(GetEquippedWeapon()); + } } } diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp index 50b69e44f..1e4c35acd 100644 --- a/src/Mobs/Pig.cpp +++ b/src/Mobs/Pig.cpp @@ -49,17 +49,17 @@ void cPig::OnRightClicked(cPlayer & a_Player) a_Player.Detach(); return; } - + if (m_Attachee->IsPlayer()) { // Another player is already sitting in here, cannot attach return; } - + // Detach whatever is sitting in this pig now: m_Attachee->Detach(); } - + // Attach the player to this pig a_Player.AttachTo(this); } @@ -100,7 +100,7 @@ void cPig::Tick(float a_Dt, cChunk & a_Chunk) bool cPig::DoTakeDamage(TakeDamageInfo & a_TDI) -{ +{ if (!super::DoTakeDamage(a_TDI)) { return false; diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index f17bc307c..da5ddc670 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -71,7 +71,7 @@ void cSkeleton::Attack(float a_Dt) { m_AttackInterval += a_Dt * m_AttackRate; - if (m_Target != nullptr && m_AttackInterval > 3.0) + if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { // Setting this higher gives us more wiggle room for attackrate Vector3d Speed = GetLookVector() * 20; diff --git a/src/Mobs/SnowGolem.cpp b/src/Mobs/SnowGolem.cpp index 76334d970..8c4178beb 100644 --- a/src/Mobs/SnowGolem.cpp +++ b/src/Mobs/SnowGolem.cpp @@ -38,7 +38,7 @@ void cSnowGolem::Tick(float a_Dt, cChunk & a_Chunk) { BLOCKTYPE BlockBelow = m_World->GetBlock((int) floor(GetPosX()), (int) floor(GetPosY()) - 1, (int) floor(GetPosZ())); BLOCKTYPE Block = m_World->GetBlock((int) floor(GetPosX()), (int) floor(GetPosY()), (int) floor(GetPosZ())); - if (Block == E_BLOCK_AIR && cBlockInfo::IsSolid(BlockBelow)) + if ((Block == E_BLOCK_AIR) && cBlockInfo::IsSolid(BlockBelow)) { m_World->SetBlock((int) floor(GetPosX()), (int) floor(GetPosY()), (int) floor(GetPosZ()), E_BLOCK_SNOW, 0); } diff --git a/src/Mobs/Witch.cpp b/src/Mobs/Witch.cpp index 747a11f97..a3cadbaa0 100644 --- a/src/Mobs/Witch.cpp +++ b/src/Mobs/Witch.cpp @@ -2,6 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "Witch.h" +#include "FastRandom.h" diff --git a/src/Mobs/Witch.h b/src/Mobs/Witch.h index 03691fef1..8230e1f98 100644 --- a/src/Mobs/Witch.h +++ b/src/Mobs/Witch.h @@ -2,7 +2,6 @@ #pragma once #include "AggressiveMonster.h" -#include "../MersenneTwister.h" diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt index 592525941..e943ceb18 100644 --- a/src/OSSupport/CMakeLists.txt +++ b/src/OSSupport/CMakeLists.txt @@ -13,12 +13,9 @@ SET (SRCS IsThread.cpp ListenThread.cpp Semaphore.cpp - Sleep.cpp Socket.cpp SocketThreads.cpp StackTrace.cpp - Thread.cpp - Timer.cpp ) SET (HDRS @@ -31,12 +28,9 @@ SET (HDRS ListenThread.h Queue.h Semaphore.h - Sleep.h Socket.h SocketThreads.h StackTrace.h - Thread.h - Timer.h ) if(NOT MSVC) diff --git a/src/OSSupport/CriticalSection.cpp b/src/OSSupport/CriticalSection.cpp index 5dfc8b5f9..13a3e4d9f 100644 --- a/src/OSSupport/CriticalSection.cpp +++ b/src/OSSupport/CriticalSection.cpp @@ -1,6 +1,5 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include "IsThread.h" @@ -9,41 +8,12 @@ //////////////////////////////////////////////////////////////////////////////// // cCriticalSection: +#ifdef _DEBUG cCriticalSection::cCriticalSection() { - #ifdef _WIN32 - InitializeCriticalSection(&m_CriticalSection); - #else - pthread_mutexattr_init(&m_Attributes); - pthread_mutexattr_settype(&m_Attributes, PTHREAD_MUTEX_RECURSIVE); - - if (pthread_mutex_init(&m_CriticalSection, &m_Attributes) != 0) - { - LOGERROR("Could not initialize Critical Section!"); - } - #endif - - #ifdef _DEBUG - m_IsLocked = 0; - #endif // _DEBUG -} - - - - - -cCriticalSection::~cCriticalSection() -{ - #ifdef _WIN32 - DeleteCriticalSection(&m_CriticalSection); - #else - if (pthread_mutex_destroy(&m_CriticalSection) != 0) - { - LOGWARNING("Could not destroy Critical Section!"); - } - pthread_mutexattr_destroy(&m_Attributes); - #endif + m_IsLocked = 0; } +#endif // _DEBUG @@ -51,15 +21,11 @@ cCriticalSection::~cCriticalSection() void cCriticalSection::Lock() { - #ifdef _WIN32 - EnterCriticalSection(&m_CriticalSection); - #else - pthread_mutex_lock(&m_CriticalSection); - #endif + m_Mutex.lock(); #ifdef _DEBUG m_IsLocked += 1; - m_OwningThreadID = cIsThread::GetCurrentID(); + m_OwningThreadID = std::this_thread::get_id(); #endif // _DEBUG } @@ -74,11 +40,7 @@ void cCriticalSection::Unlock() m_IsLocked -= 1; #endif // _DEBUG - #ifdef _WIN32 - LeaveCriticalSection(&m_CriticalSection); - #else - pthread_mutex_unlock(&m_CriticalSection); - #endif + m_Mutex.unlock(); } @@ -97,7 +59,7 @@ bool cCriticalSection::IsLocked(void) bool cCriticalSection::IsLockedByCurrentThread(void) { - return ((m_IsLocked > 0) && (m_OwningThreadID == cIsThread::GetCurrentID())); + return ((m_IsLocked > 0) && (m_OwningThreadID == std::this_thread::get_id())); } #endif // _DEBUG diff --git a/src/OSSupport/CriticalSection.h b/src/OSSupport/CriticalSection.h index c3c6e57f0..17fcdfc12 100644 --- a/src/OSSupport/CriticalSection.h +++ b/src/OSSupport/CriticalSection.h @@ -1,5 +1,7 @@ #pragma once +#include <mutex> +#include <thread> @@ -8,8 +10,6 @@ class cCriticalSection { public: - cCriticalSection(void); - ~cCriticalSection(); void Lock(void); void Unlock(void); @@ -17,6 +17,7 @@ public: // IsLocked/IsLockedByCurrentThread are only used in ASSERT statements, but because of the changes with ASSERT they must always be defined // The fake versions (in Release) will not effect the program in any way #ifdef _DEBUG + cCriticalSection(void); bool IsLocked(void); bool IsLockedByCurrentThread(void); #else @@ -27,15 +28,10 @@ public: private: #ifdef _DEBUG int m_IsLocked; // Number of times this CS is locked - unsigned long m_OwningThreadID; + std::thread::id m_OwningThreadID; #endif // _DEBUG - #ifdef _WIN32 - CRITICAL_SECTION m_CriticalSection; - #else // _WIN32 - pthread_mutex_t m_CriticalSection; - pthread_mutexattr_t m_Attributes; - #endif // else _WIN32 + std::recursive_mutex m_Mutex; } ALIGN_8; diff --git a/src/OSSupport/Event.cpp b/src/OSSupport/Event.cpp index 87bc110ce..760e536a1 100644 --- a/src/OSSupport/Event.cpp +++ b/src/OSSupport/Event.cpp @@ -12,137 +12,59 @@ -cEvent::cEvent(void) +cEvent::cEvent(void) : + m_ShouldWait(true) { -#ifdef _WIN32 - m_Event = CreateEvent(nullptr, FALSE, FALSE, nullptr); - if (m_Event == nullptr) - { - LOGERROR("cEvent: cannot create event, GLE = %u. Aborting server.", (unsigned)GetLastError()); - abort(); - } -#else // *nix - m_bIsNamed = false; - m_Event = new sem_t; - if (sem_init(m_Event, 0, 0)) - { - // This path is used by MacOS, because it doesn't support unnamed semaphores. - delete m_Event; - m_bIsNamed = true; - - AString EventName; - Printf(EventName, "cEvent%p", this); - m_Event = sem_open(EventName.c_str(), O_CREAT, 777, 0); - if (m_Event == SEM_FAILED) - { - AString error = GetOSErrorString(errno); - LOGERROR("cEvent: Cannot create event, err = %s. Aborting server.", error.c_str()); - abort(); - } - // Unlink the semaphore immediately - it will continue to function but will not pollute the namespace - // We don't store the name, so can't call this in the destructor - if (sem_unlink(EventName.c_str()) != 0) - { - AString error = GetOSErrorString(errno); - LOGWARN("ERROR: Could not unlink cEvent. (%s)", error.c_str()); - } - } -#endif // *nix } -cEvent::~cEvent() +void cEvent::Wait(void) { -#ifdef _WIN32 - CloseHandle(m_Event); -#else - if (m_bIsNamed) - { - if (sem_close(m_Event) != 0) - { - AString error = GetOSErrorString(errno); - LOGERROR("ERROR: Could not close cEvent. (%s)", error.c_str()); - } - } - else + std::unique_lock<std::mutex> Lock(m_Mutex); + while (m_ShouldWait) { - sem_destroy(m_Event); - delete m_Event; - m_Event = nullptr; + m_CondVar.wait(Lock); } -#endif + m_ShouldWait = true; } -void cEvent::Wait(void) +bool cEvent::Wait(unsigned a_TimeoutMSec) { - #ifdef _WIN32 - DWORD res = WaitForSingleObject(m_Event, INFINITE); - if (res != WAIT_OBJECT_0) - { - 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); - if (res != 0) - { - AString error = GetOSErrorString(errno); - LOGWARN("cEvent: waiting for the event failed: %i, err = %s. Continuing, but server may be unstable.", res, error.c_str()); - } - #endif -} - - - - - -bool cEvent::Wait(int a_TimeoutMSec) -{ - #ifdef _WIN32 - DWORD res = WaitForSingleObject(m_Event, (DWORD)a_TimeoutMSec); - switch (res) + auto dst = std::chrono::system_clock::now() + std::chrono::milliseconds(a_TimeoutMSec); + std::unique_lock<std::mutex> Lock(m_Mutex); // We assume that this lock is acquired without much delay - we are the only user of the mutex + while (m_ShouldWait && (std::chrono::system_clock::now() <= dst)) + { + switch (m_CondVar.wait_until(Lock, dst)) { - case WAIT_OBJECT_0: return true; // Regular event signalled - case WAIT_TIMEOUT: return false; // Regular event timeout - default: + case std::cv_status::no_timeout: { - LOGWARN("cEvent: waiting for the event failed: %u, GLE = %u. Continuing, but server may be unstable.", (unsigned)res, (unsigned)GetLastError()); - return false; + // The wait was successful, check for spurious wakeup: + if (!m_ShouldWait) + { + m_ShouldWait = true; + return true; + } + // This was a spurious wakeup, wait again: + continue; } - } - #else - // Get the current time: - timespec timeout; - if (clock_gettime(CLOCK_REALTIME, &timeout) == -1) - { - LOGWARN("cEvent: Getting current time failed: %i, err = %s. Continuing, but the server may be unstable.", errno, GetOSErrorString(errno).c_str()); - return false; - } - - // Add the specified timeout: - timeout.tv_sec += a_TimeoutMSec / 1000; - timeout.tv_nsec += (a_TimeoutMSec % 1000) * 1000000; // 1 msec = 1000000 usec - - // Wait with timeout: - int res = sem_timedwait(m_Event, &timeout); - switch (res) - { - case 0: return true; // Regular event signalled - case ETIMEDOUT: return false; // Regular even timeout - default: + + case std::cv_status::timeout: { - AString error = GetOSErrorString(errno); - LOGWARN("cEvent: waiting for the event failed: %i, err = %s. Continuing, but server may be unstable.", res, error.c_str()); + // The wait timed out, return failure: return false; } - } - #endif + } // switch (wait_until()) + } // while (m_ShouldWait && not timeout) + + // The wait timed out in the while condition: + return false; } @@ -151,19 +73,11 @@ bool cEvent::Wait(int a_TimeoutMSec) void cEvent::Set(void) { - #ifdef _WIN32 - if (!SetEvent(m_Event)) - { - LOGWARN("cEvent: Could not set cEvent: GLE = %u", (unsigned)GetLastError()); - } - #else - int res = sem_post(m_Event); - if (res != 0) - { - AString error = GetOSErrorString(errno); - LOGWARN("cEvent: Could not set cEvent: %i, err = %s", res, error.c_str()); - } - #endif + { + std::unique_lock<std::mutex> Lock(m_Mutex); + m_ShouldWait = false; + } + m_CondVar.notify_one(); } diff --git a/src/OSSupport/Event.h b/src/OSSupport/Event.h index e2fa65a05..572388a3f 100644 --- a/src/OSSupport/Event.h +++ b/src/OSSupport/Event.h @@ -1,16 +1,17 @@ // Event.h -// Interfaces to the cEvent object representing an OS-specific synchronization primitive that can be waited-for -// Implemented as an Event on Win and as a 1-semaphore on *nix +// Interfaces to the cEvent object representing a synchronization primitive that can be waited-for +// Implemented using C++11 condition variable and mutex #pragma once -#ifndef CEVENT_H_INCLUDED -#define CEVENT_H_INCLUDED + +#include <mutex> +#include <condition_variable> @@ -20,31 +21,31 @@ class cEvent { public: cEvent(void); - ~cEvent(); + /** Waits until the event has been set. + If the event has been set before it has been waited for, Wait() returns immediately. */ void Wait(void); + + /** Sets the event - releases one thread that has been waiting in Wait(). + If there was no thread waiting, the next call to Wait() will not block. */ void Set (void); /** Waits for the event until either it is signalled, or the (relative) timeout is passed. Returns true if the event was signalled, false if the timeout was hit or there was an error. */ - bool Wait(int a_TimeoutMSec); + bool Wait(unsigned a_TimeoutMSec); private: - #ifdef _WIN32 - HANDLE m_Event; - #else - sem_t * m_Event; - bool m_bIsNamed; - #endif -} ; - - - + /** Used for checking for spurious wakeups. */ + bool m_ShouldWait; + /** Mutex protecting m_ShouldWait from multithreaded access. */ + std::mutex m_Mutex; + /** The condition variable used as the Event. */ + std::condition_variable m_CondVar; +} ; -#endif // CEVENT_H_INCLUDED diff --git a/src/OSSupport/IsThread.cpp b/src/OSSupport/IsThread.cpp index 1a436623a..94bed1f56 100644 --- a/src/OSSupport/IsThread.cpp +++ b/src/OSSupport/IsThread.cpp @@ -5,49 +5,40 @@ // This class will eventually suupersede the old cThread class #include "Globals.h" - #include "IsThread.h" -// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here: #if defined(_MSC_VER) && defined(_DEBUG) -// -// Usage: SetThreadName (-1, "MainThread"); -// - -// Code adapted from MSDN: http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx - -const DWORD MS_VC_EXCEPTION = 0x406D1388; - -#pragma pack(push, 8) -typedef struct tagTHREADNAME_INFO -{ - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1 = caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. -} THREADNAME_INFO; -#pragma pack(pop) - -static void SetThreadName(DWORD dwThreadID, const char * threadName) -{ - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = threadName; - info.dwThreadID = dwThreadID; - info.dwFlags = 0; + // Code adapted from MSDN: http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx - __try - { - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info); - } - __except (EXCEPTION_EXECUTE_HANDLER) + const DWORD MS_VC_EXCEPTION = 0x406D1388; + #pragma pack(push, 8) + struct THREADNAME_INFO + { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1 = caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. + }; + #pragma pack(pop) + + /** Sets the name of a thread with the specified ID + (When in MSVC, the debugger provides "thread naming" by catching special exceptions) + */ + static void SetThreadName(std::thread * a_Thread, const char * a_ThreadName) { + THREADNAME_INFO info { 0x1000, a_ThreadName, GetThreadId(a_Thread->native_handle()), 0 }; + __try + { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info); + } + __except (EXCEPTION_EXECUTE_HANDLER) + { + } } -} #endif // _MSC_VER && _DEBUG @@ -57,13 +48,9 @@ static void SetThreadName(DWORD dwThreadID, const char * threadName) //////////////////////////////////////////////////////////////////////////////// // cIsThread: -cIsThread::cIsThread(const AString & iThreadName) : +cIsThread::cIsThread(const AString & a_ThreadName) : m_ShouldTerminate(false), - m_ThreadName(iThreadName), - #ifdef _WIN32 - m_ThreadID(0), - #endif - m_Handle(NULL_HANDLE) + m_ThreadName(a_ThreadName) { } @@ -83,35 +70,24 @@ cIsThread::~cIsThread() bool cIsThread::Start(void) { - ASSERT(m_Handle == NULL_HANDLE); // Has already started one thread? - #ifdef _WIN32 - // Create the thread suspended, so that the mHandle variable is valid in the thread procedure - m_ThreadID = 0; - m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &m_ThreadID); - if (m_Handle == NULL) - { - LOGERROR("ERROR: Could not create thread \"%s\", GLE = %u!", m_ThreadName.c_str(), (unsigned)GetLastError()); - return false; - } - ResumeThread(m_Handle); - - #if defined(_DEBUG) && defined(_MSC_VER) - // Thread naming is available only in MSVC - if (!m_ThreadName.empty()) - { - SetThreadName(m_ThreadID, m_ThreadName.c_str()); - } - #endif // _DEBUG and _MSC_VER - - #else // _WIN32 - if (pthread_create(&m_Handle, NULL, thrExecute, this)) + try + { + m_Thread = std::thread(&cIsThread::Execute, this); + + #if defined (_MSC_VER) && defined(_DEBUG) + if (!m_ThreadName.empty()) { - LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str()); - return false; + SetThreadName(&m_Thread, m_ThreadName.c_str()); } - #endif // else _WIN32 + #endif - return true; + return true; + } + catch (std::system_error & a_Exception) + { + LOGERROR("cIsThread::Start error %i: could not construct thread %s; %s", a_Exception.code().value(), m_ThreadName.c_str(), a_Exception.code().message().c_str()); + return false; + } } @@ -120,10 +96,12 @@ bool cIsThread::Start(void) void cIsThread::Stop(void) { - if (m_Handle == NULL_HANDLE) + if (!m_Thread.joinable()) { + // The thread hasn't been started or has already been joined return; } + m_ShouldTerminate = true; Wait(); } @@ -134,59 +112,23 @@ void cIsThread::Stop(void) bool cIsThread::Wait(void) { - if (m_Handle == NULL_HANDLE) + LOGD("Waiting for thread %s to finish", m_ThreadName.c_str()); + if (m_Thread.joinable()) { - return true; + try + { + m_Thread.join(); + return true; + } + catch (std::system_error & a_Exception) + { + LOGERROR("cIsThread::Wait error %i: could not join thread %s; %s", a_Exception.code().value(), m_ThreadName.c_str(), a_Exception.code().message().c_str()); + return false; + } } - - #ifdef LOGD // ProtoProxy doesn't have LOGD - LOGD("Waiting for thread %s to finish", m_ThreadName.c_str()); - #endif // LOGD - - #ifdef _WIN32 - int res = WaitForSingleObject(m_Handle, INFINITE); - m_Handle = NULL; - - #ifdef LOGD // ProtoProxy doesn't have LOGD - LOGD("Thread %s finished", m_ThreadName.c_str()); - #endif // LOGD - - return (res == WAIT_OBJECT_0); - #else // _WIN32 - int res = pthread_join(m_Handle, NULL); - m_Handle = NULL_HANDLE; - - #ifdef LOGD // ProtoProxy doesn't have LOGD - LOGD("Thread %s finished", m_ThreadName.c_str()); - #endif // LOGD - - return (res == 0); - #endif // else _WIN32 -} - - - - -unsigned long cIsThread::GetCurrentID(void) -{ - #ifdef _WIN32 - return (unsigned long) GetCurrentThreadId(); - #else - return (unsigned long) pthread_self(); - #endif -} - - - - -bool cIsThread::IsCurrentThread(void) const -{ - #ifdef _WIN32 - return (GetCurrentThreadId() == m_ThreadID); - #else - return (m_Handle == pthread_self()); - #endif + LOGD("Thread %s finished", m_ThreadName.c_str()); + return true; } diff --git a/src/OSSupport/IsThread.h b/src/OSSupport/IsThread.h index 5c8d28d2d..131c6950e 100644 --- a/src/OSSupport/IsThread.h +++ b/src/OSSupport/IsThread.h @@ -16,8 +16,7 @@ In the descending class' constructor call the Start() method to start the thread #pragma once -#ifndef CISTHREAD_H_INCLUDED -#define CISTHREAD_H_INCLUDED +#include <thread> @@ -33,7 +32,7 @@ protected: volatile bool m_ShouldTerminate; public: - cIsThread(const AString & iThreadName); + cIsThread(const AString & a_ThreadName); virtual ~cIsThread(); /// Starts the thread; returns without waiting for the actual start @@ -45,56 +44,14 @@ public: /// Waits for the thread to finish. Doesn't signalize the ShouldTerminate flag bool Wait(void); - /// Returns the OS-dependent thread ID for the caller's thread - static unsigned long GetCurrentID(void); - /** Returns true if the thread calling this function is the thread contained within this object. */ - bool IsCurrentThread(void) const; + bool IsCurrentThread(void) const { return std::this_thread::get_id() == m_Thread.get_id(); } protected: AString m_ThreadName; - - // Value used for "no handle": - #ifdef _WIN32 - #define NULL_HANDLE NULL - #else - #define NULL_HANDLE 0 - #endif - - #ifdef _WIN32 - - DWORD m_ThreadID; - HANDLE m_Handle; - - static DWORD __stdcall thrExecute(LPVOID a_Param) - { - // Create a window so that the thread can be identified by 3rd party tools: - HWND IdentificationWnd = CreateWindowA("STATIC", ((cIsThread *)a_Param)->m_ThreadName.c_str(), 0, 0, 0, 0, WS_OVERLAPPED, NULL, NULL, NULL, NULL); - - // Run the thread: - ((cIsThread *)a_Param)->Execute(); - - // Destroy the identification window: - DestroyWindow(IdentificationWnd); - - return 0; - } - - #else // _WIN32 - - pthread_t m_Handle; - - static void * thrExecute(void * a_Param) - { - (static_cast<cIsThread *>(a_Param))->Execute(); - return NULL; - } - - #endif // else _WIN32 + std::thread m_Thread; } ; - -#endif // CISTHREAD_H_INCLUDED diff --git a/src/OSSupport/Queue.h b/src/OSSupport/Queue.h index 8d096fe29..82f221453 100644 --- a/src/OSSupport/Queue.h +++ b/src/OSSupport/Queue.h @@ -163,6 +163,29 @@ public: return false; } + + /** Removes all items for which the predicate returns true. */ + template <class Predicate> + void RemoveIf(Predicate a_Predicate) + { + cCSLock Lock(m_CS); + for (auto itr = m_Contents.begin(); itr != m_Contents.end();) + { + if (a_Predicate(*itr)) + { + auto itr2 = itr; + ++itr2; + m_Contents.erase(itr); + m_evtRemoved.Set(); + itr = itr2; + } + else + { + ++itr; + } + } // for itr - m_Contents[] + } + private: /// The contents of the queue QueueType m_Contents; diff --git a/src/OSSupport/Sleep.cpp b/src/OSSupport/Sleep.cpp deleted file mode 100644 index 297d668d7..000000000 --- a/src/OSSupport/Sleep.cpp +++ /dev/null @@ -1,19 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#ifndef _WIN32 - #include <unistd.h> -#endif - - - - - -void cSleep::MilliSleep( unsigned int a_MilliSeconds) -{ -#ifdef _WIN32 - Sleep(a_MilliSeconds); // Don't tick too much -#else - usleep(a_MilliSeconds*1000); -#endif -} diff --git a/src/OSSupport/Sleep.h b/src/OSSupport/Sleep.h deleted file mode 100644 index 57d29682c..000000000 --- a/src/OSSupport/Sleep.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -class cSleep -{ -public: - static void MilliSleep( unsigned int a_MilliSeconds); -}; diff --git a/src/OSSupport/Socket.h b/src/OSSupport/Socket.h index e4ec895cb..9ec8c1111 100644 --- a/src/OSSupport/Socket.h +++ b/src/OSSupport/Socket.h @@ -74,7 +74,7 @@ public: inline static bool IsSocketError(int a_ReturnedValue) { #ifdef _WIN32 - return (a_ReturnedValue == SOCKET_ERROR || a_ReturnedValue == 0); + return ((a_ReturnedValue == SOCKET_ERROR) || (a_ReturnedValue == 0)); #else return (a_ReturnedValue <= 0); #endif diff --git a/src/OSSupport/SocketThreads.cpp b/src/OSSupport/SocketThreads.cpp index 7a3ef4274..153d6ed1d 100644 --- a/src/OSSupport/SocketThreads.cpp +++ b/src/OSSupport/SocketThreads.cpp @@ -86,7 +86,8 @@ void cSocketThreads::RemoveClient(const cCallback * a_Client) } } // for itr - m_Threads[] - ASSERT(!"Removing an unknown client"); + // This client wasn't found. + // It's not an error, because it may have been removed by a different thread in the meantime. } @@ -491,10 +492,17 @@ void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read) { case sSlot::ssNormal: { - // Notify the callback that the remote has closed the socket; keep the slot - m_Slots[i].m_Client->SocketClosed(); + // Close the socket on our side: m_Slots[i].m_State = sSlot::ssRemoteClosed; m_Slots[i].m_Socket.CloseSocket(); + + // Notify the callback that the remote has closed the socket, *after* removing the socket: + cCallback * client = m_Slots[i].m_Client; + m_Slots[i] = m_Slots[--m_NumSlots]; + if (client != nullptr) + { + client->SocketClosed(); + } break; } case sSlot::ssWritingRestOut: diff --git a/src/OSSupport/Thread.cpp b/src/OSSupport/Thread.cpp deleted file mode 100644 index faaccce96..000000000 --- a/src/OSSupport/Thread.cpp +++ /dev/null @@ -1,137 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - - - - - -// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here: -#ifdef _MSC_VER -// -// Usage: SetThreadName (-1, "MainThread"); -// - -// Code adapted from MSDN: http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx - -const DWORD MS_VC_EXCEPTION = 0x406D1388; - -#pragma pack(push, 8) -typedef struct tagTHREADNAME_INFO -{ - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1 = caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. -} THREADNAME_INFO; -#pragma pack(pop) - -static void SetThreadName(DWORD dwThreadID, const char * threadName) -{ - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = threadName; - info.dwThreadID = dwThreadID; - info.dwFlags = 0; - - __try - { - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), (ULONG_PTR *)&info); - } - __except (EXCEPTION_EXECUTE_HANDLER) - { - } -} -#endif // _MSC_VER - - - - - -cThread::cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName /* = 0 */) - : m_ThreadFunction( a_ThreadFunction) - , m_Param( a_Param) - , m_Event( new cEvent()) - , m_StopEvent( 0) -{ - if (a_ThreadName) - { - m_ThreadName.assign(a_ThreadName); - } -} - - - - - -cThread::~cThread() -{ - delete m_Event; - m_Event = NULL; - - if (m_StopEvent) - { - m_StopEvent->Wait(); - delete m_StopEvent; - m_StopEvent = NULL; - } -} - - - - - -void cThread::Start( bool a_bWaitOnDelete /* = true */) -{ - if (a_bWaitOnDelete) - m_StopEvent = new cEvent(); - -#ifndef _WIN32 - pthread_t SndThread; - if (pthread_create( &SndThread, NULL, MyThread, this)) - LOGERROR("ERROR: Could not create thread!"); -#else - DWORD ThreadID = 0; - HANDLE hThread = CreateThread(NULL // security - , 0 // stack size - , (LPTHREAD_START_ROUTINE) MyThread // function name - , this // parameters - , 0 // flags - , &ThreadID); // thread id - CloseHandle( hThread); - - #ifdef _MSC_VER - if (!m_ThreadName.empty()) - { - SetThreadName(ThreadID, m_ThreadName.c_str()); - } - #endif // _MSC_VER -#endif - - // Wait until thread has actually been created - m_Event->Wait(); -} - - - - - -#ifdef _WIN32 -unsigned long cThread::MyThread(void* a_Param) -#else -void *cThread::MyThread( void *a_Param) -#endif -{ - cThread* self = (cThread*)a_Param; - cEvent* StopEvent = self->m_StopEvent; - - ThreadFunc* ThreadFunction = self->m_ThreadFunction; - void* ThreadParam = self->m_Param; - - // Set event to let other thread know this thread has been created and it's safe to delete the cThread object - self->m_Event->Set(); - - ThreadFunction( ThreadParam); - - if (StopEvent) StopEvent->Set(); - return 0; -} diff --git a/src/OSSupport/Thread.h b/src/OSSupport/Thread.h deleted file mode 100644 index 7ee352c82..000000000 --- a/src/OSSupport/Thread.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -class cThread -{ -public: - typedef void (ThreadFunc)(void*); - cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName = 0); - ~cThread(); - - void Start( bool a_bWaitOnDelete = true); - void WaitForThread(); -private: - ThreadFunc* m_ThreadFunction; - -#ifdef _WIN32 - static unsigned long MyThread(void* a_Param); -#else - static void *MyThread( void *lpParam); -#endif - - void* m_Param; - cEvent* m_Event; - cEvent* m_StopEvent; - - AString m_ThreadName; -}; diff --git a/src/OSSupport/Timer.cpp b/src/OSSupport/Timer.cpp deleted file mode 100644 index fd838dd0d..000000000 --- a/src/OSSupport/Timer.cpp +++ /dev/null @@ -1,37 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "Timer.h" - - - - - - -cTimer::cTimer(void) -{ - #ifdef _WIN32 - QueryPerformanceFrequency(&m_TicksPerSecond); - #endif -} - - - - - -long long cTimer::GetNowTime(void) -{ - #ifdef _WIN32 - LARGE_INTEGER now; - QueryPerformanceCounter(&now); - return ((now.QuadPart * 1000) / m_TicksPerSecond.QuadPart); - #else - struct timeval now; - gettimeofday(&now, NULL); - return (long long)now.tv_sec * 1000 + (long long)now.tv_usec / 1000; - #endif -} - - - - diff --git a/src/OSSupport/Timer.h b/src/OSSupport/Timer.h deleted file mode 100644 index a059daa41..000000000 --- a/src/OSSupport/Timer.h +++ /dev/null @@ -1,32 +0,0 @@ - -// Timer.h - -// Declares the cTimer class representing an OS-independent of retrieving current time with msec accuracy - - - - - -#pragma once - - - - - -class cTimer -{ -public: - cTimer(void); - - // Returns the current time expressed in milliseconds - long long GetNowTime(void); -private: - - #ifdef _WIN32 - LARGE_INTEGER m_TicksPerSecond; - #endif -} ; - - - - diff --git a/src/ProbabDistrib.cpp b/src/ProbabDistrib.cpp index 7a5869dcc..c34c75982 100644 --- a/src/ProbabDistrib.cpp +++ b/src/ProbabDistrib.cpp @@ -5,7 +5,7 @@ #include "Globals.h" #include "ProbabDistrib.h" -#include "MersenneTwister.h" +#include "FastRandom.h" diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 67f513e44..570754204 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -161,26 +161,38 @@ class cMojangAPI::cUpdateThread : { typedef cIsThread super; public: - cUpdateThread() : - super("cMojangAPI::cUpdateThread") + cUpdateThread(cMojangAPI & a_MojangAPI) : + super("cMojangAPI::cUpdateThread"), + m_MojangAPI(a_MojangAPI) { } ~cUpdateThread() { + // Notify the thread that it should stop: + m_ShouldTerminate = true; m_evtNotify.Set(); + + // Wait for the thread to actually finish work: Stop(); } protected: + + /** The cMojangAPI instance to update. */ + cMojangAPI & m_MojangAPI; + + /** The event used for notifying that the thread should terminate, as well as timing. */ cEvent m_evtNotify; + + // cIsThread override: virtual void Execute(void) override { do { - cRoot::Get()->GetMojangAPI().Update(); - } while (!m_evtNotify.Wait(60 * 60 * 1000)); // Repeat every 60 minutes + m_MojangAPI.Update(); + } while (!m_ShouldTerminate && !m_evtNotify.Wait(60 * 60 * 1000)); // Repeat every 60 minutes until termination request } } ; @@ -197,7 +209,7 @@ cMojangAPI::cMojangAPI(void) : m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER), m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS), m_RankMgr(nullptr), - m_UpdateThread(new cUpdateThread()) + m_UpdateThread(new cUpdateThread(*this)) { } diff --git a/src/Root.cpp b/src/Root.cpp index e309bb174..9f8ffeeff 100644 --- a/src/Root.cpp +++ b/src/Root.cpp @@ -16,7 +16,6 @@ #include "Protocol/ProtocolRecognizer.h" // for protocol version constants #include "CommandOutput.h" #include "DeadlockDetect.h" -#include "OSSupport/Timer.h" #include "LoggerListeners.h" #include "BuildInfo.h" #include "IniFile.h" @@ -42,7 +41,6 @@ cRoot* cRoot::s_Root = nullptr; cRoot::cRoot(void) : m_pDefaultWorld(nullptr), - m_InputThread(nullptr), m_Server(nullptr), m_MonsterConfig(nullptr), m_CraftingRecipes(nullptr), @@ -68,26 +66,24 @@ cRoot::~cRoot() -void cRoot::InputThread(void * a_Params) +void cRoot::InputThread(cRoot & a_Params) { - cRoot & self = *(cRoot*)a_Params; - cLogCommandOutputCallback Output; - while (!self.m_bStop && !self.m_bRestart && !m_TerminateEventRaised && std::cin.good()) + while (!a_Params.m_bStop && !a_Params.m_bRestart && !m_TerminateEventRaised && std::cin.good()) { AString Command; std::getline(std::cin, Command); if (!Command.empty()) { - self.ExecuteConsoleCommand(TrimString(Command), Output); + a_Params.ExecuteConsoleCommand(TrimString(Command), Output); } } if (m_TerminateEventRaised || !std::cin.good()) { // We have come here because the std::cin has received an EOF / a terminate signal has been sent, and the server is still running; stop the server: - self.m_bStop = true; + a_Params.m_bStop = true; } } @@ -120,9 +116,7 @@ void cRoot::Start(void) m_bStop = false; while (!m_bStop) { - cTimer Time; - long long mseconds = Time.GetNowTime(); - + auto BeginTime = std::chrono::steady_clock::now(); m_bRestart = false; LoadGlobalSettings(); @@ -191,21 +185,25 @@ void cRoot::Start(void) #if !defined(ANDROID_NDK) LOGD("Starting InputThread..."); - m_InputThread = new cThread( InputThread, this, "cRoot::InputThread"); - m_InputThread->Start( false); // We should NOT wait? Otherwise we can't stop the server from other threads than the input thread + try + { + m_InputThread = std::thread(InputThread, std::ref(*this)); + m_InputThread.detach(); + } + catch (std::system_error & a_Exception) + { + LOGERROR("cRoot::Start (std::thread) error %i: could not construct input thread; %s", a_Exception.code().value(), a_Exception.what()); + } #endif - long long finishmseconds = Time.GetNowTime(); - finishmseconds -= mseconds; - - LOG("Startup complete, took %lld ms!", finishmseconds); + LOG("Startup complete, took %ld ms!", static_cast<long int>(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - BeginTime).count())); #ifdef _WIN32 EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button #endif while (!m_bStop && !m_bRestart && !m_TerminateEventRaised) // These are modified by external threads { - cSleep::MilliSleep(1000); + std::this_thread::sleep_for(std::chrono::seconds(1)); } if (m_TerminateEventRaised) @@ -213,10 +211,6 @@ void cRoot::Start(void) m_bStop = true; } - #if !defined(ANDROID_NDK) - delete m_InputThread; m_InputThread = nullptr; - #endif - // Stop the server: m_WebAdmin->Stop(); LOG("Shutting down server..."); @@ -649,6 +643,22 @@ bool cRoot::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback +bool cRoot::DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback) +{ + for (auto World : m_WorldsByName) + { + if (World.second->DoWithPlayer(a_PlayerName, a_Callback)) + { + return true; + } + } + return false; +} + + + + + AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion) { return cProtocolRecognizer::GetVersionTextFromInt(a_ProtocolVersion); diff --git a/src/Root.h b/src/Root.h index 29753a47d..2c512a5df 100644 --- a/src/Root.h +++ b/src/Root.h @@ -6,6 +6,7 @@ #include "HTTPServer/HTTPServer.h" #include "Defines.h" #include "RankManager.h" +#include <thread> @@ -128,7 +129,10 @@ public: /** Finds the player over his uuid and calls the callback */ bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << - + + /** Finds the player using it's complete username and calls the callback */ + bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); + // tolua_begin /// Sends a chat message to all connected clients (in all worlds) @@ -177,7 +181,7 @@ private: cCriticalSection m_CSPendingCommands; cCommandQueue m_PendingCommands; - cThread * m_InputThread; + std::thread m_InputThread; cServer * m_Server; cMonsterConfig * m_MonsterConfig; @@ -212,10 +216,10 @@ private: /// Does the actual work of executing a command void DoExecuteConsoleCommand(const AString & a_Cmd); - - static void InputThread(void* a_Params); static cRoot* s_Root; + + static void InputThread(cRoot & a_Params); }; // tolua_export diff --git a/src/Server.cpp b/src/Server.cpp index bbb5ecff3..3eaf6e096 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -4,7 +4,6 @@ #include "Server.h" #include "ClientHandle.h" -#include "OSSupport/Timer.h" #include "Mobs/Monster.h" #include "OSSupport/Socket.h" #include "Root.h" @@ -20,8 +19,6 @@ #include "Protocol/ProtocolRecognizer.h" #include "CommandOutput.h" -#include "MersenneTwister.h" - #include "IniFile.h" #include "Vector3.h" @@ -75,22 +72,20 @@ cServer::cTickThread::cTickThread(cServer & a_Server) : void cServer::cTickThread::Execute(void) { - cTimer Timer; - - long long msPerTick = 50; - long long LastTime = Timer.GetNowTime(); + auto LastTime = std::chrono::steady_clock::now(); + static const auto msPerTick = std::chrono::milliseconds(50); while (!m_ShouldTerminate) { - long long NowTime = Timer.GetNowTime(); - float DeltaTime = (float)(NowTime-LastTime); - m_ShouldTerminate = !m_Server.Tick(DeltaTime); - long long TickTime = Timer.GetNowTime() - NowTime; + auto NowTime = std::chrono::steady_clock::now(); + auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count(); + m_ShouldTerminate = !m_Server.Tick(static_cast<float>(msec)); + auto TickTime = std::chrono::steady_clock::now() - NowTime; if (TickTime < msPerTick) { // Stretch tick time until it's at least msPerTick - cSleep::MilliSleep((unsigned int)(msPerTick - TickTime)); + std::this_thread::sleep_for(msPerTick - TickTime); } LastTime = NowTime; @@ -201,6 +196,7 @@ bool cServer::InitServer(cIniFile & a_SettingsIni, bool a_ShouldAuth) m_Description = a_SettingsIni.GetValueSet("Server", "Description", "MCServer - in C++!"); m_MaxPlayers = a_SettingsIni.GetValueSetI("Server", "MaxPlayers", 100); m_bIsHardcore = a_SettingsIni.GetValueSetB("Server", "HardcoreEnabled", false); + m_bAllowMultiLogin = a_SettingsIni.GetValueSetB("Server", "AllowMultiLogin", false); m_PlayerCount = 0; m_PlayerCountDiff = 0; @@ -303,6 +299,23 @@ int cServer::GetNumPlayers(void) const +bool cServer::IsPlayerInQueue(AString a_Username) +{ + cCSLock Lock(m_CSClients); + for (auto client : m_Clients) + { + if ((client->GetUsername()).compare(a_Username) == 0) + { + return true; + } + } + return false; +} + + + + + void cServer::PrepareKeys(void) { LOGD("Generating protocol encryption keypair..."); diff --git a/src/Server.h b/src/Server.h index 022794bbc..aab47987f 100644 --- a/src/Server.h +++ b/src/Server.h @@ -67,6 +67,14 @@ public: // tolua_export int GetNumPlayers(void) const; void SetMaxPlayers(int a_MaxPlayers) { m_MaxPlayers = a_MaxPlayers; } + /** Check if the player is queued to be transferred to a World. + Returns true is Player is found in queue. */ + bool IsPlayerInQueue(AString a_Username); + + /** Can login more than once with same username. + Returns false if it is not allowed, true otherwise. */ + bool DoesAllowMultiLogin(void) { return m_bAllowMultiLogin; } + // Hardcore mode or not: bool IsHardcore(void) const { return m_bIsHardcore; } @@ -216,6 +224,9 @@ private: int m_MaxPlayers; bool m_bIsHardcore; + /** True - allow same username to login more than once False - only once */ + bool m_bAllowMultiLogin; + cTickThread m_TickThread; cEvent m_RestartEvent; diff --git a/src/Simulator/FluidSimulator.cpp b/src/Simulator/FluidSimulator.cpp index 9c8453d04..ecd74ee52 100644 --- a/src/Simulator/FluidSimulator.cpp +++ b/src/Simulator/FluidSimulator.cpp @@ -133,8 +133,10 @@ Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a /* Disabled because of causing problems and being useless atm char BlockBelow = m_World.GetBlock(a_X, a_Y - 1, a_Z); // If there is nothing or fluid below it -> dominating flow is down :D - if (BlockBelow == E_BLOCK_AIR || IsAllowedBlock(BlockBelow)) + if ((BlockBelow == E_BLOCK_AIR) || IsAllowedBlock(BlockBelow)) + { return Y_MINUS; + } */ NIBBLETYPE LowestPoint = m_World.GetBlockMeta(a_X, a_Y, a_Z); // Current Block Meta so only lower points will be counted @@ -182,7 +184,9 @@ Direction cFluidSimulator::GetFlowingDirection(int a_X, int a_Y, int a_Z, bool a } if (LowestPoint == m_World.GetBlockMeta(a_X, a_Y, a_Z)) + { return NONE; + } if (a_X - X > 0) { diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index 34f2da682..5febf5d6c 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -416,24 +416,30 @@ static bool isLegalUTF8(const unsigned char * source, int length) { default: return false; // Everything else falls through when "true"... - case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; - case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; + case 4: if (((a = (*--srcptr)) < 0x80) || (a > 0xbf)) return false; + case 3: if (((a = (*--srcptr)) < 0x80) || (a > 0xbf)) return false; case 2: { - if ((a = (*--srcptr)) > 0xBF) return false; + if ((a = (*--srcptr)) > 0xbf) + { + return false; + } switch (*source) { // no fall-through in this inner switch - case 0xE0: if (a < 0xA0) return false; break; - case 0xED: if (a > 0x9F) return false; break; - case 0xF0: if (a < 0x90) return false; break; - case 0xF4: if (a > 0x8F) return false; break; + case 0xe0: if (a < 0xa0) return false; break; + case 0xed: if (a > 0x9f) return false; break; + case 0xf0: if (a < 0x90) return false; break; + case 0xf4: if (a > 0x8f) return false; break; default: if (a < 0x80) return false; } } - case 1: if (*source >= 0x80 && *source < 0xC2) return false; + case 1: if ((*source >= 0x80) && (*source < 0xc2)) return false; + } + if (*source > 0xf4) + { + return false; } - if (*source > 0xF4) return false; return true; } @@ -446,11 +452,11 @@ AString UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length) AString UTF16; UTF16.reserve(a_UTF8Length * 3); - const unsigned char * source = (const unsigned char*)a_UTF8; + const unsigned char * source = (const unsigned char *)a_UTF8; const unsigned char * sourceEnd = source + a_UTF8Length; const int halfShift = 10; // used for shifting by 10 bits const unsigned int halfBase = 0x0010000UL; - const unsigned int halfMask = 0x3FFUL; + const unsigned int halfMask = 0x3ffUL; while (source < sourceEnd) { @@ -481,7 +487,7 @@ AString UTF8ToRawBEUTF16(const char * a_UTF8, size_t a_UTF8Length) if (ch <= UNI_MAX_BMP) { // Target is a character <= 0xFFFF - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) + if ((ch >= UNI_SUR_HIGH_START) && (ch <= UNI_SUR_LOW_END)) { // UTF-16 surrogate values are illegal in UTF-32 ch = ' '; @@ -520,7 +526,10 @@ are equivalent to the following loop: { ch += *source++; --tmpBytesToRead; - if (tmpBytesToRead) ch <<= 6; + if (tmpBytesToRead) + { + ch <<= 6; + } } while (tmpBytesToRead > 0); } --------------------------------------------------------------------- @@ -723,15 +732,15 @@ AString ReplaceAllCharOccurrences(const AString & a_String, char a_From, char a_ /// Converts one Hex character in a Base64 encoding into the data value static inline int UnBase64(char c) { - if (c >='A' && c <= 'Z') + if ((c >='A') && (c <= 'Z')) { return c - 'A'; } - if (c >='a' && c <= 'z') + if ((c >='a') && (c <= 'z')) { return c - 'a' + 26; } - if (c >= '0' && c <= '9') + if ((c >= '0') && (c <= '9')) { return c - '0' + 52; } diff --git a/src/Tracer.cpp b/src/Tracer.cpp index e125c6aa4..e604f4a5b 100644 --- a/src/Tracer.cpp +++ b/src/Tracer.cpp @@ -14,15 +14,15 @@ -cTracer::cTracer(cWorld * a_World) - : m_World(a_World) +cTracer::cTracer(cWorld * a_World): + m_World(a_World) { - m_NormalTable[0].Set(-1, 0, 0); - m_NormalTable[1].Set( 0, 0, -1); - m_NormalTable[2].Set( 1, 0, 0); - m_NormalTable[3].Set( 0, 0, 1); - m_NormalTable[4].Set( 0, 1, 0); - m_NormalTable[5].Set( 0, -1, 0); + m_NormalTable[0].Set(-1, 0, 0); + m_NormalTable[1].Set( 0, 0, -1); + m_NormalTable[2].Set( 1, 0, 0); + m_NormalTable[3].Set( 0, 0, 1); + m_NormalTable[4].Set( 0, 1, 0); + m_NormalTable[5].Set( 0, -1, 0); } @@ -37,10 +37,16 @@ cTracer::~cTracer() -float cTracer::SigNum( float a_Num) +float cTracer::SigNum(float a_Num) { - if (a_Num < 0.f) return -1.f; - if (a_Num > 0.f) return 1.f; + if (a_Num < 0.f) + { + return -1.f; + } + if (a_Num > 0.f) + { + return 1.f; + } return 0.f; } @@ -59,7 +65,10 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction) step.z = (int) SigNum(dir.z); // normalize the direction vector - if (dir.SqrLength() > 0.f) dir.Normalize(); + if (dir.SqrLength() > 0.f) + { + dir.Normalize(); + } // how far we must move in the ray direction before // we encounter a new voxel in x-direction @@ -127,7 +136,7 @@ void cTracer::SetValues(const Vector3f & a_Start, const Vector3f & a_Direction) -bool cTracer::Trace( const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance, bool a_LineOfSight) +bool cTracer::Trace(const Vector3f & a_Start, const Vector3f & a_Direction, int a_Distance, bool a_LineOfSight) { if ((a_Start.y < 0) || (a_Start.y >= cChunkDef::Height)) { @@ -249,35 +258,42 @@ int LinesCross(float x0, float y0, float x1, float y1, float x2, float y2, float { // float linx, liny; - float d=(x1-x0)*(y3-y2)-(y1-y0)*(x3-x2); - if (std::abs(d)<0.001) {return 0;} - float AB=((y0-y2)*(x3-x2)-(x0-x2)*(y3-y2))/d; - if (AB>=0.0 && AB<=1.0) + float d = (x1 - x0) * (y3 - y2) - (y1 - y0) * (x3 - x2); + if (std::abs(d) < 0.001) { - float CD=((y0-y2)*(x1-x0)-(x0-x2)*(y1-y0))/d; - if (CD>=0.0 && CD<=1.0) + return 0; + } + + float AB = ((y0 - y2) * (x3 - x2) - (x0 - x2) * (y3 - y2)) / d; + if ((AB >= 0.0) && (AB <= 1.0)) + { + float CD = ((y0 - y2) * (x1 - x0) - (x0 - x2) * (y1 - y0)) / d; + if ((CD >= 0.0) && (CD <= 1.0)) { - // linx=x0+AB*(x1-x0); - // liny=y0+AB*(y1-y0); + // linx = x0 + AB * (x1 - x0); + // liny = y0 + AB * (y1 - y0); return 1; } } return 0; } + + + // intersect3D_SegmentPlane(): intersect a segment and a plane // Input: a_Ray = a segment, and a_Plane = a plane = {Point V0; Vector n;} // Output: *I0 = the intersect point (when it exists) // Return: 0 = disjoint (no intersection) // 1 = intersection in the unique point *I0 // 2 = the segment lies in the plane -int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal) +int cTracer::intersect3D_SegmentPlane(const Vector3f & a_Origin, const Vector3f & a_End, const Vector3f & a_PlanePos, const Vector3f & a_PlaneNormal) { Vector3f u = a_End - a_Origin; // a_Ray.P1 - S.P0; Vector3f w = a_Origin - a_PlanePos; // S.P0 - Pn.V0; - float D = a_PlaneNormal.Dot( u); // dot(Pn.n, u); - float N = -(a_PlaneNormal.Dot( w)); // -dot(a_Plane.n, w); + float D = a_PlaneNormal.Dot(u); // dot(Pn.n, u); + float N = -(a_PlaneNormal.Dot(w)); // -dot(a_Plane.n, w); const float EPSILON = 0.0001f; if (fabs(D) < EPSILON) @@ -294,12 +310,12 @@ int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f // they are not parallel // compute intersect param float sI = N / D; - if (sI < 0 || sI > 1) + if ((sI < 0) || (sI > 1)) { return 0; // no intersection } - // Vector3f I ( a_Ray->GetOrigin() + sI * u);// S.P0 + sI * u; // compute segment intersect point + // Vector3f I (a_Ray->GetOrigin() + sI * u);// S.P0 + sI * u; // compute segment intersect point RealHit = a_Origin + u * sI; return 1; } @@ -311,9 +327,9 @@ int cTracer::intersect3D_SegmentPlane( const Vector3f & a_Origin, const Vector3f int cTracer::GetHitNormal(const Vector3f & start, const Vector3f & end, const Vector3i & a_BlockPos) { Vector3i SmallBlockPos = a_BlockPos; - char BlockID = m_World->GetBlock( a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); + char BlockID = m_World->GetBlock(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z); - if (BlockID == E_BLOCK_AIR || IsBlockWater(BlockID)) + if ((BlockID == E_BLOCK_AIR) || IsBlockWater(BlockID)) { return 0; } @@ -324,86 +340,86 @@ int cTracer::GetHitNormal(const Vector3f & start, const Vector3f & end, const Ve Vector3f Look = (end - start); Look.Normalize(); - float dot = Look.Dot( Vector3f(-1, 0, 0)); // first face normal is x -1 + float dot = Look.Dot(Vector3f(-1, 0, 0)); // first face normal is x -1 if (dot < 0) { - int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x, BlockPos.y, BlockPos.x, BlockPos.y + 1); + int Lines = LinesCross(start.x, start.y, end.x, end.y, BlockPos.x, BlockPos.y, BlockPos.x, BlockPos.y + 1); if (Lines == 1) { - Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x, BlockPos.z, BlockPos.x, BlockPos.z + 1); + Lines = LinesCross(start.x, start.z, end.x, end.z, BlockPos.x, BlockPos.z, BlockPos.x, BlockPos.z + 1); if (Lines == 1) { - intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(-1, 0, 0)); + intersect3D_SegmentPlane(start, end, BlockPos, Vector3f(-1, 0, 0)); return 1; } } } - dot = Look.Dot( Vector3f(0, 0, -1)); // second face normal is z -1 + dot = Look.Dot(Vector3f(0, 0, -1)); // second face normal is z -1 if (dot < 0) { - int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z, BlockPos.y, BlockPos.z, BlockPos.y + 1); + int Lines = LinesCross(start.z, start.y, end.z, end.y, BlockPos.z, BlockPos.y, BlockPos.z, BlockPos.y + 1); if (Lines == 1) { - Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z, BlockPos.x, BlockPos.z, BlockPos.x + 1); + Lines = LinesCross(start.z, start.x, end.z, end.x, BlockPos.z, BlockPos.x, BlockPos.z, BlockPos.x + 1); if (Lines == 1) { - intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, 0, -1)); + intersect3D_SegmentPlane(start, end, BlockPos, Vector3f(0, 0, -1)); return 2; } } } - dot = Look.Dot( Vector3f(1, 0, 0)); // third face normal is x 1 + dot = Look.Dot(Vector3f(1, 0, 0)); // third face normal is x 1 if (dot < 0) { - int Lines = LinesCross( start.x, start.y, end.x, end.y, BlockPos.x + 1, BlockPos.y, BlockPos.x + 1, BlockPos.y + 1); + int Lines = LinesCross(start.x, start.y, end.x, end.y, BlockPos.x + 1, BlockPos.y, BlockPos.x + 1, BlockPos.y + 1); if (Lines == 1) { - Lines = LinesCross( start.x, start.z, end.x, end.z, BlockPos.x + 1, BlockPos.z, BlockPos.x + 1, BlockPos.z + 1); + Lines = LinesCross(start.x, start.z, end.x, end.z, BlockPos.x + 1, BlockPos.z, BlockPos.x + 1, BlockPos.z + 1); if (Lines == 1) { - intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(1, 0, 0), Vector3f(1, 0, 0)); + intersect3D_SegmentPlane(start, end, BlockPos + Vector3f(1, 0, 0), Vector3f(1, 0, 0)); return 3; } } } - dot = Look.Dot( Vector3f(0, 0, 1)); // fourth face normal is z 1 + dot = Look.Dot(Vector3f(0, 0, 1)); // fourth face normal is z 1 if (dot < 0) { - int Lines = LinesCross( start.z, start.y, end.z, end.y, BlockPos.z + 1, BlockPos.y, BlockPos.z + 1, BlockPos.y + 1); + int Lines = LinesCross(start.z, start.y, end.z, end.y, BlockPos.z + 1, BlockPos.y, BlockPos.z + 1, BlockPos.y + 1); if (Lines == 1) { - Lines = LinesCross( start.z, start.x, end.z, end.x, BlockPos.z + 1, BlockPos.x, BlockPos.z + 1, BlockPos.x + 1); + Lines = LinesCross(start.z, start.x, end.z, end.x, BlockPos.z + 1, BlockPos.x, BlockPos.z + 1, BlockPos.x + 1); if (Lines == 1) { - intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 0, 1), Vector3f(0, 0, 1)); + intersect3D_SegmentPlane(start, end, BlockPos + Vector3f(0, 0, 1), Vector3f(0, 0, 1)); return 4; } } } - dot = Look.Dot( Vector3f(0, 1, 0)); // fifth face normal is y 1 + dot = Look.Dot(Vector3f(0, 1, 0)); // fifth face normal is y 1 if (dot < 0) { - int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y + 1, BlockPos.x, BlockPos.y + 1, BlockPos.x + 1); + int Lines = LinesCross(start.y, start.x, end.y, end.x, BlockPos.y + 1, BlockPos.x, BlockPos.y + 1, BlockPos.x + 1); if (Lines == 1) { - Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y + 1, BlockPos.z, BlockPos.y + 1, BlockPos.z + 1); + Lines = LinesCross(start.y, start.z, end.y, end.z, BlockPos.y + 1, BlockPos.z, BlockPos.y + 1, BlockPos.z + 1); if (Lines == 1) { - intersect3D_SegmentPlane( start, end, BlockPos + Vector3f(0, 1, 0), Vector3f(0, 1, 0)); + intersect3D_SegmentPlane(start, end, BlockPos + Vector3f(0, 1, 0), Vector3f(0, 1, 0)); return 5; } } } - dot = Look.Dot( Vector3f(0, -1, 0)); // sixth face normal is y -1 + dot = Look.Dot(Vector3f(0, -1, 0)); // sixth face normal is y -1 if (dot < 0) { - int Lines = LinesCross( start.y, start.x, end.y, end.x, BlockPos.y, BlockPos.x, BlockPos.y, BlockPos.x + 1); + int Lines = LinesCross(start.y, start.x, end.y, end.x, BlockPos.y, BlockPos.x, BlockPos.y, BlockPos.x + 1); if (Lines == 1) { - Lines = LinesCross( start.y, start.z, end.y, end.z, BlockPos.y, BlockPos.z, BlockPos.y, BlockPos.z + 1); + Lines = LinesCross(start.y, start.z, end.y, end.z, BlockPos.y, BlockPos.z, BlockPos.y, BlockPos.z + 1); if (Lines == 1) { - intersect3D_SegmentPlane( start, end, BlockPos, Vector3f(0, -1, 0)); + intersect3D_SegmentPlane(start, end, BlockPos, Vector3f(0, -1, 0)); return 6; } } diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index 9113ec343..e784569d9 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -1167,7 +1167,7 @@ void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player) { m_MaximumCost = 39; } - if (m_MaximumCost >= 40 && !a_Player.IsGameModeCreative()) + if ((m_MaximumCost >= 40) && !a_Player.IsGameModeCreative()) { Input.Empty(); } @@ -2322,7 +2322,7 @@ cItem * cSlotAreaTemporary::GetPlayerSlots(cPlayer & a_Player) { return nullptr; } - return &(itr->second[0]); + return itr->second.data(); } diff --git a/src/World.cpp b/src/World.cpp index 5fe64ea3a..1bee6e344 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -11,7 +11,6 @@ #include "IniFile.h" #include "ChunkMap.h" #include "Generating/ChunkDesc.h" -#include "OSSupport/Timer.h" #include "SetChunkData.h" // Serializers @@ -45,7 +44,6 @@ #include "MobCensus.h" #include "MobSpawner.h" -#include "MersenneTwister.h" #include "Generating/Trees.h" #include "Bindings/PluginManager.h" #include "Blocks/BlockHandler.h" @@ -92,7 +90,6 @@ public: m_PrepareDistance(a_PrepareDistance), m_MaxIdx(a_PrepareDistance * a_PrepareDistance), m_NumPrepared(0), - m_LastReportTime(0), m_LastReportChunkCount(0) { // Start the thread: @@ -113,12 +110,12 @@ public: m_MaxIdx = m_PrepareDistance * m_PrepareDistance; int maxQueue = std::min(m_MaxIdx - 1, 100); // Number of chunks to queue at once m_NextIdx = maxQueue; - m_LastReportTime = m_Timer.GetNowTime(); + m_LastReportTime = std::chrono::steady_clock::now(); for (int i = 0; i < maxQueue; i++) { int chunkX, chunkZ; DecodeChunkCoords(i, chunkX, chunkZ); - m_World.GetLightingThread().QueueChunk(chunkX, chunkZ, this); + m_World.PrepareChunk(chunkX, chunkZ, this); } // for i // Wait for the lighting thread to prepare everything. Event is set in the Call() callback: @@ -146,16 +143,12 @@ protected: /** Event used to signal that the preparation is finished. */ cEvent m_EvtFinished; - /** The timer used to report progress every second. */ - cTimer m_Timer; - /** The timestamp of the last progress report emitted. */ - long long m_LastReportTime; + std::chrono::steady_clock::time_point m_LastReportTime; /** Number of chunks prepared when the last progress report was emitted. */ int m_LastReportChunkCount; - // cChunkCoordCallback override: virtual void Call(int a_ChunkX, int a_ChunkZ) { @@ -178,15 +171,15 @@ protected: } // Report progress every 1 second: - long long now = m_Timer.GetNowTime(); - if (now - m_LastReportTime > 1000) + auto Now = std::chrono::steady_clock::now(); + if (Now - m_LastReportTime > std::chrono::seconds(1)) { - float percentDone = static_cast<float>(m_NumPrepared * 100) / m_MaxIdx; - float chunkSpeed = static_cast<float>((m_NumPrepared - m_LastReportChunkCount) * 1000) / (now - m_LastReportTime); - LOG("Preparing spawn (%s): %.02f%% done (%d chunks out of %d; %.02f chunks / sec)", - m_World.GetName().c_str(), percentDone, m_NumPrepared, m_MaxIdx, chunkSpeed + float PercentDone = static_cast<float>(m_NumPrepared * 100) / m_MaxIdx; + float ChunkSpeed = static_cast<float>((m_NumPrepared - m_LastReportChunkCount) * 1000) / std::chrono::duration_cast<std::chrono::milliseconds>(Now - m_LastReportTime).count(); + LOG("Preparing spawn (%s): %.02f%% (%d/%d; %.02f chunks/s)", + m_World.GetName().c_str(), PercentDone, m_NumPrepared, m_MaxIdx, ChunkSpeed ); - m_LastReportTime = now; + m_LastReportTime = Now; m_LastReportChunkCount = m_NumPrepared; } } @@ -239,23 +232,22 @@ cWorld::cTickThread::cTickThread(cWorld & a_World) : void cWorld::cTickThread::Execute(void) { - cTimer Timer; + auto LastTime = std::chrono::steady_clock::now(); + static const auto msPerTick = std::chrono::milliseconds(50); + auto TickTime = std::chrono::steady_clock::duration(50); - const Int64 msPerTick = 50; - Int64 LastTime = Timer.GetNowTime(); - - Int64 TickDuration = 50; while (!m_ShouldTerminate) { - Int64 NowTime = Timer.GetNowTime(); - float DeltaTime = (float)(NowTime - LastTime); - m_World.Tick(DeltaTime, (int)TickDuration); - TickDuration = Timer.GetNowTime() - NowTime; + auto NowTime = std::chrono::steady_clock::now(); + auto msec = std::chrono::duration_cast<std::chrono::milliseconds>(NowTime - LastTime).count(); + auto LastTickMsec = std::chrono::duration_cast<std::chrono::duration<int>>(TickTime).count(); + m_World.Tick(static_cast<float>(msec), LastTickMsec); + TickTime = std::chrono::steady_clock::now() - NowTime; - if (TickDuration < msPerTick) + if (TickTime < msPerTick) { // Stretch tick time until it's at least msPerTick - cSleep::MilliSleep((unsigned int)(msPerTick - TickDuration)); + std::this_thread::sleep_for(msPerTick - TickTime); } LastTime = NowTime; @@ -354,6 +346,10 @@ cWorld::~cWorld() Serializer.Save(); m_MapManager.SaveMapData(); + + // Explicitly destroy the chunkmap, so that it's guaranteed to be destroyed before the other internals + // This fixes crashes on stopping the server, because chunk destructor deletes entities and those access the world. + m_ChunkMap.reset(); } @@ -614,7 +610,7 @@ void cWorld::Start(void) } // Adjust the enum-backed variables into their respective bounds: - m_GameMode = (eGameMode) Clamp(GameMode, (int)gmSurvival, (int)gmAdventure); + m_GameMode = (eGameMode) Clamp(GameMode, (int)gmSurvival, (int)gmSpectator); m_TNTShrapnelLevel = (eShrapnelLevel)Clamp(TNTShrapnelLevel, (int)slNone, (int)slAll); m_Weather = (eWeather) Clamp(Weather, (int)wSunny, (int)wStorm); @@ -747,7 +743,7 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile) a_IniFile.GetValueSet("Generator", "BiomeGen", "Grown"); a_IniFile.GetValueSet("Generator", "ShapeGen", "BiomalNoise3D"); a_IniFile.GetValueSet("Generator", "CompositionGen", "Biomal"); - a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, PreSimulator"); + a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals"); break; } case dimNether: @@ -759,7 +755,7 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile) a_IniFile.GetValueSet("Generator", "HeightGen", "Flat"); a_IniFile.GetValueSet("Generator", "FlatHeight", "128"); a_IniFile.GetValueSet("Generator", "CompositionGen", "Nether"); - a_IniFile.GetValueSet("Generator", "Finishers", "WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherForts, PreSimulator"); + a_IniFile.GetValueSet("Generator", "Finishers", "SoulsandRims, WormNestCaves, BottomLava, LavaSprings, NetherClumpFoliage, NetherOreNests, NetherForts, PreSimulator"); a_IniFile.GetValueSet("Generator", "BottomLavaHeight", "30"); break; } @@ -1809,7 +1805,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_FlyAwaySpeed /= 100; // Pre-divide, so that we don't have to divide each time inside the loop for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) { - if (!IsValidItem(itr->m_ItemType) || itr->m_ItemType == E_BLOCK_AIR) + if (!IsValidItem(itr->m_ItemType) || (itr->m_ItemType == E_BLOCK_AIR)) { // Don't spawn pickup if item isn't even valid; should prevent client crashing too continue; @@ -1835,7 +1831,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double { for (cItems::const_iterator itr = a_Pickups.begin(); itr != a_Pickups.end(); ++itr) { - if (!IsValidItem(itr->m_ItemType) || itr->m_ItemType == E_BLOCK_AIR) + if (!IsValidItem(itr->m_ItemType) || (itr->m_ItemType == E_BLOCK_AIR)) { continue; } @@ -2911,6 +2907,15 @@ void cWorld::TouchChunk(int a_ChunkX, int a_ChunkZ) +void cWorld::PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter) +{ + m_ChunkMap->PrepareChunk(a_ChunkX, a_ChunkZ, a_CallAfter); +} + + + + + void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ) { m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkZ); @@ -3021,7 +3026,7 @@ void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ) void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ) { - m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkZ); + m_ChunkMap->GenerateChunk(a_ChunkX, a_ChunkZ); } @@ -3130,6 +3135,11 @@ bool cWorld::HasEntity(int a_UniqueID) } // Check if the entity is in the chunkmap: + if (m_ChunkMap.get() == nullptr) + { + // Chunkmap has already been destroyed, there are no entities anymore. + return false; + } return m_ChunkMap->HasEntity(a_UniqueID); } @@ -3668,3 +3678,4 @@ void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerated (cChunkDesc & a_Ch + diff --git a/src/World.h b/src/World.h index 31bc9dad6..6f28ba534 100644 --- a/src/World.h +++ b/src/World.h @@ -10,7 +10,6 @@ #define MAX_PLAYERS 65535 #include "Simulator/SimulatorManager.h" -#include "MersenneTwister.h" #include "ChunkMap.h" #include "WorldStorage/WorldStorage.h" #include "Generating/ChunkGenerator.h" @@ -26,6 +25,7 @@ #include "MapManager.h" #include "Blocks/WorldInterface.h" #include "Blocks/BroadcastInterface.h" +#include "FastRandom.h" #include "ClientHandle.h" @@ -375,6 +375,12 @@ public: /** Touches the chunk, causing it to be loaded or generated */ void TouchChunk(int a_ChunkX, int a_ChunkZ); + + /** Queues the chunk for preparing - making sure that it's generated and lit. + The specified chunk is queued to be loaded or generated, and lit if needed. + The specified callback is called after the chunk has been prepared. If there's no preparation to do, only the callback is called. + It is legal to call with no callback. */ + void PrepareChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallAfter = nullptr); /** Marks the chunk as failed-to-load: */ void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ); @@ -808,7 +814,7 @@ public: 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: friend class cRoot; diff --git a/src/WorldStorage/FireworksSerializer.cpp b/src/WorldStorage/FireworksSerializer.cpp index ecb600483..91a5b8b63 100644 --- a/src/WorldStorage/FireworksSerializer.cpp +++ b/src/WorldStorage/FireworksSerializer.cpp @@ -22,11 +22,11 @@ void cFireworkItem::WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFa a_Writer.AddByte("Type", a_FireworkItem.m_Type); if (!a_FireworkItem.m_Colours.empty()) { - a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size()); + a_Writer.AddIntArray("Colors", a_FireworkItem.m_Colours.data(), a_FireworkItem.m_Colours.size()); } if (!a_FireworkItem.m_FadeColours.empty()) { - a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size()); + a_Writer.AddIntArray("FadeColors", a_FireworkItem.m_FadeColours.data(), a_FireworkItem.m_FadeColours.size()); } a_Writer.EndCompound(); a_Writer.EndList(); @@ -41,11 +41,11 @@ void cFireworkItem::WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFa a_Writer.AddByte("Type", a_FireworkItem.m_Type); if (!a_FireworkItem.m_Colours.empty()) { - a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size()); + a_Writer.AddIntArray("Colors", a_FireworkItem.m_Colours.data(), a_FireworkItem.m_Colours.size()); } if (!a_FireworkItem.m_FadeColours.empty()) { - a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size()); + a_Writer.AddIntArray("FadeColors", a_FireworkItem.m_FadeColours.data(), a_FireworkItem.m_FadeColours.size()); } a_Writer.EndCompound(); break; diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp index 4a913c81a..f2d35b318 100644 --- a/src/WorldStorage/MapSerializer.cpp +++ b/src/WorldStorage/MapSerializer.cpp @@ -112,7 +112,7 @@ void cMapSerializer::SaveMapToNBT(cFastNBTWriter & a_Writer) a_Writer.AddInt("zCenter", m_Map->GetCenterZ()); const cMap::cColorList & Data = m_Map->GetData(); - a_Writer.AddByteArray("colors", (char *) &Data[0], Data.size()); + a_Writer.AddByteArray("colors", (char *)Data.data(), Data.size()); a_Writer.EndCompound(); } @@ -190,7 +190,7 @@ bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT) CurrLine = a_NBT.FindChildByName(Data, "colors"); if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_ByteArray)) { - memcpy(&m_Map->m_Data[0], a_NBT.GetData(CurrLine), NumPixels); + memcpy(m_Map->m_Data.data(), a_NBT.GetData(CurrLine), NumPixels); } return true; diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index ddee5e514..af65db700 100644..100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -631,7 +631,7 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con // Load the proper BlockEntity type based on the block type: BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, RelY, RelZ); NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, RelY, RelZ); - std::auto_ptr<cBlockEntity> be(LoadBlockEntityFromNBT(a_NBT, Child, x, y, z, BlockType, BlockMeta)); + std::unique_ptr<cBlockEntity> be(LoadBlockEntityFromNBT(a_NBT, Child, x, y, z, BlockType, BlockMeta)); if (be.get() == nullptr) { continue; @@ -858,7 +858,7 @@ cBlockEntity * cWSSAnvil::LoadBeaconFromNBT(const cParsedNBT & a_NBT, int a_TagI return nullptr; } - std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); + std::unique_ptr<cBeaconEntity> Beacon(new cBeaconEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); int CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Levels"); if (CurrentLine >= 0) @@ -908,7 +908,7 @@ cBlockEntity * cWSSAnvil::LoadChestFromNBT(const cParsedNBT & a_NBT, int a_TagId { return nullptr; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this } - std::auto_ptr<cChestEntity> Chest(new cChestEntity(a_BlockX, a_BlockY, a_BlockZ, m_World, a_ChestBlockType)); + std::unique_ptr<cChestEntity> Chest(new cChestEntity(a_BlockX, a_BlockY, a_BlockZ, m_World, a_ChestBlockType)); LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items); return Chest.release(); } @@ -925,7 +925,7 @@ cBlockEntity * cWSSAnvil::LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int return nullptr; } - std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); + std::unique_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command"); if (currentLine >= 0) @@ -967,7 +967,7 @@ cBlockEntity * cWSSAnvil::LoadDispenserFromNBT(const cParsedNBT & a_NBT, int a_T { return nullptr; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this } - std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); + std::unique_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items); return Dispenser.release(); } @@ -989,7 +989,7 @@ cBlockEntity * cWSSAnvil::LoadDropperFromNBT(const cParsedNBT & a_NBT, int a_Tag { return nullptr; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this } - std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); + std::unique_ptr<cDropperEntity> Dropper(new cDropperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items); return Dropper.release(); } @@ -1006,7 +1006,7 @@ cBlockEntity * cWSSAnvil::LoadFlowerPotFromNBT(const cParsedNBT & a_NBT, int a_T return nullptr; } - std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); + std::unique_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); short ItemType = 0, ItemData = 0; int currentLine = a_NBT.FindChildByName(a_TagIdx, "Item"); @@ -1043,7 +1043,7 @@ cBlockEntity * cWSSAnvil::LoadFurnaceFromNBT(const cParsedNBT & a_NBT, int a_Tag return nullptr; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this } - std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World)); + std::unique_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World)); // Load slots: for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child)) @@ -1148,7 +1148,7 @@ cBlockEntity * cWSSAnvil::LoadHopperFromNBT(const cParsedNBT & a_NBT, int a_TagI { return nullptr; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this } - std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); + std::unique_ptr<cHopperEntity> Hopper(new cHopperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items); return Hopper.release(); } @@ -1165,7 +1165,7 @@ cBlockEntity * cWSSAnvil::LoadJukeboxFromNBT(const cParsedNBT & a_NBT, int a_Tag return nullptr; } - std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); + std::unique_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); int Record = a_NBT.FindChildByName(a_TagIdx, "Record"); if (Record >= 0) { @@ -1186,7 +1186,7 @@ cBlockEntity * cWSSAnvil::LoadMobHeadFromNBT(const cParsedNBT & a_NBT, int a_Tag return nullptr; } - std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); + std::unique_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType"); if (currentLine >= 0) @@ -1221,7 +1221,7 @@ cBlockEntity * cWSSAnvil::LoadNoteBlockFromNBT(const cParsedNBT & a_NBT, int a_T return nullptr; } - std::auto_ptr<cNoteEntity> NoteBlock(new cNoteEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); + std::unique_ptr<cNoteEntity> NoteBlock(new cNoteEntity(a_BlockX, a_BlockY, a_BlockZ, m_World)); int note = a_NBT.FindChildByName(a_TagIdx, "note"); if (note >= 0) { @@ -1242,7 +1242,7 @@ cBlockEntity * cWSSAnvil::LoadSignFromNBT(const cParsedNBT & a_NBT, int a_TagIdx return nullptr; } - std::auto_ptr<cSignEntity> Sign(new cSignEntity(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, m_World)); + std::unique_ptr<cSignEntity> Sign(new cSignEntity(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, m_World)); int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1"); if (currentLine >= 0) @@ -1495,7 +1495,7 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cBoat> Boat(new cBoat(0, 0, 0)); + std::unique_ptr<cBoat> Boat(new cBoat(0, 0, 0)); if (!LoadEntityBaseFromNBT(*Boat.get(), a_NBT, a_TagIdx)) { return; @@ -1509,7 +1509,7 @@ void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N void cWSSAnvil::LoadEnderCrystalFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cEnderCrystal> EnderCrystal(new cEnderCrystal(0, 0, 0)); + std::unique_ptr<cEnderCrystal> EnderCrystal(new cEnderCrystal(0, 0, 0)); if (!LoadEntityBaseFromNBT(*EnderCrystal.get(), a_NBT, a_TagIdx)) { return; @@ -1526,12 +1526,15 @@ void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedN int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "TileID"); int MetaIdx = a_NBT.FindChildByName(a_TagIdx, "Data"); - if ((TypeIdx < 0) || (MetaIdx < 0)) { return; } + if ((TypeIdx < 0) || (MetaIdx < 0)) + { + return; + } int Type = a_NBT.GetInt(TypeIdx); NIBBLETYPE Meta = (NIBBLETYPE)a_NBT.GetByte(MetaIdx); - std::auto_ptr<cFallingBlock> FallingBlock(new cFallingBlock(Vector3i(0, 0, 0), Type, Meta)); + std::unique_ptr<cFallingBlock> FallingBlock(new cFallingBlock(Vector3i(0, 0, 0), Type, Meta)); if (!LoadEntityBaseFromNBT(*FallingBlock.get(), a_NBT, a_TagIdx)) { return; @@ -1545,7 +1548,7 @@ void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedN void cWSSAnvil::LoadMinecartRFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cRideableMinecart> Minecart(new cRideableMinecart(0, 0, 0, cItem(), 1)); // TODO: Load the block and the height + std::unique_ptr<cRideableMinecart> Minecart(new cRideableMinecart(0, 0, 0, cItem(), 1)); // TODO: Load the block and the height if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) { return; @@ -1564,7 +1567,7 @@ void cWSSAnvil::LoadMinecartCFromNBT(cEntityList & a_Entities, const cParsedNBT { return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this } - std::auto_ptr<cMinecartWithChest> Minecart(new cMinecartWithChest(0, 0, 0)); + std::unique_ptr<cMinecartWithChest> Minecart(new cMinecartWithChest(0, 0, 0)); if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) { return; @@ -1591,7 +1594,7 @@ void cWSSAnvil::LoadMinecartCFromNBT(cEntityList & a_Entities, const cParsedNBT void cWSSAnvil::LoadMinecartFFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cMinecartWithFurnace> Minecart(new cMinecartWithFurnace(0, 0, 0)); + std::unique_ptr<cMinecartWithFurnace> Minecart(new cMinecartWithFurnace(0, 0, 0)); if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) { return; @@ -1608,7 +1611,7 @@ void cWSSAnvil::LoadMinecartFFromNBT(cEntityList & a_Entities, const cParsedNBT void cWSSAnvil::LoadMinecartTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cMinecartWithTNT> Minecart(new cMinecartWithTNT(0, 0, 0)); + std::unique_ptr<cMinecartWithTNT> Minecart(new cMinecartWithTNT(0, 0, 0)); if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) { return; @@ -1625,7 +1628,7 @@ void cWSSAnvil::LoadMinecartTFromNBT(cEntityList & a_Entities, const cParsedNBT void cWSSAnvil::LoadMinecartHFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cMinecartWithHopper> Minecart(new cMinecartWithHopper(0, 0, 0)); + std::unique_ptr<cMinecartWithHopper> Minecart(new cMinecartWithHopper(0, 0, 0)); if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx)) { return; @@ -1654,7 +1657,7 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a return; } - std::auto_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false + std::unique_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false if (!LoadEntityBaseFromNBT(*Pickup.get(), a_NBT, a_TagIdx)) { return; @@ -1676,7 +1679,7 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a void cWSSAnvil::LoadTNTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cTNTEntity> TNT(new cTNTEntity(0.0, 0.0, 0.0, 0)); + std::unique_ptr<cTNTEntity> TNT(new cTNTEntity(0.0, 0.0, 0.0, 0)); if (!LoadEntityBaseFromNBT(*TNT.get(), a_NBT, a_TagIdx)) { return; @@ -1698,7 +1701,7 @@ void cWSSAnvil::LoadTNTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB void cWSSAnvil::LoadExpOrbFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cExpOrb> ExpOrb(new cExpOrb(0.0, 0.0, 0.0, 0)); + std::unique_ptr<cExpOrb> ExpOrb(new cExpOrb(0.0, 0.0, 0.0, 0)); if (!LoadEntityBaseFromNBT(*ExpOrb.get(), a_NBT, a_TagIdx)) { return; @@ -1797,7 +1800,7 @@ void cWSSAnvil::LoadItemFrameFromNBT(cEntityList & a_Entities, const cParsedNBT return; } - std::auto_ptr<cItemFrame> ItemFrame(new cItemFrame(BLOCK_FACE_NONE, 0.0, 0.0, 0.0)); + std::unique_ptr<cItemFrame> ItemFrame(new cItemFrame(BLOCK_FACE_NONE, 0.0, 0.0, 0.0)); if (!LoadEntityBaseFromNBT(*ItemFrame.get(), a_NBT, a_TagIdx)) { return; @@ -1822,7 +1825,7 @@ void cWSSAnvil::LoadItemFrameFromNBT(cEntityList & a_Entities, const cParsedNBT void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cArrowEntity> Arrow(new cArrowEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); + std::unique_ptr<cArrowEntity> Arrow(new cArrowEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); if (!LoadProjectileBaseFromNBT(*Arrow.get(), a_NBT, a_TagIdx)) { return; @@ -1892,7 +1895,7 @@ void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ void cWSSAnvil::LoadSplashPotionFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cSplashPotionEntity> SplashPotion(new cSplashPotionEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0), cItem())); + std::unique_ptr<cSplashPotionEntity> SplashPotion(new cSplashPotionEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0), cItem())); if (!LoadProjectileBaseFromNBT(*SplashPotion.get(), a_NBT, a_TagIdx)) { return; @@ -1916,7 +1919,7 @@ void cWSSAnvil::LoadSplashPotionFromNBT(cEntityList & a_Entities, const cParsedN void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cThrownSnowballEntity> Snowball(new cThrownSnowballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); + std::unique_ptr<cThrownSnowballEntity> Snowball(new cThrownSnowballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); if (!LoadProjectileBaseFromNBT(*Snowball.get(), a_NBT, a_TagIdx)) { return; @@ -1932,7 +1935,7 @@ void cWSSAnvil::LoadSnowballFromNBT(cEntityList & a_Entities, const cParsedNBT & void cWSSAnvil::LoadEggFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cThrownEggEntity> Egg(new cThrownEggEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); + std::unique_ptr<cThrownEggEntity> Egg(new cThrownEggEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); if (!LoadProjectileBaseFromNBT(*Egg.get(), a_NBT, a_TagIdx)) { return; @@ -1948,7 +1951,7 @@ void cWSSAnvil::LoadEggFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB void cWSSAnvil::LoadFireballFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cGhastFireballEntity> Fireball(new cGhastFireballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); + std::unique_ptr<cGhastFireballEntity> Fireball(new cGhastFireballEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); if (!LoadProjectileBaseFromNBT(*Fireball.get(), a_NBT, a_TagIdx)) { return; @@ -1964,7 +1967,7 @@ void cWSSAnvil::LoadFireballFromNBT(cEntityList & a_Entities, const cParsedNBT & void cWSSAnvil::LoadFireChargeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cFireChargeEntity> FireCharge(new cFireChargeEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); + std::unique_ptr<cFireChargeEntity> FireCharge(new cFireChargeEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); if (!LoadProjectileBaseFromNBT(*FireCharge.get(), a_NBT, a_TagIdx)) { return; @@ -1980,7 +1983,7 @@ void cWSSAnvil::LoadFireChargeFromNBT(cEntityList & a_Entities, const cParsedNBT void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cThrownEnderPearlEntity> Enderpearl(new cThrownEnderPearlEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); + std::unique_ptr<cThrownEnderPearlEntity> Enderpearl(new cThrownEnderPearlEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); if (!LoadProjectileBaseFromNBT(*Enderpearl.get(), a_NBT, a_TagIdx)) { return; @@ -1996,7 +1999,7 @@ void cWSSAnvil::LoadThrownEnderpearlFromNBT(cEntityList & a_Entities, const cPar void cWSSAnvil::LoadBatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cBat> Monster(new cBat()); + std::unique_ptr<cBat> Monster(new cBat()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2016,7 +2019,7 @@ void cWSSAnvil::LoadBatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB void cWSSAnvil::LoadBlazeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cBlaze> Monster(new cBlaze()); + std::unique_ptr<cBlaze> Monster(new cBlaze()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2036,7 +2039,7 @@ void cWSSAnvil::LoadBlazeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ void cWSSAnvil::LoadCaveSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cCaveSpider> Monster(new cCaveSpider()); + std::unique_ptr<cCaveSpider> Monster(new cCaveSpider()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2056,7 +2059,7 @@ void cWSSAnvil::LoadCaveSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT void cWSSAnvil::LoadChickenFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cChicken> Monster(new cChicken()); + std::unique_ptr<cChicken> Monster(new cChicken()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2076,7 +2079,7 @@ void cWSSAnvil::LoadChickenFromNBT(cEntityList & a_Entities, const cParsedNBT & void cWSSAnvil::LoadCowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cCow> Monster(new cCow()); + std::unique_ptr<cCow> Monster(new cCow()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2096,7 +2099,7 @@ void cWSSAnvil::LoadCowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB void cWSSAnvil::LoadCreeperFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cCreeper> Monster(new cCreeper()); + std::unique_ptr<cCreeper> Monster(new cCreeper()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2116,7 +2119,7 @@ void cWSSAnvil::LoadCreeperFromNBT(cEntityList & a_Entities, const cParsedNBT & void cWSSAnvil::LoadEnderDragonFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cEnderDragon> Monster(new cEnderDragon()); + std::unique_ptr<cEnderDragon> Monster(new cEnderDragon()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2136,7 +2139,7 @@ void cWSSAnvil::LoadEnderDragonFromNBT(cEntityList & a_Entities, const cParsedNB void cWSSAnvil::LoadEndermanFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cEnderman> Monster(new cEnderman()); + std::unique_ptr<cEnderman> Monster(new cEnderman()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2156,7 +2159,7 @@ void cWSSAnvil::LoadEndermanFromNBT(cEntityList & a_Entities, const cParsedNBT & void cWSSAnvil::LoadGhastFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cGhast> Monster(new cGhast()); + std::unique_ptr<cGhast> Monster(new cGhast()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2176,7 +2179,7 @@ void cWSSAnvil::LoadGhastFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cGiant> Monster(new cGiant()); + std::unique_ptr<cGiant> Monster(new cGiant()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2196,17 +2199,19 @@ void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Type"); + int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Type"); int ColorIdx = a_NBT.FindChildByName(a_TagIdx, "Color"); int StyleIdx = a_NBT.FindChildByName(a_TagIdx, "Style"); - - if ((TypeIdx < 0) || (ColorIdx < 0) || (StyleIdx < 0)) { return; } + if ((TypeIdx < 0) || (ColorIdx < 0) || (StyleIdx < 0)) + { + return; + } int Type = a_NBT.GetInt(TypeIdx); int Color = a_NBT.GetInt(ColorIdx); int Style = a_NBT.GetInt(StyleIdx); - std::auto_ptr<cHorse> Monster(new cHorse(Type, Color, Style, 1)); + std::unique_ptr<cHorse> Monster(new cHorse(Type, Color, Style, 1)); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { @@ -2227,7 +2232,7 @@ void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ void cWSSAnvil::LoadIronGolemFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cIronGolem> Monster(new cIronGolem()); + std::unique_ptr<cIronGolem> Monster(new cIronGolem()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2256,7 +2261,7 @@ void cWSSAnvil::LoadMagmaCubeFromNBT(cEntityList & a_Entities, const cParsedNBT int Size = a_NBT.GetInt(SizeIdx); - std::auto_ptr<cMagmaCube> Monster(new cMagmaCube(Size)); + std::unique_ptr<cMagmaCube> Monster(new cMagmaCube(Size)); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2276,7 +2281,7 @@ void cWSSAnvil::LoadMagmaCubeFromNBT(cEntityList & a_Entities, const cParsedNBT void cWSSAnvil::LoadMooshroomFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cMooshroom> Monster(new cMooshroom()); + std::unique_ptr<cMooshroom> Monster(new cMooshroom()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2296,7 +2301,7 @@ void cWSSAnvil::LoadMooshroomFromNBT(cEntityList & a_Entities, const cParsedNBT void cWSSAnvil::LoadOcelotFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cOcelot> Monster(new cOcelot()); + std::unique_ptr<cOcelot> Monster(new cOcelot()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2316,7 +2321,7 @@ void cWSSAnvil::LoadOcelotFromNBT(cEntityList & a_Entities, const cParsedNBT & a void cWSSAnvil::LoadPigFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cPig> Monster(new cPig()); + std::unique_ptr<cPig> Monster(new cPig()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2343,7 +2348,7 @@ void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ Color = (int)a_NBT.GetByte(ColorIdx); } - std::auto_ptr<cSheep> Monster(new cSheep(Color)); + std::unique_ptr<cSheep> Monster(new cSheep(Color)); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2369,7 +2374,7 @@ void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ void cWSSAnvil::LoadSilverfishFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cSilverfish> Monster(new cSilverfish()); + std::unique_ptr<cSilverfish> Monster(new cSilverfish()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2390,12 +2395,14 @@ void cWSSAnvil::LoadSilverfishFromNBT(cEntityList & a_Entities, const cParsedNBT void cWSSAnvil::LoadSkeletonFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "SkeletonType"); - - if (TypeIdx < 0) { return; } + if (TypeIdx < 0) + { + return; + } bool Type = ((a_NBT.GetByte(TypeIdx) == 1) ? true : false); - std::auto_ptr<cSkeleton> Monster(new cSkeleton(Type)); + std::unique_ptr<cSkeleton> Monster(new cSkeleton(Type)); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2424,7 +2431,7 @@ void cWSSAnvil::LoadSlimeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ int Size = a_NBT.GetInt(SizeIdx); - std::auto_ptr<cSlime> Monster(new cSlime(Size)); + std::unique_ptr<cSlime> Monster(new cSlime(Size)); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2444,7 +2451,7 @@ void cWSSAnvil::LoadSlimeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ void cWSSAnvil::LoadSnowGolemFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cSnowGolem> Monster(new cSnowGolem()); + std::unique_ptr<cSnowGolem> Monster(new cSnowGolem()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2464,7 +2471,7 @@ void cWSSAnvil::LoadSnowGolemFromNBT(cEntityList & a_Entities, const cParsedNBT void cWSSAnvil::LoadSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cSpider> Monster(new cSpider()); + std::unique_ptr<cSpider> Monster(new cSpider()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2484,7 +2491,7 @@ void cWSSAnvil::LoadSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a void cWSSAnvil::LoadSquidFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cSquid> Monster(new cSquid()); + std::unique_ptr<cSquid> Monster(new cSquid()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2505,12 +2512,14 @@ void cWSSAnvil::LoadSquidFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ void cWSSAnvil::LoadVillagerFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "Profession"); - - if (TypeIdx < 0) { return; } + if (TypeIdx < 0) + { + return; + } int Type = a_NBT.GetInt(TypeIdx); - std::auto_ptr<cVillager> Monster(new cVillager(cVillager::eVillagerType(Type))); + std::unique_ptr<cVillager> Monster(new cVillager(cVillager::eVillagerType(Type))); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2530,7 +2539,7 @@ void cWSSAnvil::LoadVillagerFromNBT(cEntityList & a_Entities, const cParsedNBT & void cWSSAnvil::LoadWitchFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cWitch> Monster(new cWitch()); + std::unique_ptr<cWitch> Monster(new cWitch()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2550,7 +2559,7 @@ void cWSSAnvil::LoadWitchFromNBT(cEntityList & a_Entities, const cParsedNBT & a_ void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cWither> Monster(new cWither()); + std::unique_ptr<cWither> Monster(new cWither()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2576,7 +2585,7 @@ void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cWolf> Monster(new cWolf()); + std::unique_ptr<cWolf> Monster(new cWolf()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2630,12 +2639,14 @@ void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { int IsVillagerIdx = a_NBT.FindChildByName(a_TagIdx, "IsVillager"); - - if (IsVillagerIdx < 0) { return; } + if (IsVillagerIdx < 0) + { + return; + } bool IsVillagerZombie = ((a_NBT.GetByte(IsVillagerIdx) == 1) ? true : false); - std::auto_ptr<cZombie> Monster(new cZombie(IsVillagerZombie)); + std::unique_ptr<cZombie> Monster(new cZombie(IsVillagerZombie)); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2655,7 +2666,7 @@ void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a void cWSSAnvil::LoadPigZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - std::auto_ptr<cZombiePigman> Monster(new cZombiePigman()); + std::unique_ptr<cZombiePigman> Monster(new cZombiePigman()); if (!LoadEntityBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx)) { return; @@ -2912,6 +2923,8 @@ cWSSAnvil::cMCAFile::cMCAFile(const AString & a_FileName, int a_RegionX, int a_R bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading) { + bool writeOutNeeded = false; + if (m_File.IsOpen()) { // Already open @@ -2937,11 +2950,26 @@ bool cWSSAnvil::cMCAFile::OpenFile(bool a_IsForReading) if (m_File.Read(m_Header, sizeof(m_Header)) != sizeof(m_Header)) { // Cannot read the header - perhaps the file has just been created? - // Try writing a nullptr header (both chunk offsets and timestamps): + // Try writing a nullptr header for chunk offsets: memset(m_Header, 0, sizeof(m_Header)); + writeOutNeeded = true; + } + + // Load the TimeStamps: + if (m_File.Read(m_TimeStamps, sizeof(m_TimeStamps)) != sizeof(m_TimeStamps)) + { + // Cannot read the time stamps - perhaps the file has just been created? + // Try writing a nullptr header for timestamps: + memset(m_TimeStamps, 0, sizeof(m_TimeStamps)); + writeOutNeeded = true; + } + + if (writeOutNeeded) + { + m_File.Seek(0); if ( - (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || // Real header - chunk offsets - (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) // Bogus data for the chunk timestamps + (m_File.Write(m_Header, sizeof(m_Header)) != sizeof(m_Header)) || // Write chunk offsets + (m_File.Write(m_TimeStamps, sizeof(m_TimeStamps)) != sizeof(m_TimeStamps)) // Write chunk timestamps ) { LOGWARNING("Cannot process MCA header in file \"%s\", chunks in that file will be lost", m_FileName.c_str()); @@ -3072,7 +3100,13 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri ); return false; } + + // Store the header info in the table m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize); + + // Set the modification time + m_TimeStamps[LocalX + 32 * LocalZ] = htonl(static_cast<u_long>(time(nullptr))); + if (m_File.Seek(0) < 0) { LOGWARNING("Cannot save chunk [%d, %d], seeking in file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); @@ -3083,6 +3117,11 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri LOGWARNING("Cannot save chunk [%d, %d], writing header to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); return false; } + if (m_File.Write(m_TimeStamps, sizeof(m_TimeStamps)) != sizeof(m_TimeStamps)) + { + LOGWARNING("Cannot save chunk [%d, %d], writing timestamps to file \"%s\" failed", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, GetFileName().c_str()); + return false; + } return true; } diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 7a98a9a04..974ba932e 100644..100755 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -80,7 +80,8 @@ protected: // First 1024 entries are chunk locations - the 3 + 1 byte sector-offset and sector-count unsigned m_Header[MCA_MAX_CHUNKS]; - // Chunk timestamps, following the chunk headers, are unused by MCS + // Chunk timestamps, following the chunk headers + unsigned m_TimeStamps[MCA_MAX_CHUNKS]; /// Finds a free location large enough to hold a_Data. Gets a hint of the chunk coords, places the data there if it fits. Returns the sector number. unsigned FindFreeLocation(int a_LocalX, int a_LocalZ, const AString & a_Data); diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp index 31318ee67..c7a295175 100644 --- a/src/WorldStorage/WorldStorage.cpp +++ b/src/WorldStorage/WorldStorage.cpp @@ -140,11 +140,11 @@ size_t cWorldStorage::GetSaveQueueLength(void) -void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ) +void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) { ASSERT(m_World->IsChunkQueued(a_ChunkX, a_ChunkZ)); - m_LoadQueue.EnqueueItem(cChunkCoords(a_ChunkX, a_ChunkZ)); + m_LoadQueue.EnqueueItem(cChunkCoordsWithCallback(a_ChunkX, a_ChunkZ, a_Callback)); m_Event.Set(); } @@ -152,11 +152,11 @@ void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ) -void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ) +void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback) { ASSERT(m_World->IsChunkValid(a_ChunkX, a_ChunkZ)); - m_SaveQueue.EnqueueItemIfNotPresent(cChunkCoords(a_ChunkX, a_ChunkZ)); + m_SaveQueue.EnqueueItem(cChunkCoordsWithCallback(a_ChunkX, a_ChunkZ, a_Callback)); m_Event.Set(); } @@ -166,7 +166,11 @@ void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ) void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkZ) { - m_LoadQueue.Remove(cChunkCoords(a_ChunkX, a_ChunkZ)); + m_LoadQueue.RemoveIf([=](cChunkCoordsWithCallback & a_Item) + { + return (a_Item.m_ChunkX == a_ChunkX) && (a_Item.m_ChunkZ == a_ChunkZ); + } + ); } @@ -175,7 +179,11 @@ void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkZ) void cWorldStorage::UnqueueSave(const cChunkCoords & a_Chunk) { - m_SaveQueue.Remove(a_Chunk); + m_SaveQueue.RemoveIf([=](cChunkCoordsWithCallback & a_Item) + { + return (a_Item.m_ChunkX == a_Chunk.m_ChunkX) && (a_Item.m_ChunkZ == a_Chunk.m_ChunkZ); + } + ); } @@ -244,14 +252,23 @@ void cWorldStorage::Execute(void) bool cWorldStorage::LoadOneChunk(void) { - cChunkCoords ToLoad(0, 0); + // Dequeue an item, bail out if there's none left: + cChunkCoordsWithCallback ToLoad(0, 0, nullptr); bool ShouldLoad = m_LoadQueue.TryDequeueItem(ToLoad); + if (!ShouldLoad) + { + return false; + } - if (ShouldLoad) + // Load the chunk: + bool res = LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkZ); + + // Call the callback, if specified: + if (ToLoad.m_Callback != nullptr) { - return LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkZ); + ToLoad.m_Callback->Call(ToLoad.m_ChunkX, ToLoad.m_ChunkZ); } - return false; + return res; } @@ -260,17 +277,30 @@ bool cWorldStorage::LoadOneChunk(void) bool cWorldStorage::SaveOneChunk(void) { - cChunkCoords ToSave(0, 0); + // Dequeue one chunk to save: + cChunkCoordsWithCallback ToSave(0, 0, nullptr); bool ShouldSave = m_SaveQueue.TryDequeueItem(ToSave); - if (ShouldSave && m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ)) + if (!ShouldSave) + { + return false; + } + + // Save the chunk, if it's valid: + if (m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ)) { m_World->MarkChunkSaving(ToSave.m_ChunkX, ToSave.m_ChunkZ); - if (m_SaveSchema->SaveChunk(ToSave)) + if (m_SaveSchema->SaveChunk(cChunkCoords(ToSave.m_ChunkX, ToSave.m_ChunkZ))) { m_World->MarkChunkSaved(ToSave.m_ChunkX, ToSave.m_ChunkZ); } } - return ShouldSave; + + // Call the callback, if specified: + if (ToSave.m_Callback != nullptr) + { + ToSave.m_Callback->Call(ToSave.m_ChunkX, ToSave.m_ChunkZ); + } + return true; } diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h index fc7e9f84c..e9ba2a8e2 100644 --- a/src/WorldStorage/WorldStorage.h +++ b/src/WorldStorage/WorldStorage.h @@ -25,7 +25,7 @@ // fwd: class cWorld; -typedef cQueue<cChunkCoords> cChunkCoordsQueue; +typedef cQueue<cChunkCoordsWithCallback> cChunkCoordsQueue; @@ -64,8 +64,8 @@ public: cWorldStorage(void); ~cWorldStorage(); - void QueueLoadChunk(int a_ChunkX, int a_ChunkZ); - void QueueSaveChunk(int a_ChunkX, int a_ChunkZ); + void QueueLoadChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = nullptr); + void QueueSaveChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = nullptr); void UnqueueLoad(int a_ChunkX, int a_ChunkZ); void UnqueueSave(const cChunkCoords & a_Chunk); diff --git a/src/XMLParser.h b/src/XMLParser.h index 5b53e55cf..ea341bce6 100644 --- a/src/XMLParser.h +++ b/src/XMLParser.h @@ -105,11 +105,11 @@ public: Destroy (); // If the encoding or seperator are empty, then nullptr - if (pszEncoding != nullptr && pszEncoding [0] == 0) + if ((pszEncoding != nullptr) && (pszEncoding[0] == 0)) { pszEncoding = nullptr; } - if (pszSep != nullptr && pszSep [0] == 0) + if ((pszSep != nullptr) && (pszSep[0] == 0)) { pszSep = nullptr; } @@ -147,7 +147,7 @@ public: bool Parse (const char *pszBuffer, int nLength, bool fIsFinal = true) { assert (m_p != nullptr); - return XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0; + return (XML_Parse (m_p, pszBuffer, nLength, fIsFinal) != 0); } // @cmember Parse internal buffer @@ -254,7 +254,9 @@ protected: XML_SetDefaultHandlerExpand (m_p, fEnable ? DefaultHandler : nullptr); } else + { XML_SetDefaultHandler (m_p, fEnable ? DefaultHandler : nullptr); + } } // @cmember Enable/Disable external entity ref handler @@ -402,11 +404,17 @@ public: { XML_expat_version v = XML_ExpatVersionInfo (); if (pnMajor) + { *pnMajor = v .major; + } if (pnMinor) + { *pnMinor = v .minor; + } if (pnMicro) + { *pnMicro = v .micro; + } } // @cmember Get last error string diff --git a/src/main.cpp b/src/main.cpp index fe4b360a5..d4adc1ed9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -163,9 +163,9 @@ BOOL CtrlHandler(DWORD fdwCtrlType) cRoot::m_TerminateEventRaised = true; LOGD("Terminate event raised from the Windows CtrlHandler"); - if (fdwCtrlType == CTRL_CLOSE_EVENT) // Console window closed via 'x' button, Windows will try to close immediately, therefore... + while (!g_ServerTerminated) { - while (!g_ServerTerminated) { cSleep::MilliSleep(100); } // Delay as much as possible to try to get the server to shut down cleanly + std::this_thread::sleep_for(std::chrono::milliseconds(50)); // Delay as much as possible to try to get the server to shut down cleanly } return TRUE; |