diff options
34 files changed, 1305 insertions, 106 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 54e1d097d..34aff5ddb 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -2118,6 +2118,7 @@ end QueueSaveAllChunks = { Params = "", Return = "", Notes = "Queues all chunks to be saved in the world storage thread" }, QueueSetBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta, TickDelay", Return = "", Notes = "Queues the block to be set to the specified blocktype and meta after the specified amount of game ticks. Uses SetBlock() for the actual setting, so simulators are woken up and block entities are handled correctly." }, QueueTask = { Params = "TaskFunction", Return = "", Notes = "Queues the specified function to be executed in the tick thread. This is the primary means of interaction with a cWorld from the WebAdmin page handlers (see {{WebWorldThreads}}). The function signature is <pre class=\"pretty-print lang-lua\">function()</pre>All return values from the function are ignored. Note that this function is actually called *after* the QueueTask() function returns. Note that it is unsafe to store references to MCServer objects, such as entities, across from the caller to the task handler function; store the EntityID instead." }, + QueueUnloadUnusedChunks = { Params = "", Return = "", Notes = "Queues a cTask that unloads chunks that are no longer needed and are saved." }, RegenerateChunk = { Params = "ChunkX, ChunkZ", Return = "", Notes = "Queues the specified chunk to be re-generated, overwriting the current data. To queue a chunk for generating only if it doesn't exist, use the GenerateChunk() instead." }, ScheduleTask = { Params = "DelayTicks, TaskFunction", Return = "", Notes = "Queues the specified function to be executed in the world's tick thread after a the specified number of ticks. This enables operations to be queued for execution in the future. The function signature is <pre class=\"pretty-print lang-lua\">function({{cWorld|World}})</pre>All return values from the function are ignored. Note that it is unsafe to store references to MCServer objects, such as entities, across from the caller to the task handler function; store the EntityID instead." }, SendBlockTo = { Params = "BlockX, BlockY, BlockZ, {{cPlayer|Player}}", Return = "", Notes = "Sends the block at the specified coords to the specified player's client, as an UpdateBlock packet." }, @@ -2147,7 +2148,6 @@ end SpawnExperienceOrb = { Params = "X, Y, Z, Reward", Return = "EntityID", Notes = "Spawns an {{cExpOrb|experience orb}} at the specified coords, with the given reward" }, SpawnPrimedTNT = { Params = "X, Y, Z, FuseTimeSecs, InitialVelocityCoeff", Return = "", Notes = "Spawns a {{cTNTEntity|primed TNT entity}} at the specified coords, with the given fuse time. The entity gets a random speed multiplied by the InitialVelocityCoeff, 1 being the default value." }, TryGetHeight = { Params = "BlockX, BlockZ", Return = "IsValid, Height", Notes = "Returns true and height of the highest non-air block if the chunk is loaded, or false otherwise." }, - UnloadUnusedChunks = { Params = "", Return = "", Notes = "Unloads chunks that are no longer needed, and are saved. NOTE: This API is deprecated and will be removed soon." }, UpdateSign = { Params = "X, Y, Z, Line1, Line2, Line3, Line4, [{{cPlayer|Player}}]", Return = "", Notes = "Sets the sign text at the specified coords. The sign-updating hooks are called for the change. The Player parameter is used to indicate the player from whom the change has come, it may be nil. Same as SetSignLiens()" }, UseBlockEntity = { Params = "{{cPlayer|Player}}, BlockX, BlockY, BlockZ", Return = "", Notes = "Makes the specified Player use the block entity at the specified coords (open chest UI, etc.) If the cords are in an unloaded chunk or there's no block entity, ignores the call." }, WakeUpSimulators = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Wakes up the simulators for the specified block." }, diff --git a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html index 1eec4842a..35c880b00 100644 --- a/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html +++ b/MCServer/Plugins/APIDump/Writing-a-MCServer-plugin.html @@ -20,13 +20,7 @@ <p> Let us begin. In order to begin development, we must firstly obtain a compiled copy of MCServer, and make sure that the Core plugin is within the Plugins folder, and activated. - Core handles much of the MCServer end-user experience and is a necessary component of - plugin development, as necessary plugin components depend on sone of its functions. - </p> - <p> - Next, we must obtain a copy of CoreMessaging.lua. This can be found - <a href="https://gist.github.com/bearbin/8715888">here.</a> - This is used to provide messaging support that is compliant with MCServer standards. + Core handles much of the MCServer end-user experience and gameplay will be very bland without it. </p> <h2>Creating the basic template</h2> <p> @@ -41,7 +35,11 @@ function Initialize(Plugin) Plugin:SetName("NewPlugin") Plugin:SetVersion(1) - PLUGIN = Plugin + -- Hooks + + PLUGIN = Plugin -- NOTE: only needed if you want OnDisable() to use GetName() or something like that + + -- Command Bindings LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion()) return true @@ -58,7 +56,8 @@ end <li><b>Plugin:SetName</b> sets the name of the plugin.</li> <li><b>Plugin:SetVersion</b> sets the revision number of the plugin. This must be an integer.</li> <li><b>LOG</b> logs to console a message, in this case, it prints that the plugin was initialised.</li> - <li>The <b>PLUGIN</b> variable just stores this plugin's object, so GetName() can be called in OnDisable (as no Plugin parameter is passed there, contrary to Initialize).</li> + <li>The <b>PLUGIN</b> variable just stores this plugin's object, so GetName() can be called in OnDisable (as no Plugin parameter is passed there, contrary to Initialize). + This global variable is only needed if you want to know the plugin details (name, etc.) when shutting down.</li> <li><b>function OnDisable</b> is called when the plugin is disabled, commonly when the server is shutting down. Perform cleanup and logging here.</li> </ul> Be sure to return true for this function, else MCS thinks you plugin had failed to initialise and prints a stacktrace with an error message. @@ -159,21 +158,23 @@ cPluginManager.BindCommand("/commandname", "permissionnode", FunctionToCall, " ~ a message. Again, see the API documentation for fuller details. But, you ask, how <i>do</i> we send a message to the client? </p> <p> - Remember that copy of CoreMessaging.lua that we downloaded earlier? Make sure that file is in your plugin folder, along with the main.lua file you are typing - your code in. Since MCS brings all the files together on JIT compile, we don't need to worry about requiring any files or such. Simply follow the below examples: + There are dedicated functions used for sending a player formatted messages. By format, I refer to coloured prefixes/coloured text (depending on configuration) + that clearly categorise what type of message a player is being sent. For example, an informational message has a yellow coloured [INFO] prefix, and a warning message + has a rose coloured [WARNING] prefix. A few of the most used functions are listed here, but see the API docs for more details. Look in the cRoot, cWorld, and cPlayer sections + for functions that broadcast to the entire server, the whole world, and a single player, respectively. </p> <pre class="prettyprint lang-lua"> -- Format: §yellow[INFO] §white%text% (yellow [INFO], white text following it) -- Use: Informational message, such as instructions for usage of a command -SendMessage(Player, "Usage: /explode [player]") +Player:SendMessageInfo("Usage: /explode [player]") -- Format: §green[INFO] §white%text% (green [INFO] etc.) -- Use: Success message, like when a command executes successfully -SendMessageSuccess(Player, "Notch was blown up!") +Player:SendMessageSuccess("Notch was blown up!") -- Format: §rose[INFO] §white%text% (rose coloured [INFO] etc.) -- Use: Failure message, like when a command was entered correctly but failed to run, such as when the destination player wasn't found in a /tp command -SendMessageFailure(Player, "Player Salted was not found") +Player:SendMessageFailure("Player Salted was not found") </pre> <p> Those are the basics. If you want to output text to the player for a reason other than the three listed above, and you want to colour the text, simply concatenate diff --git a/MCServer/Plugins/Core b/MCServer/Plugins/Core -Subproject d6ed2041469ab959bbf3842db41c0e25fd249dc +Subproject 5c8557d4fdfa580c100510cde07a1a778ea2e24 diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 8345e2169..51b3a3a87 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -19,18 +19,18 @@ function Initialize(Plugin) cPluginManager.AddHook(cPluginManager.HOOK_TICK, OnTick2); --]] - cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock); - cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem); - cPluginManager:AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage); - cPluginManager:AddHook(cPluginManager.HOOK_TICK, OnTick); - cPluginManager:AddHook(cPluginManager.HOOK_CHAT, OnChat); - cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity); - cPluginManager:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick); - cPluginManager:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); - cPluginManager:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded); - cPluginManager:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); - - PM = cRoot:Get():GetPluginManager(); + local PM = cPluginManager; + PM:AddHook(cPluginManager.HOOK_PLAYER_USING_BLOCK, OnPlayerUsingBlock); + PM:AddHook(cPluginManager.HOOK_PLAYER_USING_ITEM, OnPlayerUsingItem); + PM:AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage); + PM:AddHook(cPluginManager.HOOK_TICK, OnTick); + PM:AddHook(cPluginManager.HOOK_CHAT, OnChat); + PM:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, OnPlayerRightClickingEntity); + PM:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick); + PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); + PM:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded); + PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); + PM:BindCommand("/le", "debuggers", HandleListEntitiesCmd, "- Shows a list of all the loaded entities"); PM:BindCommand("/ke", "debuggers", HandleKillEntitiesCmd, "- Kills all the loaded entities"); PM:BindCommand("/wool", "debuggers", HandleWoolCmd, "- Sets all your armor to blue wool"); @@ -55,7 +55,8 @@ function Initialize(Plugin) 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); + Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers) + Plugin:AddWebTab("StressTest", HandleRequest_StressTest) -- Enable the following line for BlockArea / Generator interface testing: -- PluginManager:AddHook(Plugin, cPluginManager.HOOK_CHUNK_GENERATED); @@ -1038,6 +1039,68 @@ end +local g_Counter = 0 +local g_JavaScript = +[[ +<script> +function createXHR() +{ + var request = false; + try { + request = new ActiveXObject('Msxml2.XMLHTTP'); + } + catch (err2) + { + try + { + request = new ActiveXObject('Microsoft.XMLHTTP'); + } + catch (err3) + { + try + { + request = new XMLHttpRequest(); + } + catch (err1) + { + request = false; + } + } + } + return request; +} + +function RefreshCounter() +{ + var xhr = createXHR(); + xhr.onreadystatechange = function() + { + if (xhr.readyState == 4) + { + document.getElementById("cnt").innerHTML = xhr.responseText; + } + }; + xhr.open("POST", "/~webadmin/Debuggers/StressTest", true); + xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xhr.send("counter=true"); +} + +setInterval(RefreshCounter, 10) +</script> +]] + +function HandleRequest_StressTest(a_Request) + if (a_Request.PostParams["counter"]) then + g_Counter = g_Counter + 1 + return tostring(g_Counter) + end + return g_JavaScript .. "<p>The counter below should be reloading as fast as possible</p><div id='cnt'>0</div>" +end + + + + + function OnPluginMessage(a_Client, a_Channel, a_Message) LOGINFO("Received a plugin message from client " .. a_Client:GetUsername() .. ": channel '" .. a_Channel .. "', message '" .. a_Message .. "'"); 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/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/lib/polarssl b/lib/polarssl -Subproject 2cb1a0c4009ecf368ecc74eb428394e10f9e6d0 +Subproject 2ceda579893ceb23c5eb0d56df47dc235644e0f diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h index bf6289a2f..918781a00 100644 --- a/src/BlockEntities/BlockEntityWithItems.h +++ b/src/BlockEntities/BlockEntityWithItems.h @@ -11,6 +11,7 @@ #include "BlockEntity.h" #include "../ItemGrid.h" +#include "../UI/WindowOwner.h" @@ -22,6 +23,7 @@ class cBlockEntityWithItems : // tolua_end // tolua doesn't seem to support multiple inheritance? , public cItemGrid::cListener + , public cBlockEntityWindowOwner // tolua_begin { typedef cBlockEntity super; @@ -77,6 +79,11 @@ protected: ASSERT(a_Grid == &m_Contents); if (m_World != NULL) { + if (GetWindow() != NULL) + { + GetWindow()->BroadcastWholeWindow(); + } + m_World->MarkChunkDirty(GetChunkX(), GetChunkZ()); } } diff --git a/src/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h index 4110de1f3..ce16f84d7 100644 --- a/src/BlockEntities/ChestEntity.h +++ b/src/BlockEntities/ChestEntity.h @@ -2,7 +2,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -23,8 +22,7 @@ class cNBTData; // tolua_begin class cChestEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp index 7c9a40ce6..81df0fc8c 100644 --- a/src/BlockEntities/DropSpenserEntity.cpp +++ b/src/BlockEntities/DropSpenserEntity.cpp @@ -99,13 +99,6 @@ void cDropSpenserEntity::DropSpense(cChunk & a_Chunk) } m_World->BroadcastSoundParticleEffect(2000, m_PosX, m_PosY, m_PosZ, SmokeDir); m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f); - - // Update the UI window, if open: - cWindow * Window = GetWindow(); - if (Window != NULL) - { - Window->BroadcastWholeWindow(); - } } diff --git a/src/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h index f2f1eba36..47d3bd492 100644 --- a/src/BlockEntities/DropSpenserEntity.h +++ b/src/BlockEntities/DropSpenserEntity.h @@ -11,7 +11,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -31,8 +30,7 @@ class cServer; // tolua_begin class cDropSpenserEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/EnderChestEntity.h b/src/BlockEntities/EnderChestEntity.h index 0ee3cab3b..45beee45f 100644 --- a/src/BlockEntities/EnderChestEntity.h +++ b/src/BlockEntities/EnderChestEntity.h @@ -2,7 +2,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -23,8 +22,7 @@ class cNBTData; // tolua_begin class cEnderChestEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h index b08187300..5e08ae37a 100644 --- a/src/BlockEntities/FurnaceEntity.h +++ b/src/BlockEntities/FurnaceEntity.h @@ -2,7 +2,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" #include "../FurnaceRecipe.h" @@ -23,8 +22,7 @@ class cServer; // tolua_begin class cFurnaceEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index 2255cad64..31b23ac99 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -7,10 +7,12 @@ #include "HopperEntity.h" #include "../Chunk.h" #include "../Entities/Player.h" +#include "../Entities/Pickup.h" #include "../Bindings/PluginManager.h" #include "ChestEntity.h" #include "DropSpenserEntity.h" #include "FurnaceEntity.h" +#include "../BoundingBox.h" @@ -190,8 +192,87 @@ bool cHopperEntity::MoveItemsIn(cChunk & a_Chunk, Int64 a_CurrentTick) /// Moves pickups from above this hopper into it. Returns true if the contents have changed. bool cHopperEntity::MovePickupsIn(cChunk & a_Chunk, Int64 a_CurrentTick) { - // TODO - return false; + UNUSED(a_CurrentTick); + + class cHopperPickupSearchCallback : + public cEntityCallback + { + public: + cHopperPickupSearchCallback(const Vector3i & a_Pos, cItemGrid & a_Contents) : + m_Pos(a_Pos), + m_bFoundPickupsAbove(false), + m_Contents(a_Contents) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + ASSERT(a_Entity != NULL); + + if (!a_Entity->IsPickup() || a_Entity->IsDestroyed()) + { + return false; + } + + Vector3f EntityPos = a_Entity->GetPosition(); + Vector3f BlockPos(m_Pos.x + 0.5f, (float)m_Pos.y + 1, m_Pos.z + 0.5f); // One block above hopper, and search from center outwards + float Distance = (EntityPos - BlockPos).Length(); + + if (Distance < 0.5) + { + if (TrySuckPickupIn((cPickup *)a_Entity)) + { + return false; + } + } + + return false; + } + + bool TrySuckPickupIn(cPickup * a_Pickup) + { + for (int i = 0; i < ContentsWidth * ContentsHeight; i++) + { + if (m_Contents.IsSlotEmpty(i)) + { + m_bFoundPickupsAbove = true; + m_Contents.SetSlot(i, a_Pickup->GetItem()); + a_Pickup->Destroy(); // Kill pickup + + return true; + } + else if (m_Contents.GetSlot(i).IsEqual(a_Pickup->GetItem()) && !m_Contents.GetSlot(i).IsFullStack()) + { + m_bFoundPickupsAbove = true; + + int PreviousCount = m_Contents.GetSlot(i).m_ItemCount; + a_Pickup->GetItem().m_ItemCount -= m_Contents.ChangeSlotCount(i, a_Pickup->GetItem().m_ItemCount) - PreviousCount; // Set count to however many items were added + + if (a_Pickup->GetItem().IsEmpty()) + { + a_Pickup->Destroy(); // Kill pickup if all items were added + } + return true; + } + } + return false; + } + + bool FoundPickupsAbove(void) const + { + return m_bFoundPickupsAbove; + } + + protected: + Vector3i m_Pos; + bool m_bFoundPickupsAbove; + cItemGrid & m_Contents; + }; + + cHopperPickupSearchCallback HopperPickupSearchCallback(Vector3i(GetPosX(), GetPosY(), GetPosZ()), m_Contents); + a_Chunk.ForEachEntity(HopperPickupSearchCallback); + + return HopperPickupSearchCallback.FoundPickupsAbove(); } diff --git a/src/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h index 2c8b301fe..6ef98f43a 100644 --- a/src/BlockEntities/HopperEntity.h +++ b/src/BlockEntities/HopperEntity.h @@ -10,7 +10,6 @@ #pragma once #include "BlockEntityWithItems.h" -#include "../UI/WindowOwner.h" @@ -18,8 +17,7 @@ // tolua_begin class cHopperEntity : - public cBlockEntityWithItems, - public cBlockEntityWindowOwner + public cBlockEntityWithItems { typedef cBlockEntityWithItems super; diff --git a/src/BlockEntities/JukeboxEntity.h b/src/BlockEntities/JukeboxEntity.h index 996de965b..734d7bb66 100644 --- a/src/BlockEntities/JukeboxEntity.h +++ b/src/BlockEntities/JukeboxEntity.h @@ -43,6 +43,8 @@ public: void EjectRecord(void); // tolua_end + + static const char * GetClassStatic(void) { return "cJukeboxEntity"; } virtual void UsedBy(cPlayer * a_Player) override; virtual void SendTo(cClientHandle &) override { }; diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h index cf78aeac6..b698899c0 100644 --- a/src/BlockEntities/NoteEntity.h +++ b/src/BlockEntities/NoteEntity.h @@ -54,6 +54,8 @@ public: virtual void UsedBy(cPlayer * a_Player) override; virtual void SendTo(cClientHandle &) override { }; + static const char * GetClassStatic(void) { return "cNoteEntity"; } + private: char m_Pitch; } ; // tolua_export diff --git a/src/BlockEntities/SignEntity.h b/src/BlockEntities/SignEntity.h index d998ff1e8..80c7bbfdf 100644 --- a/src/BlockEntities/SignEntity.h +++ b/src/BlockEntities/SignEntity.h @@ -1,4 +1,3 @@ - // SignEntity.h // Declares the cSignEntity class representing a single sign in the world @@ -56,6 +55,8 @@ public: virtual void UsedBy(cPlayer * a_Player) override; virtual void SendTo(cClientHandle & a_Client) override; + + static const char * GetClassStatic(void) { return "cSignEntity"; } private: diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index cf5a8edfe..d568e068d 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -1128,8 +1128,9 @@ void cPlayer::SetIP(const AString & a_IP) void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { - SetPosition( a_PosX, a_PosY, a_PosZ ); + SetPosition(a_PosX, a_PosY, a_PosZ); m_LastGroundHeight = (float)a_PosY; + m_LastJumpHeight = (float)a_PosY; m_World->BroadcastTeleportEntity(*this, GetClientHandle()); m_ClientHandle->SendPlayerMoveLook(); diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp index e8b58695f..34a267bab 100644 --- a/src/ItemGrid.cpp +++ b/src/ItemGrid.cpp @@ -369,6 +369,13 @@ int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount) } m_Slots[a_SlotNum].m_ItemCount += a_AddToCount; + + cItemHandler * Handler = cItemHandler::GetItemHandler(m_Slots[a_SlotNum].m_ItemType); + if (m_Slots[a_SlotNum].m_ItemCount > Handler->GetMaxStackSize()) + { + m_Slots[a_SlotNum].m_ItemCount = Handler->GetMaxStackSize(); + } + TriggerListeners(a_SlotNum); return m_Slots[a_SlotNum].m_ItemCount; } diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index f2f0c404c..0901f85a9 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -5,7 +5,7 @@ #include "../World.h" #include "../Entities/Player.h" -#include "../MersenneTwister.h" +#include "../Tracer.h" @@ -73,6 +73,18 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) { CheckEventSeePlayer(); } + + if (m_Target == NULL) + return; + + cTracer LineOfSight(GetWorld()); + Vector3d AttackDirection(m_Target->GetPosition() - GetPosition()); + + if (ReachedFinalDestination() && !LineOfSight.Trace(GetPosition(), AttackDirection, (int)AttackDirection.Length())) + { + // Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls) + Attack(a_Dt / 1000); + } } @@ -81,7 +93,7 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk) void cAggressiveMonster::Attack(float a_Dt) { - super::Attack(a_Dt); + m_AttackInterval += a_Dt * m_AttackRate; if ((m_Target != NULL) && (m_AttackInterval > 3.0)) { diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h index 9cee4e7a7..152260f95 100644 --- a/src/Mobs/AggressiveMonster.h +++ b/src/Mobs/AggressiveMonster.h @@ -20,7 +20,7 @@ public: virtual void InStateChasing(float a_Dt) override; virtual void EventSeePlayer(cEntity *) override; - virtual void Attack(float a_Dt) override; + virtual void Attack(float a_Dt); } ; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index ad3a87725..9817901c9 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -311,9 +311,6 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk) } } - if (ReachedFinalDestination() && (m_Target != NULL)) - Attack(a_Dt); - SetPitchAndYawFromDestination(); HandleFalling(); @@ -657,17 +654,6 @@ void cMonster::InStateEscaping(float a_Dt) -// Do attack here -// a_Dt is passed so we can set attack rate -void cMonster::Attack(float a_Dt) -{ - m_AttackInterval += a_Dt * m_AttackRate; -} - - - - - void cMonster::GetMonsterConfig(const AString & a_Name) { cRoot::Get()->GetMonsterConfig()->AssignAttributes(this, a_Name); diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h index 714feddb9..4d2e099c5 100644 --- a/src/Mobs/Monster.h +++ b/src/Mobs/Monster.h @@ -112,8 +112,6 @@ public: virtual void InStateChasing (float a_Dt); virtual void InStateEscaping(float a_Dt); - virtual void Attack(float a_Dt); - int GetAttackRate() { return (int)m_AttackRate; } void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; } void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; } diff --git a/src/Simulator/IncrementalRedstoneSimulator.cpp b/src/Simulator/IncrementalRedstoneSimulator.cpp index 5dba69455..985fdee27 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.cpp +++ b/src/Simulator/IncrementalRedstoneSimulator.cpp @@ -8,6 +8,7 @@ #include "../Entities/TNTEntity.h" #include "../Blocks/BlockTorch.h" #include "../Blocks/BlockDoor.h" +#include "../Blocks/BlockFenceGate.h" #include "../Piston.h" @@ -221,6 +222,7 @@ void cIncrementalRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int { case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(a_X, dataitr->y, a_Z); break; case E_BLOCK_LEVER: HandleRedstoneLever(a_X, dataitr->y, a_Z); break; + case E_BLOCK_FENCE_GATE: HandleFenceGate(a_X, dataitr->y, a_Z); break; case E_BLOCK_TNT: HandleTNT(a_X, dataitr->y, a_Z); break; case E_BLOCK_TRAPDOOR: HandleTrapdoor(a_X, dataitr->y, a_Z); break; case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(a_X, dataitr->y, a_Z); break; @@ -316,6 +318,22 @@ void cIncrementalRedstoneSimulator::HandleRedstoneTorch(int a_BlockX, int a_Bloc { // There was a match, torch goes off m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_TORCH_OFF, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ)); + + float Pitch = (((1.0F-0.0F)*((float)rand()/RAND_MAX)) - ((1.0F-0.0F)*((float)rand()/RAND_MAX))) * 0.8F; + m_World.BroadcastSoundEffect("random.fizz", + ((int) (a_BlockX + 0.5F) * 8.0), + ((int) (a_BlockY + 0.5F) * 8.0), + ((int) (a_BlockZ + 0.5F) * 8.0), + 0.5F, + 2.6F + Pitch); + + for (int l = 0; l < 5; ++l) { + float d0 = a_BlockX + (double(rand())/RAND_MAX) * 0.6F + 0.2F; + float d1 = a_BlockY + (double(rand())/RAND_MAX) * 0.6F + 0.2F; + float d2 = a_BlockZ + (double(rand())/RAND_MAX) * 0.6F + 0.2F; + m_World.BroadcastParticleEffect("smoke", d0, d1, d2, 0.0F, 0.0F, 0.0F, 0.01F, 1); + } + return; } @@ -402,6 +420,35 @@ void cIncrementalRedstoneSimulator::HandleRedstoneLever(int a_BlockX, int a_Bloc +void cIncrementalRedstoneSimulator::HandleFenceGate(int a_BlockX, int a_BlockY, int a_BlockZ) +{ + cChunkInterface ChunkInterface(m_World.GetChunkMap()); + NIBBLETYPE MetaData = ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); + + if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) + { + if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true)) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, MetaData | 0x4); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); + } + } + else + { + if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) + { + m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, MetaData & 0xFFFFFFFB); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); + SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); + } + } +} + + + + + void cIncrementalRedstoneSimulator::HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType) { if (IsButtonOn(m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ))) @@ -737,7 +784,7 @@ void cIncrementalRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_ { if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) { - m_World.BroadcastSoundEffect("random.fuse", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); + m_World.BroadcastSoundEffect("game.tnt.primed", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, 0.6f); m_World.SpawnPrimedTNT(a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 4); // 4 seconds to boom m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); } @@ -755,6 +802,7 @@ void cIncrementalRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a { cChunkInterface ChunkInterface(m_World.GetChunkMap()); cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); } } @@ -764,6 +812,7 @@ void cIncrementalRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a { cChunkInterface ChunkInterface(m_World.GetChunkMap()); cBlockDoorHandler::ChangeDoor(ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); } } @@ -836,6 +885,7 @@ void cIncrementalRedstoneSimulator::HandleTrapdoor(int a_BlockX, int a_BlockY, i if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true)) { m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) | 0x4); + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true); } } @@ -844,6 +894,7 @@ void cIncrementalRedstoneSimulator::HandleTrapdoor(int a_BlockX, int a_BlockY, i if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false)) { m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0xB); // Take into account that the fourth bit is needed for trapdoors too + m_World.BroadcastSoundParticleEffect(1003, a_BlockX, a_BlockY, a_BlockZ, 0); SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false); } } @@ -960,7 +1011,7 @@ void cIncrementalRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_Bloc Vector3f BlockPos(m_X + 0.5f, (float)m_Y, m_Z + 0.5f); float Distance = (EntityPos - BlockPos).Length(); - if (Distance < 0.5) + if (Distance <= 0.7) { m_Entity = a_Entity; return true; // Break out, we only need to know for wooden plates that at least one entity is on top diff --git a/src/Simulator/IncrementalRedstoneSimulator.h b/src/Simulator/IncrementalRedstoneSimulator.h index 3397e143c..bcf89bb82 100644 --- a/src/Simulator/IncrementalRedstoneSimulator.h +++ b/src/Simulator/IncrementalRedstoneSimulator.h @@ -84,6 +84,8 @@ private: void HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ); /** Handles levers */ void HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ); + /** Handles Fence Gates */ + void HandleFenceGate(int a_BlockX, int a_BlockY, int a_BlockZ); /** Handles buttons */ void HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType); /** Handles daylight sensors */ @@ -207,6 +209,10 @@ private: case E_BLOCK_REDSTONE_REPEATER_ON: case E_BLOCK_BLOCK_OF_REDSTONE: case E_BLOCK_ACTIVE_COMPARATOR: + case E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE: + case E_BLOCK_STONE_PRESSURE_PLATE: + case E_BLOCK_WOODEN_PRESSURE_PLATE: { return true; } diff --git a/src/World.cpp b/src/World.cpp index f8c1091f0..cb07caa5d 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -2213,6 +2213,15 @@ void cWorld::UnloadUnusedChunks(void) +void cWorld::QueueUnloadUnusedChunks(void) +{ + QueueTask(new cWorld::cTaskUnloadUnusedChunks); +} + + + + + void cWorld::CollectPickupsByPlayer(cPlayer * a_Player) { m_ChunkMap->CollectPickupsByPlayer(a_Player); @@ -2945,6 +2954,7 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cWorld::cTaskSaveAllChunks: @@ -2958,6 +2968,18 @@ void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cWorld::cTaskUnloadUnusedChunks + +void cWorld::cTaskUnloadUnusedChunks::Run(cWorld & a_World) +{ + a_World.UnloadUnusedChunks(); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cWorld::cChunkGeneratorCallbacks: cWorld::cChunkGeneratorCallbacks::cChunkGeneratorCallbacks(cWorld & a_World) : diff --git a/src/World.h b/src/World.h index 9049971be..ca1b7dcc5 100644 --- a/src/World.h +++ b/src/World.h @@ -99,6 +99,15 @@ public: } ; + class cTaskUnloadUnusedChunks : + public cTask + { + protected: + // cTask overrides: + virtual void Run(cWorld & a_World) override; + }; + + static const char * GetClassStatic(void) // Needed for ManualBindings's ForEach templates { return "cWorld"; @@ -243,7 +252,8 @@ public: bool IsChunkValid (int a_ChunkX, int a_ChunkZ) const; bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const; - void UnloadUnusedChunks(void); // tolua_export + /** Queues a task to unload unused chunks onto the tick thread. The prefferred way of unloading*/ + void QueueUnloadUnusedChunks(void); // tolua_export void CollectPickupsByPlayer(cPlayer * a_Player); @@ -866,6 +876,9 @@ private: /** Ticks all clients that are in this world */ void TickClients(float a_Dt); + /** Unloads all chunks immediately.*/ + void UnloadUnusedChunks(void); + void UpdateSkyDarkness(void); /** <summary>Generates a random spawnpoint on solid land by walking chunks and finding their biomes</summary> */ |