summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--COMPILING.md30
-rw-r--r--MCServer/Plugins/APIDump/APIDesc.lua2
m---------MCServer/Plugins/Core0
-rw-r--r--MCServer/monsters.ini44
-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
m---------lib/polarssl0
-rw-r--r--src/World.cpp22
-rw-r--r--src/World.h15
13 files changed, 1030 insertions, 32 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/Core b/MCServer/Plugins/Core
-Subproject d6ed2041469ab959bbf3842db41c0e25fd249dc
+Subproject 5c8557d4fdfa580c100510cde07a1a778ea2e24
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/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> */