summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COMPILING.md30
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua1
m---------MCServer/Plugins/Core0
-rw-r--r--MCServer/Plugins/Debuggers/Debuggers.lua73
-rw-r--r--MCServer/Plugins/MagicCarpet/coremessaging.lua19
-rw-r--r--MCServer/Plugins/MagicCarpet/plugin.lua8
-rwxr-xr-x[-rw-r--r--]MCServer/hg0
-rw-r--r--MCServer/items.ini3
-rw-r--r--MCServer/monsters.ini44
-rwxr-xr-x[-rw-r--r--]MCServer/vg0
-rw-r--r--Tools/MCADefrag/.gitignore1
-rw-r--r--Tools/MCADefrag/CMakeLists.txt144
-rw-r--r--Tools/MCADefrag/Globals.cpp10
-rw-r--r--Tools/MCADefrag/Globals.h229
-rw-r--r--Tools/MCADefrag/MCADefrag.cpp421
-rw-r--r--Tools/MCADefrag/MCADefrag.h144
-rw-r--r--src/Bindings/LuaChunkStay.cpp173
-rw-r--r--src/Bindings/LuaChunkStay.h73
-rw-r--r--src/Bindings/LuaState.cpp105
-rw-r--r--src/Bindings/LuaState.h20
-rw-r--r--src/Bindings/ManualBindings.cpp47
-rw-r--r--src/Bindings/PluginLua.h28
-rw-r--r--src/CMakeLists.txt85
-rw-r--r--src/Chunk.cpp1
-rw-r--r--src/Chunk.h110
-rw-r--r--src/ChunkDef.h1
-rw-r--r--src/ChunkMap.cpp194
-rw-r--r--src/ChunkMap.h199
-rw-r--r--src/ChunkStay.cpp137
-rw-r--r--src/ChunkStay.h97
-rw-r--r--src/Item.h42
-rw-r--r--src/Items/ItemBoat.h3
-rw-r--r--src/LightingThread.cpp163
-rw-r--r--src/LightingThread.h77
-rw-r--r--src/World.cpp18
-rw-r--r--src/World.h6
-rw-r--r--src/WorldStorage/WSSAnvil.cpp8
37 files changed, 2169 insertions, 545 deletions
diff --git a/COMPILING.md b/COMPILING.md
index eceeefee7..d3c896bdd 100644
--- a/COMPILING.md
+++ b/COMPILING.md
@@ -49,7 +49,7 @@ There's a script file, `MCServer/profile_run.cmd` that encapsulates most of the
Install git, cmake and gcc or clang, using your platform's package manager:
```
-sudo apt-get install git cmake gcc
+sudo apt-get install git cmake gcc g++
```
### Getting the sources ###
@@ -65,17 +65,29 @@ git submodule update
Release mode is preferred for almost all cases, it has much better speed and less console spam. However, if you are developing MCServer actively, debug mode might be better.
- cmake . -DCMAKE_BUILD_TYPE=RELEASE && make
-
+Assuming you are in the MCServer folder created in the initial setup step, you need to run these commands:
+```
+mkdir Release
+cd Release
+cmake . -DCMAKE_BUILD_TYPE=RELEASE .. && make
+```
+The executable will be built in the `MCServer/MCServer` folder and will be named `MCServer`.
+
### Debug Mode ###
-Debug mode is useful if you want more debugging information about MCServer as it's running or if you want to use a debugger like GDB to debug issues and crashes.
+Debug mode is useful if you want more debugging information about MCServer while it's running or if you want to use a debugger like GDB to debug issues and crashes.
- cmake . -DCMAKE_BUILD_TYPE=DEBUG && make
+Assuming you are in the MCServer folder created in the Getting the sources step, you need to run these commands:
+```
+mkdir Debug
+cd Debug
+cmake . -DCMAKE_BUILD_TYPE=DEBUG && make`
+```
+The executable will be built in the `MCServer/MCServer` folder and will be named `MCServer_debug`.
-### 32 Bit Mode ###
+### 32 Bit Mode switch ###
-This is useful if you want to compile MCServer to use on another 32-bit machine. It can be used with debug or release mode. To use 32 bit mode, simply add:
+This is useful if you want to compile MCServer on an x64 (64-bit Intel) machine but want to use on an x86 (32-bit Intel) machine. This switch can be used with debug or release mode. Simply add:
-DFORCE_32=1
@@ -84,8 +96,10 @@ to your cmake command and 32 bit will be forced.
### Compiling for another computer ###
-When compiling for another computer it is important to set cross compiling mode. This tells the compiler not to optimise for your machine. It can be used with debug or release mode. To enable simply add:
+When cross-compiling for another computer it is important to set cross compiling mode. This tells the compiler not to optimise for your machine. This switch can be used with debug or release mode. To enable, simply add:
-DCROSSCOMPILE=1
to your cmake command.
+
+Note that cross-compilation is probably broken at this moment, since the build requires running an executable that it has built, as part of the build process.
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua
index 19553ea80..e877ec446 100644
--- a/MCServer/Plugins/APIDump/APIDesc.lua
+++ b/MCServer/Plugins/APIDump/APIDesc.lua
@@ -2035,6 +2035,7 @@ end
BroadcastSoundParticleEffect = { Params = "EffectID, X, Y, Z, EffectData, [{{cClientHandle|ExcludeClient}}]", Return = "", Notes = "Sends the specified effect to all players in this world, except the optional ExceptClient" },
CastThunderbolt = { Params = "X, Y, Z", Return = "", Notes = "Creates a thunderbolt at the specified coords" },
ChangeWeather = { Params = "", Return = "", Notes = "Forces the weather to change in the next game tick. Weather is changed according to the normal rules: wSunny <-> wRain <-> wStorm" },
+ ChunkStay = { Params = "ChunkCoordTable, OnChunkAvailable, OnAllChunksAvailable", Return = "", Notes = "Queues the specified chunks to be loaded or generated and calls the specified callbacks once they are loaded. ChunkCoordTable is an arra-table of chunk coords, each coord being a table of 2 numbers: { {Chunk1x, Chunk1z}, {Chunk2x, Chunk2z}, ...}. When any of those chunks are made available (including being available at the start of this call), the OnChunkAvailable() callback is called. When all the chunks are available, the OnAllChunksAvailable() callback is called. The function signatures are: <pre class=\"prettyprint lang-lua\">function OnChunkAvailable(ChunkX, ChunkZ)\nfunction OnAllChunksAvailable()</pre> All return values from the callbacks are ignored." },
CreateProjectile = { Params = "X, Y, Z, {{cProjectileEntity|ProjectileKind}}, {{cEntity|Creator}}, [{{Vector3d|Speed}}]", Return = "", Notes = "Creates a new projectile of the specified kind at the specified coords. The projectile's creator is set to Creator (may be nil). Optional speed indicates the initial speed for the projectile." },
DigBlock = { Params = "X, Y, Z", Return = "", Notes = "Replaces the specified block with air, without dropping the usual pickups for the block. Wakes up the simulators for the block and its neighbors." },
DoExplosionAt = { Params = "Force, X, Y, Z, CanCauseFire, Source, SourceData", Return = "", Notes = "Creates an explosion of the specified relative force in the specified position. If CanCauseFire is set, the explosion will set blocks on fire, too. The Source parameter specifies the source of the explosion, one of the esXXX constants. The SourceData parameter is specific to each source type, usually it provides more info about the source." },
diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core
-Subproject 51aa4290d7cef2aff68a35c66339535d9068f79
+Subproject 0e53588713215f0a9557ef55295e8aa22f265cb
diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua
index 624261cbf..8345e2169 100644
--- a/MCServer/Plugins/Debuggers/Debuggers.lua
+++ b/MCServer/Plugins/Debuggers/Debuggers.lua
@@ -53,6 +53,7 @@ function Initialize(Plugin)
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");
Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers);
@@ -64,7 +65,7 @@ function Initialize(Plugin)
-- TestBlockAreas();
-- TestSQLiteBindings();
-- TestExpatBindings();
- TestPluginCalls();
+ -- TestPluginCalls();
return true
end;
@@ -1061,3 +1062,73 @@ end
+
+function HandleChunkStay(a_Split, a_Player)
+ -- As an example of using ChunkStay, this call will load 3x3 chunks around the specified chunk coords,
+ -- then build an obsidian pillar in the middle of each one.
+ -- Once complete, the player will be teleported to the middle pillar
+
+ if (#a_Split ~= 3) then
+ a_Player:SendMessageInfo("Usage: /cs <ChunkX> <ChunkZ>")
+ return true
+ end
+
+ local ChunkX = tonumber(a_Split[2])
+ local ChunkZ = tonumber(a_Split[3])
+ if ((ChunkX == nil) or (ChunkZ == nil)) then
+ a_Player:SendMessageFailure("Invalid chunk coords.")
+ return true
+ end
+
+ local World = a_Player:GetWorld()
+ local PlayerID = a_Player:GetUniqueID()
+ a_Player:SendMessageInfo("Loading chunks, stand by...");
+
+ -- Set the wanted chunks:
+ local Chunks = {}
+ for z = -1, 1 do for x = -1, 1 do
+ table.insert(Chunks, {ChunkX + x, ChunkZ + z})
+ end end
+
+ -- The function that is called when all chunks are available
+ -- Will perform the actual action with all those chunks
+ -- Note that the player needs to be referenced using their EntityID - in case they disconnect before the chunks load
+ local OnAllChunksAvailable = function()
+ LOGINFO("ChunkStay all chunks now available")
+ -- Build something on the neighboring chunks, to verify:
+ for z = -1, 1 do for x = -1, 1 do
+ local BlockX = (ChunkX + x) * 16 + 8
+ local BlockZ = (ChunkZ + z) * 16 + 8
+ for y = 20, 80 do
+ World:SetBlock(BlockX, y, BlockZ, E_BLOCK_OBSIDIAN, 0)
+ end
+ end end
+
+ -- Teleport the player there for visual inspection:
+ World:DoWithEntityByID(PlayerID,
+ function (a_CallbackPlayer)
+ a_CallbackPlayer:TeleportToCoords(ChunkX * 16 + 8, 85, ChunkZ * 16 + 8)
+ a_CallbackPlayer:SendMessageSuccess("ChunkStay fully available")
+ end
+ )
+ end
+
+ -- This function will be called for each chunk that is made available
+ -- Note that the player needs to be referenced using their EntityID - in case they disconnect before the chunks load
+ local OnChunkAvailable = function(a_ChunkX, a_ChunkZ)
+ LOGINFO("ChunkStay now has chunk [" .. a_ChunkX .. ", " .. a_ChunkZ .. "]")
+ World:DoWithEntityByID(PlayerID,
+ function (a_CallbackPlayer)
+ a_CallbackPlayer:SendMessageInfo("ChunkStay now has chunk [" .. a_ChunkX .. ", " .. a_ChunkZ .. "]")
+ end
+ )
+ end
+
+ -- Process the ChunkStay:
+ World:ChunkStay(Chunks, OnChunkAvailable, OnAllChunksAvailable)
+ return true
+end
+
+
+
+
diff --git a/MCServer/Plugins/MagicCarpet/coremessaging.lua b/MCServer/Plugins/MagicCarpet/coremessaging.lua
deleted file mode 100644
index 9fb2c0db1..000000000
--- a/MCServer/Plugins/MagicCarpet/coremessaging.lua
+++ /dev/null
@@ -1,19 +0,0 @@
-Core = cPluginManager:Get():GetPlugin("Core")
-
-function SendMessage(a_Player, a_Message)
- if (Core ~= nil) then
- Core:Call("SendMessage", a_Player, a_Message)
- end
-end
-
-function SendMessageSuccess(a_Player, a_Message)
- if (Core ~= nil) then
- Core:Call("SendMessageSuccess", a_Player, a_Message)
- end
-end
-
-function SendMessageFailure(a_Player, a_Message)
- if (Core ~= nil) then
- Core:Call("SendMessageFailure", a_Player, a_Message)
- end
-end
diff --git a/MCServer/Plugins/MagicCarpet/plugin.lua b/MCServer/Plugins/MagicCarpet/plugin.lua
index b05816e48..417ea0e02 100644
--- a/MCServer/Plugins/MagicCarpet/plugin.lua
+++ b/MCServer/Plugins/MagicCarpet/plugin.lua
@@ -6,7 +6,7 @@ function Initialize( Plugin )
Plugin:SetVersion( 2 )
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
- cPluginManager.AddHook(cPluginManager.HOOK_DISCONNECT, OnDisconnect)
+ cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_DESTROYED, OnDisconnect)
local PluginManager = cPluginManager:Get()
PluginManager:BindCommand("/mc", "magiccarpet", HandleCarpetCommand, " - Spawns a magical carpet");
@@ -37,12 +37,12 @@ function HandleCarpetCommand( Split, Player )
if( Carpet == nil ) then
Carpets[ Player ] = cCarpet:new()
- SendMessageSuccess(Player, "You're on a magic carpet!")
- SendMessage(Player, "Look straight down to descend. Jump to ascend.")
+ Player:SendMessageSuccess("You're on a magic carpet!")
+ Player:SendMessageInfo("Look straight down to descend. Jump to ascend.")
else
Carpet:remove()
Carpets[ Player ] = nil
- SendMessageSuccess(Player, "The carpet vanished!")
+ Player:SendMessageSuccess("The carpet vanished!")
end
return true
diff --git a/MCServer/hg b/MCServer/hg
index 93593de8d..93593de8d 100644..100755
--- a/MCServer/hg
+++ b/MCServer/hg
diff --git a/MCServer/items.ini b/MCServer/items.ini
index c09b32f17..7b8fcf4ee 100644
--- a/MCServer/items.ini
+++ b/MCServer/items.ini
@@ -1,4 +1,5 @@
[Items]
+air=0
rock=1
stone=1
grass=2
@@ -177,6 +178,8 @@ workbench=58
crop=59
crops=59
soil=60
+farmland=60
+tilleddirt=60
furnace=61
litfurnace=62
signblock=63
diff --git a/MCServer/monsters.ini b/MCServer/monsters.ini
index a1b63423e..8cd956157 100644
--- a/MCServer/monsters.ini
+++ b/MCServer/monsters.ini
@@ -1,61 +1,61 @@
[Spider]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=2.0
SightDistance=25.0
MaxHealth=16
[Chicken]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=1.0
SightDistance=25.0
MaxHealth=4
[Cow]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=1.0
SightDistance=25.0
MaxHealth=10
[Pig]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=1.0
SightDistance=25.0
MaxHealth=10
[Sheep]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=1.0
SightDistance=25.0
MaxHealth=8
[Squid]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=1.0
SightDistance=25.0
MaxHealth=10
[Enderman]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=4.0
SightDistance=25.0
MaxHealth=40
[Zombiepigman]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=7.0
SightDistance=25.0
MaxHealth=20
[Cavespider]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=2.0
SightDistance=25.0
@@ -76,7 +76,7 @@ SightDistance=50.0
MaxHealth=10
[Silverfish]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=1.0
SightDistance=25.0
@@ -89,21 +89,21 @@ SightDistance=40.0
MaxHealth=20
[Slime]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=4.0
SightDistance=25.0
MaxHealth=16
[Zombie]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=4.0
SightDistance=25.0
MaxHealth=20
[Wolf]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=4.0
SightDistance=25.0
@@ -117,14 +117,14 @@ SightDistance=25.0
MaxHealth=20
[Villager]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=0.0
SightDistance=25.0
MaxHealth=20
[Witch]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=0.0
SightDistance=25.0
@@ -132,49 +132,49 @@ MaxHealth=26
[Ocelot]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=0.0
SightDistance=25.0
MaxHealth=10
[Mooshroom]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=0.0
SightDistance=25.0
MaxHealth=10
[Magmacube]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=6.0
SightDistance=25.0
MaxHealth=16
[Horse]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=6.0
SightDistance=25.0
MaxHealth=30
[EnderDragon]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=6.0
SightDistance=25.0
MaxHealth=200
[Giant]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=6.0
SightDistance=25.0
MaxHealth=100
[IronGolem]
-AttackRange=5.0
+AttackRange=2.0
AttackRate=1
AttackDamage=6.0
SightDistance=25.0
diff --git a/MCServer/vg b/MCServer/vg
index fcc8270d0..fcc8270d0 100644..100755
--- a/MCServer/vg
+++ b/MCServer/vg
diff --git a/Tools/MCADefrag/.gitignore b/Tools/MCADefrag/.gitignore
new file mode 100644
index 000000000..44a3e1f48
--- /dev/null
+++ b/Tools/MCADefrag/.gitignore
@@ -0,0 +1 @@
+*.mca
diff --git a/Tools/MCADefrag/CMakeLists.txt b/Tools/MCADefrag/CMakeLists.txt
new file mode 100644
index 000000000..7296b8ddc
--- /dev/null
+++ b/Tools/MCADefrag/CMakeLists.txt
@@ -0,0 +1,144 @@
+
+cmake_minimum_required (VERSION 2.6)
+
+project (MCADefrag)
+
+
+
+macro(add_flags_cxx FLAGS)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}")
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}")
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}")
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}")
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}")
+endmacro()
+
+
+
+
+# Add the preprocessor macros used for distinguishing between debug and release builds (CMake does this automatically for MSVC):
+if (NOT MSVC)
+ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -D_DEBUG")
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DNDEBUG")
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -DNDEBUG")
+endif()
+
+
+
+if(MSVC)
+ # Make build use multiple threads under MSVC:
+ add_flags_cxx("/MP")
+
+ # Make release builds use link-time code generation:
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL")
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /GL")
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG")
+ set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG")
+ set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /LTCG")
+elseif(APPLE)
+ #on os x clang adds pthread for us but we need to add it for gcc
+ if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
+ add_flags_cxx("-pthread")
+ endif()
+else()
+ # Let gcc / clang know that we're compiling a multi-threaded app:
+ add_flags_cxx("-pthread")
+endif()
+
+
+
+
+# Use static CRT in MSVC builds:
+if (MSVC)
+ string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
+ string(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
+ string(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}")
+ string(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
+endif()
+
+
+
+
+# Set include paths to the used libraries:
+include_directories("../../lib")
+include_directories("../../src")
+
+
+
+function(flatten_files arg1)
+ set(res "")
+ foreach(f ${${arg1}})
+ get_filename_component(f ${f} ABSOLUTE)
+ list(APPEND res ${f})
+ endforeach()
+ set(${arg1} "${res}" PARENT_SCOPE)
+endfunction()
+
+
+# Include the libraries:
+file(GLOB ZLIB_SRC "../../lib/zlib/*.c")
+file(GLOB ZLIB_HDR "../../lib/zlib/*.h")
+flatten_files(ZLIB_SRC)
+flatten_files(ZLIB_HDR)
+source_group("ZLib" FILES ${ZLIB_SRC} ${ZLIB_HDR})
+
+
+# Include the shared files:
+set(SHARED_SRC
+ ../../src/StringCompression.cpp
+ ../../src/StringUtils.cpp
+ ../../src/Log.cpp
+ ../../src/MCLogger.cpp
+)
+set(SHARED_HDR
+ ../../src/ByteBuffer.h
+ ../../src/StringUtils.h
+ ../../src/Log.h
+ ../../src/MCLogger.h
+)
+set(SHARED_OSS_SRC
+ ../../src/OSSupport/CriticalSection.cpp
+ ../../src/OSSupport/File.cpp
+ ../../src/OSSupport/IsThread.cpp
+ ../../src/OSSupport/Timer.cpp
+)
+set(SHARED_OSS_HDR
+ ../../src/OSSupport/CriticalSection.h
+ ../../src/OSSupport/File.h
+ ../../src/OSSupport/IsThread.h
+ ../../src/OSSupport/Timer.h
+)
+flatten_files(SHARED_SRC)
+flatten_files(SHARED_HDR)
+flatten_files(SHARED_OSS_SRC)
+flatten_files(SHARED_OSS_HDR)
+source_group("Shared" FILES ${SHARED_SRC} ${SHARED_HDR})
+source_group("Shared\\OSSupport" FILES ${SHARED_OSS_SRC} ${SHARED_OSS_HDR})
+
+
+
+# Include the main source files:
+set(SOURCES
+ MCADefrag.cpp
+ Globals.cpp
+)
+set(HEADERS
+ MCADefrag.h
+ Globals.h
+)
+
+source_group("" FILES ${SOURCES} ${HEADERS})
+
+add_executable(MCADefrag
+ ${SOURCES}
+ ${HEADERS}
+ ${SHARED_SRC}
+ ${SHARED_HDR}
+ ${SHARED_OSS_SRC}
+ ${SHARED_OSS_HDR}
+ ${ZLIB_SRC}
+ ${ZLIB_HDR}
+)
+
diff --git a/Tools/MCADefrag/Globals.cpp b/Tools/MCADefrag/Globals.cpp
new file mode 100644
index 000000000..13c6ae709
--- /dev/null
+++ b/Tools/MCADefrag/Globals.cpp
@@ -0,0 +1,10 @@
+
+// Globals.cpp
+
+// This file is used for precompiled header generation in MSVC environments
+
+#include "Globals.h"
+
+
+
+
diff --git a/Tools/MCADefrag/Globals.h b/Tools/MCADefrag/Globals.h
new file mode 100644
index 000000000..6f4bbdc76
--- /dev/null
+++ b/Tools/MCADefrag/Globals.h
@@ -0,0 +1,229 @@
+
+// Globals.h
+
+// This file gets included from every module in the project, so that global symbols may be introduced easily
+// Also used for precompiled header generation in MSVC environments
+
+
+
+
+
+// Compiler-dependent stuff:
+#if defined(_MSC_VER)
+ // MSVC produces warning C4481 on the override keyword usage, so disable the warning altogether
+ #pragma warning(disable:4481)
+
+ // Disable some warnings that we don't care about:
+ #pragma warning(disable:4100)
+
+ #define OBSOLETE __declspec(deprecated)
+
+ // No alignment needed in MSVC
+ #define ALIGN_8
+ #define ALIGN_16
+
+#elif defined(__GNUC__)
+
+ // TODO: Can GCC explicitly mark classes as abstract (no instances can be created)?
+ #define abstract
+
+ // TODO: Can GCC mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class)
+ #define override
+
+ #define OBSOLETE __attribute__((deprecated))
+
+ #define ALIGN_8 __attribute__((aligned(8)))
+ #define ALIGN_16 __attribute__((aligned(16)))
+
+ // Some portability macros :)
+ #define stricmp strcasecmp
+
+#else
+
+ #error "You are using an unsupported compiler, you might need to #define some stuff here for your compiler"
+
+ /*
+ // Copy and uncomment this into another #elif section based on your compiler identification
+
+ // Explicitly mark classes as abstract (no instances can be created)
+ #define abstract
+
+ // Mark virtual methods as overriding (forcing them to have a virtual function of the same signature in the base class)
+ #define override
+
+ // Mark functions as obsolete, so that their usage results in a compile-time warning
+ #define OBSOLETE
+
+ // Mark types / variables for alignment. Do the platforms need it?
+ #define ALIGN_8
+ #define ALIGN_16
+ */
+
+#endif
+
+
+
+
+
+// Integral types with predefined sizes:
+typedef long long Int64;
+typedef int Int32;
+typedef short Int16;
+
+typedef unsigned long long UInt64;
+typedef unsigned int UInt32;
+typedef unsigned short UInt16;
+
+typedef unsigned char Byte;
+
+
+
+
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for any class that shouldn't allow copying itself
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName &); \
+ void operator=(const TypeName &)
+
+// A macro that is used to mark unused function parameters, to avoid pedantic warnings in gcc
+#define UNUSED(X) (void)(X)
+
+
+
+
+// OS-dependent stuff:
+#ifdef _WIN32
+ #define WIN32_LEAN_AND_MEAN
+ #include <Windows.h>
+ #include <winsock2.h>
+ #include <ws2tcpip.h>
+
+ // Windows SDK defines min and max macros, messing up with our std::min and std::max usage
+ #undef min
+ #undef max
+
+ // Windows SDK defines GetFreeSpace as a constant, probably a Win16 API remnant
+ #ifdef GetFreeSpace
+ #undef GetFreeSpace
+ #endif // GetFreeSpace
+
+ #define SocketError WSAGetLastError()
+#else
+ #include <sys/types.h>
+ #include <sys/stat.h> // for mkdir
+ #include <sys/time.h>
+ #include <sys/socket.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+ #include <time.h>
+ #include <dirent.h>
+ #include <errno.h>
+ #include <iostream>
+ #include <unistd.h>
+
+ #include <cstdio>
+ #include <cstring>
+ #include <pthread.h>
+ #include <semaphore.h>
+ #include <errno.h>
+ #include <fcntl.h>
+
+ typedef int SOCKET;
+ enum
+ {
+ INVALID_SOCKET = -1,
+ };
+ #define closesocket close
+ #define SocketError errno
+#if !defined(ANDROID_NDK)
+ #include <tr1/memory>
+#endif
+#endif
+
+#if !defined(ANDROID_NDK)
+ #define USE_SQUIRREL
+#endif
+
+#if defined(ANDROID_NDK)
+ #define FILE_IO_PREFIX "/sdcard/mcserver/"
+#else
+ #define FILE_IO_PREFIX ""
+#endif
+
+
+
+
+
+// CRT stuff:
+#include <assert.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdarg.h>
+#include <time.h>
+
+
+
+
+
+// STL stuff:
+#include <vector>
+#include <list>
+#include <deque>
+#include <string>
+#include <map>
+#include <algorithm>
+#include <memory>
+
+
+
+
+
+// Common headers (without macros):
+#include "StringUtils.h"
+#include "OSSupport/CriticalSection.h"
+#include "OSSupport/IsThread.h"
+#include "OSSupport/File.h"
+
+
+
+
+
+// Common definitions:
+
+/// Evaluates to the number of elements in an array (compile-time!)
+#define ARRAYCOUNT(X) (sizeof(X) / sizeof(*(X)))
+
+/// Allows arithmetic expressions like "32 KiB" (but consider using parenthesis around it, "(32 KiB)" )
+#define KiB * 1024
+#define MiB * 1024 * 1024
+
+/// Faster than (int)floorf((float)x / (float)div)
+#define FAST_FLOOR_DIV( x, div ) ( (x) < 0 ? (((int)x / div) - 1) : ((int)x / div) )
+
+// Own version of assert() that writes failed assertions to the log for review
+#ifdef NDEBUG
+ #define ASSERT(x) ((void)0)
+#else
+ #define ASSERT assert
+#endif
+
+// Pretty much the same as ASSERT() but stays in Release builds
+#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) )
+
+
+
+
+
+/// A generic interface used mainly in ForEach() functions
+template <typename Type> class cItemCallback
+{
+public:
+ /// Called for each item in the internal list; return true to stop the loop, or false to continue enumerating
+ virtual bool Item(Type * a_Type) = 0;
+} ;
+
+
+
+
diff --git a/Tools/MCADefrag/MCADefrag.cpp b/Tools/MCADefrag/MCADefrag.cpp
new file mode 100644
index 000000000..a2de7f957
--- /dev/null
+++ b/Tools/MCADefrag/MCADefrag.cpp
@@ -0,0 +1,421 @@
+
+// MCADefrag.cpp
+
+// Implements the main app entrypoint and the cMCADefrag class representing the entire app
+
+#include "Globals.h"
+#include "MCADefrag.h"
+#include "MCLogger.h"
+#include "zlib/zlib.h"
+
+
+
+
+
+// An array of 4096 zero bytes, used for writing the padding
+static const Byte g_Zeroes[4096] = {0};
+
+
+
+
+
+int main(int argc, char ** argv)
+{
+ new cMCLogger(Printf("Defrag_%08x.log", time(NULL)));
+ cMCADefrag Defrag;
+ if (!Defrag.Init(argc, argv))
+ {
+ return 1;
+ }
+
+ Defrag.Run();
+
+ return 0;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMCADefrag:
+
+cMCADefrag::cMCADefrag(void) :
+ m_NumThreads(4),
+ m_ShouldRecompress(true)
+{
+}
+
+
+
+
+
+bool cMCADefrag::Init(int argc, char ** argv)
+{
+ // Nothing needed yet
+ return true;
+}
+
+
+
+
+
+void cMCADefrag::Run(void)
+{
+ // Fill the queue with MCA files
+ m_Queue = cFile::GetFolderContents(".");
+
+ // Start the processing threads:
+ for (int i = 0; i < m_NumThreads; i++)
+ {
+ StartThread();
+ }
+
+ // Wait for all the threads to finish:
+ while (!m_Threads.empty())
+ {
+ m_Threads.front()->Wait();
+ delete m_Threads.front();
+ m_Threads.pop_front();
+ }
+}
+
+
+
+
+void cMCADefrag::StartThread(void)
+{
+ cThread * Thread = new cThread(*this);
+ m_Threads.push_back(Thread);
+ Thread->Start();
+}
+
+
+
+
+
+AString cMCADefrag::GetNextFileName(void)
+{
+ cCSLock Lock(m_CS);
+ if (m_Queue.empty())
+ {
+ return AString();
+ }
+ AString res = m_Queue.back();
+ m_Queue.pop_back();
+ return res;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cMCADefrag::cThread:
+
+cMCADefrag::cThread::cThread(cMCADefrag & a_Parent) :
+ super("MCADefrag thread"),
+ m_Parent(a_Parent),
+ m_IsChunkUncompressed(false)
+{
+}
+
+
+
+
+
+void cMCADefrag::cThread::Execute(void)
+{
+ for (;;)
+ {
+ AString FileName = m_Parent.GetNextFileName();
+ if (FileName.empty())
+ {
+ return;
+ }
+ ProcessFile(FileName);
+ }
+}
+
+
+
+
+
+void cMCADefrag::cThread::ProcessFile(const AString & a_FileName)
+{
+ // Filter out non-MCA files:
+ if ((a_FileName.length() < 4) || (a_FileName.substr(a_FileName.length() - 4, 4) != ".mca"))
+ {
+ return;
+ }
+ LOGINFO("%s", a_FileName.c_str());
+
+ // Open input and output files:
+ AString OutFileName = a_FileName + ".new";
+ cFile In, Out;
+ if (!In.Open(a_FileName, cFile::fmRead))
+ {
+ LOGWARNING("Cannot open file %s for reading, skipping file.", a_FileName.c_str());
+ return;
+ }
+ if (!Out.Open(OutFileName.c_str(), cFile::fmWrite))
+ {
+ LOGWARNING("Cannot open file %s for writing, skipping file.", OutFileName.c_str());
+ return;
+ }
+
+ // Read the Locations and Timestamps from the input file:
+ Byte Locations[4096];
+ UInt32 Timestamps[1024];
+ if (In.Read(Locations, sizeof(Locations)) != sizeof(Locations))
+ {
+ LOGWARNING("Cannot read Locations in file %s, skipping file.", a_FileName.c_str());
+ return;
+ }
+ if (In.Read(Timestamps, sizeof(Timestamps)) != sizeof(Timestamps))
+ {
+ LOGWARNING("Cannot read Timestamps in file %s, skipping file.", a_FileName.c_str());
+ return;
+ }
+
+ // Write dummy Locations to the Out file (will be overwritten once the correct ones are known)
+ if (Out.Write(Locations, sizeof(Locations)) != sizeof(Locations))
+ {
+ LOGWARNING("Cannot write Locations to file %s, skipping file.", OutFileName.c_str());
+ return;
+ }
+ m_CurrentSectorOut = 2;
+
+ // Write a copy of the Timestamps into the Out file:
+ if (Out.Write(Timestamps, sizeof(Timestamps)) != sizeof(Timestamps))
+ {
+ LOGWARNING("Cannot write Timestamps to file %s, skipping file.", OutFileName.c_str());
+ return;
+ }
+
+ // Process each chunk:
+ for (size_t i = 0; i < 1024; i++)
+ {
+ size_t idx = i * 4;
+ if (
+ (Locations[idx] == 0) &&
+ (Locations[idx + 1] == 0) &&
+ (Locations[idx + 2] == 0) &&
+ (Locations[idx + 3] == 0)
+ )
+ {
+ // Chunk not present
+ continue;
+ }
+ m_IsChunkUncompressed = false;
+ if (!ReadChunk(In, Locations + idx))
+ {
+ LOGWARNING("Cannot read chunk #%d from file %s. Skipping file.", i, a_FileName.c_str());
+ return;
+ }
+ if (!WriteChunk(Out, Locations + idx))
+ {
+ LOGWARNING("Cannot write chunk #%d to file %s. Skipping file.", i, OutFileName.c_str());
+ return;
+ }
+ }
+
+ // Write the new Locations into the MCA header:
+ Out.Seek(0);
+ if (Out.Write(Locations, sizeof(Locations)) != sizeof(Locations))
+ {
+ LOGWARNING("Cannot write updated Locations to file %s, skipping file.", OutFileName.c_str());
+ return;
+ }
+
+ // Close the files, delete orig, rename new:
+ In.Close();
+ Out.Close();
+ cFile::Delete(a_FileName);
+ cFile::Rename(OutFileName, a_FileName);
+}
+
+
+
+
+
+bool cMCADefrag::cThread::ReadChunk(cFile & a_File, const Byte * a_LocationRaw)
+{
+ int SectorNum = (a_LocationRaw[0] << 16) | (a_LocationRaw[1] << 8) | a_LocationRaw[2];
+ int SizeInSectors = a_LocationRaw[3] * (4 KiB);
+ if (a_File.Seek(SectorNum * (4 KiB)) < 0)
+ {
+ LOGWARNING("Failed to seek to chunk data - file pos %llu (%d KiB, %.02f MiB)!", (Int64)SectorNum * (4 KiB), SectorNum * 4, ((double)SectorNum) / 256);
+ return false;
+ }
+
+ // Read the exact size:
+ Byte Buf[4];
+ if (a_File.Read(Buf, 4) != 4)
+ {
+ LOGWARNING("Failed to read chunk data length");
+ return false;
+ }
+ m_CompressedChunkDataSize = (Buf[0] << 24) | (Buf[1] << 16) | (Buf[2] << 8) | Buf[3];
+ if (m_CompressedChunkDataSize > SizeInSectors)
+ {
+ LOGWARNING("Invalid chunk data - SizeInSectors (%d) smaller that RealSize (%d)", SizeInSectors, m_CompressedChunkDataSize);
+ return false;
+ }
+
+ // Read the data:
+ if (a_File.Read(m_CompressedChunkData, m_CompressedChunkDataSize) != m_CompressedChunkDataSize)
+ {
+ LOGWARNING("Failed to read chunk data!");
+ return false;
+ }
+
+ // Uncompress the data if recompression is active
+ if (m_Parent.m_ShouldRecompress)
+ {
+ m_IsChunkUncompressed = UncompressChunk();
+ if (!m_IsChunkUncompressed)
+ {
+ LOGINFO("Chunk failed to uncompress, will be copied verbatim instead.");
+ }
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cMCADefrag::cThread::WriteChunk(cFile & a_File, Byte * a_LocationRaw)
+{
+ // Recompress the data if recompression is active:
+ if (m_Parent.m_ShouldRecompress)
+ {
+ if (!CompressChunk())
+ {
+ LOGINFO("Chunk failed to recompress, will be coped verbatim instead.");
+ }
+ }
+
+ // Update the Location:
+ a_LocationRaw[0] = m_CurrentSectorOut >> 16;
+ a_LocationRaw[1] = (m_CurrentSectorOut >> 8) & 0xff;
+ a_LocationRaw[2] = m_CurrentSectorOut & 0xff;
+ a_LocationRaw[3] = (m_CompressedChunkDataSize + (4 KiB) + 3) / (4 KiB); // +3 because the m_CompressedChunkDataSize doesn't include the exact-length
+ m_CurrentSectorOut += a_LocationRaw[3];
+
+ // Write the data length:
+ Byte Buf[4];
+ Buf[0] = m_CompressedChunkDataSize >> 24;
+ Buf[1] = (m_CompressedChunkDataSize >> 16) & 0xff;
+ Buf[2] = (m_CompressedChunkDataSize >> 8) & 0xff;
+ Buf[3] = m_CompressedChunkDataSize & 0xff;
+ if (a_File.Write(Buf, 4) != 4)
+ {
+ LOGWARNING("Failed to write chunk length!");
+ return false;
+ }
+
+ // Write the data:
+ if (a_File.Write(m_CompressedChunkData, m_CompressedChunkDataSize) != m_CompressedChunkDataSize)
+ {
+ LOGWARNING("Failed to write chunk data!");
+ return false;
+ }
+
+ // Pad onto the next sector:
+ int NumPadding = a_LocationRaw[3] * 4096 - (m_CompressedChunkDataSize + 4);
+ ASSERT(NumPadding >= 0);
+ if ((NumPadding > 0) && (a_File.Write(g_Zeroes, NumPadding) != NumPadding))
+ {
+ LOGWARNING("Failed to write padding");
+ return false;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cMCADefrag::cThread::UncompressChunk(void)
+{
+ switch (m_CompressedChunkData[0])
+ {
+ case COMPRESSION_GZIP: return UncompressChunkGzip();
+ case COMPRESSION_ZLIB: return UncompressChunkZlib();
+ }
+ LOGINFO("Chunk is compressed with in an unknown algorithm");
+ return false;
+}
+
+
+
+
+
+bool cMCADefrag::cThread::UncompressChunkGzip(void)
+{
+ // TODO
+ // This format is not used in practice
+ return false;
+}
+
+
+
+
+
+bool cMCADefrag::cThread::UncompressChunkZlib(void)
+{
+ // Uncompress the data:
+ z_stream strm;
+ strm.zalloc = (alloc_func)NULL;
+ strm.zfree = (free_func)NULL;
+ strm.opaque = NULL;
+ inflateInit(&strm);
+ strm.next_out = m_RawChunkData;
+ strm.avail_out = sizeof(m_RawChunkData);
+ strm.next_in = m_CompressedChunkData + 1; // The first byte is the compression method, skip it
+ strm.avail_in = m_CompressedChunkDataSize;
+ int res = inflate(&strm, Z_FINISH);
+ inflateEnd(&strm);
+ if (res != Z_STREAM_END)
+ {
+ LOGWARNING("Failed to uncompress chunk data: %s", strm.msg);
+ return false;
+ }
+ m_RawChunkDataSize = strm.total_out;
+
+ return true;
+}
+
+
+
+
+
+bool cMCADefrag::cThread::CompressChunk(void)
+{
+ // Check that the compressed data can fit:
+ uLongf CompressedSize = compressBound(m_RawChunkDataSize);
+ if (CompressedSize > sizeof(m_CompressedChunkData))
+ {
+ LOGINFO("Too much data for the internal compression buffer!");
+ return false;
+ }
+
+ // Compress the data using the highest compression factor:
+ int errorcode = compress2(m_CompressedChunkData + 1, &CompressedSize, m_RawChunkData, m_RawChunkDataSize, Z_BEST_COMPRESSION);
+ if (errorcode != Z_OK)
+ {
+ LOGINFO("Recompression failed: %d", errorcode);
+ return false;
+ }
+ m_CompressedChunkData[0] = COMPRESSION_ZLIB;
+ m_CompressedChunkDataSize = CompressedSize + 1;
+ return true;
+}
+
+
+
+
diff --git a/Tools/MCADefrag/MCADefrag.h b/Tools/MCADefrag/MCADefrag.h
new file mode 100644
index 000000000..d7fa1fc6e
--- /dev/null
+++ b/Tools/MCADefrag/MCADefrag.h
@@ -0,0 +1,144 @@
+
+// MCADefrag.h
+
+// Interfaces to the cMCADefrag class encapsulating the entire app
+
+
+
+
+
+#pragma once
+
+
+
+
+
+
+class cMCADefrag
+{
+public:
+ enum
+ {
+ MAX_COMPRESSED_CHUNK_DATA_SIZE = (1 MiB),
+ MAX_RAW_CHUNK_DATA_SIZE = (100 MiB),
+ } ;
+
+ cMCADefrag(void);
+
+ /** Reads the cmdline params and initializes the app.
+ Returns true if the app should continue, false if not. */
+ bool Init(int argc, char ** argv);
+
+ /** Runs the entire app. */
+ void Run(void);
+
+protected:
+ /** A single thread processing MCA files from the queue */
+ class cThread :
+ public cIsThread
+ {
+ typedef cIsThread super;
+
+ public:
+ cThread(cMCADefrag & a_Parent);
+
+ protected:
+ /** The compression methods, as specified by the MCA compression method byte. */
+ enum
+ {
+ COMPRESSION_GZIP = 1,
+ COMPRESSION_ZLIB = 2,
+ } ;
+
+
+ cMCADefrag & m_Parent;
+
+ /** The current compressed chunk data. Valid after a successful ReadChunk().
+ This contains only the compression method byte and the compressed data,
+ but not the exact-length preceding the data in the MCA file. */
+ unsigned char m_CompressedChunkData[MAX_COMPRESSED_CHUNK_DATA_SIZE];
+
+ /** Size of the actual current compressed chunk data, excluding the 4 exact-length bytes.
+ This is the amount of bytes in m_CompressedChunkData[] that are valid. */
+ int m_CompressedChunkDataSize;
+
+ /** The current raw chunk data. Valid after a successful ReadChunk(), if recompression is active. */
+ unsigned char m_RawChunkData[MAX_RAW_CHUNK_DATA_SIZE];
+
+ /** Size of the actual current raw chunk data. */
+ int m_RawChunkDataSize;
+
+ /** Number of the sector where the next chunk will be written by WriteChunk(). */
+ int m_CurrentSectorOut;
+
+ /** Set to true when the chunk has been successfully uncompressed. Only used if recompression is active.
+ WriteChunk() tests this flag to decide whether to call Compress(). */
+ bool m_IsChunkUncompressed;
+
+
+ /** Processes the specified file. */
+ void ProcessFile(const AString & a_FileName);
+
+ /** Reads the chunk data into m_CompressedChunkData.
+ Calls DecompressChunkData() if recompression is active.
+ a_LocationRaw is the pointer to the first byte of the Location data in the MCA header.
+ Returns true if successful. */
+ bool ReadChunk(cFile & a_File, const Byte * a_LocationRaw);
+
+ /** Writes the chunk data from m_CompressedData or m_RawChunkData (performing recompression) into file.
+ Calls CompressChunkData() for the actual compression, if recompression is active.
+ a_LocationRaw is the pointer to the first byte of the Location data to be put into the MCA header,
+ the chunk's location is stored in that memory area. Updates m_CurrentSectorOut.
+ Returns true if successful. */
+ bool WriteChunk(cFile & a_File, Byte * a_LocationRaw);
+
+ /** Uncompresses the chunk data from m_CompressedChunkData into m_RawChunkData.
+ Returns true if successful, false on failure. */
+ bool UncompressChunk(void);
+
+ /** Uncompresses the chunk data from m_CompressedChunkData into m_RawChunkData, using Gzip.
+ Returns true if successful, false on failure. */
+ bool UncompressChunkGzip(void);
+
+ /** Uncompresses the chunk data from m_CompressedChunkData into m_RawChunkData, using Zlib.
+ Returns true if successful, false on failure. */
+ bool UncompressChunkZlib(void);
+
+ /** Compresses the chunk data from m_RawChunkData into m_CompressedChunkData.
+ Returns true if successful, false on failure. */
+ bool CompressChunk(void);
+
+ // cIsThread overrides:
+ virtual void Execute(void) override;
+ } ;
+
+ typedef std::list<cThread *> cThreads;
+
+
+ /** The mutex protecting m_Files agains multithreaded access. */
+ cCriticalSection m_CS;
+
+ /** The queue of MCA files to be processed by the threads. Protected by m_CS. */
+ AStringVector m_Queue;
+
+ /** List of threads that the server has running. */
+ cThreads m_Threads;
+
+ /** The number of threads that should be started. Configurable on the command line. */
+ int m_NumThreads;
+
+ /** If set to true, the chunk data is recompressed while saving each MCA file. */
+ bool m_ShouldRecompress;
+
+
+ /** Starts a new processing thread and adds it to cThreads. */
+ void StartThread(void);
+
+ /** Retrieves one file from the queue (and removes it from the queue).
+ Returns an empty string when queue empty. */
+ AString GetNextFileName(void);
+} ;
+
+
+
+
diff --git a/src/Bindings/LuaChunkStay.cpp b/src/Bindings/LuaChunkStay.cpp
new file mode 100644
index 000000000..0e982637f
--- /dev/null
+++ b/src/Bindings/LuaChunkStay.cpp
@@ -0,0 +1,173 @@
+
+// LuaChunkStay.cpp
+
+// Implements the cLuaChunkStay class representing a cChunkStay binding for plugins, used by cWorld:ChunkStay() Lua API
+
+#include "Globals.h"
+#include "LuaChunkStay.h"
+#include "PluginLua.h"
+#include "../World.h"
+
+
+
+
+
+cLuaChunkStay::cLuaChunkStay(cPluginLua & a_Plugin) :
+ m_Plugin(a_Plugin),
+ m_LuaState(NULL)
+{
+}
+
+
+
+
+
+bool cLuaChunkStay::AddChunks(int a_ChunkCoordTableStackPos)
+{
+ // This function is expected to be called just once, with all the coords in a table
+ ASSERT(m_Chunks.empty());
+
+ cPluginLua::cOperation Op(m_Plugin);
+ cLuaState & L = Op();
+
+ // Check that we got a table:
+ if (!lua_istable(L, a_ChunkCoordTableStackPos))
+ {
+ LOGWARNING("%s: The parameter is not a table of coords (got %s). Ignoring the call.",
+ __FUNCTION__, lua_typename(L, lua_type(L, a_ChunkCoordTableStackPos))
+ );
+ L.LogStackTrace();
+ return false;
+ }
+
+ // Add each set of coords:
+ int NumChunks = luaL_getn(L, a_ChunkCoordTableStackPos);
+ m_Chunks.reserve(NumChunks);
+ for (int idx = 1; idx <= NumChunks; idx++)
+ {
+ // Push the idx-th element of the array onto stack top, check that it's a table:
+ lua_rawgeti(L, a_ChunkCoordTableStackPos, idx);
+ if (!lua_istable(L, -1))
+ {
+ LOGWARNING("%s: Element #%d is not a table (got %s). Ignoring the element.",
+ __FUNCTION__, idx, lua_typename(L, -1)
+ );
+ L.LogStackTrace();
+ lua_pop(L, 1);
+ continue;
+ }
+ AddChunkCoord(L, idx);
+ lua_pop(L, 1);
+ }
+
+ // If there are no chunks, log a warning and return failure:
+ if (m_Chunks.empty())
+ {
+ LOGWARNING("%s: Zero chunks to stay.", __FUNCTION__);
+ L.LogStackTrace();
+ return false;
+ }
+
+ // All ok
+ return true;
+}
+
+
+
+
+
+void cLuaChunkStay::AddChunkCoord(cLuaState & L, int a_Index)
+{
+ // Check that the element has 2 coords:
+ int NumCoords = luaL_getn(L, -1);
+ if (NumCoords != 2)
+ {
+ LOGWARNING("%s: Element #%d doesn't contain 2 coords (got %d). Ignoring the element.",
+ __FUNCTION__, a_Index, NumCoords
+ );
+ return;
+ }
+
+ // Read the two coords from the element:
+ lua_rawgeti(L, -1, 1);
+ lua_rawgeti(L, -2, 2);
+ int ChunkX = luaL_checkint(L, -2);
+ int ChunkZ = luaL_checkint(L, -1);
+ lua_pop(L, 2);
+
+ // Check that a coord is not yet present:
+ for (cChunkCoordsVector::iterator itr = m_Chunks.begin(), end = m_Chunks.end(); itr != end; ++itr)
+ {
+ if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
+ {
+ LOGWARNING("%s: Element #%d is a duplicate, ignoring it.",
+ __FUNCTION__, a_Index
+ );
+ return;
+ }
+ } // for itr - m_Chunks[]
+
+ m_Chunks.push_back(cChunkCoords(ChunkX, ZERO_CHUNK_Y, ChunkZ));
+}
+
+
+
+
+
+void cLuaChunkStay::Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPos, int a_OnAllChunksAvailableStackPos)
+{
+ // Get the references to the callback functions:
+ m_LuaState = &m_Plugin.GetLuaState();
+ m_OnChunkAvailable.RefStack(*m_LuaState, a_OnChunkAvailableStackPos);
+ m_OnAllChunksAvailable.RefStack(*m_LuaState, a_OnAllChunksAvailableStackPos);
+
+ // Enable the ChunkStay:
+ super::Enable(a_ChunkMap);
+}
+
+
+
+
+
+void cLuaChunkStay::OnChunkAvailable(int a_ChunkX, int a_ChunkZ)
+{
+ // DEBUG:
+ LOGD("LuaChunkStay: Chunk [%d, %d] is now available, calling the callback...", a_ChunkX, a_ChunkZ);
+
+ cPluginLua::cOperation Op(m_Plugin);
+ Op().Call((int)m_OnChunkAvailable, a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+bool cLuaChunkStay::OnAllChunksAvailable(void)
+{
+ {
+ // Call the callback:
+ cPluginLua::cOperation Op(m_Plugin);
+ Op().Call((int)m_OnAllChunksAvailable);
+
+ // Remove the callback references - they won't be needed anymore
+ m_OnChunkAvailable.UnRef();
+ m_OnAllChunksAvailable.UnRef();
+ }
+
+ // Disable the ChunkStay by returning true
+ return true;
+}
+
+
+
+
+
+void cLuaChunkStay::OnDisabled(void)
+{
+ // This object is no longer needed, delete it
+ delete this;
+}
+
+
+
+
diff --git a/src/Bindings/LuaChunkStay.h b/src/Bindings/LuaChunkStay.h
new file mode 100644
index 000000000..49ab9a0ad
--- /dev/null
+++ b/src/Bindings/LuaChunkStay.h
@@ -0,0 +1,73 @@
+
+// LuaChunkStay.h
+
+// Declares the cLuaChunkStay class representing a cChunkStay binding for plugins, used by cWorld:ChunkStay() Lua API
+
+
+
+
+
+#pragma once
+
+#include "LuaState.h"
+#include "../ChunkStay.h"
+
+
+
+
+
+// fwd:
+class cPluginLua;
+
+
+
+
+
+class cLuaChunkStay
+ : public cChunkStay
+{
+ typedef cChunkStay super;
+
+public:
+ cLuaChunkStay(cPluginLua & a_Plugin);
+
+ ~cLuaChunkStay() { }
+
+ /** Adds chunks in the specified on-stack Lua table.
+ Returns true if any chunk added, false (plus log warning) if none. */
+ bool AddChunks(int a_ChunkCoordTableStackPos);
+
+ /** Enables the ChunkStay for the specified chunkmap, with the specified Lua callbacks. */
+ void Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPos, int a_OnAllChunksAvailableStackPos);
+
+protected:
+ /** The plugin which has created the ChunkStay, via cWorld:ChunkStay() binding method. */
+ cPluginLua & m_Plugin;
+
+ /** The Lua state associated with the callbacks. Only valid when enabled. */
+ cLuaState * m_LuaState;
+
+ /** The Lua function to call in OnChunkAvailable. Only valid when enabled. */
+ cLuaState::cRef m_OnChunkAvailable;
+
+ /** The Lua function to call in OnAllChunksAvailable. Only valid when enabled. */
+ cLuaState::cRef m_OnAllChunksAvailable;
+
+
+ // cChunkStay overrides:
+ virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override;
+ virtual bool OnAllChunksAvailable(void) override;
+ virtual void OnDisabled(void) override;
+
+ /** Adds a single chunk coord from the table at the top of the Lua stack.
+ Expects the top element to be a table, checks that it contains two numbers.
+ Uses those two numbers as chunk coords appended to m_Chunks.
+ If the coords are already present, gives a warning and ignores the pair.
+ The a_Index parameter is only for the error messages. */
+ void AddChunkCoord(cLuaState & a_LuaState, int a_Index);
+} ;
+
+
+
+
+
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index ac5ce47e1..c6be7be3c 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -735,17 +735,20 @@ bool cLuaState::CallFunction(int a_NumResults)
ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); // The function to call
ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 2)); // The error handler
- int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, -m_NumCurrentFunctionArgs - 2);
+ // Save the current "stack" state and reset, in case the callback calls another function:
+ AString CurrentFunctionName;
+ std::swap(m_CurrentFunctionName, CurrentFunctionName);
+ int NumArgs = m_NumCurrentFunctionArgs;
+ m_NumCurrentFunctionArgs = -1;
+
+ // Call the function:
+ int s = lua_pcall(m_LuaState, NumArgs, a_NumResults, -NumArgs - 2);
if (s != 0)
{
// The error has already been printed together with the stacktrace
- LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), m_CurrentFunctionName.c_str());
- m_NumCurrentFunctionArgs = -1;
- m_CurrentFunctionName.clear();
+ LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), CurrentFunctionName.c_str());
return false;
}
- m_NumCurrentFunctionArgs = -1;
- m_CurrentFunctionName.clear();
// Remove the error handler from the stack:
lua_remove(m_LuaState, -a_NumResults - 1);
@@ -941,10 +944,42 @@ bool cLuaState::CheckParamFunction(int a_StartParam, int a_EndParam)
lua_Debug entry;
VERIFY(lua_getstack(m_LuaState, 0, &entry));
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
- AString ErrMsg = Printf("Error in function '%s' parameter #%d. Function expected, got %s",
+ luaL_error(m_LuaState, "Error in function '%s' parameter #%d. Function expected, got %s",
+ (entry.name != NULL) ? entry.name : "?", i, GetTypeText(i).c_str()
+ );
+ return false;
+ } // for i - Param
+
+ // All params checked ok
+ return true;
+}
+
+
+
+
+
+bool cLuaState::CheckParamFunctionOrNil(int a_StartParam, int a_EndParam)
+{
+ ASSERT(IsValid());
+
+ if (a_EndParam < 0)
+ {
+ a_EndParam = a_StartParam;
+ }
+
+ for (int i = a_StartParam; i <= a_EndParam; i++)
+ {
+ if (lua_isfunction(m_LuaState, i) || lua_isnil(m_LuaState, i))
+ {
+ continue;
+ }
+ // Not the correct parameter
+ lua_Debug entry;
+ VERIFY(lua_getstack(m_LuaState, 0, &entry));
+ VERIFY(lua_getinfo (m_LuaState, "n", &entry));
+ luaL_error(m_LuaState, "Error in function '%s' parameter #%d. Function expected, got %s",
(entry.name != NULL) ? entry.name : "?", i, GetTypeText(i).c_str()
);
- LogStackTrace();
return false;
} // for i - Param
@@ -1206,7 +1241,7 @@ void cLuaState::LogStack(const char * a_Header)
void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header)
{
LOGD((a_Header != NULL) ? a_Header : "Lua C API Stack contents:");
- for (int i = lua_gettop(a_LuaState); i >= 0; i--)
+ for (int i = lua_gettop(a_LuaState); i > 0; i--)
{
AString Value;
int Type = lua_type(a_LuaState, i);
@@ -1240,13 +1275,21 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cLuaState::cRef:
+cLuaState::cRef::cRef(void) :
+ m_LuaState(NULL),
+ m_Ref(LUA_REFNIL)
+{
+}
+
+
+
+
+
cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) :
- m_LuaState(a_LuaState)
+ m_LuaState(NULL),
+ m_Ref(LUA_REFNIL)
{
- ASSERT(m_LuaState.IsValid());
-
- lua_pushvalue(m_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack
- m_Ref = luaL_ref(m_LuaState, LUA_REGISTRYINDEX);
+ RefStack(a_LuaState, a_StackPos);
}
@@ -1255,12 +1298,42 @@ cLuaState::cRef::cRef(cLuaState & a_LuaState, int a_StackPos) :
cLuaState::cRef::~cRef()
{
- ASSERT(m_LuaState.IsValid());
+ if (m_LuaState != NULL)
+ {
+ UnRef();
+ }
+}
+
+
+
+
+
+void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos)
+{
+ ASSERT(a_LuaState.IsValid());
+ if (m_LuaState != NULL)
+ {
+ UnRef();
+ }
+ m_LuaState = &a_LuaState;
+ lua_pushvalue(a_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack
+ m_Ref = luaL_ref(a_LuaState, LUA_REGISTRYINDEX);
+}
+
+
+
+
+
+void cLuaState::cRef::UnRef(void)
+{
+ ASSERT(m_LuaState->IsValid()); // The reference should be destroyed before destroying the LuaState
if (IsValid())
{
- luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref);
+ luaL_unref(*m_LuaState, LUA_REGISTRYINDEX, m_Ref);
}
+ m_LuaState = NULL;
+ m_Ref = LUA_REFNIL;
}
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index dda45bb28..b9bf10142 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -65,14 +65,27 @@ class cLuaState
{
public:
- /** Used for storing references to object in the global registry */
+ /** Used for storing references to object in the global registry.
+ Can be bound (contains a reference) or unbound (doesn't contain reference).
+ The reference can also be reset by calling RefStack(). */
class cRef
{
public:
+ /** Creates an unbound reference object. */
+ cRef(void);
+
/** Creates a reference in the specified LuaState for object at the specified StackPos */
cRef(cLuaState & a_LuaState, int a_StackPos);
+
~cRef();
+ /** Creates a reference to Lua object at the specified stack pos, binds this object to it.
+ Calls UnRef() first if previously bound to another reference. */
+ void RefStack(cLuaState & a_LuaState, int a_StackPos);
+
+ /** Removes the bound reference, resets the object to Unbound state. */
+ void UnRef(void);
+
/** Returns true if the reference is valid */
bool IsValid(void) const {return (m_Ref != LUA_REFNIL); }
@@ -80,7 +93,7 @@ public:
operator int(void) const { return m_Ref; }
protected:
- cLuaState & m_LuaState;
+ cLuaState * m_LuaState;
int m_Ref;
} ;
@@ -820,6 +833,9 @@ public:
/** Returns true if the specified parameters on the stack are functions; also logs warning if not */
bool CheckParamFunction(int a_StartParam, int a_EndParam = -1);
+ /** Returns true if the specified parameters on the stack are functions or nils; also logs warning if not */
+ bool CheckParamFunctionOrNil(int a_StartParam, int a_EndParam = -1);
+
/** Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) */
bool CheckParamEnd(int a_Param);
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 841ec5cf2..2a7631120 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -8,6 +8,7 @@
#include "PluginLua.h"
#include "PluginManager.h"
#include "LuaWindow.h"
+#include "LuaChunkStay.h"
#include "../Root.h"
#include "../World.h"
#include "../Entities/Player.h"
@@ -1638,6 +1639,51 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S)
+static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
+{
+ /* Function signature:
+ World:ChunkStay(ChunkCoordTable, OnChunkAvailable, OnAllChunksAvailable)
+ ChunkCoordTable == { {Chunk1x, Chunk1z}, {Chunk2x, Chunk2z}, ... }
+ */
+
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType (1, "cWorld") ||
+ !L.CheckParamTable (2) ||
+ !L.CheckParamFunctionOrNil(3, 4)
+ )
+ {
+ return 0;
+ }
+
+ cPluginLua * Plugin = GetLuaPlugin(tolua_S);
+ if (Plugin == NULL)
+ {
+ return 0;
+ }
+ cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin);
+
+ // Read the params:
+ cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, NULL);
+ if (World == NULL)
+ {
+ LOGWARNING("World:ChunkStay(): invalid world parameter");
+ L.LogStackTrace();
+ return 0;
+ }
+ if (!ChunkStay->AddChunks(2))
+ {
+ return 0;
+ }
+
+ ChunkStay->Enable(*World->GetChunkMap(), 3, 4);
+ return 0;
+}
+
+
+
+
+
static int tolua_cPlayer_GetGroups(lua_State* tolua_S)
{
cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
@@ -2360,6 +2406,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cWorld");
+ tolua_function(tolua_S, "ChunkStay", tolua_cWorld_ChunkStay);
tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index ad0cfbe5a..a177f5288 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -35,7 +35,33 @@ class cPluginLua :
public:
// tolua_end
- cPluginLua( const AString & a_PluginDirectory );
+ /** A RAII-style mutex lock for accessing the internal LuaState.
+ This will be the only way to retrieve the plugin's LuaState;
+ therefore it directly supports accessing the LuaState of the locked plugin.
+ Usage:
+ cPluginLua::cOperation Op(SomePlugin);
+ Op().Call(...) // Call a function in the plugin's LuaState
+ */
+ class cOperation
+ {
+ public:
+ cOperation(cPluginLua & a_Plugin) :
+ m_Plugin(a_Plugin),
+ m_Lock(a_Plugin.m_CriticalSection)
+ {
+ }
+
+ cLuaState & operator ()(void) { return m_Plugin.m_LuaState; }
+
+ protected:
+ cPluginLua & m_Plugin;
+
+ /** RAII lock for m_Plugin.m_CriticalSection */
+ cCSLock m_Lock;
+ } ;
+
+
+ cPluginLua(const AString & a_PluginDirectory);
~cPluginLua();
virtual void OnDisable(void) override;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 944150a44..6ba952f92 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -20,60 +20,60 @@ if (NOT MSVC)
set(BINDING_DEPENDECIES
${CMAKE_CURRENT_SOURCE_DIR}/Bindings/virtual_method_hooks.lua
${CMAKE_CURRENT_SOURCE_DIR}/Bindings/AllToLua.pkg
- ChunkDef.h
- BiomeDef.h
- OSSupport/File.h
Bindings/LuaFunctions.h
- Bindings/PluginManager.h
+ Bindings/LuaWindow.h
Bindings/Plugin.h
Bindings/PluginLua.h
+ Bindings/PluginManager.h
Bindings/WebPlugin.h
- Bindings/LuaWindow.h
+ BiomeDef.h
+ BlockArea.h
+ BlockEntities/BlockEntity.h
+ BlockEntities/BlockEntityWithItems.h
+ BlockEntities/ChestEntity.h
+ BlockEntities/DispenserEntity.h
+ BlockEntities/DropSpenserEntity.h
+ BlockEntities/DropperEntity.h
+ BlockEntities/FurnaceEntity.h
+ BlockEntities/HopperEntity.h
+ BlockEntities/JukeboxEntity.h
+ BlockEntities/NoteEntity.h
+ BlockEntities/SignEntity.h
BlockID.h
- StringUtils.h
- Defines.h
+ BoundingBox.h
ChatColor.h
+ ChunkDef.h
ClientHandle.h
+ CraftingRecipes.h
+ Cuboid.h
+ Defines.h
+ Enchantments.h
+ Entities/Effects.h
Entities/Entity.h
Entities/Floater.h
Entities/Pawn.h
- Entities/Player.h
Entities/Pickup.h
+ Entities/Player.h
Entities/ProjectileEntity.h
Entities/TNTEntity.h
- Entities/Effects.h
- Server.h
- World.h
+ Generating/ChunkDesc.h
+ Group.h
Inventory.h
- Enchantments.h
Item.h
ItemGrid.h
- BlockEntities/BlockEntity.h
- BlockEntities/BlockEntityWithItems.h
- BlockEntities/ChestEntity.h
- BlockEntities/DropSpenserEntity.h
- BlockEntities/DispenserEntity.h
- BlockEntities/DropperEntity.h
- BlockEntities/FurnaceEntity.h
- BlockEntities/HopperEntity.h
- BlockEntities/JukeboxEntity.h
- BlockEntities/NoteEntity.h
- BlockEntities/SignEntity.h
- WebAdmin.h
- Root.h
- Vector3f.h
- Vector3d.h
- Vector3i.h
Matrix4f.h
- Cuboid.h
- BoundingBox.h
+ Mobs/Monster.h
+ OSSupport/File.h
+ Root.h
+ Server.h
+ StringUtils.h
Tracer.h
- Group.h
- BlockArea.h
- Generating/ChunkDesc.h
- CraftingRecipes.h
UI/Window.h
- Mobs/Monster.h
+ Vector3d.h
+ Vector3f.h
+ Vector3i.h
+ WebAdmin.h
+ World.h
)
include_directories(Bindings)
@@ -91,7 +91,18 @@ if (NOT MSVC)
DEPENDS ${BINDING_DEPENDECIES}
)
#add cpp files here
- add_library(Bindings Bindings/PluginManager Bindings/LuaState Bindings/WebPlugin Bindings/Bindings Bindings/ManualBindings Bindings/LuaWindow Bindings/Plugin Bindings/PluginLua Bindings/WebPlugin)
+ add_library(Bindings
+ Bindings/Bindings
+ Bindings/LuaChunkStay
+ Bindings/LuaState
+ Bindings/LuaWindow
+ Bindings/ManualBindings
+ Bindings/Plugin
+ Bindings/PluginLua
+ Bindings/PluginManager
+ Bindings/WebPlugin
+ Bindings/WebPlugin
+ )
target_link_libraries(Bindings lua sqlite tolualib)
@@ -143,6 +154,7 @@ else ()
"${PATH}/*.cpp"
"${PATH}/*.h"
"${PATH}/*.rc"
+ "${PATH}/*.pkg"
)
source_group("${PATH}" FILES ${FOLDER_FILES})
endfunction(includefolder)
@@ -154,6 +166,7 @@ else ()
file(GLOB_RECURSE SOURCE
"*.cpp"
"*.h"
+ "*.pkg"
)
include_directories("${PROJECT_SOURCE_DIR}")
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index b48bfe65e..3028d24d0 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -422,7 +422,6 @@ bool cChunk::HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ)
-/// Sets or resets the internal flag that prevents chunk from being unloaded
void cChunk::Stay(bool a_Stay)
{
m_StayCount += (a_Stay ? 1 : -1);
diff --git a/src/Chunk.h b/src/Chunk.h
index 3dc83b157..93eba217e 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -85,10 +85,10 @@ public:
void MarkLoaded(void); // Marks the chunk as freshly loaded. Fails if the chunk is already valid
void MarkLoadFailed(void); // Marks the chunk as failed to load. Ignored is the chunk is already valid
- /// Gets all chunk data, calls the a_Callback's methods for each data type
+ /** Gets all chunk data, calls the a_Callback's methods for each data type */
void GetAllData(cChunkDataCallback & a_Callback);
- /// Sets all chunk data
+ /** Sets all chunk data */
void SetAllData(
const BLOCKTYPE * a_BlockTypes,
const NIBBLETYPE * a_BlockMeta,
@@ -104,27 +104,29 @@ public:
const cChunkDef::BlockNibbles & a_SkyLight
);
- /// Copies m_BlockData into a_BlockTypes, only the block types
+ /** Copies m_BlockData into a_BlockTypes, only the block types */
void GetBlockTypes(BLOCKTYPE * a_BlockTypes);
- /// Writes the specified cBlockArea at the coords specified. Note that the coords may extend beyond the chunk!
+ /** Writes the specified cBlockArea at the coords specified. Note that the coords may extend beyond the chunk! */
void WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes);
- /// Returns true if there is a block entity at the coords specified
+ /** Returns true if there is a block entity at the coords specified */
bool HasBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// Sets or resets the internal flag that prevents chunk from being unloaded
+ /** Sets or resets the internal flag that prevents chunk from being unloaded.
+ The flag is cumulative - it can be set multiple times and then needs to be un-set that many times
+ before the chunk is unloadable again. */
void Stay(bool a_Stay = true);
- /// Recence all mobs proximities to players in order to know what to do with them
+ /** Recence all mobs proximities to players in order to know what to do with them */
void CollectMobCensus(cMobCensus& toFill);
- /// Try to Spawn Monsters inside chunk
+ /** Try to Spawn Monsters inside chunk */
void SpawnMobs(cMobSpawner& a_MobSpawner);
void Tick(float a_Dt);
- /// Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks
+ /** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */
void TickBlock(int a_RelX, int a_RelY, int a_RelZ);
int GetPosX(void) const { return m_PosX; }
@@ -137,13 +139,13 @@ public:
// SetBlock() does a lot of work (heightmap, tickblocks, blockentities) so a BlockIdx version doesn't make sense
void SetBlock( const Vector3i & a_RelBlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta ) { SetBlock( a_RelBlockPos.x, a_RelBlockPos.y, a_RelBlockPos.z, a_BlockType, a_BlockMeta ); }
- /// Queues a block change till the specified world tick
+ /** Queues a block change till the specified world tick */
void QueueSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Int64 a_Tick, BLOCKTYPE a_PreviousBlockType = E_BLOCK_AIR);
- /// Queues block for ticking (m_ToTickQueue)
+ /** Queues block for ticking (m_ToTickQueue) */
void QueueTickBlock(int a_RelX, int a_RelY, int a_RelZ);
- /// Queues all 6 neighbors of the specified block for ticking (m_ToTickQueue). If any are outside the chunk, relays the checking to the proper neighboring chunk
+ /** Queues all 6 neighbors of the specified block for ticking (m_ToTickQueue). If any are outside the chunk, relays the checking to the proper neighboring chunk */
void QueueTickBlockNeighbors(int a_RelX, int a_RelY, int a_RelZ);
void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta ); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc.
@@ -175,14 +177,14 @@ public:
void CollectPickupsByPlayer(cPlayer * a_Player);
- /// Sets the sign text. Returns true if successful. Also sends update packets to all clients in the chunk
+ /** Sets the sign text. Returns true if successful. Also sends update packets to all clients in the chunk */
bool SetSignLines(int a_RelX, int a_RelY, int a_RelZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
int GetHeight( int a_X, int a_Z );
void SendBlockTo(int a_RelX, int a_RelY, int a_RelZ, cClientHandle * a_Client);
- /// Adds a client to the chunk; returns true if added, false if already there
+ /** Adds a client to the chunk; returns true if added, false if already there */
bool AddClient (cClientHandle* a_Client );
void RemoveClient (cClientHandle* a_Client );
@@ -193,55 +195,55 @@ public:
void RemoveEntity(cEntity * a_Entity);
bool HasEntity(int a_EntityID);
- /// Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true
+ /** Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible
- /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found.
+ /** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. */
bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult); // Lua-accessible
- /// Calls the callback for each block entity; returns true if all block entities processed, false if the callback aborted by returning true
+ /** Calls the callback for each block entity; returns true if all block entities processed, false if the callback aborted by returning true */
bool ForEachBlockEntity(cBlockEntityCallback & a_Callback); // Lua-accessible
- /// Calls the callback for each chest; returns true if all chests processed, false if the callback aborted by returning true
+ /** Calls the callback for each chest; returns true if all chests processed, false if the callback aborted by returning true */
bool ForEachChest(cChestCallback & a_Callback); // Lua-accessible
- /// Calls the callback for each dispenser; returns true if all dispensers processed, false if the callback aborted by returning true
+ /** Calls the callback for each dispenser; returns true if all dispensers processed, false if the callback aborted by returning true */
bool ForEachDispenser(cDispenserCallback & a_Callback);
- /// Calls the callback for each dropper; returns true if all droppers processed, false if the callback aborted by returning true
+ /** Calls the callback for each dropper; returns true if all droppers processed, false if the callback aborted by returning true */
bool ForEachDropper(cDropperCallback & a_Callback);
- /// Calls the callback for each dropspenser; returns true if all dropspensers processed, false if the callback aborted by returning true
+ /** Calls the callback for each dropspenser; returns true if all dropspensers processed, false if the callback aborted by returning true */
bool ForEachDropSpenser(cDropSpenserCallback & a_Callback);
- /// Calls the callback for each furnace; returns true if all furnaces processed, false if the callback aborted by returning true
+ /** Calls the callback for each furnace; returns true if all furnaces processed, false if the callback aborted by returning true */
bool ForEachFurnace(cFurnaceCallback & a_Callback); // Lua-accessible
- /// Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found
+ /** Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found */
bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback); // Lua-acessible
- /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found
+ /** Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found */
bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible
- /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found
+ /** Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found */
bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback);
- /// Calls the callback for the dispenser at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found
+ /** Calls the callback for the dispenser at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found */
bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback);
- /// Calls the callback for the dispenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found
+ /** Calls the callback for the dispenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found */
bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback);
- /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found
+ /** Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found */
bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible
- /// Calls the callback for the noteblock at the specified coords; returns false if there's no noteblock at those coords or callback returns true, returns true if found
+ /** Calls the callback for the noteblock at the specified coords; returns false if there's no noteblock at those coords or callback returns true, returns true if found */
bool DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBlockCallback & a_Callback);
- /// Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found
+ /** Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found */
bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback);
- /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
+ /** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible
void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // [x, y, z] in world block coords
@@ -292,7 +294,7 @@ public:
m_IsSaving = false;
}
- /// Sets the blockticking to start at the specified block. Only one blocktick may be set, second call overwrites the first call
+ /** Sets the blockticking to start at the specified block. Only one blocktick may be set, second call overwrites the first call */
inline void SetNextBlockTick(int a_RelX, int a_RelY, int a_RelZ)
{
m_BlockTickX = a_RelX;
@@ -310,34 +312,34 @@ public:
inline NIBBLETYPE GetBlockLight(int a_Idx) const {return cChunkDef::GetNibble(m_BlockLight, a_Idx); }
inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx); }
- /// Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ /** Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
- /// Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ /** Same as GetBlockType(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
bool UnboundedRelGetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType) const;
- /// Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ /** Same as GetBlockMeta(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
bool UnboundedRelGetBlockMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockMeta) const;
- /// Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ /** Same as GetBlockBlockLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
bool UnboundedRelGetBlockBlockLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight) const;
- /// Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ /** Same as GetBlockSkyLight(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
bool UnboundedRelGetBlockSkyLight(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_SkyLight) const;
- /// Queries both BlockLight and SkyLight, relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ /** Queries both BlockLight and SkyLight, relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
bool UnboundedRelGetBlockLights(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE & a_BlockLight, NIBBLETYPE & a_SkyLight) const;
- /// Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ /** Same as SetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
bool UnboundedRelSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- /// Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success
+ /** Same as FastSetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
bool UnboundedRelFastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- /// Same as QueueTickBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s in such a case), ignores unsuccessful attempts
+ /** Same as QueueTickBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s in such a case), ignores unsuccessful attempts */
void UnboundedQueueTickBlock(int a_RelX, int a_RelY, int a_RelZ);
- /// Light alterations based on time
+ /** Light alterations based on time */
NIBBLETYPE GetTimeAlteredLight(NIBBLETYPE a_Skylight) const;
@@ -389,7 +391,7 @@ private:
cEntityList m_Entities;
cBlockEntityList m_BlockEntities;
- /// Number of times the chunk has been requested to stay (by various cChunkStay objects); if zero, the chunk can be unloaded
+ /** Number of times the chunk has been requested to stay (by various cChunkStay objects); if zero, the chunk can be unloaded */
int m_StayCount;
int m_PosX, m_PosY, m_PosZ;
@@ -427,40 +429,40 @@ private:
void RemoveBlockEntity(cBlockEntity * a_BlockEntity);
void AddBlockEntity (cBlockEntity * a_BlockEntity);
- /// Creates a block entity for each block that needs a block entity and doesn't have one in the list
+ /** Creates a block entity for each block that needs a block entity and doesn't have one in the list */
void CreateBlockEntities(void);
- /// Wakes up each simulator for its specific blocks; through all the blocks in the chunk
+ /** Wakes up each simulator for its specific blocks; through all the blocks in the chunk */
void WakeUpSimulators(void);
// Makes a copy of the list
cClientHandleList GetAllClients(void) const {return m_LoadedByClient; }
- /// Sends m_PendingSendBlocks to all clients
+ /** Sends m_PendingSendBlocks to all clients */
void BroadcastPendingBlockChanges(void);
- /// Checks the block scheduled for checking in m_ToTickBlocks[]
+ /** Checks the block scheduled for checking in m_ToTickBlocks[] */
void CheckBlocks();
- /// Ticks several random blocks in the chunk
+ /** Ticks several random blocks in the chunk */
void TickBlocks(void);
- /// Adds snow to the top of snowy biomes and hydrates farmland / fills cauldrons in rainy biomes
+ /** Adds snow to the top of snowy biomes and hydrates farmland / fills cauldrons in rainy biomes */
void ApplyWeatherToTop(void);
- /// Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking)
+ /** Grows sugarcane by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) */
void GrowSugarcane (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks);
- /// Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking)
+ /** Grows cactus by the specified number of blocks, but no more than 3 blocks high (used by both bonemeal and ticking) */
void GrowCactus (int a_RelX, int a_RelY, int a_RelZ, int a_NumBlocks);
- /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem)
+ /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem) */
void GrowMelonPumpkin(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, MTRand & a_Random);
- /// Called by Tick() when an entity moves out of this chunk into a neighbor; moves the entity and sends spawn / despawn packet to clients
+ /** Called by Tick() when an entity moves out of this chunk into a neighbor; moves the entity and sends spawn / despawn packet to clients */
void MoveEntityToNewChunk(cEntity * a_Entity);
- /// Processes all blocks that have been scheduled for replacement by the QueueSetBlock() function
+ /** Processes all blocks that have been scheduled for replacement by the QueueSetBlock() function */
void ProcessQueuedSetBlocks(void);
};
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
index 1c4aa6aca..f48dc4fd5 100644
--- a/src/ChunkDef.h
+++ b/src/ChunkDef.h
@@ -481,6 +481,7 @@ public:
} ;
typedef std::list<cChunkCoords> cChunkCoordsList;
+typedef std::vector<cChunkCoords> cChunkCoordsVector;
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 6acea903b..0c5a8d9b9 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -134,7 +134,7 @@ cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk(int a_ChunkX, int a_ChunkZ)
-cChunkPtr cChunkMap::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ )
+cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
// No need to lock m_CSLayers, since it's already locked by the operation that called us
ASSERT(m_CSLayers.IsLockedByCurrentThread());
@@ -897,17 +897,34 @@ void cChunkMap::SetChunkData(
bool a_MarkDirty
)
{
- cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
- if (Chunk == NULL)
- {
- return;
- }
- Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_BiomeMap, a_BlockEntities);
-
- if (a_MarkDirty)
{
- Chunk->MarkDirty();
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ if (Chunk == NULL)
+ {
+ return;
+ }
+ Chunk->SetAllData(a_BlockTypes, a_BlockMeta, a_BlockLight, a_BlockSkyLight, a_HeightMap, a_BiomeMap, a_BlockEntities);
+
+ if (a_MarkDirty)
+ {
+ Chunk->MarkDirty();
+ }
+
+ // Notify relevant ChunkStays:
+ for (cChunkStays::iterator itr = m_ChunkStays.begin(); itr != m_ChunkStays.end(); )
+ {
+ if ((*itr)->ChunkAvailable(a_ChunkX, a_ChunkZ))
+ {
+ cChunkStays::iterator cur = itr;
+ ++itr;
+ m_ChunkStays.erase(cur);
+ }
+ else
+ {
+ ++itr;
+ }
+ } // for itr - m_ChunkStays[]
}
// Notify plugins of the chunk becoming available
@@ -2206,24 +2223,6 @@ bool cChunkMap::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const ASt
-void cChunkMap::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay)
-{
- cCSLock Lock(m_CSLayers);
- for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr)
- {
- cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
- if (Chunk == NULL)
- {
- continue;
- }
- Chunk->Stay(a_Stay);
- }
-}
-
-
-
-
-
void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
@@ -2810,12 +2809,16 @@ void cChunkMap::cChunkLayer::UnloadUnusedChunks(void)
-void cChunkMap::FastSetBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+void cChunkMap::FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
cCSLock Lock(m_CSFastSetBlock);
- m_FastSetBlockQueue.push_back(sSetBlock(a_X, a_Y, a_Z, a_BlockType, a_BlockMeta));
+ m_FastSetBlockQueue.push_back(sSetBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta));
}
+
+
+
+
void cChunkMap::FastSetQueuedBlocks()
{
// Asynchronously set blocks:
@@ -2834,110 +2837,75 @@ void cChunkMap::FastSetQueuedBlocks()
}
-///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cChunkStay:
-
-cChunkStay::cChunkStay(cWorld * a_World) :
- m_World(a_World),
- m_IsEnabled(false)
-{
-}
-
-
-
-
-
-cChunkStay::~cChunkStay()
-{
- Clear();
-}
-
-
-
-
-
-void cChunkStay::Clear(void)
-{
- if (m_IsEnabled)
- {
- Disable();
- }
- m_Chunks.clear();
-}
-
-
-void cChunkStay::Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cChunkMap::AddChunkStay(cChunkStay & a_ChunkStay)
{
- ASSERT(!m_IsEnabled);
-
- for (cChunkCoordsList::const_iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ cCSLock Lock(m_CSLayers);
+
+ // Add it to the list:
+ ASSERT(std::find(m_ChunkStays.begin(), m_ChunkStays.end(), &a_ChunkStay) == m_ChunkStays.end()); // Has not yet been added
+ m_ChunkStays.push_back(&a_ChunkStay);
+
+ // Schedule all chunks to be loaded / generated, and mark each as locked:
+ const cChunkCoordsVector & WantedChunks = a_ChunkStay.GetChunks();
+ for (cChunkCoordsVector::const_iterator itr = WantedChunks.begin(); itr != WantedChunks.end(); ++itr)
{
- if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
+ cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ if (Chunk == NULL)
{
- // Already present
- return;
+ continue;
}
- } // for itr - Chunks[]
- m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
+ Chunk->Stay(true);
+ if (Chunk->IsValid())
+ {
+ a_ChunkStay.ChunkAvailable(itr->m_ChunkX, itr->m_ChunkZ);
+ }
+ } // for itr - WantedChunks[]
}
-void cChunkStay::Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+/** Removes the specified cChunkStay descendant from the internal list of ChunkStays. */
+void cChunkMap::DelChunkStay(cChunkStay & a_ChunkStay)
{
- ASSERT(!m_IsEnabled);
-
- for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ cCSLock Lock(m_CSLayers);
+
+ // Remove from the list of active chunkstays:
+ bool HasFound = false;
+ for (cChunkStays::iterator itr = m_ChunkStays.begin(), end = m_ChunkStays.end(); itr != end; ++itr)
{
- if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
+ if (*itr == &a_ChunkStay)
{
- // Found, un-"stay"
- m_Chunks.erase(itr);
- return;
+ m_ChunkStays.erase(itr);
+ HasFound = true;
+ break;
}
- } // for itr - m_Chunks[]
-}
-
-
-
-
-
-void cChunkStay::Enable(void)
-{
- ASSERT(!m_IsEnabled);
+ } // for itr - m_ChunkStays[]
- m_World->ChunksStay(*this, true);
- m_IsEnabled = true;
-}
-
-
-
-
-
-void cChunkStay::Load(void)
-{
- for (cChunkCoordsList::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ if (!HasFound)
{
- m_World->TouchChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
- } // for itr - m_Chunks[]
+ ASSERT(!"Removing a cChunkStay that hasn't been added!");
+ return;
+ }
+
+ // Unmark all contained chunks:
+ const cChunkCoordsVector & Chunks = a_ChunkStay.GetChunks();
+ for (cChunkCoordsVector::const_iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr)
+ {
+ cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ if (Chunk == NULL)
+ {
+ continue;
+ }
+ Chunk->Stay(false);
+ } // for itr - Chunks[]
}
-void cChunkStay::Disable(void)
-{
- ASSERT(m_IsEnabled);
-
- m_World->ChunksStay(*this, false);
- m_IsEnabled = false;
-}
-
-
-
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 62c74d81c..d713d0cf5 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -85,19 +85,19 @@ public:
void BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL);
void BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ );
- /// Sends the block entity, if it is at the coords specified, to a_Client
+ /** Sends the block entity, if it is at the coords specified, to a_Client */
void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client);
- /// a_Player rclked block entity at the coords specified, handle it
+ /** a_Player rclked block entity at the coords specified, handle it */
void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z);
- /// 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
+ /** 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);
- /// Wakes up simulators for the specified block
+ /** Wakes up simulators for the specified block */
void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// Wakes up the simulators for the specified area of blocks
+ /** Wakes up the simulators for the specified area of blocks */
void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ);
void MarkChunkDirty (int a_ChunkX, int a_ChunkZ);
@@ -130,7 +130,7 @@ public:
bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback);
- /// Copies the chunk's blocktypes into a_Blocks; returns true if successful
+ /** Copies the chunk's blocktypes into a_Blocks; returns true if successful */
bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks);
bool IsChunkValid (int a_ChunkX, int a_ChunkZ);
@@ -153,154 +153,151 @@ public:
bool GetBlockTypeMeta (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
bool GetBlockInfo (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight);
- /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType
+ /** Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType */
void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType);
- /// Special function used for growing trees, replaces only blocks that tree may overwrite
+ /** Special function used for growing trees, replaces only blocks that tree may overwrite */
void ReplaceTreeBlocks(const sSetBlockVector & a_Blocks);
EMCSBiome GetBiomeAt (int a_BlockX, int a_BlockZ);
- /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read.
+ /** Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. */
bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure);
bool DigBlock (int a_X, int a_Y, int a_Z);
void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player);
- /// Compares clients of two chunks, calls the callback accordingly
+ /** Compares clients of two chunks, calls the callback accordingly */
void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback);
- /// Compares clients of two chunks, calls the callback accordingly
+ /** Compares clients of two chunks, calls the callback accordingly */
void CompareChunkClients(cChunk * a_Chunk1, cChunk * a_Chunk2, cClientDiffCallback & a_Callback);
- /// Adds client to a chunk, if not already present; returns true if added, false if present
+ /** Adds client to a chunk, if not already present; returns true if added, false if present */
bool AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
- /// Removes the client from the chunk
+ /** Removes the client from the chunk */
void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
- /// Removes the client from all chunks it is present in
+ /** Removes the client from all chunks it is present in */
void RemoveClientFromChunks(cClientHandle * a_Client);
- /// Adds the entity to its appropriate chunk, takes ownership of the entity pointer
+ /** Adds the entity to its appropriate chunk, takes ownership of the entity pointer */
void AddEntity(cEntity * a_Entity);
- /// Returns true if the entity with specified ID is present in the chunks
+ /** Returns true if the entity with specified ID is present in the chunks */
bool HasEntity(int a_EntityID);
- /// Removes the entity from its appropriate chunk
+ /** Removes the entity from its appropriate chunk */
void RemoveEntity(cEntity * a_Entity);
- /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true
+ /** Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible
- /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true
+ /** Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Lua-accessible
- /// Destroys and returns a list of blocks destroyed in the explosion at the specified coordinates
+ /** Destroys and returns a list of blocks destroyed in the explosion at the specified coordinates */
void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlockAffected);
- /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false.
+ /** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false. */
bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Lua-accessible
- /// Calls the callback for each block entity in the specified chunk; returns true if all block entities processed, false if the callback aborted by returning true
+ /** Calls the callback for each block entity in the specified chunk; returns true if all block entities processed, false if the callback aborted by returning true */
bool ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback); // Lua-accessible
- /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true
+ /** Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true */
bool ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Lua-accessible
- /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true
+ /** Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true */
bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback);
- /// Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true
+ /** Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true */
bool ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback);
- /// Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true
+ /** Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true */
bool ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback);
- /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true
+ /** Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true */
bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Lua-accessible
- /// Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found
+ /** Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found */
bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback); // Lua-acessible
- /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found
+ /** Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found */
bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible
- /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found
+ /** Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found */
bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Lua-accessible
- /// Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found
+ /** Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found */
bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); // Lua-accessible
- /// Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found
+ /** Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found */
bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); // Lua-accessible
- /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found
+ /** Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found */
bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible
- /// Calls the callback for the noteblock at the specified coords; returns false if there's no noteblock at those coords or callback returns true, returns true if found
+ /** Calls the callback for the noteblock at the specified coords; returns false if there's no noteblock at those coords or callback returns true, returns true if found */
bool DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBlockCallback & a_Callback); // Lua-accessible
- /// Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found
+ /** Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found */
bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Lua-accessible
- /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
+ /** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible
- /// Touches the chunk, causing it to be loaded or generated
+ /** Touches the chunk, causing it to be loaded or generated */
void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
- /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before)
+ /** Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) */
bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
- /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid()
+ /** Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() */
void LoadChunks(const cChunkCoordsList & a_Chunks);
- /// Marks the chunk as failed-to-load
+ /** Marks the chunk as failed-to-load */
void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
- /// Sets the sign text. Returns true if sign text changed.
+ /** Sets the sign text. Returns true if sign text changed. */
bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
- /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable; to be used only by cChunkStay!
- void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true);
-
- /// Marks the chunk as being regenerated - all its clients want that chunk again (used by cWorld::RegenerateChunk() )
+ /** Marks the chunk as being regenerated - all its clients want that chunk again (used by cWorld::RegenerateChunk() ) */
void MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ);
bool IsChunkLighted(int a_ChunkX, int a_ChunkZ);
- /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully
+ /** Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully */
bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback);
- /// Writes the block area into the specified coords. Returns true if all chunks have been processed. Prefer cBlockArea::Write() instead.
+ /** Writes the block area into the specified coords. Returns true if all chunks have been processed. Prefer cBlockArea::Write() instead. */
bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes);
- /// Returns the number of valid chunks and the number of dirty chunks
+ /** Returns the number of valid chunks and the number of dirty chunks */
void GetChunkStats(int & a_NumChunksValid, int & a_NumChunksDirty);
- /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem)
+ /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem) */
void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, MTRand & a_Rand);
- /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config
+ /** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config */
void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
- /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config
+ /** Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config */
void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
- /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call
+ /** Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call */
void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player
+ /** Make a Mob census, of all mobs, their family, their chunk and theyr distance to closest player */
void CollectMobCensus(cMobCensus& a_ToFill);
- /// Try to Spawn Monsters inside all Chunks
+ /** Try to Spawn Monsters inside all Chunks */
void SpawnMobs(cMobSpawner& a_MobSpawner);
void Tick(float a_Dt);
- /// Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks
+ /** Ticks a single block. Used by cWorld::TickQueuedBlocks() to tick the queued blocks */
void TickBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
void UnloadUnusedChunks(void);
@@ -312,15 +309,20 @@ public:
void ChunkValidated(void); // Called by chunks that have become valid
- /// Queues the specified block for ticking (block update)
+ /** Queues the specified block for ticking (block update) */
void QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// Returns the CS for locking the chunkmap; only cWorld::cLock may use this function!
+ /** Returns the CS for locking the chunkmap; only cWorld::cLock may use this function! */
cCriticalSection & GetCS(void) { return m_CSLayers; }
private:
- friend class cChunk; // The chunks can manipulate neighbors while in their Tick() method, using LockedGetBlock() and LockedSetBlock()
+ // The chunks can manipulate neighbors while in their Tick() method, using LockedGetBlock() and LockedSetBlock()
+ friend class cChunk;
+
+ // The chunkstay can (de-)register itself using AddChunkStay() and DelChunkStay()
+ friend class cChunkStay;
+
class cChunkLayer
{
@@ -328,10 +330,10 @@ private:
cChunkLayer(int a_LayerX, int a_LayerZ, cChunkMap * a_Parent);
~cChunkLayer();
- /// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check
+ /** Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check */
cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ );
- /// Returns the specified chunk, or NULL if not created yet
+ /** Returns the specified chunk, or NULL if not created yet */
cChunk * FindChunk(int a_ChunkX, int a_ChunkZ);
int GetX(void) const {return m_LayerX; }
@@ -344,22 +346,22 @@ private:
void Save(void);
void UnloadUnusedChunks(void);
- /// Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player
+ /** Collect a mob census, of all mobs, their megatype, their chunk and their distance o closest player */
void CollectMobCensus(cMobCensus& a_ToFill);
- /// Try to Spawn Monsters inside all Chunks
+ /** Try to Spawn Monsters inside all Chunks */
void SpawnMobs(cMobSpawner& a_MobSpawner);
void Tick(float a_Dt);
void RemoveClient(cClientHandle * a_Client);
- /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true
+ /** Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible
- /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found.
+ /** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. */
bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackReturn); // Lua-accessible
- /// Returns true if there is an entity with the specified ID within this layer's chunks
+ /** Returns true if there is an entity with the specified ID within this layer's chunks */
bool HasEntity(int a_EntityID);
protected:
@@ -372,17 +374,19 @@ private:
};
typedef std::list<cChunkLayer *> cChunkLayerList;
+
+ typedef std::list<cChunkStay *> cChunkStays;
- /// Finds the cChunkLayer object responsible for the specified chunk; returns NULL if not found. Assumes m_CSLayers is locked.
+ /** Finds the cChunkLayer object responsible for the specified chunk; returns NULL if not found. Assumes m_CSLayers is locked. */
cChunkLayer * FindLayerForChunk(int a_ChunkX, int a_ChunkZ);
- /// Returns the specified cChunkLayer object; returns NULL if not found. Assumes m_CSLayers is locked.
+ /** Returns the specified cChunkLayer object; returns NULL if not found. Assumes m_CSLayers is locked. */
cChunkLayer * FindLayer(int a_LayerX, int a_LayerZ);
- /// Returns the cChunkLayer object responsible for the specified chunk; creates it if not found.
+ /** Returns the cChunkLayer object responsible for the specified chunk; creates it if not found. */
cChunkLayer * GetLayerForChunk (int a_ChunkX, int a_ChunkZ);
- /// Returns the specified cChunkLayer object; creates it if not found.
+ /** Returns the specified cChunkLayer object; creates it if not found. */
cChunkLayer * GetLayer(int a_LayerX, int a_LayerZ);
void RemoveLayer(cChunkLayer * a_Layer);
@@ -395,67 +399,42 @@ private:
cCriticalSection m_CSFastSetBlock;
sSetBlockList m_FastSetBlockQueue;
+
+ /** The cChunkStay descendants that are currently enabled in this chunkmap */
+ cChunkStays m_ChunkStays;
cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid
cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate
cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate
- /// Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load)
+ /** Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) */
bool LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
- /// Gets a block type in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load)
+ /** Gets a block type in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) */
bool LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType);
- /// Gets a block meta in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load)
+ /** Gets a block meta in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) */
bool LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE & a_BlockMeta);
- /// Sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load)
+ /** Sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) */
bool LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- /// Fast-sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load)
+ /** Fast-sets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) */
bool LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
- /// Locates a chunk ptr in the chunkmap; doesn't create it when not found; assumes m_CSLayers is locked. To be called only from cChunkMap.
+ /** Locates a chunk ptr in the chunkmap; doesn't create it when not found; assumes m_CSLayers is locked. To be called only from cChunkMap. */
cChunk * FindChunk(int a_ChunkX, int a_ChunkZ);
-};
-
-
-
-
-/** Makes chunks stay loaded until this object is cleared or destroyed
-Works by setting internal flags in the cChunk that it should not be unloaded.
-To optimize for speed, cChunkStay has an Enabled flag, it will "stay" the chunks only when enabled and it will refuse manipulations when enabled
-The object itself is not made thread-safe, it's supposed to be used from a single thread only.
-*/
-class cChunkStay
-{
-public:
- cChunkStay(cWorld * a_World);
- ~cChunkStay();
-
- void Clear(void);
-
- void Add(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
- void Remove(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
-
- void Enable(void);
- void Disable(void);
+ /** Adds a new cChunkStay descendant to the internal list of ChunkStays; loads its chunks.
+ To be used only by cChunkStay; others should use cChunkStay::Enable() instead */
+ void AddChunkStay(cChunkStay & a_ChunkStay);
- /// Queues each chunk in m_Chunks[] for loading / generating
- void Load(void);
+ /** Removes the specified cChunkStay descendant from the internal list of ChunkStays.
+ To be used only by cChunkStay; others should use cChunkStay::Disable() instead */
+ void DelChunkStay(cChunkStay & a_ChunkStay);
- // Allow cChunkStay be passed to functions expecting a const cChunkCoordsList &
- operator const cChunkCoordsList(void) const {return m_Chunks; }
-
-protected:
+};
- cWorld * m_World;
-
- bool m_IsEnabled;
-
- cChunkCoordsList m_Chunks;
-} ;
diff --git a/src/ChunkStay.cpp b/src/ChunkStay.cpp
new file mode 100644
index 000000000..6b1d5ee34
--- /dev/null
+++ b/src/ChunkStay.cpp
@@ -0,0 +1,137 @@
+
+// ChunkStay.cpp
+
+// Implements the cChunkStay class representing a base for classes that keep chunks loaded
+
+#include "Globals.h"
+#include "ChunkStay.h"
+#include "ChunkMap.h"
+
+
+
+
+
+cChunkStay::cChunkStay(void) :
+ m_ChunkMap(NULL)
+{
+}
+
+
+
+
+
+cChunkStay::~cChunkStay()
+{
+ Clear();
+}
+
+
+
+
+
+void cChunkStay::Clear(void)
+{
+ if (m_ChunkMap != NULL)
+ {
+ Disable();
+ }
+ m_Chunks.clear();
+}
+
+
+
+
+
+void cChunkStay::Add(int a_ChunkX, int a_ChunkZ)
+{
+ ASSERT(m_ChunkMap == NULL);
+
+ for (cChunkCoordsVector::const_iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ {
+ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
+ {
+ // Already present
+ return;
+ }
+ } // for itr - Chunks[]
+ m_Chunks.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
+}
+
+
+
+
+
+void cChunkStay::Remove(int a_ChunkX, int a_ChunkZ)
+{
+ ASSERT(m_ChunkMap == NULL);
+
+ for (cChunkCoordsVector::iterator itr = m_Chunks.begin(); itr != m_Chunks.end(); ++itr)
+ {
+ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
+ {
+ // Found, un-"stay"
+ m_Chunks.erase(itr);
+ return;
+ }
+ } // for itr - m_Chunks[]
+}
+
+
+
+
+
+void cChunkStay::Enable(cChunkMap & a_ChunkMap)
+{
+ ASSERT(m_ChunkMap == NULL);
+
+ m_OutstandingChunks = m_Chunks;
+ m_ChunkMap = &a_ChunkMap;
+ a_ChunkMap.AddChunkStay(*this);
+}
+
+
+
+
+
+void cChunkStay::Disable(void)
+{
+ ASSERT(m_ChunkMap != NULL);
+
+ m_ChunkMap->DelChunkStay(*this);
+ m_ChunkMap = NULL;
+}
+
+
+
+
+
+bool cChunkStay::ChunkAvailable(int a_ChunkX, int a_ChunkZ)
+{
+ // Check if this is a chunk that we want:
+ bool IsMine = false;
+ for (cChunkCoordsVector::iterator itr = m_OutstandingChunks.begin(), end = m_OutstandingChunks.end(); itr != end; ++itr)
+ {
+ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
+ {
+ m_OutstandingChunks.erase(itr);
+ IsMine = true;
+ break;
+ }
+ } // for itr - m_OutstandingChunks[]
+ if (!IsMine)
+ {
+ return false;
+ }
+
+ // Call the appropriate callbacks:
+ OnChunkAvailable(a_ChunkX, a_ChunkZ);
+ if (m_OutstandingChunks.empty())
+ {
+ return OnAllChunksAvailable();
+ }
+ return false;
+}
+
+
+
+
diff --git a/src/ChunkStay.h b/src/ChunkStay.h
new file mode 100644
index 000000000..2510cb490
--- /dev/null
+++ b/src/ChunkStay.h
@@ -0,0 +1,97 @@
+
+// ChunkStay.h
+
+/* Declares the cChunkStay class representing a base for classes that want to wait for certain chunks to load,
+then do some action on them. While the object is enabled, the chunks contained within are locked and will
+not unload
+*/
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd
+class cChunkMap;
+
+
+
+
+
+/** Makes chunks stay loaded until this object is cleared or destroyed
+Works by setting internal flags in the cChunk that it should not be unloaded.
+To optimize for speed, cChunkStay has an Enabled flag, it will "stay" the chunks only when enabled
+and it will refuse chunk-list manipulations when enabled.
+The object itself is not made thread-safe, it's supposed to be used from a single thread only.
+This class is abstract, the descendants are expected to provide the OnChunkAvailable() and
+the OnAllChunksAvailable() callback implementations. Note that those are called from the contexts of
+different threads' - the caller, the Loader or the Generator thread.
+*/
+class cChunkStay
+{
+public:
+ cChunkStay(void);
+ virtual ~cChunkStay();
+
+ void Clear(void);
+
+ /** Adds a chunk to be locked from unloading.
+ To be used only while the ChunkStay object is not enabled. */
+ void Add(int a_ChunkX, int a_ChunkZ);
+
+ /** Releases the chunk so that it's no longer locked from unloading.
+ To be used only while the ChunkStay object is not enabled. */
+ void Remove(int a_ChunkX, int a_ChunkZ);
+
+ /** Enables the ChunkStay on the specified chunkmap, causing it to load and generate chunks.
+ All the contained chunks are queued for loading / generating. */
+ void Enable (cChunkMap & a_ChunkMap);
+
+ /** Disables the ChunkStay, the chunks are released and the ChunkStay
+ object can be edited with Add() and Remove() again*/
+ virtual void Disable(void);
+
+ /** Returns all the chunks that should be kept */
+ const cChunkCoordsVector & GetChunks(void) const { return m_Chunks; }
+
+ /** Called when a specific chunk become available. */
+ virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) = 0;
+
+ /** Caled once all of the contained chunks are available.
+ If returns true, the ChunkStay is automatically disabled by the ChunkMap; if it returns false, the ChunkStay is kept. */
+ virtual bool OnAllChunksAvailable(void) = 0;
+
+ /** Called by the ChunkMap when the ChunkStay is disabled. The object may choose to delete itself. */
+ virtual void OnDisabled(void) = 0;
+
+protected:
+
+ friend class cChunkMap;
+
+
+ /** The chunkmap where the object is enabled.
+ Valid only after call to Enable() and before Disable(). */
+ cChunkMap * m_ChunkMap;
+
+ /** The list of chunks to lock from unloading. */
+ cChunkCoordsVector m_Chunks;
+
+ /** The chunks that still need loading */
+ cChunkCoordsVector m_OutstandingChunks;
+
+
+ /** Called by cChunkMap when a chunk is available, checks m_NumLoaded and triggers the appropriate callbacks.
+ May be called for chunks outside this ChunkStay.
+ Returns true if the ChunkStay is to be automatically disabled by the ChunkMap; returns false to keep the ChunkStay. */
+ bool ChunkAvailable(int a_ChunkX, int a_ChunkZ);
+} ;
+
+
+
+
+
diff --git a/src/Item.h b/src/Item.h
index 4782f31c1..cc3b3c961 100644
--- a/src/Item.h
+++ b/src/Item.h
@@ -32,7 +32,7 @@ namespace Json
class cItem
{
public:
- /// Creates an empty item
+ /** Creates an empty item */
cItem(void) :
m_ItemType(E_ITEM_EMPTY),
m_ItemCount(0),
@@ -43,7 +43,7 @@ public:
}
- /// Creates an item of the specified type, by default 1 piece with no damage and no enchantments
+ /** Creates an item of the specified type, by default 1 piece with no damage and no enchantments */
cItem(
short a_ItemType,
char a_ItemCount = 1,
@@ -55,9 +55,9 @@ public:
m_ItemType (a_ItemType),
m_ItemCount (a_ItemCount),
m_ItemDamage (a_ItemDamage),
+ m_Enchantments(a_Enchantments),
m_CustomName (a_CustomName),
- m_Lore (a_Lore),
- m_Enchantments(a_Enchantments)
+ m_Lore (a_Lore)
{
if (!IsValidItem(m_ItemType))
{
@@ -70,14 +70,14 @@ public:
}
- /// Creates an exact copy of the item
+ /** Creates an exact copy of the item */
cItem(const cItem & a_CopyFrom) :
m_ItemType (a_CopyFrom.m_ItemType),
m_ItemCount (a_CopyFrom.m_ItemCount),
m_ItemDamage (a_CopyFrom.m_ItemDamage),
+ m_Enchantments(a_CopyFrom.m_Enchantments),
m_CustomName (a_CopyFrom.m_CustomName),
- m_Lore (a_CopyFrom.m_Lore),
- m_Enchantments(a_CopyFrom.m_Enchantments)
+ m_Lore (a_CopyFrom.m_Lore)
{
}
@@ -106,8 +106,8 @@ public:
return ((m_ItemType <= 0) || (m_ItemCount <= 0));
}
- /* Returns true if this itemstack can stack with the specified stack (types match, enchantments etc.) ItemCounts are ignored!
- */
+ /* Returns true if this itemstack can stack with the specified stack (types match, enchantments etc.)
+ ItemCounts are ignored. */
bool IsEqual(const cItem & a_Item) const
{
return (
@@ -135,38 +135,38 @@ public:
bool IsCustomNameEmpty(void) const { return (m_CustomName.empty()); }
bool IsLoreEmpty(void) const { return (m_Lore.empty()); }
- /// Returns a copy of this item with m_ItemCount set to 1. Useful to preserve enchantments etc. on stacked items
+ /** Returns a copy of this item with m_ItemCount set to 1. Useful to preserve enchantments etc. on stacked items */
cItem CopyOne(void) const;
- /// Adds the specified count to this object and returns the reference to self (useful for chaining)
+ /** Adds the specified count to this object and returns the reference to self (useful for chaining) */
cItem & AddCount(char a_AmountToAdd);
- /// Returns the maximum damage value that this item can have; zero if damage is not applied
+ /** Returns the maximum damage value that this item can have; zero if damage is not applied */
short GetMaxDamage(void) const;
- /// Damages a weapon / tool. Returns true when damage reaches max value and the item should be destroyed
+ /** Damages a weapon / tool. Returns true when damage reaches max value and the item should be destroyed */
bool DamageItem(short a_Amount = 1);
inline bool IsDamageable(void) const { return (GetMaxDamage() > 0); }
- /// Returns true if the item is stacked up to its maximum stacking.
+ /** Returns true if the item is stacked up to its maximum stacking. */
bool IsFullStack(void) const;
- /// Returns the maximum amount of stacked items of this type.
+ /** Returns the maximum amount of stacked items of this type. */
char GetMaxStackSize(void) const;
// tolua_end
- /// Returns the cItemHandler responsible for this item type
+ /** Returns the cItemHandler responsible for this item type */
cItemHandler * GetHandler(void) const;
- /// Saves the item data into JSON representation
+ /** Saves the item data into JSON representation */
void GetJson(Json::Value & a_OutValue) const;
- /// Loads the item data from JSON representation
+ /** Loads the item data from JSON representation */
void FromJson(const Json::Value & a_Value);
- /// Returns true if the specified item type is enchantable (as per 1.2.5 protocol requirements)
+ /** Returns true if the specified item type is enchantable (as per 1.2.5 protocol requirements) */
static bool IsEnchantable(short a_ItemType); // tolua_export
// tolua_begin
@@ -193,7 +193,7 @@ class cItems // tolua_export
public:
// tolua_begin
- /// Need a Lua-accessible constructor
+ /** Need a Lua-accessible constructor */
cItems(void) {}
cItem * Get (int a_Idx);
@@ -216,7 +216,7 @@ public:
-/// Used to store loot probability tables
+/** Used to store loot probability tables */
class cLootProbab
{
public:
diff --git a/src/Items/ItemBoat.h b/src/Items/ItemBoat.h
index 31d1ca52e..a28ec8e22 100644
--- a/src/Items/ItemBoat.h
+++ b/src/Items/ItemBoat.h
@@ -1,4 +1,3 @@
-
// ItemBoat.h
// Declares the various boat ItemHandlers
@@ -31,7 +30,7 @@ public:
virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
{
- if (a_Dir != BLOCK_FACE_YM || a_Dir != BLOCK_FACE_NONE)
+ if ((a_Dir != BLOCK_FACE_YM) && (a_Dir != BLOCK_FACE_NONE))
{
return false;
}
diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp
index a823c08cc..9c81d004d 100644
--- a/src/LightingThread.cpp
+++ b/src/LightingThread.cpp
@@ -6,6 +6,7 @@
#include "Globals.h"
#include "LightingThread.h"
#include "ChunkMap.h"
+#include "ChunkStay.h"
#include "World.h"
@@ -109,9 +110,14 @@ void cLightingThread::Stop(void)
{
{
cCSLock Lock(m_CS);
- for (sItems::iterator itr = m_Queue.begin(), end = m_Queue.end(); itr != end; ++itr)
+ for (cChunkStays::iterator itr = m_PendingQueue.begin(), end = m_PendingQueue.end(); itr != end; ++itr)
{
- delete itr->m_ChunkStay;
+ delete *itr;
+ }
+ m_PendingQueue.clear();
+ for (cChunkStays::iterator itr = m_Queue.begin(), end = m_Queue.end(); itr != end; ++itr)
+ {
+ delete *itr;
}
m_Queue.clear();
}
@@ -129,25 +135,14 @@ void cLightingThread::QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback
{
ASSERT(m_World != NULL); // Did you call Start() properly?
- cChunkStay * ChunkStay = new cChunkStay(m_World);
- ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ + 1);
- ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ);
- ChunkStay->Add(a_ChunkX + 1, ZERO_CHUNK_Y, a_ChunkZ - 1);
- ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ + 1);
- ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
- ChunkStay->Add(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ - 1);
- ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ + 1);
- ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ);
- ChunkStay->Add(a_ChunkX - 1, ZERO_CHUNK_Y, a_ChunkZ - 1);
- ChunkStay->Enable();
- ChunkStay->Load();
- cCSLock Lock(m_CS);
- m_Queue.push_back(sItem(a_ChunkX, a_ChunkZ, ChunkStay, a_CallbackAfter));
- if (m_Queue.size() > WARN_ON_QUEUE_SIZE)
+ cChunkStay * ChunkStay = new cLightingChunkStay(*this, a_ChunkX, a_ChunkZ, a_CallbackAfter);
{
- LOGINFO("Lighting thread overloaded, %d items in queue", m_Queue.size());
+ // The ChunkStay will enqueue itself using the QueueChunkStay() once it is fully loaded
+ // In the meantime, put it into the PendingQueue so that it can be removed when stopping the thread
+ cCSLock Lock(m_CS);
+ m_PendingQueue.push_back(ChunkStay);
}
- m_evtItemAdded.Set();
+ ChunkStay->Enable(*m_World->GetChunkMap());
}
@@ -157,7 +152,7 @@ void cLightingThread::QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback
void cLightingThread::WaitForQueueEmpty(void)
{
cCSLock Lock(m_CS);
- while (!m_ShouldTerminate && (!m_Queue.empty() || !m_PostponedQueue.empty()))
+ while (!m_ShouldTerminate && (!m_Queue.empty() || !m_PendingQueue.empty()))
{
cCSUnlock Unlock(Lock);
m_evtQueueEmpty.Wait();
@@ -171,43 +166,7 @@ void cLightingThread::WaitForQueueEmpty(void)
size_t cLightingThread::GetQueueLength(void)
{
cCSLock Lock(m_CS);
- return m_Queue.size() + m_PostponedQueue.size();
-}
-
-
-
-
-
-void cLightingThread::ChunkReady(int a_ChunkX, int a_ChunkZ)
-{
- // Check all the items in the m_PostponedQueue, if the chunk is their neighbor, move the item to m_Queue
-
- bool NewlyAdded = false;
- {
- cCSLock Lock(m_CS);
- for (sItems::iterator itr = m_PostponedQueue.begin(); itr != m_PostponedQueue.end(); )
- {
- if (
- (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) &&
- (itr->z - a_ChunkZ >= -1) && (itr->z - a_ChunkZ <= 1)
- )
- {
- // It is a neighbor
- m_Queue.push_back(*itr);
- itr = m_PostponedQueue.erase(itr);
- NewlyAdded = true;
- }
- else
- {
- ++itr;
- }
- } // for itr - m_PostponedQueue[]
- } // Lock(m_CS)
-
- if (NewlyAdded)
- {
- m_evtItemAdded.Set(); // Notify the thread it has some work to do
- }
+ return m_Queue.size() + m_PendingQueue.size();
}
@@ -233,14 +192,14 @@ void cLightingThread::Execute(void)
}
// Process one items from the queue:
- sItem Item;
+ cLightingChunkStay * Item;
{
cCSLock Lock(m_CS);
if (m_Queue.empty())
{
continue;
}
- Item = m_Queue.front();
+ Item = (cLightingChunkStay *)m_Queue.front();
m_Queue.pop_front();
if (m_Queue.empty())
{
@@ -248,7 +207,7 @@ void cLightingThread::Execute(void)
}
} // CSLock(m_CS)
- LightChunk(Item);
+ LightChunk(*Item);
}
}
@@ -257,23 +216,11 @@ void cLightingThread::Execute(void)
-void cLightingThread::LightChunk(cLightingThread::sItem & a_Item)
+void cLightingThread::LightChunk(cLightingChunkStay & a_Item)
{
cChunkDef::BlockNibbles BlockLight, SkyLight;
- if (!ReadChunks(a_Item.x, a_Item.z))
- {
- // Neighbors not available. Re-queue in the postponed queue
- cCSLock Lock(m_CS);
- m_PostponedQueue.push_back(a_Item);
- return;
- }
-
- /*
- // DEBUG: torch somewhere:
- m_BlockTypes[19 + 24 * cChunkDef::Width * 3 + (m_HeightMap[24 + 24 * cChunkDef::Width * 3] / 2) * BlocksPerYLayer] = E_BLOCK_TORCH;
- // m_HeightMap[24 + 24 * cChunkDef::Width * 3]++;
- */
+ ReadChunks(a_Item.m_ChunkX, a_Item.m_ChunkZ);
PrepareBlockLight();
CalcLight(m_BlockLight);
@@ -339,13 +286,13 @@ void cLightingThread::LightChunk(cLightingThread::sItem & a_Item)
CompressLight(m_BlockLight, BlockLight);
CompressLight(m_SkyLight, SkyLight);
- m_World->ChunkLighted(a_Item.x, a_Item.z, BlockLight, SkyLight);
+ m_World->ChunkLighted(a_Item.m_ChunkX, a_Item.m_ChunkZ, BlockLight, SkyLight);
- if (a_Item.m_Callback != NULL)
+ if (a_Item.m_CallbackAfter != NULL)
{
- a_Item.m_Callback->Call(a_Item.x, a_Item.z);
+ a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ);
}
- delete a_Item.m_ChunkStay;
+ delete &a_Item;
}
@@ -561,3 +508,63 @@ void cLightingThread::CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_Ch
+
+void cLightingThread::QueueChunkStay(cLightingChunkStay & a_ChunkStay)
+{
+ // Move the ChunkStay from the Pending queue to the lighting queue.
+ {
+ cCSLock Lock(m_CS);
+ m_PendingQueue.remove(&a_ChunkStay);
+ m_Queue.push_back(&a_ChunkStay);
+ }
+ m_evtItemAdded.Set();
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cLightingThread::cLightingChunkStay:
+
+cLightingThread::cLightingChunkStay::cLightingChunkStay(cLightingThread & a_LightingThread, int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter) :
+ m_LightingThread(a_LightingThread),
+ m_ChunkX(a_ChunkX),
+ m_ChunkZ(a_ChunkZ),
+ m_CallbackAfter(a_CallbackAfter)
+{
+ Add(a_ChunkX + 1, a_ChunkZ + 1);
+ Add(a_ChunkX + 1, a_ChunkZ);
+ Add(a_ChunkX + 1, a_ChunkZ - 1);
+ Add(a_ChunkX, a_ChunkZ + 1);
+ Add(a_ChunkX, a_ChunkZ);
+ Add(a_ChunkX, a_ChunkZ - 1);
+ Add(a_ChunkX - 1, a_ChunkZ + 1);
+ Add(a_ChunkX - 1, a_ChunkZ);
+ Add(a_ChunkX - 1, a_ChunkZ - 1);
+}
+
+
+
+
+
+bool cLightingThread::cLightingChunkStay::OnAllChunksAvailable(void)
+{
+ m_LightingThread.QueueChunkStay(*this);
+
+ // Keep the ChunkStay alive:
+ return false;
+}
+
+
+
+
+
+void cLightingThread::cLightingChunkStay::OnDisabled(void)
+{
+ // Nothing needed in this callback
+}
+
+
+
+
diff --git a/src/LightingThread.h b/src/LightingThread.h
index d8ce59f01..81dd9d61f 100644
--- a/src/LightingThread.h
+++ b/src/LightingThread.h
@@ -33,6 +33,7 @@ Chunks from m_PostponedQueue are moved back into m_Queue when their neighbors ge
#include "OSSupport/IsThread.h"
#include "ChunkDef.h"
+#include "ChunkStay.h"
@@ -41,9 +42,6 @@ Chunks from m_PostponedQueue are moved back into m_Queue when their neighbors ge
// fwd: "cWorld.h"
class cWorld;
-// fwd: "cChunkMap.h"
-class cChunkStay;
-
@@ -62,43 +60,50 @@ public:
void Stop(void);
- /// Queues the entire chunk for lighting
+ /** Queues the entire chunk for lighting */
void QueueChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter = NULL);
- /// Blocks until the queue is empty or the thread is terminated
+ /** Blocks until the queue is empty or the thread is terminated */
void WaitForQueueEmpty(void);
size_t GetQueueLength(void);
- /// Called from cWorld when a chunk gets valid. Chunks in m_PostponedQueue may need moving into m_Queue
- void ChunkReady(int a_ChunkX, int a_ChunkZ);
-
protected:
- struct sItem
+ class cLightingChunkStay :
+ public cChunkStay
{
- int x, z;
- cChunkStay * m_ChunkStay;
- cChunkCoordCallback * m_Callback;
+ public:
+ cLightingThread & m_LightingThread;
+ int m_ChunkX;
+ int m_ChunkZ;
+ cChunkCoordCallback * m_CallbackAfter;
- sItem(void) {} // empty default constructor needed
- sItem(int a_X, int a_Z, cChunkStay * a_ChunkStay, cChunkCoordCallback * a_Callback) :
- x(a_X),
- z(a_Z),
- m_ChunkStay(a_ChunkStay),
- m_Callback(a_Callback)
- {
- }
+ cLightingChunkStay(cLightingThread & a_LightingThread, int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_CallbackAfter);
+
+ protected:
+ virtual void OnChunkAvailable(int a_ChunkX, int a_ChunkZ) override {}
+ virtual bool OnAllChunksAvailable(void) override;
+ virtual void OnDisabled(void) override;
} ;
- typedef std::list<sItem> sItems;
+ typedef std::list<cChunkStay *> cChunkStays;
+
- cWorld * m_World;
+ cWorld * m_World;
+
+ /** The mutex to protect m_Queue and m_PendingQueue */
cCriticalSection m_CS;
- sItems m_Queue;
- sItems m_PostponedQueue; // Chunks that have been postponed due to missing neighbors
- cEvent m_evtItemAdded; // Set when queue is appended, or to stop the thread
- cEvent m_evtQueueEmpty; // Set when the queue gets empty
+
+ /** The ChunkStays that are loaded and are waiting to be lit. */
+ cChunkStays m_Queue;
+
+ /** The ChunkStays that are waiting for load. Used for stopping the thread. */
+ cChunkStays m_PendingQueue;
+
+ cEvent m_evtItemAdded; // Set when queue is appended, or to stop the thread
+ cEvent m_evtQueueEmpty; // Set when the queue gets empty
+
// Buffers for the 3x3 chunk data
// These buffers alone are 1.7 MiB in size, therefore they cannot be located on the stack safely - some architectures may have only 1 MiB for stack, or even less
@@ -124,29 +129,29 @@ protected:
virtual void Execute(void) override;
- /// Lights the entire chunk. If neighbor chunks don't exist, touches them and re-queues the chunk
- void LightChunk(sItem & a_Item);
+ /** Lights the entire chunk. If neighbor chunks don't exist, touches them and re-queues the chunk */
+ void LightChunk(cLightingChunkStay & a_Item);
- /// Prepares m_BlockTypes and m_HeightMap data; returns false if any of the chunks fail. Zeroes out the light arrays
+ /** Prepares m_BlockTypes and m_HeightMap data; returns false if any of the chunks fail. Zeroes out the light arrays */
bool ReadChunks(int a_ChunkX, int a_ChunkZ);
- /// Uses m_HeightMap to initialize the m_SkyLight[] data; fills in seeds for the skylight
+ /** Uses m_HeightMap to initialize the m_SkyLight[] data; fills in seeds for the skylight */
void PrepareSkyLight(void);
- /// Uses m_BlockTypes to initialize the m_BlockLight[] data; fills in seeds for the blocklight
+ /** Uses m_BlockTypes to initialize the m_BlockLight[] data; fills in seeds for the blocklight */
void PrepareBlockLight(void);
- /// Calculates light in the light array specified, using stored seeds
+ /** Calculates light in the light array specified, using stored seeds */
void CalcLight(NIBBLETYPE * a_Light);
- /// Does one step in the light calculation - one seed propagation and seed recalculation
+ /** Does one step in the light calculation - one seed propagation and seed recalculation */
void CalcLightStep(
NIBBLETYPE * a_Light,
int a_NumSeedsIn, unsigned char * a_IsSeedIn, unsigned int * a_SeedIdxIn,
int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut
);
- /// Compresses from 1-block-per-byte (faster calc) into 2-blocks-per-byte (MC storage):
+ /** Compresses from 1-block-per-byte (faster calc) into 2-blocks-per-byte (MC storage): */
void CompressLight(NIBBLETYPE * a_LightArray, NIBBLETYPE * a_ChunkLight);
inline void PropagateLight(
@@ -174,6 +179,10 @@ protected:
}
}
+ /** Queues a chunkstay that has all of its chunks loaded.
+ Called by cLightingChunkStay when all of its chunks are loaded. */
+ void QueueChunkStay(cLightingChunkStay & a_ChunkStay);
+
} ;
diff --git a/src/World.cpp b/src/World.cpp
index 6090443a8..cb07caa5d 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -248,11 +248,11 @@ cWorld::cWorld(const AString & a_WorldName) :
m_SkyDarkness(0),
m_Weather(eWeather_Sunny),
m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :)
+ m_bCommandBlocksEnabled(false),
+ m_bUseChatPrefixes(true),
m_Scoreboard(this),
m_GeneratorCallbacks(*this),
- m_TickThread(*this),
- m_bCommandBlocksEnabled(false),
- m_bUseChatPrefixes(true)
+ m_TickThread(*this)
{
LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str());
@@ -2148,9 +2148,6 @@ void cWorld::SetChunkData(
{
m_ChunkSender.ChunkReady(a_ChunkX, a_ChunkZ);
}
-
- // Notify the lighting thread that the chunk has become valid (in case it is a neighbor of a postponed chunk):
- m_Lighting.ChunkReady(a_ChunkX, a_ChunkZ);
}
@@ -2573,15 +2570,6 @@ bool cWorld::SetCommandBlockCommand(int a_BlockX, int a_BlockY, int a_BlockZ, co
-void cWorld::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay)
-{
- m_ChunkMap->ChunksStay(a_Chunks, a_Stay);
-}
-
-
-
-
-
void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ)
{
m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ);
diff --git a/src/World.h b/src/World.h
index f22a8f49c..de4fa6ea6 100644
--- a/src/World.h
+++ b/src/World.h
@@ -330,9 +330,6 @@ public:
/** Sets the command block command. Returns true if command changed. */
bool SetCommandBlockCommand(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Command); // tolua_export
- /** Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable. To be used only by cChunkStay! */
- void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true);
-
/** Regenerate the given chunk: */
void RegenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export
@@ -816,6 +813,7 @@ private:
/** Whether command blocks are enabled or not */
bool m_bCommandBlocksEnabled;
+
/** Whether prefixes such as [INFO] are prepended to SendMessageXXX() / BroadcastChatXXX() functions */
bool m_bUseChatPrefixes;
@@ -859,7 +857,7 @@ private:
cWorld(const AString & a_WorldName);
- ~cWorld();
+ virtual ~cWorld();
void Tick(float a_Dt, int a_LastTickDurationMSec);
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index d72165c46..e95813a3c 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -1083,6 +1083,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadHorseFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
+ else if (strncmp(a_IDTag, "Villager", a_IDTagLength) == 0)
+ {
+ LoadVillagerFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
else if (strncmp(a_IDTag, "VillagerGolem", a_IDTagLength) == 0)
{
LoadIronGolemFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
@@ -1131,10 +1135,6 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadSquidFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
- else if (strncmp(a_IDTag, "Villager", a_IDTagLength) == 0)
- {
- LoadVillagerFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
- }
else if (strncmp(a_IDTag, "Witch", a_IDTagLength) == 0)
{
LoadWitchFromNBT(a_Entities, a_NBT, a_EntityTagIdx);