summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt53
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--CONTRIBUTORS1
-rw-r--r--Install/MCServer_high_detail_debug.cmd2
-rw-r--r--Install/MCServer_medium_detail_debug.cmd2
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua10
-rw-r--r--MCServer/Plugins/APIDump/main.css2
-rw-r--r--MCServer/Plugins/APIDump/main_APIDump.lua32
-rw-r--r--MCServer/Plugins/Debuggers/Debuggers.lua111
-rw-r--r--MCServer/Plugins/Debuggers/Info.lua6
m---------MCServer/Plugins/MagicCarpet0
-rw-r--r--SetFlags.cmake12
-rw-r--r--Tools/MCADefrag/MCADefrag.cpp2
-rw-r--r--Tools/ProtoProxy/ProtoProxy.cpp2
m---------lib/SQLiteCpp0
-rw-r--r--src/Bindings/ManualBindings.cpp58
-rw-r--r--src/BlockEntities/BlockEntity.h1
-rw-r--r--src/BlockEntities/FurnaceEntity.cpp42
-rw-r--r--src/BlockEntities/FurnaceEntity.h8
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/Chunk.cpp16
-rw-r--r--src/Chunk.h1
-rw-r--r--src/ChunkMap.cpp16
-rw-r--r--src/ChunkMap.h1
-rw-r--r--src/ChunkSender.cpp242
-rw-r--r--src/ChunkSender.h81
-rw-r--r--src/ClientHandle.cpp27
-rw-r--r--src/ClientHandle.h8
-rw-r--r--src/Globals.h2
-rw-r--r--src/LineBlockTracer.cpp19
-rw-r--r--src/LoggerListeners.cpp20
-rw-r--r--src/LoggerListeners.h2
-rw-r--r--src/OSSupport/CMakeLists.txt2
-rw-r--r--src/OSSupport/Event.cpp4
-rw-r--r--src/OSSupport/Semaphore.cpp107
-rw-r--r--src/OSSupport/Semaphore.h17
-rw-r--r--src/Root.cpp2
-rw-r--r--src/World.cpp57
-rw-r--r--src/World.h2
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.cpp5
-rw-r--r--src/main.cpp195
41 files changed, 635 insertions, 538 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ae83662c9..53e836262 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -3,6 +3,11 @@ cmake_minimum_required (VERSION 2.8.7)
# Without this, the MSVC variable isn't defined for MSVC builds ( http://www.cmake.org/pipermail/cmake/2011-November/047130.html )
enable_language(CXX C)
+# Enable the support for solution folders in MSVC
+if (MSVC)
+ set_property(GLOBAL PROPERTY USE_FOLDERS ON)
+endif()
+
# These env variables are used for configuring Travis CI builds.
# See https://github.com/mc-server/MCServer/pull/767
if(DEFINED ENV{TRAVIS_MCSERVER_BUILD_TYPE})
@@ -19,6 +24,7 @@ if(DEFINED ENV{TRAVIS_BUILD_WITH_COVERAGE})
endif()
if(DEFINED ENV{MCSERVER_BUILD_ID})
+ # The build info is defined by the build system (Travis / Jenkins)
set(BUILD_ID $ENV{MCSERVER_BUILD_ID})
set(BUILD_SERIES_NAME $ENV{MCSERVER_BUILD_SERIES_NAME})
set(BUILD_DATETIME $ENV{MCSERVER_BUILD_DATETIME})
@@ -29,12 +35,41 @@ if(DEFINED ENV{MCSERVER_BUILD_ID})
execute_process(
COMMAND git rev-parse HEAD
RESULT_VARIABLE GIT_EXECUTED
- OUTPUT_VARIABLE BUILD_COMMIT_ID)
- string(STRIP ${BUILD_COMMIT_ID} BUILD_COMMIT_ID)
+ OUTPUT_VARIABLE BUILD_COMMIT_ID
+ )
+ string(STRIP ${BUILD_COMMIT_ID} BUILD_COMMIT_ID)
if (NOT (GIT_EXECUTED EQUAL 0))
message(FATAL_ERROR "Could not identifiy git commit id")
endif()
endif()
+else()
+ # This is a local build, stuff in some basic info:
+ set(BUILD_ID "Unknown")
+ set(BUILD_SERIES_NAME "local build")
+ execute_process(
+ COMMAND git rev-parse HEAD
+ RESULT_VARIABLE GIT_EXECUTED
+ OUTPUT_VARIABLE BUILD_COMMIT_ID
+ )
+ if (NOT(GIT_EXECUTED EQUAL 0))
+ set(BUILD_COMMIT_ID "Unknown")
+ endif()
+ string(STRIP ${BUILD_COMMIT_ID} BUILD_COMMIT_ID)
+ execute_process(
+ COMMAND git log -1 --date=iso --pretty=format:%ai
+ RESULT_VARIABLE GIT_EXECUTED
+ OUTPUT_VARIABLE BUILD_DATETIME
+ )
+ if (NOT(GIT_EXECUTED EQUAL 0))
+ set(BUILD_DATETIME "Unknown")
+ endif()
+ string(STRIP ${BUILD_DATETIME} BUILD_DATETIME)
+
+ # The BUILD_COMMIT_ID and BUILD_DATETIME aren't updated on each repo pull
+ # They are only updated when cmake re-configures the project
+ # Therefore mark them as "approx: "
+ set(BUILD_COMMIT_ID "approx: ${BUILD_COMMIT_ID}")
+ set(BUILD_DATETIME "approx: ${BUILD_DATETIME}")
endif()
# We need C++11 features, Visual Studio has those from VS2012, but it needs a new platform toolset for those; VS2013 supports them natively:
@@ -67,6 +102,7 @@ if(${BUILD_UNSTABLE_TOOLS})
add_subdirectory(Tools/GeneratorPerformanceTest/)
endif()
+include(CheckCXXCompilerFlag)
include(SetFlags.cmake)
set_flags()
set_lib_flags()
@@ -102,6 +138,7 @@ set(SQLITECPP_RUN_DOXYGEN OFF CACHE BOOL "Run Doxygen C++ documentation tool
set(SQLITECPP_BUILD_EXAMPLES OFF CACHE BOOL "Build examples." FORCE)
set(SQLITECPP_BUILD_TESTS OFF CACHE BOOL "Build and run tests." FORCE)
set(SQLITECPP_INTERNAL_SQLITE OFF CACHE BOOL "Add the internal SQLite3 source to the project." FORCE)
+set(SQLITE_ENABLE_COLUMN_METADATA OFF CACHE BOOL "" FORCE)
# Set options for LibEvent, disable all their tests and benchmarks:
set(EVENT__DISABLE_OPENSSL YES CACHE BOOL "Disable OpenSSL in LibEvent" FORCE)
@@ -130,7 +167,7 @@ add_subdirectory(lib/sqlite/)
add_subdirectory(lib/SQLiteCpp/)
add_subdirectory(lib/expat/)
add_subdirectory(lib/luaexpat/)
-add_subdirectory(lib/libevent/)
+add_subdirectory(lib/libevent/ EXCLUDE_FROM_ALL)
# Add proper include directories so that SQLiteCpp can find SQLite3:
get_property(SQLITECPP_INCLUDES DIRECTORY "lib/SQLiteCpp/" PROPERTY INCLUDE_DIRECTORIES)
@@ -160,3 +197,13 @@ if(${SELF_TEST})
add_subdirectory (tests)
endif()
+# Put project into solution folders in MSVC:
+if (MSVC)
+ set_target_properties(event_core event_extra expat jsoncpp lua luaexpat mbedtls sqlite SQLiteCpp tolualib zlib PROPERTIES FOLDER Lib)
+ set_target_properties(luaproxy tolua PROPERTIES FOLDER Support)
+ if (${SELF_TEST})
+ set_target_properties(Network PROPERTIES FOLDER Lib)
+ set_target_properties(arraystocoords-exe coordinates-exe copies-exe copyblocks-exe creatable-exe EchoServer Google-exe ChunkBuffer NameLookup PROPERTIES FOLDER Tests)
+ endif()
+endif()
+
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4e57e6efb..1b43ed214 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -23,7 +23,7 @@ Here are the conventions:
- This helps prevent mistakes such as `if (a & 1 == 0)`
*
* Use the provided wrappers for OS stuff:
- - Threading is done by inheriting from `cIsThread`, thread synchronization through `cCriticalSection`, `cSemaphore` and `cEvent`, file access and filesystem operations through the `cFile` class, high-precision timers through `cTimer`, high-precision sleep through `cSleep`
+ - Threading is done by inheriting from `cIsThread`, thread synchronization through `cCriticalSection` and `cEvent`, file access and filesystem operations through the `cFile` class, high-precision timers through `cTimer`, high-precision sleep through `cSleep`
* No magic numbers, use named constants:
- `E_ITEM_XXX`, `E_BLOCK_XXX` and `E_META_XXX` for items and blocks
- `cEntity::etXXX` for entity types, `cMonster::mtXXX` for mob types
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index b7ecd45a3..82ae6a7a8 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -8,6 +8,7 @@ derouinw
Diusrex
Duralex
FakeTruth (founder)
+HaoTNN
Howaner
jan64
jasperarmstrong
diff --git a/Install/MCServer_high_detail_debug.cmd b/Install/MCServer_high_detail_debug.cmd
index 384189c42..d94af8150 100644
--- a/Install/MCServer_high_detail_debug.cmd
+++ b/Install/MCServer_high_detail_debug.cmd
@@ -1 +1 @@
-MCServer /cdf \ No newline at end of file
+MCServer --crash-dump-full
diff --git a/Install/MCServer_medium_detail_debug.cmd b/Install/MCServer_medium_detail_debug.cmd
index b5bb0954c..0a33c27fd 100644
--- a/Install/MCServer_medium_detail_debug.cmd
+++ b/Install/MCServer_medium_detail_debug.cmd
@@ -1 +1 @@
-MCServer /cdg \ No newline at end of file
+MCServer --crash-dump-globals
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index 161fe59d1..ba3c8b332 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -2038,7 +2038,7 @@ a_Player:OpenWindow(Window);
BroadcastChat =
{
{ Params = "MessageText, MessageType", Return = "", Notes = "Broadcasts a message to all players, with its message type set to MessageType (default: mtCustom)." },
- { Params = "{{cCompositeChat|CompositeChat}}", Return = "", Notes = "Broadcasts a {{cCompositeChat|composite chat message} to all players." },
+ { Params = "{{cCompositeChat|CompositeChat}}", Return = "", Notes = "Broadcasts a {{cCompositeChat|composite chat message}} to all players." },
},
BroadcastChatDeath = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtDeath. Use for when a player has died." },
BroadcastChatFailure = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtFailure. Use for a command that failed to run because of insufficient permissions, etc." },
@@ -2048,12 +2048,16 @@ a_Player:OpenWindow(Window);
BroadcastChatLeave = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtLeave. Use for players leaving the server." },
BroadcastChatSuccess = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtSuccess. Use for success messages." },
BroadcastChatWarning = { Params = "MessageText", Return = "", Notes = "Broadcasts the specified message to all players, with its message type set to mtWarning. Use for concerning events, such as plugin reload etc." },
- CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil.<br><br><b>NOTE</b>This function is currently unsafe, do not use!" },
+ CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil.<br><br><b>NOTE:</b> This function is currently unsafe, do not use!" },
FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "bool", Notes = "Calls the given callback function for the player with the name best matching the name string provided.<br>This function is case-insensitive and will match partial names.<br>Returns false if player not found or there is ambiguity, true otherwise. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre>" },
DoWithPlayerByUUID = { Params = "PlayerUUID, CallbackFunction", Return = "bool", Notes = "If there is the player with the uuid, calls the CallbackFunction with the {{cPlayer}} parameter representing the player. The CallbackFunction has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|Player}})</pre> The function returns false if the player was not found, or whatever bool value the callback returned if the player was found." },
ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" },
ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" },
- Get = { Params = "", Return = "Root object", Notes = "(STATIC)This function returns the cRoot object." },
+ Get = { Params = "", Return = "Root object", Notes = "(STATIC) This function returns the cRoot object." },
+ GetBuildCommitID = { Params = "", Return = "string", Notes = "(STATIC) For official builds (Travis CI / Jenkins) it returns the exact commit hash used for the build. For unofficial local builds, returns the approximate commit hash (since the true one cannot be determined), formatted as \"approx: &lt;CommitHash&gt;\"." },
+ GetBuildDateTime = { Params = "", Return = "string", Notes = "(STATIC) For official builds (Travic CI / Jenkins) it returns the date and time of the build. For unofficial local builds, returns the approximate datetime of the commit (since the true one cannot be determined), formatted as \"approx: &lt;DateTime-iso8601&gt;\"." },
+ GetBuildID = { Params = "", Return = "string", Notes = "(STATIC) For official builds (Travis CI / Jenkins) it returns the unique ID of the build, as recognized by the build system. For unofficial local builds, returns the string \"Unknown\"." },
+ GetBuildSeriesName = { Params = "", Return = "string", Notes = "(STATIC) For official builds (Travis CI / Jenkins) it returns the series name of the build (for example \"MCServer Windows x64 Master\"). For unofficial local builds, returns the string \"local build\"." },
GetCraftingRecipes = { Params = "", Return = "{{cCraftingRecipe|cCraftingRecipe}}", Notes = "Returns the CraftingRecipes object" },
GetDefaultWorld = { Params = "", Return = "{{cWorld|cWorld}}", Notes = "Returns the world object from the default world." },
GetFurnaceFuelBurnTime = { Params = "{{cItem|Fuel}}", Return = "number", Notes = "(STATIC) Returns the number of ticks for how long the item would fuel a furnace. Returns zero if not a fuel." },
diff --git a/MCServer/Plugins/APIDump/main.css b/MCServer/Plugins/APIDump/main.css
index 8041e0d01..e5685caab 100644
--- a/MCServer/Plugins/APIDump/main.css
+++ b/MCServer/Plugins/APIDump/main.css
@@ -61,7 +61,7 @@ footer
font-family: Segoe UI Light, Helvetica;
}
-#content
+#content, #timestamp
{
padding: 0px 25px 25px 25px;
}
diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua
index e841922b6..543a299af 100644
--- a/MCServer/Plugins/APIDump/main_APIDump.lua
+++ b/MCServer/Plugins/APIDump/main_APIDump.lua
@@ -153,6 +153,19 @@ end
+--- Returns the timestamp in HTML format
+-- The timestamp will be inserted to all generated HTML files
+local function GetHtmlTimestamp()
+ return string.format("<div id='timestamp'>Generated on %s, Build ID %s, Commit %s</div>",
+ os.date("%Y-%m-%d %H:%M:%S"),
+ cRoot:GetBuildID(), cRoot:GetBuildCommitID()
+ )
+end
+
+
+
+
+
local function WriteArticles(f)
f:write([[
<a name="articles"><h2>Articles</h2></a>
@@ -296,7 +309,9 @@ local function WriteHtmlHook(a_Hook, a_HookNav)
f:write("<p>", (example.Desc or "<i>missing Desc</i>"), "</p>\n");
f:write("<pre class=\"prettyprint lang-lua\">", (example.Code or "<i>missing Code</i>"), "\n</pre>\n\n");
end
- f:write([[</td></tr></table></div><script>prettyPrint();</script></body></html>]]);
+ f:write([[</td></tr></table></div><script>prettyPrint();</script>]])
+ f:write(GetHtmlTimestamp())
+ f:write([[</body></html>]])
f:close();
end
@@ -941,8 +956,10 @@ local function WriteHtmlClass(a_ClassAPI, a_ClassMenu)
end
end
- cf:write([[</td></tr></table></div><script>prettyPrint();</script></body></html>]]);
- cf:close();
+ cf:write([[</td></tr></table></div><script>prettyPrint();</script>]])
+ cf:write(GetHtmlTimestamp())
+ cf:write([[</body></html>]])
+ cf:close()
end
@@ -1320,11 +1337,10 @@ local function DumpAPIHtml(a_API)
WriteStats(f);
- f:write([[ </ul>
- </div>
- </body>
-</html>]]);
- f:close();
+ f:write([[</ul></div>]])
+ f:write(GetHtmlTimestamp())
+ f:write([[</body></html>]])
+ f:close()
LOG("API subfolder written");
end
diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua
index a49f8b5a6..bd0b94a06 100644
--- a/MCServer/Plugins/Debuggers/Debuggers.lua
+++ b/MCServer/Plugins/Debuggers/Debuggers.lua
@@ -1880,6 +1880,117 @@ end
+--- Returns the square of the distance from the specified point to the specified line
+local function SqDistPtFromLine(x, y, x1, y1, x2, y2)
+ local dx = x - x1
+ local dy = y - y1
+ local px = x2 - x1
+ local py = y2 - y1
+ local ss = px * dx + py * dy
+ local ds = px * px + py * py
+
+ if (ss < 0) then
+ -- Return sqdistance from point 1
+ return dx * dx + dy * dy
+ end
+ if (ss > ds) then
+ -- Return sqdistance from point 2
+ return ((x2 - x) * (x2 - x) + (y2 - y) * (y2 - y))
+ end
+
+ -- Return sqdistance from the line
+ if ((px * px + py * py) == 0) then
+ return dx * dx + dy * dy
+ else
+ return (py * dx - px * dy) * (py * dx - px * dy) / (px * px + py * py)
+ end
+end
+
+
+
+
+
+function HandleConsoleTestTracer(a_Split, a_EntireCmd)
+ -- Check required params:
+ if not(a_Split[7]) then
+ return true, "Usage: " .. a_Split[1] .. " <x1> <y1> <z1> <x2> <y2> <z2> [<WorldName>]"
+ end
+ local Coords = {}
+ for i = 1, 6 do
+ local v = tonumber(a_Split[i + 1])
+ if not(v) then
+ return true, "Parameter " .. (i + 1) .. " (" .. tostring(a_Split[i + 1]) .. ") not a number "
+ end
+ Coords[i] = v
+ end
+
+ -- Get the world in which to test:
+ local World
+ if (a_Split[8]) then
+ World = cRoot:GetWorld(a_Split[2])
+ else
+ World = cRoot:Get():GetDefaultWorld()
+ end
+ if not(World) then
+ return true, "No such world"
+ end
+
+ -- Define the callbacks to use for tracing:
+ local Callbacks =
+ {
+ OnNextBlock = function(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_EntryFace)
+ LOG(string.format("{%d, %d, %d}: %s", a_BlockX, a_BlockY, a_BlockZ, ItemToString(cItem(a_BlockType, 1, a_BlockMeta))))
+ end,
+ OnNextBlockNoData = function(a_BlockX, a_BlockY, a_BlockZ, a_EntryFace)
+ LOG(string.format("{%d, %d, %d} (no data)", a_BlockX, a_BlockY, a_BlockZ))
+ end,
+ OnNoChunk = function()
+ LOG("Chunk not loaded")
+ end,
+ OnNoMoreHits = function()
+ LOG("Trace finished")
+ end,
+ OnOutOfWorld = function()
+ LOG("Out of world")
+ end,
+ OnIntoWorld = function()
+ LOG("Into world")
+ end,
+ }
+
+ -- Approximate the chunks needed for the trace by iterating over all chunks and measuring their center's distance from the traced line
+ local Chunks = {}
+ local sx = math.floor(Coords[1] / 16)
+ local sz = math.floor(Coords[3] / 16)
+ local ex = math.floor(Coords[4] / 16)
+ local ez = math.floor(Coords[6] / 16)
+ local sgnx = (sx < ex) and 1 or -1
+ local sgnz = (sz < ez) and 1 or -1
+ for z = sz, ez, sgnz do
+ local ChunkCenterZ = z * 16 + 8
+ for x = sx, ex, sgnx do
+ local ChunkCenterX = x * 16 + 8
+ local sqdist = SqDistPtFromLine(ChunkCenterX, ChunkCenterZ, Coords[1], Coords[3], Coords[4], Coords[6])
+ if (sqdist <= 128) then
+ table.insert(Chunks, {x, z})
+ end
+ end
+ end
+
+ -- Load the chunks and do the trace once loaded:
+ World:ChunkStay(Chunks,
+ nil,
+ function()
+ cLineBlockTracer:Trace(World, Callbacks, Coords[1], Coords[2], Coords[3], Coords[4], Coords[5], Coords[6])
+ end
+ )
+ return true
+end
+
+
+
+
+
function HandleConsoleBBox(a_Split)
local bbox = cBoundingBox(0, 10, 0, 10, 0, 10)
local v1 = Vector3d(1, 1, 1)
diff --git a/MCServer/Plugins/Debuggers/Info.lua b/MCServer/Plugins/Debuggers/Info.lua
index 2e170487b..a76690ea1 100644
--- a/MCServer/Plugins/Debuggers/Info.lua
+++ b/MCServer/Plugins/Debuggers/Info.lua
@@ -235,6 +235,12 @@ g_PluginInfo =
Handler = HandleConsoleSchedule,
HelpString = "Tests the world scheduling",
},
+
+ ["testtracer"] =
+ {
+ Handler = HandleConsoleTestTracer,
+ HelpString = "Tests the cLineBlockTracer",
+ }
}, -- ConsoleCommands
} -- g_PluginInfo
diff --git a/MCServer/Plugins/MagicCarpet b/MCServer/Plugins/MagicCarpet
-Subproject 94da343b62f0498a5843247f36d6ee00cbeb8f2
+Subproject 493f2dfa6d39f134e37c4c614cf8d6ffd10c825
diff --git a/SetFlags.cmake b/SetFlags.cmake
index b79551eef..57cab5a1c 100644
--- a/SetFlags.cmake
+++ b/SetFlags.cmake
@@ -63,7 +63,7 @@ macro(set_flags)
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG")
set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /LTCG")
elseif(APPLE)
-
+
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
execute_process(COMMAND ${CMAKE_C_COMPILER} -dumpversion
OUTPUT_VARIABLE GCC_VERSION)
@@ -262,8 +262,11 @@ macro(set_exe_flags)
add_flags_cxx("-Wno-documentation")
endif()
if ("${CLANG_VERSION}" VERSION_GREATER 3.5)
- # Use this flag to ignore error for a reserved macro problem in sqlite 3
- add_flags_cxx("-Wno-reserved-id-macro")
+ check_cxx_compiler_flag(-Wno-reserved-id-macro HAS_NO_RESERVED_ID_MACRO)
+ if (HAS_NO_RESERVED_ID_MACRO)
+ # Use this flag to ignore error for a reserved macro problem in sqlite 3
+ add_flags_cxx("-Wno-reserved-id-macro")
+ endif()
endif()
endif()
endif()
@@ -277,7 +280,7 @@ endmacro()
# set_source_files_properties(${FILENAME} PROPERTIES COMPILE_FLAGS "-Wno-error=missing-prototypes -Wno-error=deprecated")
# set_source_files_properties(${FILENAME} PROPERTIES COMPILE_FLAGS "-Wno-error=shadow -Wno-error=old-style-cast -Wno-error=switch-enum -Wno-error=switch")
# set_source_files_properties(${FILENAME} PROPERTIES COMPILE_FLAGS "-Wno-error=float-equal -Wno-error=global-constructors")
-
+
# if ("${CLANG_VERSION}" VERSION_GREATER 3.0)
# # flags that are not present in 3.0
# set_source_files_properties(${FILENAME} PROPERTIES COMPILE_FLAGS "-Wno-error=covered-switch-default ")
@@ -286,4 +289,3 @@ endmacro()
# endif()
# endforeach()
# endif()
-
diff --git a/Tools/MCADefrag/MCADefrag.cpp b/Tools/MCADefrag/MCADefrag.cpp
index 0d38a87f1..80c6f5be2 100644
--- a/Tools/MCADefrag/MCADefrag.cpp
+++ b/Tools/MCADefrag/MCADefrag.cpp
@@ -22,7 +22,7 @@ static const Byte g_Zeroes[4096] = {0};
int main(int argc, char ** argv)
{
- cLogger::cListener * consoleLogListener = MakeConsoleListener();
+ cLogger::cListener * consoleLogListener = MakeConsoleListener(false);
cLogger::cListener * fileLogListener = new cFileListener();
cLogger::GetInstance().AttachListener(consoleLogListener);
cLogger::GetInstance().AttachListener(fileLogListener);
diff --git a/Tools/ProtoProxy/ProtoProxy.cpp b/Tools/ProtoProxy/ProtoProxy.cpp
index 3f427f83f..2d27d7556 100644
--- a/Tools/ProtoProxy/ProtoProxy.cpp
+++ b/Tools/ProtoProxy/ProtoProxy.cpp
@@ -16,7 +16,7 @@ int main(int argc, char ** argv)
{
// Initialize logging subsystem:
cLogger::InitiateMultithreading();
- auto consoleLogListener = MakeConsoleListener();
+ auto consoleLogListener = MakeConsoleListener(false);
auto fileLogListener = new cFileListener();
cLogger::GetInstance().AttachListener(consoleLogListener);
cLogger::GetInstance().AttachListener(fileLogListener);
diff --git a/lib/SQLiteCpp b/lib/SQLiteCpp
-Subproject b17195b8d03e8908807c51f4d6ce610b148fc1b
+Subproject 49679e7b54726e2d94d3aad76a65aeb9c1088af
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index bce1891e2..7655d8c83 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -34,6 +34,7 @@
#include "../CompositeChat.h"
#include "../StringCompression.h"
#include "../CommandOutput.h"
+#include "../BuildInfo.h"
@@ -2079,6 +2080,50 @@ static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S)
+static int tolua_cRoot_GetBuildCommitID(lua_State * tolua_S)
+{
+ cLuaState L(tolua_S);
+ L.Push(BUILD_COMMIT_ID);
+ return 1;
+}
+
+
+
+
+
+static int tolua_cRoot_GetBuildDateTime(lua_State * tolua_S)
+{
+ cLuaState L(tolua_S);
+ L.Push(BUILD_DATETIME);
+ return 1;
+}
+
+
+
+
+
+static int tolua_cRoot_GetBuildID(lua_State * tolua_S)
+{
+ cLuaState L(tolua_S);
+ L.Push(BUILD_ID);
+ return 1;
+}
+
+
+
+
+
+static int tolua_cRoot_GetBuildSeriesName(lua_State * tolua_S)
+{
+ cLuaState L(tolua_S);
+ L.Push(BUILD_SERIES_NAME);
+ return 1;
+}
+
+
+
+
+
static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S)
{
cLuaState L(tolua_S);
@@ -2092,7 +2137,8 @@ static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S)
}
// Check the input param:
- cItem * Input = (cItem *)tolua_tousertype(L, 2, nullptr);
+ cItem * Input = nullptr;
+ L.GetStackValue(2, Input);
if (Input == nullptr)
{
LOGWARNING("cRoot:GetFurnaceRecipe: the Input parameter is nil or missing.");
@@ -2109,9 +2155,9 @@ static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S)
}
// Push the output, number of ticks and input as the three return values:
- tolua_pushusertype(L, Recipe->Out, "const cItem");
- tolua_pushnumber (L, (lua_Number)(Recipe->CookTime));
- tolua_pushusertype(L, Recipe->In, "const cItem");
+ L.Push(Recipe->Out);
+ L.Push(Recipe->CookTime);
+ L.Push(Recipe->In);
return 3;
}
@@ -2868,6 +2914,10 @@ void cManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "DoWithPlayerByUUID", DoWith <cRoot, cPlayer, &cRoot::DoWithPlayerByUUID>);
tolua_function(tolua_S, "ForEachPlayer", ForEach<cRoot, cPlayer, &cRoot::ForEachPlayer>);
tolua_function(tolua_S, "ForEachWorld", ForEach<cRoot, cWorld, &cRoot::ForEachWorld>);
+ tolua_function(tolua_S, "GetBuildCommitID", tolua_cRoot_GetBuildCommitID);
+ tolua_function(tolua_S, "GetBuildDateTime", tolua_cRoot_GetBuildDateTime);
+ tolua_function(tolua_S, "GetBuildID", tolua_cRoot_GetBuildID);
+ tolua_function(tolua_S, "GetBuildSeriesName", tolua_cRoot_GetBuildSeriesName);
tolua_function(tolua_S, "GetFurnaceRecipe", tolua_cRoot_GetFurnaceRecipe);
tolua_endmodule(tolua_S);
diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h
index 85f75a523..71367efb6 100644
--- a/src/BlockEntities/BlockEntity.h
+++ b/src/BlockEntities/BlockEntity.h
@@ -85,6 +85,7 @@ public:
// tolua_begin
// Position, in absolute block coordinates:
+ Vector3i GetPos(void) const { return Vector3i{m_PosX, m_PosY, m_PosZ}; }
int GetPosX(void) const { return m_PosX; }
int GetPosY(void) const { return m_PosY; }
int GetPosZ(void) const { return m_PosZ; }
diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp
index 2621b560b..d1588160d 100644
--- a/src/BlockEntities/FurnaceEntity.cpp
+++ b/src/BlockEntities/FurnaceEntity.cpp
@@ -32,7 +32,8 @@ cFurnaceEntity::cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTY
m_NeedCookTime(0),
m_TimeCooked(0),
m_FuelBurnTime(0),
- m_TimeBurned(0)
+ m_TimeBurned(0),
+ m_IsLoading(false)
{
m_Contents.AddListener(*this);
}
@@ -178,20 +179,15 @@ void cFurnaceEntity::BurnNewFuel(void)
{
cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe();
int NewTime = FR->GetBurnTime(m_Contents.GetSlot(fsFuel));
- if (NewTime == 0)
+ if ((NewTime == 0) || !CanCookInputToOutput())
{
// The item in the fuel slot is not suitable
+ // or the input and output isn't available for cooking
SetBurnTimes(0, 0);
SetIsCooking(false);
return;
}
- // Is the input and output ready for cooking?
- if (!CanCookInputToOutput())
- {
- return;
- }
-
// Burn one new fuel:
SetBurnTimes(NewTime, 0);
SetIsCooking(true);
@@ -218,6 +214,11 @@ void cFurnaceEntity::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
return;
}
+ if (m_IsLoading)
+ {
+ return;
+ }
+
ASSERT(a_ItemGrid == &m_Contents);
switch (a_SlotNum)
{
@@ -238,7 +239,10 @@ void cFurnaceEntity::UpdateInput(void)
if (!m_Contents.GetSlot(fsInput).IsEqual(m_LastInput))
{
// The input is different from what we had before, reset the cooking time
- m_TimeCooked = 0;
+ if (!m_IsLoading)
+ {
+ m_TimeCooked = 0;
+ }
}
m_LastInput = m_Contents.GetSlot(fsInput);
@@ -253,13 +257,17 @@ void cFurnaceEntity::UpdateInput(void)
else
{
m_NeedCookTime = m_CurrentRecipe->CookTime;
- SetIsCooking(true);
// Start burning new fuel if there's no flame now:
if (GetFuelBurnTimeLeft() <= 0)
{
BurnNewFuel();
}
+ // Already burning, set cooking to ensure that cooking is occuring
+ else
+ {
+ SetIsCooking(true);
+ }
}
}
@@ -293,11 +301,19 @@ void cFurnaceEntity::UpdateOutput(void)
return;
}
- // No need to burn new fuel, the Tick() function will take care of that
-
// Can cook, start cooking if not already underway:
m_NeedCookTime = m_CurrentRecipe->CookTime;
- SetIsCooking(m_FuelBurnTime > 0);
+
+ // Check if fuel needs to start a burn
+ if (GetFuelBurnTimeLeft() <= 0)
+ {
+ BurnNewFuel();
+ }
+ // Already burning, set cooking to ensure that cooking is occuring
+ else
+ {
+ SetIsCooking(true);
+ }
}
diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h
index 8b3ba3e36..8734d763c 100644
--- a/src/BlockEntities/FurnaceEntity.h
+++ b/src/BlockEntities/FurnaceEntity.h
@@ -101,6 +101,11 @@ public:
m_TimeCooked = a_TimeCooked;
}
+ void SetLoading(bool a_IsLoading)
+ {
+ m_IsLoading = a_IsLoading;
+ }
+
protected:
/** Block meta of the block currently represented by this entity */
@@ -129,6 +134,9 @@ protected:
/** Amount of ticks that the current fuel has been burning */
int m_TimeBurned;
+
+ /** Is the block currently being loaded into the world? */
+ bool m_IsLoading;
/** Sends the specified progressbar value to all clients of the window */
void BroadcastProgress(short a_ProgressbarID, short a_Value);
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5556ddc4d..c68795bb3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -83,6 +83,7 @@ SET (HDRS
BlockTracer.h
Broadcaster.h
BoundingBox.h
+ BuildInfo.h
BuildInfo.h.cmake
ByteBuffer.h
ChatColor.h
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 7af669163..02bc2ba8c 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -2852,22 +2852,6 @@ void cChunk::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cons
-void cChunk::BroadcastChunkData(cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude)
-{
- for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
- {
- if (*itr == a_Exclude)
- {
- continue;
- }
- (*itr)->SendChunkData(m_PosX, m_PosZ, a_Serializer);
- } // for itr - LoadedByClient[]
-}
-
-
-
-
-
void cChunk::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude)
{
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
diff --git a/src/Chunk.h b/src/Chunk.h
index f57769107..fd9ea0b0c 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -319,7 +319,6 @@ public:
void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockBreakAnimation(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = nullptr);
- void BroadcastChunkData (cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = nullptr);
void BroadcastCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
void BroadcastEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude = nullptr);
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 2f38e4cd6..4db73971c 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -409,22 +409,6 @@ void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, c
-void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude)
-{
- cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
- if (Chunk == nullptr)
- {
- return;
- }
- // It's perfectly legal to broadcast packets even to invalid chunks!
- Chunk->BroadcastChunkData(a_Serializer, a_Exclude);
-}
-
-
-
-
-
void cChunkMap::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 964188bbe..916a3433d 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -73,7 +73,6 @@ public:
void BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockBreakAnimation(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude);
- void BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = nullptr);
void BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp
index 2f18ea75c..877aacfc5 100644
--- a/src/ChunkSender.cpp
+++ b/src/ChunkSender.cpp
@@ -13,6 +13,7 @@
#include "BlockEntities/BlockEntity.h"
#include "Protocol/ChunkDataSerializer.h"
#include "ClientHandle.h"
+#include "Chunk.h"
@@ -28,25 +29,29 @@ class cNotifyChunkSender :
{
virtual void Call(int a_ChunkX, int a_ChunkZ) override
{
- m_ChunkSender->ChunkReady(a_ChunkX, a_ChunkZ);
+ cChunkSender & ChunkSender = m_ChunkSender;
+ m_World.DoWithChunk(
+ a_ChunkX, a_ChunkZ,
+ [&ChunkSender] (cChunk & a_Chunk) -> bool
+ {
+ ChunkSender.QueueSendChunkTo(a_Chunk.GetPosX(), a_Chunk.GetPosZ(), cChunkSender::PRIORITY_BROADCAST, a_Chunk.GetAllClients());
+ return true;
+ }
+ );
}
- cChunkSender * m_ChunkSender;
-public:
- cNotifyChunkSender(cChunkSender * a_ChunkSender) : m_ChunkSender(a_ChunkSender) {}
-};
-
-
-
+ cChunkSender & m_ChunkSender;
+ cWorld & m_World;
+public:
+ cNotifyChunkSender(cChunkSender & a_ChunkSender, cWorld & a_World) : m_ChunkSender(a_ChunkSender), m_World(a_World) {}
-////////////////////////////////////////////////////////////////////////////////
-// cChunkSender:
+};
-cChunkSender::cChunkSender(void) :
+cChunkSender::cChunkSender(cWorld & a_World) :
super("ChunkSender"),
- m_World(nullptr),
+ m_World(a_World),
m_RemoveCount(0)
{
}
@@ -64,10 +69,9 @@ cChunkSender::~cChunkSender()
-bool cChunkSender::Start(cWorld * a_World)
+bool cChunkSender::Start()
{
m_ShouldTerminate = false;
- m_World = a_World;
return super::Start();
}
@@ -86,12 +90,30 @@ void cChunkSender::Stop(void)
-void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkZ)
+void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, cClientHandle * a_Client)
{
- // This is probably never gonna be called twice for the same chunk, and if it is, we don't mind, so we don't check
+ ASSERT(a_Client != nullptr);
{
+ cChunkCoords Chunk{a_ChunkX, a_ChunkZ};
cCSLock Lock(m_CS);
- m_ChunksReady.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
+ auto iter = m_ChunkInfo.find(Chunk);
+ if (iter != m_ChunkInfo.end())
+ {
+ auto & info = iter->second;
+ if (info.m_Priority > a_Priority)
+ {
+ m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
+ info.m_Priority = a_Priority;
+ }
+ info.m_Clients.insert(a_Client);
+ }
+ else
+ {
+ m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
+ auto info = sSendChunk{Chunk, a_Priority};
+ info.m_Clients.insert(a_Client);
+ m_ChunkInfo.emplace(Chunk, info);
+ }
}
m_evtQueue.Set();
}
@@ -100,40 +122,29 @@ void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkZ)
-void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, cClientHandle * a_Client)
+
+void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, std::list<cClientHandle *> a_Clients)
{
- ASSERT(a_Client != nullptr);
{
- sSendChunk Chunk(a_ChunkX, a_ChunkZ, a_Client);
-
+ cChunkCoords Chunk{a_ChunkX, a_ChunkZ};
cCSLock Lock(m_CS);
- if (
- std::find(m_SendChunksLowPriority.begin(), m_SendChunksLowPriority.end(), Chunk) != m_SendChunksLowPriority.end() ||
- std::find(m_SendChunksMediumPriority.begin(), m_SendChunksMediumPriority.end(), Chunk) != m_SendChunksMediumPriority.end() ||
- std::find(m_SendChunksHighPriority.begin(), m_SendChunksHighPriority.end(), Chunk) != m_SendChunksHighPriority.end()
- )
+ auto iter = m_ChunkInfo.find(Chunk);
+ if (iter != m_ChunkInfo.end())
{
- // Already queued, bail out
- return;
- }
-
- switch (a_Priority)
- {
- case E_CHUNK_PRIORITY_LOW:
- {
- m_SendChunksLowPriority.push_back(Chunk);
- break;
- }
- case E_CHUNK_PRIORITY_MEDIUM:
- {
- m_SendChunksMediumPriority.push_back(Chunk);
- break;
- }
- case E_CHUNK_PRIORITY_HIGH:
+ auto & info = iter->second;
+ if (info.m_Priority > a_Priority)
{
- m_SendChunksHighPriority.push_back(Chunk);
- break;
+ m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
+ info.m_Priority = a_Priority;
}
+ info.m_Clients.insert(a_Clients.begin(), a_Clients.end());
+ }
+ else
+ {
+ m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
+ auto info = sSendChunk{Chunk, a_Priority};
+ info.m_Clients.insert(a_Clients.begin(), a_Clients.end());
+ m_ChunkInfo.emplace(Chunk, info);
}
}
m_evtQueue.Set();
@@ -147,33 +158,12 @@ void cChunkSender::RemoveClient(cClientHandle * a_Client)
{
{
cCSLock Lock(m_CS);
- for (sSendChunkList::iterator itr = m_SendChunksLowPriority.begin(); itr != m_SendChunksLowPriority.end();)
- {
- if (itr->m_Client == a_Client)
- {
- itr = m_SendChunksLowPriority.erase(itr);
- continue;
- }
- ++itr;
- } // for itr - m_SendChunksLowPriority[]
- for (sSendChunkList::iterator itr = m_SendChunksMediumPriority.begin(); itr != m_SendChunksMediumPriority.end();)
+ for (auto && pair : m_ChunkInfo)
{
- if (itr->m_Client == a_Client)
- {
- itr = m_SendChunksMediumPriority.erase(itr);
- continue;
- }
- ++itr;
- } // for itr - m_SendChunksMediumPriority[]
- for (sSendChunkList::iterator itr = m_SendChunksHighPriority.begin(); itr != m_SendChunksHighPriority.end();)
- {
- if (itr->m_Client == a_Client)
- {
- itr = m_SendChunksHighPriority.erase(itr);
- continue;
- }
- ++itr;
- } // for itr - m_SendChunksHighPriority[]
+ auto && clients = pair.second.m_Clients;
+ clients.erase(a_Client); // nop for sets that do not contain a_Client
+ }
+
m_RemoveCount++;
}
m_evtQueue.Set();
@@ -189,7 +179,7 @@ void cChunkSender::Execute(void)
while (!m_ShouldTerminate)
{
cCSLock Lock(m_CS);
- while (m_ChunksReady.empty() && m_SendChunksLowPriority.empty() && m_SendChunksMediumPriority.empty() && m_SendChunksHighPriority.empty())
+ do
{
int RemoveCount = m_RemoveCount;
m_RemoveCount = 0;
@@ -203,52 +193,24 @@ void cChunkSender::Execute(void)
{
return;
}
- } // while (empty)
-
- if (!m_SendChunksHighPriority.empty())
- {
- // Take one from the queue:
- sSendChunk Chunk(m_SendChunksHighPriority.front());
- m_SendChunksHighPriority.pop_front();
- Lock.Unlock();
+ } while (m_SendChunks.empty());
- SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client);
- }
- else if (!m_ChunksReady.empty())
+ // Take one from the queue:
+ auto Chunk = m_SendChunks.top().m_Chunk;
+ m_SendChunks.pop();
+ auto itr = m_ChunkInfo.find(Chunk);
+ if (itr == m_ChunkInfo.end())
{
- // Take one from the queue:
- cChunkCoords Coords(m_ChunksReady.front());
- m_ChunksReady.pop_front();
- Lock.Unlock();
-
- SendChunk(Coords.m_ChunkX, Coords.m_ChunkZ, nullptr);
+ continue;
}
- else if (!m_SendChunksMediumPriority.empty())
- {
- // Take one from the queue:
- sSendChunk Chunk(m_SendChunksMediumPriority.front());
- m_SendChunksMediumPriority.pop_front();
- Lock.Unlock();
+
+ std::unordered_set<cClientHandle *> clients;
+ std::swap(itr->second.m_Clients, clients);
+ m_ChunkInfo.erase(itr);
- SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client);
- }
- else
- {
- // Take one from the queue:
- sSendChunk Chunk(m_SendChunksLowPriority.front());
- m_SendChunksLowPriority.pop_front();
- Lock.Unlock();
-
- SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client);
- }
- Lock.Lock();
- int RemoveCount = m_RemoveCount;
- m_RemoveCount = 0;
Lock.Unlock();
- for (int i = 0; i < RemoveCount; i++)
- {
- m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted
- }
+
+ SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, clients);
} // while (!mShouldTerminate)
}
@@ -256,64 +218,60 @@ void cChunkSender::Execute(void)
-void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
+void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set<cClientHandle *> a_Clients)
{
- ASSERT(m_World != nullptr);
// Ask the client if it still wants the chunk:
- if ((a_Client != nullptr) && !a_Client->WantsSendChunk(a_ChunkX, a_ChunkZ))
+ for (auto itr = a_Clients.begin(); itr != a_Clients.end();)
{
- return;
+ if (!(*itr)->WantsSendChunk(a_ChunkX, a_ChunkZ))
+ {
+ itr = a_Clients.erase(itr);
+ }
+ else
+ {
+ itr++;
+ }
}
// If the chunk has no clients, no need to packetize it:
- if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ))
+ if (!m_World.HasChunkAnyClients(a_ChunkX, a_ChunkZ))
{
return;
}
// If the chunk is not valid, do nothing - whoever needs it has queued it for loading / generating
- if (!m_World->IsChunkValid(a_ChunkX, a_ChunkZ))
+ if (!m_World.IsChunkValid(a_ChunkX, a_ChunkZ))
{
return;
}
// If the chunk is not lighted, queue it for relighting and get notified when it's ready:
- if (!m_World->IsChunkLighted(a_ChunkX, a_ChunkZ))
+ if (!m_World.IsChunkLighted(a_ChunkX, a_ChunkZ))
{
- m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, cpp14::make_unique<cNotifyChunkSender>(this));
+ m_World.QueueLightChunk(a_ChunkX, a_ChunkZ, cpp14::make_unique<cNotifyChunkSender>(*this, m_World));
return;
}
// Query and prepare chunk data:
- if (!m_World->GetChunkData(a_ChunkX, a_ChunkZ, *this))
+ if (!m_World.GetChunkData(a_ChunkX, a_ChunkZ, *this))
{
return;
}
cChunkDataSerializer Data(m_BlockTypes, m_BlockMetas, m_BlockLight, m_BlockSkyLight, m_BiomeMap);
- // Send:
- if (a_Client == nullptr)
- {
- m_World->BroadcastChunkData(a_ChunkX, a_ChunkZ, Data);
- }
- else
+ for (auto client : a_Clients)
{
- a_Client->SendChunkData(a_ChunkX, a_ChunkZ, Data);
- }
+ // Send:
+ client->SendChunkData(a_ChunkX, a_ChunkZ, Data);
- // Send block-entity packets:
- for (sBlockCoords::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
- {
- if (a_Client == nullptr)
- {
- m_World->BroadcastBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ);
- }
- else
+ // Send block-entity packets:
+ for (auto Pos : m_BlockEntities)
{
- m_World->SendBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ, *a_Client);
- }
- } // for itr - m_Packets[]
+ m_World.SendBlockEntity(Pos.x, Pos.y, Pos.z, *client);
+ } // for itr - m_Packets[]
+
+ }
m_BlockEntities.clear();
// TODO: Send entity spawn packets
@@ -325,7 +283,7 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Clien
void cChunkSender::BlockEntity(cBlockEntity * a_Entity)
{
- m_BlockEntities.push_back(sBlockCoord(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ()));
+ m_BlockEntities.push_back(a_Entity->GetPos());
}
diff --git a/src/ChunkSender.h b/src/ChunkSender.h
index 1376baeb3..b0c48b92b 100644
--- a/src/ChunkSender.h
+++ b/src/ChunkSender.h
@@ -29,6 +29,9 @@ Note that it may be called by world's BroadcastToChunk() if the client is still
#include "ChunkDef.h"
#include "ChunkDataCallback.h"
+#include <unordered_set>
+#include <unordered_map>
+
@@ -53,87 +56,64 @@ class cChunkSender:
{
typedef cIsThread super;
public:
- cChunkSender(void);
+ cChunkSender(cWorld & a_World);
~cChunkSender();
enum eChunkPriority
{
E_CHUNK_PRIORITY_HIGH = 0,
- E_CHUNK_PRIORITY_MEDIUM = 1,
- E_CHUNK_PRIORITY_LOW = 2,
+ PRIORITY_BROADCAST,
+ E_CHUNK_PRIORITY_MEDIUM,
+ E_CHUNK_PRIORITY_LOW,
+
};
- bool Start(cWorld * a_World);
+ bool Start();
void Stop(void);
- /// Notifies that a chunk has become ready and it should be sent to all its clients
- void ChunkReady(int a_ChunkX, int a_ChunkZ);
-
/// Queues a chunk to be sent to a specific client
void QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, cClientHandle * a_Client);
+ void QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, std::list<cClientHandle *> a_Client);
/// Removes the a_Client from all waiting chunk send operations
void RemoveClient(cClientHandle * a_Client);
protected:
+
+ struct sChunkQueue
+ {
+ eChunkPriority m_Priority;
+ cChunkCoords m_Chunk;
+
+ bool operator <(const sChunkQueue & a_Other) const { return this->m_Priority < a_Other.m_Priority; }
+ };
/// Used for sending chunks to specific clients
struct sSendChunk
{
- int m_ChunkX;
- int m_ChunkZ;
- cClientHandle * m_Client;
-
- sSendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) :
- m_ChunkX(a_ChunkX),
- m_ChunkZ(a_ChunkZ),
- m_Client(a_Client)
- {
- }
-
- bool operator ==(const sSendChunk & a_Other)
- {
- return (
- (a_Other.m_ChunkX == m_ChunkX) &&
- (a_Other.m_ChunkZ == m_ChunkZ) &&
- (a_Other.m_Client == m_Client)
- );
- }
- } ;
- typedef std::list<sSendChunk> sSendChunkList;
-
- struct sBlockCoord
- {
- int m_BlockX;
- int m_BlockY;
- int m_BlockZ;
-
- sBlockCoord(int a_BlockX, int a_BlockY, int a_BlockZ) :
- m_BlockX(a_BlockX),
- m_BlockY(a_BlockY),
- m_BlockZ(a_BlockZ)
+ cChunkCoords m_Chunk;
+ std::unordered_set<cClientHandle *> m_Clients;
+ eChunkPriority m_Priority;
+ sSendChunk(cChunkCoords a_Chunk, eChunkPriority a_Priority) :
+ m_Chunk(a_Chunk),
+ m_Priority(a_Priority)
{
}
- } ;
-
- typedef std::vector<sBlockCoord> sBlockCoords;
+ };
- cWorld * m_World;
+ cWorld & m_World;
cCriticalSection m_CS;
- cChunkCoordsList m_ChunksReady;
- sSendChunkList m_SendChunksLowPriority;
- sSendChunkList m_SendChunksMediumPriority;
- sSendChunkList m_SendChunksHighPriority;
+ std::priority_queue<sChunkQueue> m_SendChunks;
+ std::unordered_map<cChunkCoords, sSendChunk, cChunkCoordsHash> m_ChunkInfo;
cEvent m_evtQueue; // Set when anything is added to m_ChunksReady
cEvent m_evtRemoved; // Set when removed clients are safe to be deleted
int m_RemoveCount; // Number of threads waiting for a client removal (m_evtRemoved needs to be set this many times)
-
// Data about the chunk that is being sent:
// NOTE that m_BlockData[] is inherited from the cChunkDataCollector
unsigned char m_BiomeMap[cChunkDef::Width * cChunkDef::Width];
- sBlockCoords m_BlockEntities; // Coords of the block entities to send
+ std::vector<Vector3i> m_BlockEntities; // Coords of the block entities to send
// TODO: sEntityIDs m_Entities; // Entity-IDs of the entities to send
// cIsThread override:
@@ -146,9 +126,8 @@ protected:
virtual void BlockEntity (cBlockEntity * a_Entity) override;
/// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == nullptr
- void SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
+ void SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set<cClientHandle *> a_Clients);
} ;
-
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index d89f7ab77..9ed89e0a3 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -456,7 +456,7 @@ bool cClientHandle::StreamNextChunk(void)
// If the chunk already loading / loaded -> skip
if (
- (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) ||
+ (m_ChunksToSend.find(Coords) != m_ChunksToSend.end()) ||
(std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end())
)
{
@@ -494,7 +494,7 @@ bool cClientHandle::StreamNextChunk(void)
// If the chunk already loading / loaded -> skip
if (
- (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), Coords) != m_ChunksToSend.end()) ||
+ (m_ChunksToSend.find(Coords) != m_ChunksToSend.end()) ||
(std::find(m_LoadedChunks.begin(), m_LoadedChunks.end(), Coords) != m_LoadedChunks.end())
)
{
@@ -541,7 +541,7 @@ void cClientHandle::UnloadOutOfRangeChunks(void)
}
}
- for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();)
+ for (auto itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();)
{
int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
@@ -583,7 +583,7 @@ void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ, cChunkSender::eChunk
{
cCSLock Lock(m_CSChunkLists);
m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
- m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
+ m_ChunksToSend.emplace(a_ChunkX, a_ChunkZ);
}
World->SendChunkTo(a_ChunkX, a_ChunkZ, a_Priority, this);
}
@@ -2179,15 +2179,12 @@ void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializ
bool Found = false;
{
cCSLock Lock(m_CSChunkLists);
- for (cChunkCoordsList::iterator itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end(); ++itr)
+ auto itr = m_ChunksToSend.find(cChunkCoords{a_ChunkX, a_ChunkZ});
+ if (itr != m_ChunksToSend.end())
{
- if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
- {
- m_ChunksToSend.erase(itr);
- Found = true;
- break;
- }
- } // for itr - m_ChunksToSend[]
+ m_ChunksToSend.erase(itr);
+ Found = true;
+ }
}
if (!Found)
{
@@ -2950,7 +2947,7 @@ bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkZ)
}
cCSLock Lock(m_CSChunkLists);
- return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkZ)) != m_ChunksToSend.end());
+ return m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) != m_ChunksToSend.end();
}
@@ -2966,9 +2963,9 @@ void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ)
LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, this);
cCSLock Lock(m_CSChunkLists);
- if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkZ)) == m_ChunksToSend.end())
+ if (m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) == m_ChunksToSend.end())
{
- m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
+ m_ChunksToSend.emplace(a_ChunkX, a_ChunkZ);
}
}
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 13b5f87e4..27acc4d37 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -377,10 +377,10 @@ private:
AString m_Password;
Json::Value m_Properties;
- cCriticalSection m_CSChunkLists;
- cChunkCoordsList m_LoadedChunks; // Chunks that the player belongs to
- cChunkCoordsList m_ChunksToSend; // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them)
- cChunkCoordsList m_SentChunks; // Chunks that are currently sent to the client
+ cCriticalSection m_CSChunkLists;
+ cChunkCoordsList m_LoadedChunks; // Chunks that the player belongs to
+ std::unordered_set<cChunkCoords, cChunkCoordsHash> m_ChunksToSend; // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them)
+ cChunkCoordsList m_SentChunks; // Chunks that are currently sent to the client
cProtocol * m_Protocol;
diff --git a/src/Globals.h b/src/Globals.h
index 27d944fcc..4c9091e85 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -221,6 +221,7 @@ template class SizeChecker<UInt8, 1>;
#include <semaphore.h>
#include <errno.h>
#include <fcntl.h>
+ #include <unistd.h>
#endif
#if defined(ANDROID_NDK)
@@ -265,7 +266,6 @@ template class SizeChecker<UInt8, 1>;
// Common headers (part 1, without macros):
#include "StringUtils.h"
#include "OSSupport/CriticalSection.h"
- #include "OSSupport/Semaphore.h"
#include "OSSupport/Event.h"
#include "OSSupport/File.h"
#include "Logger.h"
diff --git a/src/LineBlockTracer.cpp b/src/LineBlockTracer.cpp
index 315e8d082..587fa0e7c 100644
--- a/src/LineBlockTracer.cpp
+++ b/src/LineBlockTracer.cpp
@@ -154,38 +154,45 @@ bool cLineBlockTracer::MoveToNextBlock(void)
{
// Find out which of the current block's walls gets hit by the path:
static const double EPS = 0.00001;
- double Coeff = 1;
- enum eDirection
+ enum
{
dirNONE,
dirX,
dirY,
dirZ,
} Direction = dirNONE;
+
+ // Calculate the next YZ wall hit:
+ double Coeff = 1;
if (std::abs(m_DiffX) > EPS)
{
double DestX = (m_DirX > 0) ? (m_CurrentX + 1) : m_CurrentX;
- Coeff = (DestX - m_StartX) / m_DiffX;
- if (Coeff <= 1)
+ double CoeffX = (DestX - m_StartX) / m_DiffX;
+ if (CoeffX <= 1) // We need to include equality for the last block in the trace
{
+ Coeff = CoeffX;
Direction = dirX;
}
}
+
+ // If the next XZ wall hit is closer, use it instead:
if (std::abs(m_DiffY) > EPS)
{
double DestY = (m_DirY > 0) ? (m_CurrentY + 1) : m_CurrentY;
double CoeffY = (DestY - m_StartY) / m_DiffY;
- if (CoeffY < Coeff)
+ if (CoeffY <= Coeff) // We need to include equality for the last block in the trace
{
Coeff = CoeffY;
Direction = dirY;
}
}
+
+ // If the next XY wall hit is closer, use it instead:
if (std::abs(m_DiffZ) > EPS)
{
double DestZ = (m_DirZ > 0) ? (m_CurrentZ + 1) : m_CurrentZ;
double CoeffZ = (DestZ - m_StartZ) / m_DiffZ;
- if (CoeffZ < Coeff)
+ if (CoeffZ <= Coeff) // We need to include equality for the last block in the trace
{
Direction = dirZ;
}
diff --git a/src/LoggerListeners.cpp b/src/LoggerListeners.cpp
index 31b12af1e..132751e8e 100644
--- a/src/LoggerListeners.cpp
+++ b/src/LoggerListeners.cpp
@@ -238,8 +238,26 @@ public:
-cLogger::cListener * MakeConsoleListener(void)
+// Listener for when stdout is closed, i.e. When running as a daemon.
+class cNullConsoleListener
+ : public cLogger::cListener
+{
+ virtual void Log(AString a_Message, cLogger::eLogLevel a_LogLevel) override
+ {
+ }
+};
+
+
+
+
+
+cLogger::cListener * MakeConsoleListener(bool a_IsService)
{
+ if (a_IsService)
+ {
+ return new cNullConsoleListener;
+ }
+
#ifdef _WIN32
// See whether we are writing to a console the default console attrib:
bool ShouldColorOutput = (_isatty(_fileno(stdin)) != 0);
diff --git a/src/LoggerListeners.h b/src/LoggerListeners.h
index c419aa75a..a7f9a35a5 100644
--- a/src/LoggerListeners.h
+++ b/src/LoggerListeners.h
@@ -25,7 +25,7 @@ private:
-cLogger::cListener * MakeConsoleListener();
+cLogger::cListener * MakeConsoleListener(bool a_IsService);
diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt
index d1db0373d..df47394ae 100644
--- a/src/OSSupport/CMakeLists.txt
+++ b/src/OSSupport/CMakeLists.txt
@@ -15,7 +15,6 @@ SET (SRCS
IsThread.cpp
NetworkInterfaceEnum.cpp
NetworkSingleton.cpp
- Semaphore.cpp
ServerHandleImpl.cpp
StackTrace.cpp
TCPLinkImpl.cpp
@@ -34,7 +33,6 @@ SET (HDRS
Network.h
NetworkSingleton.h
Queue.h
- Semaphore.h
ServerHandleImpl.h
StackTrace.h
TCPLinkImpl.h
diff --git a/src/OSSupport/Event.cpp b/src/OSSupport/Event.cpp
index 760e536a1..38144ead3 100644
--- a/src/OSSupport/Event.cpp
+++ b/src/OSSupport/Event.cpp
@@ -1,8 +1,8 @@
// Event.cpp
-// Implements 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
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
diff --git a/src/OSSupport/Semaphore.cpp b/src/OSSupport/Semaphore.cpp
deleted file mode 100644
index 6a2d57901..000000000
--- a/src/OSSupport/Semaphore.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-
-
-
-
-cSemaphore::cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount /* = 0 */)
-#ifndef _WIN32
- : m_bNamed( false)
-#endif
-{
-#ifndef _WIN32
- (void)a_MaxCount;
- m_Handle = new sem_t;
- if (sem_init( (sem_t*)m_Handle, 0, 0))
- {
- LOG("WARNING cSemaphore: Could not create unnamed semaphore, fallback to named.");
- delete (sem_t*)m_Handle; // named semaphores return their own address
- m_bNamed = true;
-
- AString Name;
- Printf(Name, "cSemaphore%p", this);
- m_Handle = sem_open(Name.c_str(), O_CREAT, 777, a_InitialCount);
- if (m_Handle == SEM_FAILED)
- {
- LOG("ERROR: Could not create Semaphore. (%i)", errno);
- }
- else
- {
- if (sem_unlink(Name.c_str()) != 0)
- {
- LOG("ERROR: Could not unlink cSemaphore. (%i)", errno);
- }
- }
- }
-#else
- m_Handle = CreateSemaphore(
- nullptr, // security attribute
- a_InitialCount, // initial count
- a_MaxCount, // maximum count
- 0 // name (optional)
- );
-#endif
-}
-
-
-
-
-
-cSemaphore::~cSemaphore()
-{
-#ifdef _WIN32
- CloseHandle( m_Handle);
-#else
- if (m_bNamed)
- {
- if (sem_close( (sem_t*)m_Handle) != 0)
- {
- LOG("ERROR: Could not close cSemaphore. (%i)", errno);
- }
- }
- else
- {
- sem_destroy( (sem_t*)m_Handle);
- delete (sem_t*)m_Handle;
- }
- m_Handle = 0;
-
-#endif
-}
-
-
-
-
-
-void cSemaphore::Wait()
-{
-#ifndef _WIN32
- if (sem_wait( (sem_t*)m_Handle) != 0)
- {
- LOG("ERROR: Could not wait for cSemaphore. (%i)", errno);
- }
-#else
- WaitForSingleObject( m_Handle, INFINITE);
-#endif
-}
-
-
-
-
-
-void cSemaphore::Signal()
-{
-#ifndef _WIN32
- if (sem_post( (sem_t*)m_Handle) != 0)
- {
- LOG("ERROR: Could not signal cSemaphore. (%i)", errno);
- }
-#else
- ReleaseSemaphore( m_Handle, 1, nullptr);
-#endif
-}
-
-
-
-
diff --git a/src/OSSupport/Semaphore.h b/src/OSSupport/Semaphore.h
deleted file mode 100644
index 57fa4bdb2..000000000
--- a/src/OSSupport/Semaphore.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#pragma once
-
-class cSemaphore
-{
-public:
- cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount = 0);
- ~cSemaphore();
-
- void Wait();
- void Signal();
-private:
- void * m_Handle; // HANDLE pointer
-
-#ifndef _WIN32
- bool m_bNamed;
-#endif
-};
diff --git a/src/Root.cpp b/src/Root.cpp
index 54e65b6da..8d344ee65 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -106,7 +106,7 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
EnableMenuItem(hmenu, SC_CLOSE, MF_GRAYED); // Disable close button when starting up; it causes problems with our CTRL-CLOSE handling
#endif
- cLogger::cListener * consoleLogListener = MakeConsoleListener();
+ cLogger::cListener * consoleLogListener = MakeConsoleListener(m_RunAsService);
cLogger::cListener * fileLogListener = new cFileListener();
cLogger::GetInstance().AttachListener(consoleLogListener);
cLogger::GetInstance().AttachListener(fileLogListener);
diff --git a/src/World.cpp b/src/World.cpp
index f7d2165c7..3ff8e0723 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -186,6 +186,7 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin
m_Scoreboard(this),
m_MapManager(this),
m_GeneratorCallbacks(*this),
+ m_ChunkSender(*this),
m_TickThread(*this)
{
LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str());
@@ -509,7 +510,7 @@ void cWorld::Start(void)
m_Lighting.Start(this);
m_Storage.Start(this, m_StorageSchema, m_StorageCompressionFactor);
m_Generator.Start(m_GeneratorCallbacks, m_GeneratorCallbacks, IniFile);
- m_ChunkSender.Start(this);
+ m_ChunkSender.Start();
m_TickThread.Start();
// Init of the spawn monster time (as they are supposed to have different spawn rate)
@@ -1326,6 +1327,30 @@ bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback
+bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, std::function<bool(cChunk &)> a_Callback)
+{
+ struct cCallBackWrapper : cChunkCallback
+ {
+ cCallBackWrapper(std::function<bool(cChunk &)> a_InnerCallback) :
+ m_Callback(a_InnerCallback)
+ {
+ }
+
+ virtual bool Item(cChunk * a_Chunk)
+ {
+ return m_Callback(*a_Chunk);
+ }
+
+ private:
+ std::function<bool(cChunk &)> m_Callback;
+ } callback(a_Callback);
+ return m_ChunkMap->DoWithChunk(a_ChunkX, a_ChunkZ, callback);
+}
+
+
+
+
+
bool cWorld::DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback)
{
return m_ChunkMap->DoWithChunkAt(a_BlockPos, a_Callback);
@@ -2001,15 +2026,6 @@ void cWorld::BroadcastChat(const cCompositeChat & a_Message, const cClientHandle
-void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude)
-{
- m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude);
-}
-
-
-
-
-
void cWorld::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude)
{
m_ChunkMap->BroadcastCollectEntity(a_Entity, a_Player, a_Exclude);
@@ -2461,10 +2477,23 @@ void cWorld::SetChunkData(cSetChunkData & a_SetChunkData)
// If a client is requesting this chunk, send it to them:
int ChunkX = a_SetChunkData.GetChunkX();
int ChunkZ = a_SetChunkData.GetChunkZ();
- if (m_ChunkMap->HasChunkAnyClients(ChunkX, ChunkZ))
- {
- m_ChunkSender.ChunkReady(ChunkX, ChunkZ);
- }
+ cChunkSender & ChunkSender = m_ChunkSender;
+ DoWithChunk(
+ ChunkX, ChunkZ,
+ [&ChunkSender] (cChunk & a_Chunk) -> bool
+ {
+ if (a_Chunk.HasAnyClients())
+ {
+ ChunkSender.QueueSendChunkTo(
+ a_Chunk.GetPosX(),
+ a_Chunk.GetPosZ(),
+ cChunkSender::PRIORITY_BROADCAST,
+ a_Chunk.GetAllClients()
+ );
+ }
+ return true;
+ }
+ );
// Save the chunk right after generating, so that we don't have to generate it again on next run
if (a_SetChunkData.ShouldMarkDirty())
diff --git a/src/World.h b/src/World.h
index 064b50165..078a25562 100644
--- a/src/World.h
+++ b/src/World.h
@@ -231,7 +231,6 @@ public:
void BroadcastChat (const cCompositeChat & a_Message, const cClientHandle * a_Exclude = nullptr);
// tolua_end
- void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = nullptr);
void BroadcastCollectEntity (const cEntity & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
void BroadcastEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude = nullptr);
@@ -609,6 +608,7 @@ public:
/** Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback */
bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback);
+ bool DoWithChunk(int a_ChunkX, int a_ChunkZ, std::function<bool(cChunk &)> a_Callback);
/** Calls the callback for the chunk at the block position specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback **/
bool DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback);
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index 392b9bf83..8ca49c2c0 100755
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -1052,7 +1052,8 @@ cBlockEntity * cWSSAnvil::LoadFurnaceFromNBT(const cParsedNBT & a_NBT, int a_Tag
}
std::unique_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World));
-
+ Furnace->SetLoading(true);
+
// Load slots:
for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child))
{
@@ -1085,9 +1086,9 @@ cBlockEntity * cWSSAnvil::LoadFurnaceFromNBT(const cParsedNBT & a_NBT, int a_Tag
// Anvil doesn't store the time that an item takes to cook. We simply use the default - 10 seconds (200 ticks)
Furnace->SetCookTimes(200, ct);
}
-
// Restart cooking:
Furnace->ContinueCooking();
+ Furnace->SetLoading(false);
return Furnace.release();
}
diff --git a/src/main.cpp b/src/main.cpp
index d5c39cecd..2103f3356 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -39,7 +39,7 @@ bool cRoot::m_RunAsService = false;
#if defined(_WIN32)
- SERVICE_STATUS_HANDLE g_StatusHandle = NULL;
+ SERVICE_STATUS_HANDLE g_StatusHandle = nullptr;
HANDLE g_ServiceThread = INVALID_HANDLE_VALUE;
#define SERVICE_NAME "MCServerService"
#endif
@@ -145,7 +145,7 @@ Its purpose is to create the crashdump using the dbghlp DLLs
*/
LONG WINAPI LastChanceExceptionFilter(__in struct _EXCEPTION_POINTERS * a_ExceptionInfo)
{
- char * newStack = &g_ExceptionStack[sizeof(g_ExceptionStack)];
+ char * newStack = &g_ExceptionStack[sizeof(g_ExceptionStack) - 1];
char * oldStack;
// Use the substitute stack:
@@ -249,7 +249,8 @@ void universalMain(std::unique_ptr<cSettingsRepositoryInterface> overridesRepo)
-#if defined(_WIN32)
+
+#if defined(_WIN32) // Windows service support.
////////////////////////////////////////////////////////////////////////////////
// serviceWorkerThread: Keep the service alive
@@ -266,6 +267,7 @@ DWORD WINAPI serviceWorkerThread(LPVOID lpParam)
+
////////////////////////////////////////////////////////////////////////////////
// serviceSetState: Set the internal status of the service
@@ -319,14 +321,10 @@ void WINAPI serviceCtrlHandler(DWORD CtrlCode)
void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
{
- #if defined(_DEBUG) && defined(DEBUG_SERVICE_STARTUP)
- Sleep(10000);
- #endif
-
char applicationFilename[MAX_PATH];
char applicationDirectory[MAX_PATH];
- GetModuleFileName(NULL, applicationFilename, sizeof(applicationFilename)); // This binary's file path.
+ GetModuleFileName(nullptr, applicationFilename, sizeof(applicationFilename)); // This binary's file path.
// Strip off the filename, keep only the path:
strncpy_s(applicationDirectory, sizeof(applicationDirectory), applicationFilename, (strrchr(applicationFilename, '\\') - applicationFilename));
@@ -337,8 +335,7 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
SetCurrentDirectory(applicationDirectory);
g_StatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, serviceCtrlHandler);
-
- if (g_StatusHandle == NULL)
+ if (g_StatusHandle == nullptr)
{
OutputDebugStringA("RegisterServiceCtrlHandler() failed\n");
serviceSetState(0, SERVICE_STOPPED, GetLastError());
@@ -347,8 +344,9 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
serviceSetState(SERVICE_ACCEPT_STOP, SERVICE_RUNNING, 0);
- g_ServiceThread = CreateThread(NULL, 0, serviceWorkerThread, NULL, 0, NULL);
- if (g_ServiceThread == NULL)
+ DWORD ThreadID;
+ g_ServiceThread = CreateThread(nullptr, 0, serviceWorkerThread, nullptr, 0, &ThreadID);
+ if (g_ServiceThread == nullptr)
{
OutputDebugStringA("CreateThread() failed\n");
serviceSetState(0, SERVICE_STOPPED, GetLastError());
@@ -360,7 +358,9 @@ void WINAPI serviceMain(DWORD argc, TCHAR *argv[])
serviceSetState(0, SERVICE_STOPPED, 0);
}
-#endif
+#endif // Windows service support.
+
+
@@ -368,46 +368,33 @@ std::unique_ptr<cMemorySettingsRepository> parseArguments(int argc, char **argv)
{
try
{
+ // Parse the comand line args:
TCLAP::CmdLine cmd("MCServer");
-
- TCLAP::ValueArg<int> slotsArg("s", "max-players", "Maximum number of slots for the server to use, overrides setting in setting.ini", false, -1, "number", cmd);
-
- TCLAP::MultiArg<int> portsArg("p", "port", "The port number the server should listen to", false, "port", cmd);
-
- TCLAP::SwitchArg commLogArg("", "log-comm", "Log server client communications to file", cmd);
-
- TCLAP::SwitchArg commLogInArg("", "log-comm-in", "Log inbound server client communications to file", cmd);
-
- TCLAP::SwitchArg commLogOutArg("", "log-comm-out", "Log outbound server client communications to file", cmd);
-
- TCLAP::SwitchArg noBufArg("", "no-output-buffering", "Disable output buffering", cmd);
-
- TCLAP::SwitchArg runAsServiceArg("d", "run-as-service", "Run as a service on Windows", cmd);
-
- cmd.ignoreUnmatched(true);
-
+ TCLAP::ValueArg<int> slotsArg ("s", "max-players", "Maximum number of slots for the server to use, overrides setting in setting.ini", false, -1, "number", cmd);
+ TCLAP::MultiArg<int> portsArg ("p", "port", "The port number the server should listen to", false, "port", cmd);
+ TCLAP::SwitchArg commLogArg ("", "log-comm", "Log server client communications to file", cmd);
+ TCLAP::SwitchArg commLogInArg ("", "log-comm-in", "Log inbound server client communications to file", cmd);
+ TCLAP::SwitchArg commLogOutArg ("", "log-comm-out", "Log outbound server client communications to file", cmd);
+ TCLAP::SwitchArg crashDumpFull ("", "crash-dump-full", "Crashdumps created by the server will contain full server memory", cmd);
+ TCLAP::SwitchArg crashDumpGlobals("", "crash-dump-globals", "Crashdumps created by the server will contain the global variables' values", cmd);
+ TCLAP::SwitchArg noBufArg ("", "no-output-buffering", "Disable output buffering", cmd);
+ TCLAP::SwitchArg runAsServiceArg ("d", "service", "Run as a service on Windows, or daemon on UNIX like systems", cmd);
cmd.parse(argc, argv);
+ // Copy the parsed args' values into a settings repository:
auto repo = cpp14::make_unique<cMemorySettingsRepository>();
-
if (slotsArg.isSet())
{
-
int slots = slotsArg.getValue();
-
repo->AddValue("Server", "MaxPlayers", static_cast<Int64>(slots));
-
}
-
if (portsArg.isSet())
{
- std::vector<int> ports = portsArg.getValue();
- for (auto port : ports)
+ for (auto port: portsArg.getValue())
{
repo->AddValue("Server", "Port", static_cast<Int64>(port));
}
}
-
if (commLogArg.getValue())
{
g_ShouldLogCommIn = true;
@@ -418,115 +405,133 @@ std::unique_ptr<cMemorySettingsRepository> parseArguments(int argc, char **argv)
g_ShouldLogCommIn = commLogInArg.getValue();
g_ShouldLogCommOut = commLogOutArg.getValue();
}
-
if (noBufArg.getValue())
{
setvbuf(stdout, nullptr, _IONBF, 0);
}
+ repo->SetReadOnly();
+ // Set the service flag directly to cRoot:
if (runAsServiceArg.getValue())
{
cRoot::m_RunAsService = true;
}
- repo->SetReadOnly();
+ // Apply the CrashDump flags for platforms that support them:
+ #if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) // 32-bit Windows app compiled in MSVC
+ if (crashDumpGlobals.getValue())
+ {
+ g_DumpFlags = static_cast<MINIDUMP_TYPE>(g_DumpFlags | MiniDumpWithDataSegs);
+ }
+ if (crashDumpFull.getValue())
+ {
+ g_DumpFlags = static_cast<MINIDUMP_TYPE>(g_DumpFlags | MiniDumpWithFullMemory);
+ }
+ #endif // 32-bit Windows app compiled in MSVC
return repo;
}
- catch (TCLAP::ArgException &e)
+ catch (const TCLAP::ArgException & exc)
{
- printf("error reading command line %s for arg %s", e.error().c_str(), e.argId().c_str());
+ printf("Error reading command line %s for arg %s", exc.error().c_str(), exc.argId().c_str());
return cpp14::make_unique<cMemorySettingsRepository>();
}
}
+
+
+
////////////////////////////////////////////////////////////////////////////////
// main:
int main(int argc, char **argv)
{
-
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
- InitLeakFinder();
+ InitLeakFinder();
#endif
// Magic code to produce dump-files on Windows if the server crashes:
- #if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER)
- HINSTANCE hDbgHelp = LoadLibrary("DBGHELP.DLL");
- g_WriteMiniDump = (pMiniDumpWriteDump)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
- if (g_WriteMiniDump != nullptr)
- {
- _snprintf_s(g_DumpFileName, ARRAYCOUNT(g_DumpFileName), _TRUNCATE, "crash_mcs_%x.dmp", GetCurrentProcessId());
- SetUnhandledExceptionFilter(LastChanceExceptionFilter);
-
- // Parse arguments for minidump flags:
- for (int i = 0; i < argc; i++)
+ #if defined(_WIN32) && !defined(_WIN64) && defined(_MSC_VER) // 32-bit Windows app compiled in MSVC
+ HINSTANCE hDbgHelp = LoadLibrary("DBGHELP.DLL");
+ g_WriteMiniDump = (pMiniDumpWriteDump)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
+ if (g_WriteMiniDump != nullptr)
{
- if (_stricmp(argv[i], "/cdg") == 0)
- {
- // Add globals to the dump
- g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithDataSegs);
- }
- else if (_stricmp(argv[i], "/cdf") == 0)
- {
- // Add full memory to the dump (HUUUGE file)
- g_DumpFlags = (MINIDUMP_TYPE)(g_DumpFlags | MiniDumpWithFullMemory);
- }
- } // for i - argv[]
- }
- #endif // _WIN32 && !_WIN64
+ _snprintf_s(g_DumpFileName, ARRAYCOUNT(g_DumpFileName), _TRUNCATE, "crash_mcs_%x.dmp", GetCurrentProcessId());
+ SetUnhandledExceptionFilter(LastChanceExceptionFilter);
+ }
+ #endif // 32-bit Windows app compiled in MSVC
// End of dump-file magic
+
#if defined(_DEBUG) && defined(_MSC_VER)
- _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+ _CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
- // _X: The simple built-in CRT leak finder - simply break when allocating the Nth block ({N} is listed in the leak output)
- // Only useful when the leak is in the same sequence all the time
- // _CrtSetBreakAlloc(85950);
+ // _X: The simple built-in CRT leak finder - simply break when allocating the Nth block ({N} is listed in the leak output)
+ // Only useful when the leak is in the same sequence all the time
+ // _CrtSetBreakAlloc(85950);
#endif // _DEBUG && _MSC_VER
#ifndef _DEBUG
- std::signal(SIGSEGV, NonCtrlHandler);
- std::signal(SIGTERM, NonCtrlHandler);
- std::signal(SIGINT, NonCtrlHandler);
- std::signal(SIGABRT, NonCtrlHandler);
- #ifdef SIGABRT_COMPAT
- std::signal(SIGABRT_COMPAT, NonCtrlHandler);
- #endif // SIGABRT_COMPAT
+ std::signal(SIGSEGV, NonCtrlHandler);
+ std::signal(SIGTERM, NonCtrlHandler);
+ std::signal(SIGINT, NonCtrlHandler);
+ std::signal(SIGABRT, NonCtrlHandler);
+ #ifdef SIGABRT_COMPAT
+ std::signal(SIGABRT_COMPAT, NonCtrlHandler);
+ #endif // SIGABRT_COMPAT
#endif
- // DEBUG: test the dumpfile creation:
- // *((int *)0) = 0;
-
auto argsRepo = parseArguments(argc, argv);
-
- #if defined(_WIN32)
+
// Attempt to run as a service
if (cRoot::m_RunAsService)
{
- SERVICE_TABLE_ENTRY ServiceTable[] =
- {
- { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)serviceMain },
- { NULL, NULL }
- };
+ #if defined(_WIN32) // Windows service.
+ SERVICE_TABLE_ENTRY ServiceTable[] =
+ {
+ { SERVICE_NAME, (LPSERVICE_MAIN_FUNCTION)serviceMain },
+ { nullptr, nullptr }
+ };
- if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
- {
- LOGERROR("Attempted, but failed, service startup.");
- return GetLastError();
- }
+ if (StartServiceCtrlDispatcher(ServiceTable) == FALSE)
+ {
+ LOGERROR("Attempted, but failed, service startup.");
+ return GetLastError();
+ }
+ #else // UNIX daemon.
+ pid_t pid = fork();
+
+ // fork() returns a negative value on error.
+ if (pid < 0)
+ {
+ LOGERROR("Could not fork process.");
+ return EXIT_FAILURE;
+ }
+
+ // Check if we are the parent or child process. Parent stops here.
+ if (pid > 0)
+ {
+ return EXIT_SUCCESS;
+ }
+
+ // Child process now goes quiet, running in the background.
+ close(STDIN_FILENO);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+
+ universalMain(std::move(argsRepo));
+ #endif
}
else
- #endif
{
// Not running as a service, do normal startup
universalMain(std::move(argsRepo));
}
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
- DeinitLeakFinder();
+ DeinitLeakFinder();
#endif
return EXIT_SUCCESS;