summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/AllocationPool.h4
-rw-r--r--src/Bindings/AllToLua.pkg1
-rw-r--r--src/Bindings/CMakeLists.txt2
-rw-r--r--src/Bindings/LuaChunkStay.cpp2
-rw-r--r--src/Bindings/LuaState.cpp72
-rw-r--r--src/Bindings/LuaState.h21
-rw-r--r--src/Bindings/ManualBindings.cpp237
-rw-r--r--src/Bindings/ManualBindings.h18
-rw-r--r--src/Bindings/ManualBindings_RankManager.cpp1007
-rw-r--r--src/Bindings/Plugin.h3
-rw-r--r--src/Bindings/PluginLua.cpp25
-rw-r--r--src/Bindings/PluginLua.h7
-rw-r--r--src/Bindings/PluginManager.cpp19
-rw-r--r--src/Bindings/PluginManager.h2
-rw-r--r--src/Bindings/gen_LuaState_Call.lua30
-rw-r--r--src/BlockArea.cpp22
-rw-r--r--src/BlockArea.h2
-rw-r--r--src/BlockEntities/CommandBlockEntity.cpp35
-rw-r--r--src/BlockEntities/FurnaceEntity.h2
-rw-r--r--src/BlockID.h2
-rw-r--r--src/Blocks/BlockAnvil.h11
-rw-r--r--src/Blocks/BlockFire.h5
-rw-r--r--src/Blocks/BlockHandler.cpp39
-rw-r--r--src/Blocks/BlockIce.h22
-rw-r--r--src/Blocks/BlockLeaves.h2
-rw-r--r--src/Blocks/ClearMetaOnDrop.h2
-rw-r--r--src/Blocks/MetaRotator.h10
-rw-r--r--src/BoundingBox.h11
-rw-r--r--src/ByteBuffer.cpp2
-rw-r--r--src/CMakeLists.txt13
-rw-r--r--src/CheckBasicStyle.lua3
-rw-r--r--src/Chunk.cpp42
-rw-r--r--src/Chunk.h8
-rw-r--r--src/ChunkDef.h49
-rw-r--r--src/ChunkMap.cpp356
-rw-r--r--src/ChunkMap.h20
-rw-r--r--src/ChunkSender.cpp14
-rw-r--r--src/ChunkSender.h7
-rw-r--r--src/ChunkStay.cpp2
-rw-r--r--src/ClientHandle.cpp116
-rw-r--r--src/ClientHandle.h4
-rw-r--r--src/CraftingRecipes.cpp23
-rw-r--r--src/CraftingRecipes.h6
-rw-r--r--src/DeadlockDetect.cpp4
-rw-r--r--src/Defines.h2
-rw-r--r--src/Entities/ArrowEntity.cpp37
-rw-r--r--src/Entities/ArrowEntity.h5
-rw-r--r--src/Entities/Entity.cpp225
-rw-r--r--src/Entities/EntityEffect.cpp4
-rw-r--r--src/Entities/ItemFrame.cpp1
-rw-r--r--src/Entities/Minecart.cpp94
-rw-r--r--src/Entities/Pickup.cpp8
-rw-r--r--src/Entities/Player.cpp283
-rw-r--r--src/Entities/Player.h54
-rw-r--r--src/Entities/ProjectileEntity.cpp5
-rw-r--r--src/Entities/ProjectileEntity.h6
-rw-r--r--src/FastRandom.h2
-rw-r--r--src/FurnaceRecipe.cpp243
-rw-r--r--src/FurnaceRecipe.h37
-rw-r--r--src/Generating/BioGen.cpp55
-rw-r--r--src/Generating/BioGen.h26
-rw-r--r--src/Generating/CMakeLists.txt2
-rw-r--r--src/Generating/Caves.cpp3
-rw-r--r--src/Generating/ChunkGenerator.cpp39
-rw-r--r--src/Generating/ChunkGenerator.h12
-rw-r--r--src/Generating/ComposableGenerator.cpp22
-rw-r--r--src/Generating/DungeonRoomsFinisher.cpp279
-rw-r--r--src/Generating/DungeonRoomsFinisher.h52
-rw-r--r--src/Generating/HeiGen.cpp8
-rw-r--r--src/Group.cpp41
-rw-r--r--src/Group.h44
-rw-r--r--src/GroupManager.cpp227
-rw-r--r--src/GroupManager.h36
-rw-r--r--src/HTTPServer/HTTPConnection.cpp3
-rw-r--r--src/HTTPServer/HTTPFormParser.cpp8
-rw-r--r--src/Inventory.cpp2
-rw-r--r--src/Item.cpp80
-rw-r--r--src/Item.h6
-rw-r--r--src/Items/ItemBow.h12
-rw-r--r--src/Items/ItemFood.h6
-rw-r--r--src/Items/ItemHandler.cpp6
-rw-r--r--src/Items/ItemHandler.h2
-rw-r--r--src/Items/ItemSeeds.h2
-rw-r--r--src/LightingThread.cpp6
-rw-r--r--src/LinearUpscale.h6
-rw-r--r--src/LoggerListeners.cpp8
-rw-r--r--src/Mobs/CMakeLists.txt1
-rw-r--r--src/Mobs/Monster.cpp11
-rw-r--r--src/Mobs/Sheep.cpp7
-rw-r--r--src/Noise.cpp5
-rw-r--r--src/OSSupport/Queue.h2
-rw-r--r--src/PolarSSL++/SslContext.cpp1
-rw-r--r--src/Protocol/MojangAPI.cpp28
-rw-r--r--src/Protocol/MojangAPI.h26
-rw-r--r--src/Protocol/Protocol17x.cpp104
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp58
-rw-r--r--src/Protocol/ProtocolRecognizer.h2
-rw-r--r--src/RankManager.cpp1837
-rw-r--r--src/RankManager.h246
-rw-r--r--src/Root.cpp31
-rw-r--r--src/Root.h9
-rw-r--r--src/Server.cpp83
-rw-r--r--src/Server.h2
-rw-r--r--src/SetChunkData.cpp36
-rw-r--r--src/SetChunkData.h3
-rw-r--r--src/Simulator/VanillaFluidSimulator.cpp2
-rw-r--r--src/StringCompression.cpp62
-rw-r--r--src/StringCompression.h2
-rw-r--r--src/StringUtils.h63
-rw-r--r--src/UI/SlotArea.cpp189
-rw-r--r--src/UI/SlotArea.h7
-rw-r--r--src/UI/Window.cpp33
-rw-r--r--src/UI/Window.h3
-rw-r--r--src/World.cpp77
-rw-r--r--src/World.h13
-rw-r--r--src/WorldStorage/FastNBT.h4
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp1
-rw-r--r--src/WorldStorage/WSSAnvil.cpp498
-rw-r--r--src/WorldStorage/WSSAnvil.h31
-rw-r--r--src/WorldStorage/WSSCompact.cpp6
-rw-r--r--src/WorldStorage/WorldStorage.cpp28
-rw-r--r--src/WorldStorage/WorldStorage.h12
122 files changed, 6028 insertions, 1778 deletions
diff --git a/src/AllocationPool.h b/src/AllocationPool.h
index 98ab9b389..060cd2918 100644
--- a/src/AllocationPool.h
+++ b/src/AllocationPool.h
@@ -3,7 +3,7 @@
#include <memory>
-template<class T>
+template <class T>
class cAllocationPool
{
public:
@@ -34,7 +34,7 @@ public:
/** Allocates memory storing unused elements in a linked list. Keeps at least NumElementsInReserve
elements in the list unless malloc fails so that the program has a reserve to handle OOM.**/
-template<class T, size_t NumElementsInReserve>
+template <class T, size_t NumElementsInReserve>
class cListAllocationPool : public cAllocationPool<T>
{
public:
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index 88faa9dfc..37e6aecd2 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -67,7 +67,6 @@ $cfile "../Root.h"
$cfile "../Cuboid.h"
$cfile "../BoundingBox.h"
$cfile "../Tracer.h"
-$cfile "../Group.h"
$cfile "../BlockArea.h"
$cfile "../Generating/ChunkDesc.h"
$cfile "../CraftingRecipes.h"
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
index 54152668a..7a1769e9a 100644
--- a/src/Bindings/CMakeLists.txt
+++ b/src/Bindings/CMakeLists.txt
@@ -11,6 +11,7 @@ SET (SRCS
LuaState.cpp
LuaWindow.cpp
ManualBindings.cpp
+ ManualBindings_RankManager.cpp
Plugin.cpp
PluginLua.cpp
PluginManager.cpp
@@ -96,7 +97,6 @@ set(BINDING_DEPENDENCIES
../Entities/HangingEntity.h
../Entities/ItemFrame.h
../Generating/ChunkDesc.h
- ../Group.h
../Inventory.h
../Item.h
../ItemGrid.h
diff --git a/src/Bindings/LuaChunkStay.cpp b/src/Bindings/LuaChunkStay.cpp
index 59b02d8f7..154bcb200 100644
--- a/src/Bindings/LuaChunkStay.cpp
+++ b/src/Bindings/LuaChunkStay.cpp
@@ -107,7 +107,7 @@ void cLuaChunkStay::AddChunkCoord(cLuaState & L, int a_Index)
}
} // for itr - m_Chunks[]
- m_Chunks.push_back(cChunkCoords(ChunkX, ZERO_CHUNK_Y, ChunkZ));
+ m_Chunks.push_back(cChunkCoords(ChunkX, ChunkZ));
}
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index e123a87c9..ba2f3c5e0 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -460,7 +460,43 @@ void cLuaState::Push(const Vector3d & a_Vector)
{
ASSERT(IsValid());
- tolua_pushusertype(m_LuaState, (void *)&a_Vector, "Vector3d");
+ tolua_pushusertype(m_LuaState, (void *)&a_Vector, "Vector3<double>");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(const Vector3d * a_Vector)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, (void *)a_Vector, "Vector3<double>");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(const Vector3i & a_Vector)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, (void *)&a_Vector, "Vector3<int>");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
+void cLuaState::Push(const Vector3i * a_Vector)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, (void *)a_Vector, "Vector3<int>");
m_NumCurrentFunctionArgs += 1;
}
@@ -708,11 +744,11 @@ void cLuaState::Push(TakeDamageInfo * a_TDI)
-void cLuaState::Push(Vector3i * a_Vector)
+void cLuaState::Push(Vector3d * a_Vector)
{
ASSERT(IsValid());
- tolua_pushusertype(m_LuaState, a_Vector, "Vector3i");
+ tolua_pushusertype(m_LuaState, a_Vector, "Vector3<double>");
m_NumCurrentFunctionArgs += 1;
}
@@ -720,11 +756,11 @@ void cLuaState::Push(Vector3i * a_Vector)
-void cLuaState::Push(Vector3d * a_Vector)
+void cLuaState::Push(Vector3i * a_Vector)
{
ASSERT(IsValid());
- tolua_pushusertype(m_LuaState, a_Vector, "Vector3d");
+ tolua_pushusertype(m_LuaState, a_Vector, "Vector3<int>");
m_NumCurrentFunctionArgs += 1;
}
@@ -823,6 +859,32 @@ void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal)
+void cLuaState::GetStackValue(int a_StackPos, pBoundingBox & a_ReturnedVal)
+{
+ tolua_Error err;
+ if (tolua_isusertype(m_LuaState, a_StackPos, "cBoundingBox", false, &err))
+ {
+ a_ReturnedVal = *((cBoundingBox **)lua_touserdata(m_LuaState, a_StackPos));
+ }
+}
+
+
+
+
+
+void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal)
+{
+ tolua_Error err;
+ if (tolua_isusertype(m_LuaState, a_StackPos, "cWorld", false, &err))
+ {
+ a_ReturnedVal = *((cWorld **)lua_touserdata(m_LuaState, a_StackPos));
+ }
+}
+
+
+
+
+
bool cLuaState::CallFunction(int a_NumResults)
{
ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index afac77ce8..44f187701 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -59,6 +59,10 @@ class cTNTEntity;
class cCreeper;
class cHopperEntity;
class cBlockEntity;
+class cBoundingBox;
+
+typedef cBoundingBox * pBoundingBox;
+typedef cWorld * pWorld;
@@ -186,6 +190,9 @@ public:
void Push(const HTTPRequest * a_Request);
void Push(const HTTPTemplateRequest * a_Request);
void Push(const Vector3d & a_Vector);
+ void Push(const Vector3d * a_Vector);
+ void Push(const Vector3i & a_Vector);
+ void Push(const Vector3i * a_Vector);
// Push a value onto the stack (keep alpha-sorted):
void Push(bool a_Value);
@@ -227,6 +234,12 @@ public:
/** Retrieve value at a_StackPos, if it is a valid number, converting and clamping it to eWeather.
If not, a_Value is unchanged. */
void GetStackValue(int a_StackPos, eWeather & a_Value);
+
+ /** Retrieve value at a_StackPos, if it is a valid cBoundingBox class. If not, a_Value is unchanged */
+ void GetStackValue(int a_StackPos, pBoundingBox & a_Value);
+
+ /** Retrieve value at a_StackPos, if it is a valid cWorld class. If not, a_Value is unchanged */
+ void GetStackValue(int a_StackPos, pWorld & a_Value);
// Include the cLuaState::Call() overload implementation that is generated by the gen_LuaState_Call.lua script:
@@ -325,6 +338,14 @@ protected:
*/
bool PushFunction(int a_FnRef);
+ /** Pushes a function that has been saved as a reference.
+ Returns true if successful. Logs a warning on failure
+ */
+ bool PushFunction(const cRef & a_FnRef)
+ {
+ return PushFunction((int)a_FnRef);
+ }
+
/** Pushes a function that is stored in a referenced table by name
Returns true if successful. Logs a warning on failure
*/
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 20cc1d091..603098bc6 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -301,11 +301,11 @@ static int tolua_cFile_GetFolderContents(lua_State * tolua_S)
-template<
+template <
class Ty1,
class Ty2,
bool (Ty1::*Func1)(const AString &, cItemCallback<Ty2> &)
- >
+>
static int tolua_DoWith(lua_State* tolua_S)
{
int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */
@@ -395,7 +395,7 @@ static int tolua_DoWith(lua_State* tolua_S)
-template<
+template <
class Ty1,
class Ty2,
bool (Ty1::*Func1)(int, cItemCallback<Ty2> &)
@@ -485,7 +485,7 @@ static int tolua_DoWithID(lua_State* tolua_S)
-template<
+template <
class Ty1,
class Ty2,
bool (Ty1::*Func1)(int, int, int, cItemCallback<Ty2> &)
@@ -580,7 +580,7 @@ static int tolua_DoWithXYZ(lua_State* tolua_S)
-template<
+template <
class Ty1,
class Ty2,
bool (Ty1::*Func1)(int, int, cItemCallback<Ty2> &)
@@ -676,7 +676,76 @@ static int tolua_ForEachInChunk(lua_State * tolua_S)
-template<
+template <
+ class Ty1,
+ class Ty2,
+ bool (Ty1::*Func1)(const cBoundingBox &, cItemCallback<Ty2> &)
+>
+static int tolua_ForEachInBox(lua_State * tolua_S)
+{
+ // Check params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamUserType(2, "cBoundingBox") ||
+ !L.CheckParamFunction(3) ||
+ !L.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ Ty1 * Self = NULL;
+ cBoundingBox * Box = NULL;
+ L.GetStackValues(1, Self, Box);
+ ASSERT(Self != NULL); // We have verified the type at the top, so we should get valid objects here
+ ASSERT(Box != NULL);
+
+ // Create a reference for the function:
+ cLuaState::cRef FnRef(L, 3);
+
+ // Callback wrapper for the Lua function:
+ class cLuaCallback : public cItemCallback<Ty2>
+ {
+ public:
+ cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FuncRef) :
+ m_LuaState(a_LuaState),
+ m_FnRef(a_FuncRef)
+ {}
+
+ private:
+ // cItemCallback<Ty2> overrides:
+ virtual bool Item(Ty2 * a_Item) override
+ {
+ bool res = false;
+ if (!m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res))
+ {
+ LOGWARNING("Failed to call Lua callback");
+ m_LuaState.LogStackTrace();
+ return true; // Abort enumeration
+ }
+
+ return res;
+ }
+ cLuaState & m_LuaState;
+ cLuaState::cRef & m_FnRef;
+ } Callback(L, FnRef);
+
+ bool bRetVal = (Self->*Func1)(*Box, Callback);
+
+ FnRef.UnRef();
+
+ /* Push return value on stack */
+ tolua_pushboolean(tolua_S, bRetVal);
+ return 1;
+}
+
+
+
+
+
+template <
class Ty1,
class Ty2,
bool (Ty1::*Func1)(cItemCallback<Ty2> &)
@@ -1803,49 +1872,30 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
-static int tolua_cPlayer_GetGroups(lua_State * tolua_S)
+static int tolua_cPlayer_GetPermissions(lua_State * tolua_S)
{
- cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL);
+ // Function signature: cPlayer:GetPermissions() -> {permissions-array}
- const cPlayer::GroupList & AllGroups = self->GetGroups();
-
- lua_createtable(tolua_S, (int)AllGroups.size(), 0);
- int newTable = lua_gettop(tolua_S);
- int index = 1;
- cPlayer::GroupList::const_iterator iter = AllGroups.begin();
- while (iter != AllGroups.end())
+ // Check the params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cPlayer") ||
+ !L.CheckParamEnd (2)
+ )
{
- const cGroup * Group = *iter;
- tolua_pushusertype(tolua_S, (void *)Group, "const cGroup");
- lua_rawseti(tolua_S, newTable, index);
- ++iter;
- ++index;
+ return 0;
}
- return 1;
-}
-
-
-
-
-
-static int tolua_cPlayer_GetResolvedPermissions(lua_State * tolua_S)
-{
- cPlayer * self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL);
-
- cPlayer::StringList AllPermissions = self->GetResolvedPermissions();
- lua_createtable(tolua_S, (int)AllPermissions.size(), 0);
- int newTable = lua_gettop(tolua_S);
- int index = 1;
- cPlayer::StringList::iterator iter = AllPermissions.begin();
- while (iter != AllPermissions.end())
+ // Get the params:
+ cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL);
+ if (self == NULL)
{
- std::string & Permission = *iter;
- lua_pushlstring(tolua_S, Permission.c_str(), Permission.length());
- lua_rawseti(tolua_S, newTable, index);
- ++iter;
- ++index;
+ LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self);
+ return 0;
}
+
+ // Push the permissions:
+ L.Push(self->GetPermissions());
return 1;
}
@@ -1902,6 +1952,40 @@ static int tolua_cPlayer_OpenWindow(lua_State * tolua_S)
+static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S)
+{
+ // Function signature: cPlayer:PermissionMatches(PermissionStr, TemplateStr) -> bool
+
+ // Check the params:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cPlayer") ||
+ !L.CheckParamString (2, 3) ||
+ !L.CheckParamEnd (4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL);
+ if (self == NULL)
+ {
+ LOGWARNING("%s: invalid self (%p)", __FUNCTION__, self);
+ return 0;
+ }
+ AString Permission, Template;
+ L.GetStackValues(2, Permission, Template);
+
+ // Push the result of the match:
+ L.Push(self->PermissionMatches(StringSplit(Permission, "."), StringSplit(Template, ".")));
+ return 1;
+}
+
+
+
+
+
template <
class OBJTYPE,
void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef)
@@ -2399,6 +2483,62 @@ static int tolua_cMojangAPI_GetUUIDsFromPlayerNames(lua_State * L)
+static int tolua_cMojangAPI_MakeUUIDDashed(lua_State * L)
+{
+ // Function signature: cMojangAPI:MakeUUIDDashed(UUID) -> string
+
+ // Check params:
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cMojangAPI") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString UUID;
+ S.GetStackValue(2, UUID);
+
+ // Push the result:
+ S.Push(cRoot::Get()->GetMojangAPI().MakeUUIDDashed(UUID));
+ return 1;
+}
+
+
+
+
+
+static int tolua_cMojangAPI_MakeUUIDShort(lua_State * L)
+{
+ // Function signature: cMojangAPI:MakeUUIDShort(UUID) -> string
+
+ // Check params:
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cMojangAPI") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString UUID;
+ S.GetStackValue(2, UUID);
+
+ // Push the result:
+ S.Push(cRoot::Get()->GetMojangAPI().MakeUUIDShort(UUID));
+ return 1;
+}
+
+
+
+
+
static int Lua_ItemGrid_GetSlotCoords(lua_State * L)
{
tolua_Error tolua_err;
@@ -2592,7 +2732,7 @@ static int tolua_cRoot_GetFurnaceRecipe(lua_State * tolua_S)
// Get the recipe for the input
cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe();
- const cFurnaceRecipe::Recipe * Recipe = FR->GetRecipeFrom(*Input);
+ const cFurnaceRecipe::cRecipe * Recipe = FR->GetRecipeFrom(*Input);
if (Recipe == NULL)
{
// There is no such furnace recipe for this input, return no value
@@ -3256,6 +3396,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>);
+ tolua_function(tolua_S, "ForEachEntityInBox", tolua_ForEachInBox< cWorld, cEntity, &cWorld::ForEachEntityInBox>);
tolua_function(tolua_S, "ForEachEntityInChunk", tolua_ForEachInChunk<cWorld, cEntity, &cWorld::ForEachEntityInChunk>);
tolua_function(tolua_S, "ForEachFurnaceInChunk", tolua_ForEachInChunk<cWorld, cFurnaceEntity, &cWorld::ForEachFurnaceInChunk>);
tolua_function(tolua_S, "ForEachPlayer", tolua_ForEach< cWorld, cPlayer, &cWorld::ForEachPlayer>);
@@ -3295,9 +3436,9 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cPlayer");
- tolua_function(tolua_S, "GetGroups", tolua_cPlayer_GetGroups);
- tolua_function(tolua_S, "GetResolvedPermissions", tolua_cPlayer_GetResolvedPermissions);
- tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow);
+ tolua_function(tolua_S, "GetPermissions", tolua_cPlayer_GetPermissions);
+ tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow);
+ tolua_function(tolua_S, "PermissionMatches", tolua_cPlayer_PermissionMatches);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cLuaWindow");
@@ -3340,6 +3481,8 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "GetPlayerNameFromUUID", tolua_cMojangAPI_GetPlayerNameFromUUID);
tolua_function(tolua_S, "GetUUIDFromPlayerName", tolua_cMojangAPI_GetUUIDFromPlayerName);
tolua_function(tolua_S, "GetUUIDsFromPlayerNames", tolua_cMojangAPI_GetUUIDsFromPlayerNames);
+ tolua_function(tolua_S, "MakeUUIDDashed", tolua_cMojangAPI_MakeUUIDDashed);
+ tolua_function(tolua_S, "MakeUUIDShort", tolua_cMojangAPI_MakeUUIDShort);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cItemGrid");
@@ -3347,6 +3490,8 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_function(tolua_S, "md5", tolua_md5);
+
+ BindRankManager(tolua_S);
tolua_endmodule(tolua_S);
}
diff --git a/src/Bindings/ManualBindings.h b/src/Bindings/ManualBindings.h
index 36161c6a2..1b6e65654 100644
--- a/src/Bindings/ManualBindings.h
+++ b/src/Bindings/ManualBindings.h
@@ -1,8 +1,24 @@
#pragma once
struct lua_State;
+
+
+
+
+
+/** Provides namespace for the bindings. */
class ManualBindings
{
public:
- static void Bind( lua_State* tolua_S);
+ /** Binds all the manually implemented functions to tolua_S. */
+ static void Bind(lua_State * tolua_S);
+
+protected:
+ /** Binds the manually implemented cRankManager glue code to tolua_S.
+ Implemented in ManualBindings_RankManager.cpp. */
+ static void BindRankManager(lua_State * tolua_S);
};
+
+
+
+
diff --git a/src/Bindings/ManualBindings_RankManager.cpp b/src/Bindings/ManualBindings_RankManager.cpp
new file mode 100644
index 000000000..2e93ad264
--- /dev/null
+++ b/src/Bindings/ManualBindings_RankManager.cpp
@@ -0,0 +1,1007 @@
+
+// ManualBindings_RankManager.cpp
+
+// Implements the cRankManager Lua bindings
+
+#include "Globals.h"
+#include "ManualBindings.h"
+#include "../Root.h"
+#include "tolua++/include/tolua++.h"
+#include "LuaState.h"
+
+
+
+
+
+/** Binds cRankManager::AddGroup */
+static int tolua_cRankManager_AddGroup(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:AddGroup(GroupName)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Read the params:
+ AString GroupName;
+ S.GetStackValue(2, GroupName);
+
+ // Add the group:
+ cRoot::Get()->GetRankManager().AddGroup(GroupName);
+ return 0;
+}
+
+
+
+
+
+/** Binds cRankManager::AddGroupToRank */
+static int tolua_cRankManager_AddGroupToRank(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:AddGroupToRank(GroupName, RankName) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Read the params:
+ AString GroupName, RankName;
+ S.GetStackValues(2, GroupName, RankName);
+
+ // Add the group to the rank:
+ S.Push(cRoot::Get()->GetRankManager().AddGroupToRank(GroupName, RankName));
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::AddPermissionToGroup */
+static int tolua_cRankManager_AddPermissionToGroup(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:AddPermissionToGroup(Permission, GroupName) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Read the params:
+ AString GroupName, Permission;
+ S.GetStackValues(2, Permission, GroupName);
+
+ // Add the group to the rank:
+ S.Push(cRoot::Get()->GetRankManager().AddPermissionToGroup(Permission, GroupName));
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::AddRank */
+static int tolua_cRankManager_AddRank(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:AddRank(RankName)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 5) ||
+ !S.CheckParamEnd(6)
+ )
+ {
+ return 0;
+ }
+
+ // Read the params:
+ AString RankName, MsgPrefix, MsgSuffix, MsgNameColorCode;
+ S.GetStackValues(2, RankName, MsgPrefix, MsgSuffix, MsgNameColorCode);
+
+ // Add the rank:
+ cRoot::Get()->GetRankManager().AddRank(RankName, MsgPrefix, MsgSuffix, MsgNameColorCode);
+ return 0;
+}
+
+
+
+
+
+/** Binds cRankManager::GetAllGroups */
+static int tolua_cRankManager_GetAllGroups(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetAllGroups() -> arraytable of GroupNames
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the groups:
+ AStringVector Groups = cRoot::Get()->GetRankManager().GetAllGroups();
+
+ // Push the results:
+ S.Push(Groups);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::GetAllPermissions */
+static int tolua_cRankManager_GetAllPermissions(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetAllPermissions() -> arraytable of Permissions
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the permissions:
+ AStringVector Permissions = cRoot::Get()->GetRankManager().GetAllPermissions();
+
+ // Push the results:
+ S.Push(Permissions);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::GetAllRanks */
+static int tolua_cRankManager_GetAllRanks(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetAllRanks() -> arraytable of RankNames
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Get the ranks:
+ AStringVector Ranks = cRoot::Get()->GetRankManager().GetAllRanks();
+
+ // Push the results:
+ S.Push(Ranks);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::GetDefaultRank */
+static int tolua_cRankManager_GetDefaultRank(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetDefaultRank() -> string
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamEnd(2)
+ )
+ {
+ return 0;
+ }
+
+ // Return the rank name:
+ S.Push(cRoot::Get()->GetRankManager().GetDefaultRank());
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::GetGroupPermissions */
+static int tolua_cRankManager_GetGroupPermissions(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetGroupPermissions(GroupName) -> arraytable of permissions
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString GroupName;
+ S.GetStackValue(2, GroupName);
+
+ // Get the permissions:
+ AStringVector Permissions = cRoot::Get()->GetRankManager().GetGroupPermissions(GroupName);
+
+ // Push the results:
+ S.Push(Permissions);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::GetPlayerGroups */
+static int tolua_cRankManager_GetPlayerGroups(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetPlayerGroups(PlayerUUID) -> arraytable of GroupNames
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString PlayerUUID;
+ S.GetStackValue(2, PlayerUUID);
+
+ // Get the groups:
+ AStringVector Groups = cRoot::Get()->GetRankManager().GetPlayerGroups(PlayerUUID);
+
+ // Push the results:
+ S.Push(Groups);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::GetPlayerMsgVisuals */
+static int tolua_cRankManager_GetPlayerMsgVisuals(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetPlayerMsgVisuals(PlayerUUID) -> string, string, string
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString PlayerUUID;
+ S.GetStackValue(2, PlayerUUID);
+
+ // Get the permissions:
+ AString MsgPrefix, MsgSuffix, MsgNameColorCode;
+ if (!cRoot::Get()->GetRankManager().GetPlayerMsgVisuals(PlayerUUID, MsgPrefix, MsgSuffix, MsgNameColorCode))
+ {
+ return 0;
+ }
+
+ // Push the results:
+ S.Push(MsgPrefix);
+ S.Push(MsgSuffix);
+ S.Push(MsgNameColorCode);
+ return 3;
+}
+
+
+
+
+
+/** Binds cRankManager::GetPlayerPermissions */
+static int tolua_cRankManager_GetPlayerPermissions(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetPlayerPermissions(PlayerUUID) -> arraytable of permissions
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString PlayerUUID;
+ S.GetStackValue(2, PlayerUUID);
+
+ // Get the permissions:
+ AStringVector Permissions = cRoot::Get()->GetRankManager().GetPlayerPermissions(PlayerUUID);
+
+ // Push the results:
+ S.Push(Permissions);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::GetPlayerRankName */
+static int tolua_cRankManager_GetPlayerRankName(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetPlayerRankName(PlayerUUID) -> string
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString PlayerUUID;
+ S.GetStackValue(2, PlayerUUID);
+
+ // Get the rank name:
+ AString RankName = cRoot::Get()->GetRankManager().GetPlayerRankName(PlayerUUID);
+
+ // Push the result:
+ S.Push(RankName);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::GetRankGroups */
+static int tolua_cRankManager_GetRankGroups(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetRankGroups(RankName) -> arraytable of groupnames
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString RankName;
+ S.GetStackValue(2, RankName);
+
+ // Get the groups:
+ AStringVector Groups = cRoot::Get()->GetRankManager().GetRankGroups(RankName);
+
+ // Push the results:
+ S.Push(Groups);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::GetRankPermissions */
+static int tolua_cRankManager_GetRankPermissions(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetRankPermissions(RankName) -> arraytable of permissions
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString RankName;
+ S.GetStackValue(2, RankName);
+
+ // Get the permissions:
+ AStringVector Permissions = cRoot::Get()->GetRankManager().GetRankPermissions(RankName);
+
+ // Push the results:
+ S.Push(Permissions);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::GetRankVisuals */
+static int tolua_cRankManager_GetRankVisuals(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GetRankVisuals(RankName) -> MsgPrefix, MsgSuffix, MsgNameColorCode
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString RankName;
+ S.GetStackValue(2, RankName);
+
+ // Get the visuals:
+ AString MsgPrefix, MsgSuffix, MsgNameColorCode;
+ if (!cRoot::Get()->GetRankManager().GetRankVisuals(RankName, MsgPrefix, MsgSuffix, MsgNameColorCode))
+ {
+ // No such rank, return nothing:
+ return 0;
+ }
+
+ // Push the results:
+ S.Push(MsgPrefix);
+ S.Push(MsgSuffix);
+ S.Push(MsgNameColorCode);
+ return 3;
+}
+
+
+
+
+
+/** Binds cRankManager::GroupExists */
+static int tolua_cRankManager_GroupExists(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:GroupExists(GroupName) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString GroupName;
+ S.GetStackValue(2, GroupName);
+
+ // Get the response:
+ bool res = cRoot::Get()->GetRankManager().GroupExists(GroupName);
+
+ // Push the result:
+ S.Push(res);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::IsGroupInRank */
+static int tolua_cRankManager_IsGroupInRank(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:IsGroupInRank(GroupName, RankName) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString GroupName, RankName;
+ S.GetStackValues(2, GroupName, RankName);
+
+ // Get the response:
+ bool res = cRoot::Get()->GetRankManager().IsGroupInRank(GroupName, RankName);
+
+ // Push the result:
+ S.Push(res);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::IsPermissionInGroup */
+static int tolua_cRankManager_IsPermissionInGroup(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:IsPermissionInGroup(Permission, GroupName) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString GroupName, Permission;
+ S.GetStackValues(2, Permission, GroupName);
+
+ // Get the response:
+ bool res = cRoot::Get()->GetRankManager().IsPermissionInGroup(Permission, GroupName);
+
+ // Push the result:
+ S.Push(res);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::IsPlayerRankSet */
+static int tolua_cRankManager_IsPlayerRankSet(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:IsPlayerRankSet(PlayerUUID) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString PlayerUUID;
+ S.GetStackValue(2, PlayerUUID);
+
+ // Get the response:
+ bool res = cRoot::Get()->GetRankManager().IsPlayerRankSet(PlayerUUID);
+
+ // Push the result:
+ S.Push(res);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::RankExists */
+static int tolua_cRankManager_RankExists(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:RankExists(RankName) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString RankName;
+ S.GetStackValue(2, RankName);
+
+ // Get the response:
+ bool res = cRoot::Get()->GetRankManager().RankExists(RankName);
+
+ // Push the result:
+ S.Push(res);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::RemoveGroup */
+static int tolua_cRankManager_RemoveGroup(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:RemoveGroup(GroupName)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString GroupName;
+ S.GetStackValue(2, GroupName);
+
+ // Remove the group:
+ cRoot::Get()->GetRankManager().RemoveGroup(GroupName);
+ return 0;
+}
+
+
+
+
+
+/** Binds cRankManager::RemoveGroupFromRank */
+static int tolua_cRankManager_RemoveGroupFromRank(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:RemoveGroupFromRank(GroupName, RankName)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString GroupName, RankName;
+ S.GetStackValues(2, GroupName, RankName);
+
+ // Remove the group:
+ cRoot::Get()->GetRankManager().RemoveGroupFromRank(GroupName, RankName);
+ return 0;
+}
+
+
+
+
+
+/** Binds cRankManager::RemovePermissionFromGroup */
+static int tolua_cRankManager_RemovePermissionFromGroup(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:RemovePermissionFromGroup(Permission, GroupName)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString GroupName, Permission;
+ S.GetStackValues(2, Permission, GroupName);
+
+ // Remove the group:
+ cRoot::Get()->GetRankManager().RemovePermissionFromGroup(Permission, GroupName);
+ return 0;
+}
+
+
+
+
+
+/** Binds cRankManager::RemovePlayerRank */
+static int tolua_cRankManager_RemovePlayerRank(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:RemovePlayerRank(PlayerUUID)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString PlayerUUID;
+ S.GetStackValue(2, PlayerUUID);
+
+ // Remove the player's rank:
+ cRoot::Get()->GetRankManager().RemovePlayerRank(PlayerUUID);
+ return 0;
+}
+
+
+
+
+
+/** Binds cRankManager::RemoveRank */
+static int tolua_cRankManager_RemoveRank(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:RemoveRank(RankName, [ReplacementRankName])
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ // Param 3 is otpional, defaults to nil -> empty string
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString RankName, ReplacementRankName;
+ S.GetStackValues(2, RankName, ReplacementRankName);
+
+ // Remove the rank:
+ cRoot::Get()->GetRankManager().RemoveRank(RankName, ReplacementRankName);
+ return 0;
+}
+
+
+
+
+
+/** Binds cRankManager::RenameGroup */
+static int tolua_cRankManager_RenameGroup(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:RenameGroup(OldName, NewName)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString OldName, NewName;
+ S.GetStackValues(2, OldName, NewName);
+
+ // Remove the group:
+ bool res = cRoot::Get()->GetRankManager().RenameGroup(OldName, NewName);
+
+ // Push the result:
+ S.Push(res);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::RenameRank */
+static int tolua_cRankManager_RenameRank(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:RenameRank(OldName, NewName)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString OldName, NewName;
+ S.GetStackValues(2, OldName, NewName);
+
+ // Remove the rank:
+ bool res = cRoot::Get()->GetRankManager().RenameRank(OldName, NewName);
+
+ // Push the result:
+ S.Push(res);
+ return 1;
+}
+
+
+
+
+
+/** Binds cRankManager::SetDefaultRank */
+static int tolua_cRankManager_SetDefaultRank(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:SetDefaultRank(RankName) -> bool
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2) ||
+ !S.CheckParamEnd(3)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString RankName;
+ S.GetStackValue(2, RankName);
+
+ // Set the rank, return the result:
+ S.Push(cRoot::Get()->GetRankManager().SetDefaultRank(RankName));
+ return 0;
+}
+
+
+
+
+
+/** Binds cRankManager::SetPlayerRank */
+static int tolua_cRankManager_SetPlayerRank(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:SetPlayerRank(PlayerUUID, PlayerName, RankName)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 4) ||
+ !S.CheckParamEnd(5)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString PlayerUUID, PlayerName, RankName;
+ S.GetStackValues(2, PlayerUUID, PlayerName, RankName);
+
+ // Set the rank:
+ cRoot::Get()->GetRankManager().SetPlayerRank(PlayerUUID, PlayerName, RankName);
+ return 0;
+}
+
+
+
+
+
+/** Binds cRankManager::SetRankVisuals */
+static int tolua_cRankManager_SetRankVisuals(lua_State * L)
+{
+ // Function signature:
+ // cRankManager:SetRankVisuals(RankName, MsgPrefix, MsgSuffix, MsgNameColorCode)
+
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserTable(1, "cRankManager") ||
+ !S.CheckParamString(2, 5) ||
+ !S.CheckParamEnd(6)
+ )
+ {
+ return 0;
+ }
+
+ // Get the params:
+ AString RankName, MsgPrefix, MsgSuffix, MsgNameColorCode;
+ S.GetStackValues(2, RankName, MsgPrefix, MsgSuffix, MsgNameColorCode);
+
+ // Set the visuals:
+ cRoot::Get()->GetRankManager().SetRankVisuals(RankName, MsgPrefix, MsgSuffix, MsgNameColorCode);
+ return 0;
+}
+
+
+
+
+
+void ManualBindings::BindRankManager(lua_State * tolua_S)
+{
+ // Create the cRankManager class in the API:
+ tolua_usertype(tolua_S, "cRankManager");
+ tolua_cclass(tolua_S, "cRankManager", "cRankManager", "", NULL);
+
+ // Fill in the functions (alpha-sorted):
+ tolua_beginmodule(tolua_S, "cRankManager");
+ tolua_function(tolua_S, "AddGroup", tolua_cRankManager_AddGroup);
+ tolua_function(tolua_S, "AddGroupToRank", tolua_cRankManager_AddGroupToRank);
+ tolua_function(tolua_S, "AddPermissionToGroup", tolua_cRankManager_AddPermissionToGroup);
+ tolua_function(tolua_S, "AddRank", tolua_cRankManager_AddRank);
+ tolua_function(tolua_S, "GetAllGroups", tolua_cRankManager_GetAllGroups);
+ tolua_function(tolua_S, "GetAllPermissions", tolua_cRankManager_GetAllPermissions);
+ tolua_function(tolua_S, "GetAllRanks", tolua_cRankManager_GetAllRanks);
+ tolua_function(tolua_S, "GetDefaultRank", tolua_cRankManager_GetDefaultRank);
+ tolua_function(tolua_S, "GetGroupPermissions", tolua_cRankManager_GetGroupPermissions);
+ tolua_function(tolua_S, "GetPlayerGroups", tolua_cRankManager_GetPlayerGroups);
+ tolua_function(tolua_S, "GetPlayerMsgVisuals", tolua_cRankManager_GetPlayerMsgVisuals);
+ tolua_function(tolua_S, "GetPlayerPermissions", tolua_cRankManager_GetPlayerPermissions);
+ tolua_function(tolua_S, "GetPlayerRankName", tolua_cRankManager_GetPlayerRankName);
+ tolua_function(tolua_S, "GetRankGroups", tolua_cRankManager_GetRankGroups);
+ tolua_function(tolua_S, "GetRankPermissions", tolua_cRankManager_GetRankPermissions);
+ tolua_function(tolua_S, "GetRankVisuals", tolua_cRankManager_GetRankVisuals);
+ tolua_function(tolua_S, "GroupExists", tolua_cRankManager_GroupExists);
+ tolua_function(tolua_S, "IsGroupInRank", tolua_cRankManager_IsGroupInRank);
+ tolua_function(tolua_S, "IsPermissionInGroup", tolua_cRankManager_IsPermissionInGroup);
+ tolua_function(tolua_S, "IsPlayerRankSet", tolua_cRankManager_IsPlayerRankSet);
+ tolua_function(tolua_S, "RankExists", tolua_cRankManager_RankExists);
+ tolua_function(tolua_S, "RemoveGroup", tolua_cRankManager_RemoveGroup);
+ tolua_function(tolua_S, "RemoveGroupFromRank", tolua_cRankManager_RemoveGroupFromRank);
+ tolua_function(tolua_S, "RemovePermissionFromGroup", tolua_cRankManager_RemovePermissionFromGroup);
+ tolua_function(tolua_S, "RemovePlayerRank", tolua_cRankManager_RemovePlayerRank);
+ tolua_function(tolua_S, "RemoveRank", tolua_cRankManager_RemoveRank);
+ tolua_function(tolua_S, "RenameGroup", tolua_cRankManager_RenameGroup);
+ tolua_function(tolua_S, "RenameRank", tolua_cRankManager_RenameRank);
+ tolua_function(tolua_S, "SetDefaultRank", tolua_cRankManager_SetDefaultRank);
+ tolua_function(tolua_S, "SetPlayerRank", tolua_cRankManager_SetPlayerRank);
+ tolua_function(tolua_S, "SetRankVisuals", tolua_cRankManager_SetRankVisuals);
+ tolua_endmodule(tolua_S);
+}
+
+
+
+
diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h
index 2cc5cade3..c9a53346d 100644
--- a/src/Bindings/Plugin.h
+++ b/src/Bindings/Plugin.h
@@ -73,7 +73,7 @@ public:
virtual bool OnPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel) = 0;
virtual bool OnPlayerJoined (cPlayer & a_Player) = 0;
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0;
- virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition) = 0;
+ virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0;
virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0;
@@ -91,6 +91,7 @@ public:
virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos) = 0;
virtual bool OnProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity) = 0;
+ virtual bool OnServerPing (cClientHandle & a_ClientHandle, AString & a_ServerDescription, int & a_OnlinePlayersCount, int & a_MaxPlayersCount, AString & a_Favicon) = 0;
virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) = 0;
virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) = 0;
virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) = 0;
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 37db78994..2c2d05547 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -835,14 +835,14 @@ bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_Block
-bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition)
+bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition)
{
cCSLock Lock(m_CriticalSection);
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_MOVING];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
- m_LuaState.Call((int)(**itr), &a_Player, &a_OldPosition, &a_NewPosition, cLuaState::Return, res);
+ m_LuaState.Call((int)(**itr), &a_Player, a_OldPosition, a_NewPosition, cLuaState::Return, res);
if (res)
{
return true;
@@ -1193,6 +1193,26 @@ bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity
+bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_ServerDescription, int & a_OnlinePlayersCount, int & a_MaxPlayersCount, AString & a_Favicon)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SERVER_PING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_ClientHandle, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon, cLuaState::Return, res, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity)
{
cCSLock Lock(m_CriticalSection);
@@ -1570,6 +1590,7 @@ const char * cPluginLua::GetHookFnName(int a_HookType)
case cPluginManager::HOOK_PLUGINS_LOADED: return "OnPluginsLoaded";
case cPluginManager::HOOK_POST_CRAFTING: return "OnPostCrafting";
case cPluginManager::HOOK_PRE_CRAFTING: return "OnPreCrafting";
+ case cPluginManager::HOOK_SERVER_PING: return "OnServerPing";
case cPluginManager::HOOK_SPAWNED_ENTITY: return "OnSpawnedEntity";
case cPluginManager::HOOK_SPAWNED_MONSTER: return "OnSpawnedMonster";
case cPluginManager::HOOK_SPAWNING_ENTITY: return "OnSpawningEntity";
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index 702f82d84..3bd55823d 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -79,6 +79,7 @@ public:
virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) /*override*/;
virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) /*override*/;
virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) /*override*/;
+
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) /*override*/;
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) /*override*/;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) /*override*/;
@@ -89,6 +90,7 @@ public:
virtual bool OnHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) /*override*/;
virtual bool OnKilling (cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) /*override*/;
virtual bool OnLogin (cClientHandle * a_Client, int a_ProtocolVersion, const AString & a_Username) /*override*/;
+
virtual bool OnPlayerAnimation (cPlayer & a_Player, int a_Animation) /*override*/;
virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) /*override*/;
virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) /*override*/;
@@ -98,8 +100,8 @@ public:
virtual bool OnPlayerFishing (cPlayer & a_Player, cItems & a_Reward) /*override*/;
virtual bool OnPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel) /*override*/;
virtual bool OnPlayerJoined (cPlayer & a_Player) /*override*/;
- virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition) /*override*/;
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) /*override*/;
+ virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) /*override*/;
virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) /*override*/;
virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) /*override*/;
virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) /*override*/;
@@ -111,12 +113,15 @@ public:
virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) /*override*/;
virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) /*override*/;
virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) /*override*/;
+
virtual bool OnPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) /*override*/;
virtual bool OnPluginsLoaded (void) /*override*/;
virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) /*override*/;
virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) /*override*/;
virtual bool OnProjectileHitBlock (cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos) /*override*/;
virtual bool OnProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity) /*override*/;
+
+ virtual bool OnServerPing (cClientHandle & a_ClientHandle, AString & a_ServerDescription, int & a_OnlinePlayersCount, int & a_MaxPlayersCount, AString & a_Favicon) override;
virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) /*override*/;
virtual bool OnSpawnedMonster (cWorld & a_World, cMonster & a_Monster) /*override*/;
virtual bool OnSpawningEntity (cWorld & a_World, cEntity & a_Entity) /*override*/;
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index dbc359f0e..f62e6ae02 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -1189,6 +1189,25 @@ bool cPluginManager::CallHookProjectileHitEntity(cProjectileEntity & a_Projectil
+bool cPluginManager::CallHookServerPing(cClientHandle & a_ClientHandle, AString & a_ServerDescription, int & a_OnlinePlayersCount, int & a_MaxPlayersCount, AString & a_Favicon)
+{
+ FIND_HOOK(HOOK_SERVER_PING);
+ VERIFY_HOOK;
+
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnServerPing(a_ClientHandle, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginManager::CallHookSpawnedEntity(cWorld & a_World, cEntity & a_Entity)
{
FIND_HOOK(HOOK_SPAWNED_ENTITY);
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index e0573f386..cef6619d7 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -120,6 +120,7 @@ public:
HOOK_PRE_CRAFTING,
HOOK_PROJECTILE_HIT_BLOCK,
HOOK_PROJECTILE_HIT_ENTITY,
+ HOOK_SERVER_PING,
HOOK_SPAWNED_ENTITY,
HOOK_SPAWNED_MONSTER,
HOOK_SPAWNING_ENTITY,
@@ -225,6 +226,7 @@ public:
bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
bool CallHookProjectileHitBlock (cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos);
bool CallHookProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity);
+ bool CallHookServerPing (cClientHandle & a_ClientHandle, AString & a_ServerDescription, int & a_OnlinePlayersCount, int & a_MaxPlayersCount, AString & a_Favicon);
bool CallHookSpawnedEntity (cWorld & a_World, cEntity & a_Entity);
bool CallHookSpawnedMonster (cWorld & a_World, cMonster & a_Monster);
bool CallHookSpawningEntity (cWorld & a_World, cEntity & a_Entity);
diff --git a/src/Bindings/gen_LuaState_Call.lua b/src/Bindings/gen_LuaState_Call.lua
index 17bae82b3..7f62573c7 100644
--- a/src/Bindings/gen_LuaState_Call.lua
+++ b/src/Bindings/gen_LuaState_Call.lua
@@ -54,6 +54,7 @@ local Combinations =
{9, 2},
-- Special combinations:
+ {5, 5},
{7, 3},
{8, 3},
{9, 5},
@@ -108,7 +109,7 @@ local function WriteOverload(f, a_NumParams, a_NumReturns)
-- Write the function signature:
f:write("bool Call(")
- f:write("FnT a_Function")
+ f:write("const FnT & a_Function")
for i = 1, a_NumParams do
f:write(", ParamT", i, " a_Param", i)
end
@@ -182,6 +183,33 @@ for _, combination in ipairs(Combinations) do
WriteOverload(f, combination[1], combination[2])
end
+-- Generate the cLuaState::GetStackValues() multi-param templates:
+for i = 2, 6 do
+ f:write("/** Reads ", i, " consecutive values off the stack */\ntemplate <\n")
+
+ -- Write the template function header:
+ local txt = {}
+ for idx = 1, i do
+ table.insert(txt, "\ttypename ArgT" .. idx)
+ end
+ f:write(table.concat(txt, ",\n"))
+
+ -- Write the argument declarations:
+ txt = {}
+ f:write("\n>\nvoid GetStackValues(\n\tint a_BeginPos,\n")
+ for idx = 1, i do
+ table.insert(txt, "\tArgT" .. idx .. " & Arg" .. idx)
+ end
+ f:write(table.concat(txt, ",\n"))
+
+ -- Write the function body:
+ f:write("\n)\n{\n")
+ for idx = 1, i do
+ f:write("\tGetStackValue(a_BeginPos + ", idx - 1, ", Arg", idx, ");\n")
+ end
+ f:write("}\n\n\n\n\n\n")
+end
+
-- Close the generated file
f:close()
diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp
index a0dcb5ec8..ba55528b8 100644
--- a/src/BlockArea.cpp
+++ b/src/BlockArea.cpp
@@ -28,7 +28,7 @@ typedef void (CombinatorFunc)(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLE
// This wild construct allows us to pass a function argument and still have it inlined by the compiler :)
/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function
-template<bool MetasValid, CombinatorFunc Combinator>
+template <bool MetasValid, CombinatorFunc Combinator>
void InternalMergeBlocks(
BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes,
NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas,
@@ -74,7 +74,7 @@ void InternalMergeBlocks(
/// Combinator used for cBlockArea::msOverwrite merging
-template<bool MetaValid>
+template <bool MetaValid>
void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
a_DstType = a_SrcType;
@@ -89,7 +89,7 @@ void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLE
/// Combinator used for cBlockArea::msFillAir merging
-template<bool MetaValid>
+template <bool MetaValid>
void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if (a_DstType == E_BLOCK_AIR)
@@ -108,7 +108,7 @@ void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETY
/// Combinator used for cBlockArea::msImprint merging
-template<bool MetaValid>
+template <bool MetaValid>
void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if (a_SrcType != E_BLOCK_AIR)
@@ -127,7 +127,7 @@ void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETY
/// Combinator used for cBlockArea::msLake merging
-template<bool MetaValid>
+template <bool MetaValid>
void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// Sponge is the NOP block
@@ -201,7 +201,7 @@ void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE
/** Combinator used for cBlockArea::msSpongePrint merging */
-template<bool MetaValid>
+template <bool MetaValid>
void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// Sponge overwrites nothing, everything else overwrites anything
@@ -220,7 +220,7 @@ void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBB
/** Combinator used for cBlockArea::msDifference merging */
-template<bool MetaValid>
+template <bool MetaValid>
void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta)))
@@ -246,7 +246,7 @@ void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBL
/** Combinator used for cBlockArea::msMask merging */
-template<bool MetaValid>
+template <bool MetaValid>
void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// If the blocks are the same, keep the dest; otherwise replace with air
@@ -1764,7 +1764,9 @@ NIBBLETYPE cBlockArea::GetNibble(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBL
cBlockArea::cChunkReader::cChunkReader(cBlockArea & a_Area) :
m_Area(a_Area),
- m_Origin(a_Area.m_Origin.x, a_Area.m_Origin.y, a_Area.m_Origin.z)
+ m_Origin(a_Area.m_Origin.x, a_Area.m_Origin.y, a_Area.m_Origin.z),
+ m_CurrentChunkX(0),
+ m_CurrentChunkZ(0)
{
}
@@ -2119,7 +2121,7 @@ void cBlockArea::RelSetData(
-template<bool MetasValid>
+template <bool MetasValid>
void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas)
{
// Block types are compulsory, block metas are voluntary
diff --git a/src/BlockArea.h b/src/BlockArea.h
index 051d9e058..dbf7b345d 100644
--- a/src/BlockArea.h
+++ b/src/BlockArea.h
@@ -362,7 +362,7 @@ protected:
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
);
- template<bool MetasValid>
+ template <bool MetasValid>
void MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas);
// tolua_begin
} ;
diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp
index 45f8a3e4d..20702a9ac 100644
--- a/src/BlockEntities/CommandBlockEntity.cpp
+++ b/src/BlockEntities/CommandBlockEntity.cpp
@@ -13,6 +13,7 @@
#include "../Root.h"
#include "../Server.h" // ExecuteConsoleCommand()
#include "../Chunk.h"
+#include "../ChatColor.h"
@@ -187,12 +188,11 @@ void cCommandBlockEntity::SaveToJson(Json::Value & a_Value)
void cCommandBlockEntity::Execute()
{
- if (m_World != NULL)
+ ASSERT(m_World != NULL); // Execute should not be called before the command block is attached to a world
+
+ if (!m_World->AreCommandBlocksEnabled())
{
- if (!m_World->AreCommandBlocksEnabled())
- {
- return;
- }
+ return;
}
class CommandBlockOutCb :
@@ -206,15 +206,28 @@ void cCommandBlockEntity::Execute()
virtual void Out(const AString & a_Text)
{
// Overwrite field
- m_CmdBlock->SetLastOutput(a_Text);
+ m_CmdBlock->SetLastOutput(cClientHandle::FormatChatPrefix(m_CmdBlock->GetWorld()->ShouldUseChatPrefixes(), "SUCCESS", cChatColor::Green, cChatColor::White) + a_Text);
}
} CmdBlockOutCb(this);
- LOGD("cCommandBlockEntity: Executing command %s", m_Command.c_str());
-
- cServer * Server = cRoot::Get()->GetServer();
-
- Server->ExecuteConsoleCommand(m_Command, CmdBlockOutCb);
+ // Administrator commands are not executable by command blocks:
+ if (
+ (m_Command != "stop") &&
+ (m_Command != "restart") &&
+ (m_Command != "kick") &&
+ (m_Command != "ban") &&
+ (m_Command != "ipban")
+ )
+ {
+ cServer * Server = cRoot::Get()->GetServer();
+ LOGD("cCommandBlockEntity: Executing command %s", m_Command.c_str());
+ Server->ExecuteConsoleCommand(m_Command, CmdBlockOutCb);
+ }
+ else
+ {
+ SetLastOutput(cClientHandle::FormatChatPrefix(GetWorld()->ShouldUseChatPrefixes(), "FAILURE", cChatColor::Rose, cChatColor::White) + "Adminstration commands can not be executed");
+ LOGD("cCommandBlockEntity: Prevented execution of administration command %s", m_Command.c_str());
+ }
// TODO 2014-01-18 xdot: Update the signal strength.
m_Result = 0;
diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h
index 1d3844399..474c0c973 100644
--- a/src/BlockEntities/FurnaceEntity.h
+++ b/src/BlockEntities/FurnaceEntity.h
@@ -105,7 +105,7 @@ protected:
NIBBLETYPE m_BlockMeta;
/// The recipe for the current input slot
- const cFurnaceRecipe::Recipe * m_CurrentRecipe;
+ const cFurnaceRecipe::cRecipe * m_CurrentRecipe;
/// The item that is being smelted
cItem m_LastInput;
diff --git a/src/BlockID.h b/src/BlockID.h
index 08c576886..d835f9518 100644
--- a/src/BlockID.h
+++ b/src/BlockID.h
@@ -362,6 +362,8 @@ enum ENUM_ITEM_ID
E_ITEM_LEAD = 420,
E_ITEM_NAME_TAG = 421,
E_ITEM_MINECART_WITH_COMMAND_BLOCK = 422,
+ E_ITEM_RAW_MUTTON = 423,
+ E_ITEM_MUTTON = 424,
// Keep these two as the last values of the consecutive list, without a number - they will get their correct number assigned automagically by C++
// IsValidItem() depends on this!
diff --git a/src/Blocks/BlockAnvil.h b/src/Blocks/BlockAnvil.h
index 859f49b8b..6607ce5d4 100644
--- a/src/Blocks/BlockAnvil.h
+++ b/src/Blocks/BlockAnvil.h
@@ -40,14 +40,15 @@ public:
) /*override*/
{
a_BlockType = m_BlockType;
- NIBBLETYPE HighBits = a_BlockMeta & 0x0c; // Only highest two bits are preserved
+ NIBBLETYPE Meta = (NIBBLETYPE)a_Player->GetEquippedItem().m_ItemDamage;
int Direction = (int)floor(a_Player->GetYaw() * 4.0 / 360.0 + 1.5) & 0x3;
+
switch (Direction)
{
- case 0: a_BlockMeta = 0x2 | HighBits; break;
- case 1: a_BlockMeta = 0x3 | HighBits; break;
- case 2: a_BlockMeta = 0x0 | HighBits; break;
- case 3: a_BlockMeta = 0x1 | HighBits; break;
+ case 0: a_BlockMeta = 0x2 | Meta << 2; break;
+ case 1: a_BlockMeta = 0x3 | Meta << 2; break;
+ case 2: a_BlockMeta = 0x0 | Meta << 2; break;
+ case 3: a_BlockMeta = 0x1 | Meta << 2; break;
default:
{
return false;
diff --git a/src/Blocks/BlockFire.h b/src/Blocks/BlockFire.h
index a07ee0ab4..469247fc1 100644
--- a/src/Blocks/BlockFire.h
+++ b/src/Blocks/BlockFire.h
@@ -40,11 +40,6 @@ public:
FindAndSetPortalFrame(a_BlockX, a_BlockY - 1, a_BlockZ, a_ChunkInterface, a_WorldInterface);
}
- virtual void OnDigging(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) /*override*/
- {
- a_ChunkInterface.DigBlock(a_WorldInterface, a_BlockX, a_BlockY, a_BlockZ);
- }
-
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) /*override*/
{
// No pickups from this block
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index ea4010ee7..73a1deecf 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -431,10 +431,45 @@ void cBlockHandler::DropBlock(cChunkInterface & a_ChunkInterface, cWorldInterfac
else
{
// TODO: Add a proper overridable function for this
- Pickups.Add(m_BlockType, 1, Meta);
+ if (a_Digger != NULL)
+ {
+ cEnchantments Enchantments = a_Digger->GetEquippedWeapon().m_Enchantments;
+ if ((Enchantments.GetLevel(cEnchantments::enchSilkTouch) > 0) && a_Digger->IsPlayer())
+ {
+ switch (m_BlockType)
+ {
+ case E_BLOCK_CAKE:
+ case E_BLOCK_CARROTS:
+ case E_BLOCK_COCOA_POD:
+ case E_BLOCK_DOUBLE_STONE_SLAB:
+ case E_BLOCK_DOUBLE_WOODEN_SLAB:
+ case E_BLOCK_FIRE:
+ case E_BLOCK_FARMLAND:
+ case E_BLOCK_MELON_STEM:
+ case E_BLOCK_MOB_SPAWNER:
+ case E_BLOCK_NETHER_WART:
+ case E_BLOCK_POTATOES:
+ case E_BLOCK_PUMPKIN_STEM:
+ case E_BLOCK_SNOW:
+ case E_BLOCK_SUGARCANE:
+ case E_BLOCK_TALL_GRASS:
+ case E_BLOCK_CROPS:
+ {
+ // Silktouch can't be used for this blocks
+ ConvertToPickups(Pickups, Meta);
+ break;
+ };
+ default: Pickups.Add(m_BlockType, 1, Meta);
+ }
+ }
+ else
+ {
+ Pickups.Add(m_BlockType, 1, Meta);
+ }
+ }
}
}
-
+
// Allow plugins to modify the pickups:
a_BlockPluginInterface.CallHookBlockToPickups(a_Digger, a_BlockX, a_BlockY, a_BlockZ, m_BlockType, Meta, Pickups);
diff --git a/src/Blocks/BlockIce.h b/src/Blocks/BlockIce.h
index 387dd8e54..9aa769a5a 100644
--- a/src/Blocks/BlockIce.h
+++ b/src/Blocks/BlockIce.h
@@ -30,18 +30,18 @@ public:
{
return;
}
-
- BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
- if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
+
+ cEnchantments Enchantments = a_Player->GetInventory().GetEquippedItem().m_Enchantments;
+ if (Enchantments.GetLevel(cEnchantments::enchSilkTouch) == 0)
{
- return;
+ BLOCKTYPE BlockBelow = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
+ if (!cBlockInfo::FullyOccupiesVoxel(BlockBelow) && !IsBlockLiquid(BlockBelow))
+ {
+ return;
+ }
+
+ a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
+ // This is called later than the real destroying of this ice block
}
-
- a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
- // This is called later than the real destroying of this ice block
}
} ;
-
-
-
-
diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h
index e37477718..7c804367a 100644
--- a/src/Blocks/BlockLeaves.h
+++ b/src/Blocks/BlockLeaves.h
@@ -152,7 +152,7 @@ bool HasNearLog(cBlockArea & a_Area, int a_BlockX, int a_BlockY, int a_BlockZ)
a_Area.SetBlockType(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SPONGE);
for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++)
{
- for (int y = a_BlockY - i; y <= a_BlockY + i; y++)
+ for (int y = std::max(a_BlockY - i, 0); y <= std::min(a_BlockY + i, 255); y++)
{
for (int z = a_BlockZ - i; z <= a_BlockZ + i; z++)
{
diff --git a/src/Blocks/ClearMetaOnDrop.h b/src/Blocks/ClearMetaOnDrop.h
index ff9b0136f..68381de34 100644
--- a/src/Blocks/ClearMetaOnDrop.h
+++ b/src/Blocks/ClearMetaOnDrop.h
@@ -7,7 +7,7 @@
// For example to use in class Foo which should inherit Bar use
// class Foo : public cClearMetaOnDrop<Bar>;
-template<class Base>
+template <class Base>
class cClearMetaOnDrop : public Base
{
public:
diff --git a/src/Blocks/MetaRotator.h b/src/Blocks/MetaRotator.h
index 4ee98a661..0bf450e9c 100644
--- a/src/Blocks/MetaRotator.h
+++ b/src/Blocks/MetaRotator.h
@@ -20,7 +20,7 @@ Usage:
Inherit from this class providing your base class as Base, the BitMask for the direction bits in bitmask and the masked value for the directions in North, East, South, West. There is also an aptional parameter AssertIfNotMatched. Set this if it is invalid for a block to exist in any other state.
*/
-template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched = false>
+template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched = false>
class cMetaRotator : public Base
{
public:
@@ -41,7 +41,7 @@ public:
-template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
+template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCW(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
@@ -63,7 +63,7 @@ NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatc
-template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
+template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaRotateCCW(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
@@ -85,7 +85,7 @@ NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatc
-template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
+template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorXY(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
@@ -102,7 +102,7 @@ NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatc
-template<class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
+template <class Base, NIBBLETYPE BitMask, NIBBLETYPE North, NIBBLETYPE East, NIBBLETYPE South, NIBBLETYPE West, bool AssertIfNotMatched>
NIBBLETYPE cMetaRotator<Base, BitMask, North, East, South, West, AssertIfNotMatched>::MetaMirrorYZ(NIBBLETYPE a_Meta)
{
NIBBLETYPE OtherMeta = a_Meta & (~BitMask);
diff --git a/src/BoundingBox.h b/src/BoundingBox.h
index 793466302..928e62afa 100644
--- a/src/BoundingBox.h
+++ b/src/BoundingBox.h
@@ -80,6 +80,17 @@ public:
/// Calculates the intersection of the two bounding boxes; returns true if nonempty
bool Intersect(const cBoundingBox & a_Other, cBoundingBox & a_Intersection);
+ double GetMinX(void) const { return m_Min.x; }
+ double GetMinY(void) const { return m_Min.y; }
+ double GetMinZ(void) const { return m_Min.z; }
+
+ double GetMaxX(void) const { return m_Max.x; }
+ double GetMaxY(void) const { return m_Max.y; }
+ double GetMaxZ(void) const { return m_Max.z; }
+
+ const Vector3d & GetMin(void) const { return m_Min; }
+ const Vector3d & GetMax(void) const { return m_Max; }
+
protected:
Vector3d m_Min;
Vector3d m_Max;
diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp
index 64b31c60b..96556bf61 100644
--- a/src/ByteBuffer.cpp
+++ b/src/ByteBuffer.cpp
@@ -27,7 +27,7 @@
)
#define IS_LITTLE_ENDIAN
#elif ( \
- defined (__ARMEB__) || defined(__sparc) \
+ defined (__ARMEB__) || defined(__sparc) || defined(__powerpc__) || defined(__POWERPC__) \
)
#define IS_BIG_ENDIAN
#else
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 36ab8cf4e..53765685b 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -34,8 +34,6 @@ SET (SRCS
FastRandom.cpp
FurnaceRecipe.cpp
Globals.cpp
- Group.cpp
- GroupManager.cpp
Inventory.cpp
Item.cpp
ItemGrid.cpp
@@ -53,6 +51,7 @@ SET (SRCS
MonsterConfig.cpp
Noise.cpp
ProbabDistrib.cpp
+ RankManager.cpp
RCONServer.cpp
Root.cpp
Scoreboard.cpp
@@ -98,8 +97,6 @@ SET (HDRS
ForEachChunkProvider.h
FurnaceRecipe.h
Globals.h
- Group.h
- GroupManager.h
Inventory.h
Item.h
ItemGrid.h
@@ -122,6 +119,7 @@ SET (HDRS
MonsterConfig.h
Noise.h
ProbabDistrib.h
+ RankManager.h
RCONServer.h
Root.h
Scoreboard.h
@@ -258,6 +256,11 @@ set(EXECUTABLE MCServer)
if (MSVC)
get_directory_property(BINDING_OUTPUTS DIRECTORY "Bindings" DEFINITION BINDING_OUTPUTS)
get_directory_property(BINDING_DEPENDENCIES DIRECTORY "Bindings" DEFINITION BINDING_DEPENDENCIES)
+
+ # The paths in BINDING_DEPENDENCIES are relative to the Bindings folder, convert them relative to this folder:
+ foreach (dep ${BINDING_DEPENDENCIES})
+ list (APPEND BINDINGS_DEPENDENCIES "Bindings/${dep}")
+ endforeach(dep)
ADD_CUSTOM_COMMAND(
OUTPUT ${BINDING_OUTPUTS}
@@ -270,7 +273,7 @@ if (MSVC)
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/
# add any new generation dependencies here
- DEPENDS ${BINDING_DEPENDENCIES}
+ DEPENDS ${BINDINGS_DEPENDENCIES}
)
endif()
diff --git a/src/CheckBasicStyle.lua b/src/CheckBasicStyle.lua
index bf81a7cd5..b244b1fbc 100644
--- a/src/CheckBasicStyle.lua
+++ b/src/CheckBasicStyle.lua
@@ -108,7 +108,7 @@ local g_ViolationPatterns =
-- Check that all commas have spaces after them and not in front of them:
{" ,", "Extra space before a \",\""},
- {",[^%s\"%%]", "Needs a space after a \",\""}, -- Report all except >> "," << needed for splitting and >>,%s<< needed for formatting
+ {",[^%s\"%%\']", "Needs a space after a \",\""}, -- Report all except >> "," << needed for splitting and >>,%s<< needed for formatting
-- Check that opening braces are not at the end of a code line:
{"[^%s].-{\n?$", "Brace should be on a separate line"},
@@ -119,6 +119,7 @@ local g_ViolationPatterns =
{"while%(", "Needs a space after \"while\""},
{"switch%(", "Needs a space after \"switch\""},
{"catch%(", "Needs a space after \"catch\""},
+ {"template<", "Needs a space after \"template\""},
-- No space after keyword's parenthesis:
{"[^%a#]if %( ", "Remove the space after \"(\""},
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 66fd7d7fa..782229f52 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -37,6 +37,7 @@
#include "MobSpawner.h"
#include "BlockInServerPluginInterface.h"
#include "SetChunkData.h"
+#include "BoundingBox.h"
#include "json/json.h"
@@ -65,7 +66,7 @@ sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_Bloc
// cChunk:
cChunk::cChunk(
- int a_ChunkX, int a_ChunkY, int a_ChunkZ,
+ int a_ChunkX, int a_ChunkZ,
cChunkMap * a_ChunkMap, cWorld * a_World,
cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP,
cAllocationPool<cChunkData::sChunkSection> & a_Pool
@@ -77,7 +78,6 @@ cChunk::cChunk(
m_HasLoadFailed(false),
m_StayCount(0),
m_PosX(a_ChunkX),
- m_PosY(a_ChunkY),
m_PosZ(a_ChunkZ),
m_World(a_World),
m_ChunkMap(a_ChunkMap),
@@ -296,6 +296,16 @@ void cChunk::SetAllData(cSetChunkData & a_SetChunkData)
}
m_BlockEntities.clear();
std::swap(a_SetChunkData.GetBlockEntities(), m_BlockEntities);
+
+ // Check that all block entities have a valid blocktype at their respective coords (DEBUG-mode only):
+ #ifdef _DEBUG
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
+ {
+ BLOCKTYPE EntityBlockType = (*itr)->GetBlockType();
+ BLOCKTYPE WorldBlockType = GetBlock((*itr)->GetRelX(), (*itr)->GetPosY(), (*itr)->GetRelZ());
+ ASSERT(EntityBlockType == WorldBlockType);
+ } // for itr - m_BlockEntities
+ #endif // _DEBUG
// Set all block entities' World variable:
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
@@ -643,7 +653,7 @@ void cChunk::MoveEntityToNewChunk(cEntity * a_Entity)
cChunk * Neighbor = GetNeighborChunk(a_Entity->GetChunkX() * cChunkDef::Width, a_Entity->GetChunkZ() * cChunkDef::Width);
if (Neighbor == NULL)
{
- Neighbor = m_ChunkMap->GetChunkNoLoad(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
+ Neighbor = m_ChunkMap->GetChunkNoLoad(a_Entity->GetChunkX(), a_Entity->GetChunkZ());
if (Neighbor == NULL)
{
// TODO: What to do with this?
@@ -1950,6 +1960,30 @@ bool cChunk::ForEachEntity(cEntityCallback & a_Callback)
+bool cChunk::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback)
+{
+ // The entity list is locked by the parent chunkmap's CS
+ for (cEntityList::iterator itr = m_Entities.begin(), itr2 = itr; itr != m_Entities.end(); itr = itr2)
+ {
+ ++itr2;
+ cBoundingBox EntBox((*itr)->GetPosition(), (*itr)->GetWidth() / 2, (*itr)->GetHeight());
+ if (!EntBox.DoesIntersect(a_Box))
+ {
+ // The entity is not in the specified box
+ continue;
+ }
+ if (a_Callback.Item(*itr))
+ {
+ return false;
+ }
+ } // for itr - m_Entitites[]
+ return true;
+}
+
+
+
+
+
bool cChunk::DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult)
{
// The entity list is locked by the parent chunkmap's CS
@@ -2593,7 +2627,7 @@ cChunk * cChunk::GetRelNeighborChunk(int a_RelX, int a_RelZ)
int BlockZ = m_PosZ * cChunkDef::Width + a_RelZ;
int ChunkX, ChunkZ;
BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ);
- return m_ChunkMap->GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ return m_ChunkMap->GetChunkNoLoad(ChunkX, ChunkZ);
}
// Walk the neighbors:
diff --git a/src/Chunk.h b/src/Chunk.h
index 72a1f6c95..0f4928b90 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -67,7 +67,7 @@ class cChunk :
{
public:
cChunk(
- int a_ChunkX, int a_ChunkY, int a_ChunkZ, // Chunk coords
+ int a_ChunkX, int a_ChunkZ, // Chunk coords
cChunkMap * a_ChunkMap, cWorld * a_World, // Parent objects
cChunk * a_NeighborXM, cChunk * a_NeighborXP, cChunk * a_NeighborZM, cChunk * a_NeighborZP, // Neighbor chunks
cAllocationPool<cChunkData::sChunkSection> & a_Pool
@@ -155,7 +155,7 @@ public:
void FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, BLOCKTYPE a_BlockMeta, bool a_SendToClients = true); // Doesn't force block updates on neighbors, use for simple changes such as grass growing etc.
BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const;
- BLOCKTYPE GetBlock(Vector3i a_cords) const { return GetBlock(a_cords.x, a_cords.y, a_cords.z);}
+ BLOCKTYPE GetBlock(const Vector3i & a_RelCoords) const { return GetBlock(a_RelCoords.x, a_RelCoords.y, a_RelCoords.z); }
void GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
void GetBlockInfo (int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight);
@@ -216,6 +216,10 @@ public:
/** Calls the callback for each entity; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntity(cEntityCallback & a_Callback); // Lua-accessible
+ /** Calls the callback for each entity that has a nonempty intersection with the specified boundingbox.
+ Returns true if all entities processed, false if the callback aborted by returning true. */
+ bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback); // Lua-accessible
+
/** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found. */
bool DoWithEntityByID(int a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult); // Lua-accessible
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
index dbb782d26..f4ed66c4b 100644
--- a/src/ChunkDef.h
+++ b/src/ChunkDef.h
@@ -16,11 +16,6 @@
-/** This is really only a placeholder to be used in places where we need to "make up" a chunk's Y coord.
-It will help us when the new chunk format comes out and we need to patch everything up for compatibility.
-*/
-#define ZERO_CHUNK_Y 0
-
// Used to smoothly convert to new axis ordering. One will be removed when deemed stable.
#define AXIS_ORDER_YZX 1 // Original (1.1-)
#define AXIS_ORDER_XZY 2 // New (1.2+)
@@ -197,32 +192,32 @@ public:
inline static int GetHeight(const HeightMap & a_HeightMap, int a_X, int a_Z)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
return a_HeightMap[a_X + Width * a_Z];
}
inline static void SetHeight(HeightMap & a_HeightMap, int a_X, int a_Z, unsigned char a_Height)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
a_HeightMap[a_X + Width * a_Z] = a_Height;
}
inline static EMCSBiome GetBiome(const BiomeMap & a_BiomeMap, int a_X, int a_Z)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
return a_BiomeMap[a_X + Width * a_Z];
}
inline static void SetBiome(BiomeMap & a_BiomeMap, int a_X, int a_Z, EMCSBiome a_Biome)
{
- ASSERT((a_X >= 0) && (a_X <= Width));
- ASSERT((a_Z >= 0) && (a_Z <= Width));
+ ASSERT((a_X >= 0) && (a_X < Width));
+ ASSERT((a_Z >= 0) && (a_Z < Width));
a_BiomeMap[a_X + Width * a_Z] = a_Biome;
}
@@ -377,14 +372,13 @@ class cChunkCoords
{
public:
int m_ChunkX;
- int m_ChunkY;
int m_ChunkZ;
- cChunkCoords(int a_ChunkX, int a_ChunkY, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ) {}
+ cChunkCoords(int a_ChunkX, int a_ChunkZ) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ) {}
bool operator == (const cChunkCoords & a_Other) const
{
- return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkY == a_Other.m_ChunkY) && (m_ChunkZ == a_Other.m_ChunkZ));
+ return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkZ == a_Other.m_ChunkZ));
}
} ;
@@ -395,6 +389,27 @@ typedef std::vector<cChunkCoords> cChunkCoordsVector;
+class cChunkCoordsWithBool
+{
+public:
+ int m_ChunkX;
+ int m_ChunkZ;
+ bool m_ForceGenerate;
+
+ cChunkCoordsWithBool(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ), m_ForceGenerate(a_ForceGenerate){}
+
+ bool operator == (const cChunkCoordsWithBool & a_Other) const
+ {
+ return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkZ == a_Other.m_ChunkZ) && (m_ForceGenerate == a_Other.m_ForceGenerate));
+ }
+};
+
+typedef std::list<cChunkCoordsWithBool> cChunkCoordsWithBoolList;
+
+
+
+
+
/// Interface class used as a callback for operations that involve chunk coords
class cChunkCoordCallback
{
@@ -419,7 +434,7 @@ public:
X Data;
cCoordWithData(int a_X, int a_Y, int a_Z) :
- x(a_X), y(a_Y), z(a_Z)
+ x(a_X), y(a_Y), z(a_Z), Data()
{
}
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 15c0481eb..f6373bc37 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -143,7 +143,7 @@ cChunkMap::cChunkLayer * cChunkMap::GetLayerForChunk(int a_ChunkX, int a_ChunkZ)
-cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkZ)
{
// No need to lock m_CSLayers, since it's already locked by the operation that called us
ASSERT(m_CSLayers.IsLockedByCurrentThread());
@@ -155,14 +155,14 @@ cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
return NULL;
}
- cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+ cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkZ);
if (Chunk == NULL)
{
return NULL;
}
if (!(Chunk->IsValid()))
{
- m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, true);
+ m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkZ, true);
}
return Chunk;
}
@@ -171,7 +171,7 @@ cChunkPtr cChunkMap::GetChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
-cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkZ)
{
// No need to lock m_CSLayers, since it's already locked by the operation that called us
cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ);
@@ -181,14 +181,14 @@ cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ)
return NULL;
}
- cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+ cChunkPtr Chunk = Layer->GetChunk(a_ChunkX, a_ChunkZ);
if (Chunk == NULL)
{
return NULL;
}
if (!(Chunk->IsValid()))
{
- m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ, false);
+ m_World->GetStorage().QueueLoadChunk(a_ChunkX, a_ChunkZ, false);
}
return Chunk;
@@ -198,7 +198,7 @@ cChunkPtr cChunkMap::GetChunkNoGen( int a_ChunkX, int a_ChunkY, int a_ChunkZ)
-cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkZ)
{
// No need to lock m_CSLayers, since it's already locked by the operation that called us
cChunkLayer * Layer = GetLayerForChunk( a_ChunkX, a_ChunkZ);
@@ -208,7 +208,7 @@ cChunkPtr cChunkMap::GetChunkNoLoad( int a_ChunkX, int a_ChunkY, int a_ChunkZ)
return NULL;
}
- return Layer->GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+ return Layer->GetChunk(a_ChunkX, a_ChunkZ);
}
@@ -222,7 +222,7 @@ bool cChunkMap::LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTY
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return false;
@@ -244,7 +244,7 @@ bool cChunkMap::LockedGetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLO
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return false;
@@ -265,7 +265,7 @@ bool cChunkMap::LockedGetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIB
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return false;
@@ -284,7 +284,7 @@ bool cChunkMap::LockedSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTY
// We already have m_CSLayers locked since this can be called only from within the tick thread
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return false;
@@ -303,7 +303,7 @@ bool cChunkMap::LockedFastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLO
// We already have m_CSLayers locked since this can be called only from within the tick thread
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return false;
@@ -336,7 +336,7 @@ cChunk * cChunkMap::FindChunk(int a_ChunkX, int a_ChunkZ)
void cChunkMap::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity * a_Vehicle)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -356,7 +356,7 @@ void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, c
x = a_BlockX;
z = a_BlockZ;
cChunkDef::BlockToChunk(x, z, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return;
@@ -375,7 +375,7 @@ void cChunkMap::BroadcastBlockBreakAnimation(int a_entityID, int a_blockX, int a
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_blockX, a_blockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return;
@@ -393,7 +393,7 @@ void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, c
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return;
@@ -408,7 +408,7 @@ void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, c
void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, 0, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if (Chunk == NULL)
{
return;
@@ -424,7 +424,7 @@ void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSeriali
void cChunkMap::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -440,7 +440,7 @@ void cChunkMap::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer &
void cChunkMap::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -456,7 +456,7 @@ void cChunkMap::BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHa
void cChunkMap::BroadcastEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -472,7 +472,7 @@ void cChunkMap::BroadcastEntityEffect(const cEntity & a_Entity, int a_EffectID,
void cChunkMap::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -488,7 +488,7 @@ void cChunkMap::BroadcastEntityEquipment(const cEntity & a_Entity, short a_SlotN
void cChunkMap::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -504,7 +504,7 @@ void cChunkMap::BroadcastEntityHeadLook(const cEntity & a_Entity, const cClientH
void cChunkMap::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -520,7 +520,7 @@ void cChunkMap::BroadcastEntityLook(const cEntity & a_Entity, const cClientHandl
void cChunkMap::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -536,7 +536,7 @@ void cChunkMap::BroadcastEntityMetadata(const cEntity & a_Entity, const cClientH
void cChunkMap::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -552,7 +552,7 @@ void cChunkMap::BroadcastEntityRelMove(const cEntity & a_Entity, char a_RelX, ch
void cChunkMap::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -568,7 +568,7 @@ void cChunkMap::BroadcastEntityRelMoveLook(const cEntity & a_Entity, char a_RelX
void cChunkMap::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -584,7 +584,7 @@ void cChunkMap::BroadcastEntityStatus(const cEntity & a_Entity, char a_Status, c
void cChunkMap::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -600,7 +600,7 @@ void cChunkMap::BroadcastEntityVelocity(const cEntity & a_Entity, const cClientH
void cChunkMap::BroadcastEntityAnimation(const cEntity & a_Entity, char a_Animation, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -619,7 +619,7 @@ void cChunkMap::BroadcastParticleEffect(const AString & a_ParticleName, float a_
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk((int) a_SrcX, (int) a_SrcZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return;
@@ -636,7 +636,7 @@ void cChunkMap::BroadcastRemoveEntityEffect(const cEntity & a_Entity, int a_Effe
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -655,7 +655,7 @@ void cChunkMap::BroadcastSoundEffect(const AString & a_SoundName, double a_X, do
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk((int)std::floor(a_X), (int)std::floor(a_Z), ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return;
@@ -674,7 +674,7 @@ void cChunkMap::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_S
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_SrcX, a_SrcZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return;
@@ -690,7 +690,7 @@ void cChunkMap::BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_S
void cChunkMap::BroadcastSpawnEntity(cEntity & a_Entity, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), ZERO_CHUNK_Y, a_Entity.GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity.GetChunkX(), a_Entity.GetChunkZ());
if (Chunk == NULL)
{
return;
@@ -708,7 +708,7 @@ void cChunkMap::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, c
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return;
@@ -727,7 +727,7 @@ void cChunkMap::BroadcastUseBed(const cEntity & a_Entity, int a_BlockX, int a_Bl
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return;
@@ -745,7 +745,7 @@ void cChunkMap::SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClien
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return;
@@ -763,7 +763,7 @@ void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, i
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return;
@@ -778,7 +778,7 @@ void cChunkMap::UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, i
bool cChunkMap::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
if (Chunk == NULL)
{
return false;
@@ -795,7 +795,7 @@ void cChunkMap::WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ)
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, 0, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return;
@@ -824,7 +824,7 @@ void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_M
int MaxZ = std::min(a_MaxBlockZ, z * cChunkDef::Width + cChunkDef::Width - 1);
for (int x = MinChunkX; x <= MaxChunkX; x++)
{
- cChunkPtr Chunk = GetChunkNoGen(x, 0, z);
+ cChunkPtr Chunk = GetChunkNoGen(x, z);
if ((Chunk == NULL) || !Chunk->IsValid())
{
continue;
@@ -852,7 +852,7 @@ void cChunkMap::WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_M
void cChunkMap::MarkRedstoneDirty(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return;
@@ -867,7 +867,7 @@ void cChunkMap::MarkRedstoneDirty(int a_ChunkX, int a_ChunkZ)
void cChunkMap::MarkChunkDirty(int a_ChunkX, int a_ChunkZ, bool a_MarkRedstoneDirty)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return;
@@ -886,7 +886,7 @@ void cChunkMap::MarkChunkDirty(int a_ChunkX, int a_ChunkZ, bool a_MarkRedstoneDi
void cChunkMap::MarkChunkSaving(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return;
@@ -901,7 +901,7 @@ void cChunkMap::MarkChunkSaving(int a_ChunkX, int a_ChunkZ)
void cChunkMap::MarkChunkSaved (int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return;
@@ -919,7 +919,7 @@ void cChunkMap::SetChunkData(cSetChunkData & a_SetChunkData)
int ChunkZ = a_SetChunkData.GetChunkZ();
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return;
@@ -964,7 +964,7 @@ void cChunkMap::ChunkLighted(
)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
if (Chunk == NULL)
{
return;
@@ -980,7 +980,7 @@ void cChunkMap::ChunkLighted(
bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -996,7 +996,7 @@ bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_
bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -1012,7 +1012,7 @@ bool cChunkMap::GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blo
bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
return (Chunk != NULL) && Chunk->IsValid();
}
@@ -1023,7 +1023,7 @@ bool cChunkMap::IsChunkValid(int a_ChunkX, int a_ChunkZ)
bool cChunkMap::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
return (Chunk != NULL) && Chunk->HasAnyClients();
}
@@ -1038,7 +1038,7 @@ int cChunkMap::GetHeight(int a_BlockX, int a_BlockZ)
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ, BlockY = 0;
cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
if (Chunk == NULL)
{
return 0;
@@ -1065,7 +1065,7 @@ bool cChunkMap::TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height)
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ, BlockY = 0;
cChunkDef::AbsoluteToRelative(a_BlockX, BlockY, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -1088,7 +1088,7 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
int ChunkX = a_BlockList.front().ChunkX;
int ChunkZ = a_BlockList.front().ChunkZ;
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();)
@@ -1135,7 +1135,7 @@ void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player)
int BlockX = (int)(a_Player->GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway
int BlockY = (int)(a_Player->GetPosY());
int BlockZ = (int)(a_Player->GetPosZ());
- int ChunkX, ChunkZ, ChunkY = ZERO_CHUNK_Y;
+ int ChunkX = 0, ChunkZ = 0;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
int OtherChunkX = ChunkX + ((BlockX > 8) ? 1 : -1);
int OtherChunkZ = ChunkZ + ((BlockZ > 8) ? 1 : -1);
@@ -1144,13 +1144,13 @@ void cChunkMap::CollectPickupsByPlayer(cPlayer * a_Player)
// The only time the chunks are not valid is when the player is downloading the initial world and they should not call this at that moment
cCSLock Lock(m_CSLayers);
- GetChunkNoLoad(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer(a_Player);
+ GetChunkNoLoad(ChunkX, ChunkZ)->CollectPickupsByPlayer(a_Player);
// Check the neighboring chunks as well:
- GetChunkNoLoad(OtherChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer (a_Player);
- GetChunkNoLoad(OtherChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player);
- GetChunkNoLoad(ChunkX, ChunkY, ChunkZ)->CollectPickupsByPlayer (a_Player);
- GetChunkNoLoad(ChunkX, ChunkY, OtherChunkZ)->CollectPickupsByPlayer(a_Player);
+ GetChunkNoLoad(OtherChunkX, ChunkZ)->CollectPickupsByPlayer (a_Player);
+ GetChunkNoLoad(OtherChunkX, OtherChunkZ)->CollectPickupsByPlayer(a_Player);
+ GetChunkNoLoad(ChunkX, ChunkZ)->CollectPickupsByPlayer (a_Player);
+ GetChunkNoLoad(ChunkX, OtherChunkZ)->CollectPickupsByPlayer(a_Player);
}
@@ -1177,7 +1177,7 @@ BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
return Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
@@ -1206,7 +1206,7 @@ NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ)
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
return Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ);
@@ -1224,7 +1224,7 @@ NIBBLETYPE cChunkMap::GetBlockSkyLight(int a_BlockX, int a_BlockY, int a_BlockZ)
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
return Chunk->GetSkyLight(a_BlockX, a_BlockY, a_BlockZ);
@@ -1242,7 +1242,7 @@ NIBBLETYPE cChunkMap::GetBlockBlockLight(int a_BlockX, int a_BlockY, int a_Block
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
return Chunk->GetBlockLight(a_BlockX, a_BlockY, a_BlockZ);
@@ -1261,7 +1261,7 @@ void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYP
// a_BlockXYZ now contains relative coords!
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
Chunk->SetMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta);
@@ -1284,7 +1284,7 @@ void cChunkMap::SetBlock(cWorldInterface & a_WorldInterface, int a_BlockX, int a
cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta, a_SendToClients);
@@ -1303,7 +1303,7 @@ void cChunkMap::QueueSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYP
cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
Chunk->QueueSetBlock(X, Y, Z, a_BlockType, a_BlockMeta, a_Tick, a_PreviousBlockType);
@@ -1320,7 +1320,7 @@ bool cChunkMap::GetBlockTypeMeta(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCK
cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
Chunk->GetBlockTypeMeta(X, Y, Z, a_BlockType, a_BlockMeta);
@@ -1339,7 +1339,7 @@ bool cChunkMap::GetBlockInfo(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE
cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk( ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
Chunk->GetBlockInfo(X, Y, Z, a_BlockType, a_Meta, a_SkyLight, a_BlockLight);
@@ -1357,7 +1357,7 @@ void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_Filt
cCSLock Lock(m_CSLayers);
for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ);
+ cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
continue;
@@ -1378,7 +1378,7 @@ void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks)
cCSLock Lock(m_CSLayers);
for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ);
+ cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
continue;
@@ -1413,7 +1413,7 @@ EMCSBiome cChunkMap::GetBiomeAt (int a_BlockX, int a_BlockZ)
cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
return Chunk->GetBiomeAt(X, Z);
@@ -1434,7 +1434,7 @@ bool cChunkMap::SetBiomeAt(int a_BlockX, int a_BlockZ, EMCSBiome a_Biome)
cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
if ((Chunk != NULL) && Chunk->IsValid())
{
Chunk->SetBiomeAt(X, Z, a_Biome);
@@ -1467,7 +1467,7 @@ bool cChunkMap::SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMC
{
int MinRelZ = (z == MinChunkZ) ? MinZ : 0;
int MaxRelZ = (z == MaxChunkZ) ? MaxZ : cChunkDef::Width - 1;
- cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z);
+ cChunkPtr Chunk = GetChunkNoLoad(x, z);
if ((Chunk != NULL) && Chunk->IsValid())
{
Chunk->SetAreaBiome(MinRelX, MaxRelX, MinRelZ, MaxRelZ, a_Biome);
@@ -1491,7 +1491,7 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
cCSLock Lock(m_CSLayers);
for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
{
- cChunkPtr Chunk = GetChunk(itr->ChunkX, ZERO_CHUNK_Y, itr->ChunkZ);
+ cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
if (!a_ContinueOnFailure)
@@ -1519,7 +1519,7 @@ bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr DestChunk = GetChunk( ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr DestChunk = GetChunk( ChunkX, ChunkZ);
if ((DestChunk == NULL) || !DestChunk->IsValid())
{
return false;
@@ -1542,7 +1542,7 @@ void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player)
cChunkDef::AbsoluteToRelative(a_X, a_Y, a_Z, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
if ((Chunk != NULL) && (Chunk->IsValid()))
{
Chunk->SendBlockTo(a_X, a_Y, a_Z, a_Player->GetClientHandle());
@@ -1556,12 +1556,12 @@ void cChunkMap::SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player)
void cChunkMap::CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, ZERO_CHUNK_Y, a_ChunkZ1);
+ cChunkPtr Chunk1 = GetChunkNoGen(a_ChunkX1, a_ChunkZ1);
if (Chunk1 == NULL)
{
return;
}
- cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, ZERO_CHUNK_Y, a_ChunkZ2);
+ cChunkPtr Chunk2 = GetChunkNoGen(a_ChunkX2, a_ChunkZ2);
if (Chunk2 == NULL)
{
return;
@@ -1623,7 +1623,7 @@ void cChunkMap::CompareChunkClients(cChunk * a_Chunk1, cChunk * a_Chunk2, cClien
bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunk(a_ChunkX, a_ChunkZ);
if (Chunk == NULL)
{
return false;
@@ -1638,7 +1638,7 @@ bool cChunkMap::AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Cli
void cChunkMap::RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if (Chunk == NULL)
{
return;
@@ -1667,7 +1667,7 @@ void cChunkMap::RemoveClientFromChunks(cClientHandle * a_Client)
void cChunkMap::AddEntity(cEntity * a_Entity)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), a_Entity->GetChunkZ());
if (
(Chunk == NULL) || // Chunk not present at all
(!Chunk->IsValid() && !a_Entity->IsPlayer()) // Chunk present, but no valid data; players need to spawn in such chunks (#953)
@@ -1688,7 +1688,7 @@ void cChunkMap::AddEntity(cEntity * a_Entity)
void cChunkMap::AddEntityIfNotPresent(cEntity * a_Entity)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), a_Entity->GetChunkZ());
if (
(Chunk == NULL) || // Chunk not present at all
(!Chunk->IsValid() && !a_Entity->IsPlayer()) // Chunk present, but no valid data; players need to spawn in such chunks (#953)
@@ -1729,7 +1729,7 @@ bool cChunkMap::HasEntity(int a_UniqueID)
void cChunkMap::RemoveEntity(cEntity * a_Entity)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
+ cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), a_Entity->GetChunkZ());
// Even if a chunk is not valid, it may still contain entities such as players; make sure to remove them (#1190)
if (Chunk == NULL)
@@ -1763,7 +1763,7 @@ bool cChunkMap::ForEachEntity(cEntityCallback & a_Callback)
bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -1775,6 +1775,38 @@ bool cChunkMap::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback
+bool cChunkMap::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback)
+{
+ // Calculate the chunk range for the box:
+ int MinChunkX = (int)floor(a_Box.GetMinX() / cChunkDef::Width);
+ int MinChunkZ = (int)floor(a_Box.GetMinZ() / cChunkDef::Width);
+ int MaxChunkX = (int)floor((a_Box.GetMaxX() + cChunkDef::Width) / cChunkDef::Width);
+ int MaxChunkZ = (int)floor((a_Box.GetMaxZ() + cChunkDef::Width) / cChunkDef::Width);
+
+ // Iterate over each chunk in the range:
+ cCSLock Lock(m_CSLayers);
+ for (int z = MinChunkZ; z <= MaxChunkZ; z++)
+ {
+ for (int x = MinChunkX; x <= MaxChunkX; x++)
+ {
+ cChunkPtr Chunk = GetChunkNoGen(x, z);
+ if ((Chunk == NULL) || !Chunk->IsValid())
+ {
+ continue;
+ }
+ if (!Chunk->ForEachEntityInBox(a_Box, a_Callback))
+ {
+ return false;
+ }
+ } // for x
+ } // for z
+ return true;
+}
+
+
+
+
+
void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlocksAffected)
{
// Don't explode if outside of Y range (prevents the following test running into unallocated memory):
@@ -1880,21 +1912,19 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
}
else if ((m_World->GetTNTShrapnelLevel() > slNone) && (m_World->GetTickRandomNumber(100) < 20)) // 20% chance of flinging stuff around
{
- if (!cBlockInfo::FullyOccupiesVoxel(Block))
+ // If the block is shrapnel-able, make a falling block entity out of it:
+ if (
+ ((m_World->GetTNTShrapnelLevel() == slAll) && cBlockInfo::FullyOccupiesVoxel(Block)) ||
+ ((m_World->GetTNTShrapnelLevel() == slGravityAffectedOnly) && ((Block == E_BLOCK_SAND) || (Block == E_BLOCK_GRAVEL)))
+ )
{
- break;
+ m_World->SpawnFallingBlock(bx + x, by + y + 5, bz + z, Block, area.GetBlockMeta(bx + x, by + y, bz + z));
}
- else if ((m_World->GetTNTShrapnelLevel() == slGravityAffectedOnly) && ((Block != E_BLOCK_SAND) && (Block != E_BLOCK_GRAVEL)))
- {
- break;
- }
- m_World->SpawnFallingBlock(bx + x, by + y + 5, bz + z, Block, area.GetBlockMeta(bx + x, by + y, bz + z));
}
area.SetBlockTypeMeta(bx + x, by + y, bz + z, E_BLOCK_AIR, 0);
a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
break;
-
}
} // switch (BlockType)
} // for z
@@ -1916,51 +1946,31 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
virtual bool Item(cEntity * a_Entity) /*override*/
{
- if (a_Entity->IsPickup())
- {
- if (((cPickup *)a_Entity)->GetAge() < 20) // If pickup age is smaller than one second, it is invincible (so we don't kill pickups that were just spawned)
- {
- return false;
- }
- }
-
- Vector3d EntityPos = a_Entity->GetPosition();
- cBoundingBox bbEntity(EntityPos, a_Entity->GetWidth() / 2, a_Entity->GetHeight());
-
- if (!m_bbTNT.IsInside(bbEntity)) // IsInside actually acts like DoesSurround
+ if (a_Entity->IsPickup() && (a_Entity->GetTicksAlive() < 20))
{
+ // If pickup age is smaller than one second, it is invincible (so we don't kill pickups that were just spawned)
return false;
}
-
- Vector3d AbsoluteEntityPos(abs(EntityPos.x), abs(EntityPos.y), abs(EntityPos.z));
-
- // Work out how far we are from the edge of the TNT's explosive effect
- AbsoluteEntityPos -= m_ExplosionPos;
-
- // All to positive
- AbsoluteEntityPos.x = abs(AbsoluteEntityPos.x);
- AbsoluteEntityPos.y = abs(AbsoluteEntityPos.y);
- AbsoluteEntityPos.z = abs(AbsoluteEntityPos.z);
-
- double FinalDamage = (((1 / AbsoluteEntityPos.x) + (1 / AbsoluteEntityPos.y) + (1 / AbsoluteEntityPos.z)) * 2) * m_ExplosionSize;
-
- // Clip damage values
- FinalDamage = Clamp(FinalDamage, 0.0, (double)a_Entity->GetMaxHealth());
+ Vector3d DistanceFromExplosion = a_Entity->GetPosition() - m_ExplosionPos;
+
if (!a_Entity->IsTNT() && !a_Entity->IsFallingBlock()) // Don't apply damage to other TNT entities and falling blocks, they should be invincible
{
- a_Entity->TakeDamage(dtExplosion, NULL, (int)FinalDamage, 0);
- }
+ cBoundingBox bbEntity(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight());
- // Apply force to entities around the explosion - code modified from World.cpp DoExplosionAt()
- Vector3d distance_explosion = a_Entity->GetPosition() - m_ExplosionPos;
- if (distance_explosion.SqrLength() < 4096.0)
- {
- distance_explosion.Normalize();
- distance_explosion *= m_ExplosionSize * m_ExplosionSize;
+ if (!m_bbTNT.IsInside(bbEntity)) // If bbEntity is inside bbTNT, not vice versa!
+ {
+ return false;
+ }
- a_Entity->AddSpeed(distance_explosion);
+ // Ensure that the damage dealt is inversely proportional to the distance to the TNT centre - the closer a player is, the harder they are hit
+ a_Entity->TakeDamage(dtExplosion, NULL, (int)((1 / DistanceFromExplosion.Length()) * 6 * m_ExplosionSize), 0);
}
+
+ // Apply force to entities around the explosion - code modified from World.cpp DoExplosionAt()
+ DistanceFromExplosion.Normalize();
+ DistanceFromExplosion *= m_ExplosionSize * m_ExplosionSize;
+ a_Entity->AddSpeed(DistanceFromExplosion);
return false;
}
@@ -2011,7 +2021,7 @@ bool cChunkMap::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback)
bool cChunkMap::ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2026,7 +2036,7 @@ bool cChunkMap::ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEnti
bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2041,7 +2051,7 @@ bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback &
bool cChunkMap::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2056,7 +2066,7 @@ bool cChunkMap::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCa
bool cChunkMap::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2071,7 +2081,7 @@ bool cChunkMap::ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallba
bool cChunkMap::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2086,7 +2096,7 @@ bool cChunkMap::ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpens
bool cChunkMap::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2104,7 +2114,7 @@ bool cChunkMap::DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cB
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2122,7 +2132,7 @@ bool cChunkMap::DoWithBeaconAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBeacon
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2140,7 +2150,7 @@ bool cChunkMap::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCa
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2158,7 +2168,7 @@ bool cChunkMap::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDis
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2176,7 +2186,7 @@ bool cChunkMap::DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropp
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2194,7 +2204,7 @@ bool cChunkMap::DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cD
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2212,7 +2222,7 @@ bool cChunkMap::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurna
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2229,7 +2239,7 @@ bool cChunkMap::DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNot
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2246,7 +2256,7 @@ bool cChunkMap::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, c
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2264,7 +2274,7 @@ bool cChunkMap::DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHe
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2282,7 +2292,7 @@ bool cChunkMap::DoWithFlowerPotAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFlo
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2300,7 +2310,7 @@ bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString &
int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2312,10 +2322,10 @@ bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString &
-void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
- GetChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+ GetChunk(a_ChunkX, a_ChunkZ);
}
@@ -2323,11 +2333,11 @@ void cChunkMap::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
/// Loads the chunk synchronously, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before)
-bool cChunkMap::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+bool cChunkMap::LoadChunk(int a_ChunkX, int a_ChunkZ)
{
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkY, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if (Chunk == NULL)
{
// Internal error
@@ -2344,7 +2354,7 @@ bool cChunkMap::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
return false;
}
}
- return m_World->GetStorage().LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+ return m_World->GetStorage().LoadChunk(a_ChunkX, a_ChunkZ);
}
@@ -2356,7 +2366,7 @@ void cChunkMap::LoadChunks(const cChunkCoordsList & a_Chunks)
{
for (cChunkCoordsList::const_iterator itr = a_Chunks.begin(); itr != a_Chunks.end(); ++itr)
{
- LoadChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ LoadChunk(itr->m_ChunkX, itr->m_ChunkZ);
} // for itr - a_Chunks[]
}
@@ -2364,10 +2374,10 @@ void cChunkMap::LoadChunks(const cChunkCoordsList & a_Chunks)
-void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cChunkMap::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkY, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
if (Chunk == NULL)
{
return;
@@ -2384,7 +2394,7 @@ bool cChunkMap::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const ASt
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return false;
@@ -2399,7 +2409,7 @@ bool cChunkMap::SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const ASt
void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
if (Chunk == NULL)
{
// Not present
@@ -2415,7 +2425,7 @@ void cChunkMap::MarkChunkRegenerating(int a_ChunkX, int a_ChunkZ)
bool cChunkMap::IsChunkLighted(int a_ChunkX, int a_ChunkZ)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
if (Chunk == NULL)
{
// Not present
@@ -2436,7 +2446,7 @@ bool cChunkMap::ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinCh
{
for (int x = a_MinChunkX; x <= a_MaxChunkX; x++)
{
- cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z);
+ cChunkPtr Chunk = GetChunkNoLoad(x, z);
if ((Chunk == NULL) || (!Chunk->IsValid()))
{
// Not present / not valid
@@ -2478,7 +2488,7 @@ bool cChunkMap::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBl
{
for (int x = MinChunkX; x <= MaxChunkX; x++)
{
- cChunkPtr Chunk = GetChunkNoLoad(x, ZERO_CHUNK_Y, z);
+ cChunkPtr Chunk = GetChunkNoLoad(x, z);
if ((Chunk == NULL) || (!Chunk->IsValid()))
{
// Not present / not valid
@@ -2519,7 +2529,7 @@ void cChunkMap::GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCK
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk != NULL)
{
Chunk->GrowMelonPumpkin(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_Rand);
@@ -2536,7 +2546,7 @@ void cChunkMap::GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Nu
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk != NULL)
{
Chunk->GrowSugarcane(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
@@ -2553,7 +2563,7 @@ void cChunkMap::GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBl
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk != NULL)
{
Chunk->GrowCactus(a_BlockX, a_BlockY, a_BlockZ, a_NumBlocksToGrow);
@@ -2570,7 +2580,7 @@ void cChunkMap::SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ)
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk != NULL)
{
Chunk->SetNextBlockTick(a_BlockX, a_BlockY, a_BlockZ);
@@ -2625,7 +2635,7 @@ void cChunkMap::TickBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if ((Chunk == NULL) || !Chunk->IsValid())
{
return;
@@ -2694,7 +2704,7 @@ void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
// a_BlockXYZ now contains relative coords!
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(ChunkX, ChunkZ);
if (Chunk != NULL)
{
Chunk->QueueTickBlock(a_BlockX, a_BlockY, a_BlockZ);
@@ -2708,7 +2718,7 @@ void cChunkMap::QueueTickBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
void cChunkMap::SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked)
{
cCSLock Lock(m_CSLayers);
- cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(a_ChunkX, a_ChunkZ);
if (Chunk != NULL)
{
Chunk->SetAlwaysTicked(a_AlwaysTicked);
@@ -2753,7 +2763,7 @@ cChunkMap::cChunkLayer::~cChunkLayer()
-cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkZ)
{
// Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check
@@ -2773,7 +2783,7 @@ cChunkPtr cChunkMap::cChunkLayer::GetChunk( int a_ChunkX, int a_ChunkY, int a_Ch
cChunk * neixp = (LocalX < LAYER_SIZE - 1) ? m_Chunks[Index + 1] : m_Parent->FindChunk(a_ChunkX + 1, a_ChunkZ);
cChunk * neizm = (LocalZ > 0) ? m_Chunks[Index - LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX, a_ChunkZ - 1);
cChunk * neizp = (LocalZ < LAYER_SIZE - 1) ? m_Chunks[Index + LAYER_SIZE] : m_Parent->FindChunk(a_ChunkX, a_ChunkZ + 1);
- m_Chunks[Index] = new cChunk(a_ChunkX, 0, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp, m_Pool);
+ m_Chunks[Index] = new cChunk(a_ChunkX, a_ChunkZ, m_Parent, m_Parent->GetWorld(), neixm, neixp, neizm, neizp, m_Pool);
}
return m_Chunks[Index];
}
@@ -2973,7 +2983,7 @@ void cChunkMap::cChunkLayer::Save(void)
{
if ((m_Chunks[i] != NULL) && m_Chunks[i]->IsValid() && m_Chunks[i]->IsDirty())
{
- World->GetStorage().QueueSaveChunk(m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosY(), m_Chunks[i]->GetPosZ());
+ World->GetStorage().QueueSaveChunk(m_Chunks[i]->GetPosX(), m_Chunks[i]->GetPosZ());
}
} // for i - m_Chunks[]
}
@@ -3046,7 +3056,7 @@ void cChunkMap::AddChunkStay(cChunkStay & a_ChunkStay)
const cChunkCoordsVector & WantedChunks = a_ChunkStay.GetChunks();
for (cChunkCoordsVector::const_iterator itr = WantedChunks.begin(); itr != WantedChunks.end(); ++itr)
{
- cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ);
if (Chunk == NULL)
{
continue;
@@ -3095,7 +3105,7 @@ void cChunkMap::DelChunkStay(cChunkStay & a_ChunkStay)
const cChunkCoordsVector & Chunks = a_ChunkStay.GetChunks();
for (cChunkCoordsVector::const_iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr)
{
- cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoLoad(itr->m_ChunkX, itr->m_ChunkZ);
if (Chunk == NULL)
{
continue;
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 777c7074b..dd72e0b39 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -36,6 +36,7 @@ class cBlockArea;
class cMobCensus;
class cMobSpawner;
class cSetChunkData;
+class cBoundingBox;
typedef std::list<cClientHandle *> cClientHandleList;
typedef cChunk * cChunkPtr;
@@ -209,6 +210,11 @@ public:
/** Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Lua-accessible
+ /** Calls the callback for each entity that has a nonempty intersection with the specified boundingbox.
+ Returns true if all entities processed, false if the callback aborted by returning true.
+ If any chunk in the box is missing, ignores the entities in that chunk silently. */
+ bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback); // Lua-accessible
+
/** Destroys and returns a list of blocks destroyed in the explosion at the specified coordinates */
void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlockAffected);
@@ -270,16 +276,16 @@ public:
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible
/** Touches the chunk, causing it to be loaded or generated */
- void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ void TouchChunk(int a_ChunkX, int a_ChunkZ);
/** Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) */
- bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ bool LoadChunk(int a_ChunkX, int a_ChunkZ);
/** Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() */
void LoadChunks(const cChunkCoordsList & a_Chunks);
/** Marks the chunk as failed-to-load */
- void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ);
/** Sets the sign text. Returns true if sign text changed. */
bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
@@ -363,7 +369,7 @@ private:
~cChunkLayer();
/** Always returns an assigned chunkptr, but the chunk needn't be valid (loaded / generated) - callers must check */
- cChunkPtr GetChunk( int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ cChunkPtr GetChunk( int a_ChunkX, int a_ChunkZ);
/** Returns the specified chunk, or NULL if not created yet */
cChunk * FindChunk(int a_ChunkX, int a_ChunkZ);
@@ -456,9 +462,9 @@ private:
std::auto_ptr<cAllocationPool<cChunkData::sChunkSection> > m_Pool;
- cChunkPtr GetChunk (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid
- cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate
- cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ); // Doesn't load, doesn't generate
+ cChunkPtr GetChunk (int a_ChunkX, int a_ChunkZ); // Also queues the chunk for loading / generating if not valid
+ cChunkPtr GetChunkNoGen (int a_ChunkX, int a_ChunkZ); // Also queues the chunk for loading if not valid; doesn't generate
+ cChunkPtr GetChunkNoLoad(int a_ChunkX, int a_ChunkZ); // Doesn't load, doesn't generate
/** Gets a block in any chunk while in the cChunk's Tick() method; returns true if successful, false if chunk not loaded (doesn't queue load) */
bool LockedGetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta);
diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp
index ebcf0e272..95c4d03d2 100644
--- a/src/ChunkSender.cpp
+++ b/src/ChunkSender.cpp
@@ -81,7 +81,7 @@ void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkZ)
// This is probably never gonna be called twice for the same chunk, and if it is, we don't mind, so we don't check
{
cCSLock Lock(m_CS);
- m_ChunksReady.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
+ m_ChunksReady.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
}
m_evtQueue.Set();
}
@@ -95,12 +95,12 @@ void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle *
ASSERT(a_Client != NULL);
{
cCSLock Lock(m_CS);
- if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)) != m_SendChunks.end())
+ if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, a_ChunkZ, a_Client)) != m_SendChunks.end())
{
// Already queued, bail out
return;
}
- m_SendChunks.push_back(sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client));
+ m_SendChunks.push_back(sSendChunk(a_ChunkX, a_ChunkZ, a_Client));
}
m_evtQueue.Set();
}
@@ -160,7 +160,7 @@ void cChunkSender::Execute(void)
m_ChunksReady.pop_front();
Lock.Unlock();
- SendChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, NULL);
+ SendChunk(Coords.m_ChunkX, Coords.m_ChunkZ, NULL);
}
else
{
@@ -169,7 +169,7 @@ void cChunkSender::Execute(void)
m_SendChunks.pop_front();
Lock.Unlock();
- SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkY, Chunk.m_ChunkZ, Chunk.m_Client);
+ SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client);
}
Lock.Lock();
int RemoveCount = m_RemoveCount;
@@ -186,14 +186,14 @@ void cChunkSender::Execute(void)
-void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
+void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
{
ASSERT(m_World != NULL);
// Ask the client if it still wants the chunk:
if (a_Client != NULL)
{
- if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ))
+ if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkZ))
{
return;
}
diff --git a/src/ChunkSender.h b/src/ChunkSender.h
index c0c427dbd..91ca28f05 100644
--- a/src/ChunkSender.h
+++ b/src/ChunkSender.h
@@ -95,13 +95,11 @@ protected:
struct sSendChunk
{
int m_ChunkX;
- int m_ChunkY;
int m_ChunkZ;
cClientHandle * m_Client;
- sSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) :
+ sSendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) :
m_ChunkX(a_ChunkX),
- m_ChunkY(a_ChunkY),
m_ChunkZ(a_ChunkZ),
m_Client(a_Client)
{
@@ -111,7 +109,6 @@ protected:
{
return (
(a_Other.m_ChunkX == m_ChunkX) &&
- (a_Other.m_ChunkY == m_ChunkY) &&
(a_Other.m_ChunkZ == m_ChunkZ) &&
(a_Other.m_Client == m_Client)
);
@@ -162,7 +159,7 @@ protected:
virtual void BlockEntity (cBlockEntity * a_Entity) /*override*/;
/// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == NULL
- void SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client);
+ void SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
} ;
diff --git a/src/ChunkStay.cpp b/src/ChunkStay.cpp
index b5002a63d..38aa89a37 100644
--- a/src/ChunkStay.cpp
+++ b/src/ChunkStay.cpp
@@ -51,7 +51,7 @@ void cChunkStay::Add(int a_ChunkX, int a_ChunkZ)
return;
}
} // for itr - Chunks[]
- m_Chunks.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
+ m_Chunks.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
}
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index d5ccbbc33..88dc15d04 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -75,11 +75,21 @@ cClientHandle::cClientHandle(const cSocket * a_Socket, int a_ViewDistance) :
m_TimeSinceLastPacket(0),
m_Ping(1000),
m_PingID(1),
+ m_PingStartTime(0),
+ m_LastPingTime(1000),
m_BlockDigAnimStage(-1),
+ m_BlockDigAnimSpeed(0),
+ m_BlockDigAnimX(0),
+ m_BlockDigAnimY(256), // Invalid Y, so that the coords don't get picked up
+ m_BlockDigAnimZ(0),
m_HasStartedDigging(false),
+ m_LastDigBlockX(0),
+ m_LastDigBlockY(256), // Invalid Y, so that the coords don't get picked up
+ m_LastDigBlockZ(0),
m_State(csConnected),
m_ShouldCheckDownloaded(false),
m_NumExplosionsThisTick(0),
+ m_NumBlockChangeInteractionsThisTick(0),
m_UniqueID(0),
m_HasSentPlayerChunk(false),
m_Locale("en_GB")
@@ -462,13 +472,13 @@ void cClientHandle::StreamChunks(void)
// For each distance touch chunks in a hollow square centered around current position:
for (int i = -d; i <= d; ++i)
{
- World->TouchChunk(ChunkPosX + d, ZERO_CHUNK_Y, ChunkPosZ + i);
- World->TouchChunk(ChunkPosX - d, ZERO_CHUNK_Y, ChunkPosZ + i);
+ World->TouchChunk(ChunkPosX + d, ChunkPosZ + i);
+ World->TouchChunk(ChunkPosX - d, ChunkPosZ + i);
} // for i
for (int i = -d + 1; i < d; ++i)
{
- World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ + d);
- World->TouchChunk(ChunkPosX + i, ZERO_CHUNK_Y, ChunkPosZ - d);
+ World->TouchChunk(ChunkPosX + i, ChunkPosZ + d);
+ World->TouchChunk(ChunkPosX + i, ChunkPosZ - d);
} // for i
} // for d
}
@@ -491,8 +501,8 @@ void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ)
{
{
cCSLock Lock(m_CSChunkLists);
- m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
- m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
+ m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
+ m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
}
World->SendChunkTo(a_ChunkX, a_ChunkZ, this);
}
@@ -912,19 +922,36 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
return;
}
- if (
- ((a_Status == DIG_STATUS_STARTED) || (a_Status == DIG_STATUS_FINISHED)) && // Only do a radius check for block destruction - things like pickup tossing send coordinates that are to be ignored
- ((Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) ||
- (Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) ||
- (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6))
- )
+ if ((a_Status == DIG_STATUS_STARTED) || (a_Status == DIG_STATUS_FINISHED))
{
- m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
- if (cBlockInfo::GetHandler(m_Player->GetWorld()->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))->IsClickedThrough())
+ if (a_BlockFace == BLOCK_FACE_NONE)
{
- m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player);
+ return;
+ }
+
+ /* Check for clickthrough-blocks:
+ When the user breaks a fire block, the client send the wrong block location.
+ We must find the right block with the face direction. */
+ int BlockX = a_BlockX;
+ int BlockY = a_BlockY;
+ int BlockZ = a_BlockZ;
+ AddFaceDirection(BlockX, BlockY, BlockZ, a_BlockFace);
+ if (cBlockInfo::GetHandler(m_Player->GetWorld()->GetBlock(BlockX, BlockY, BlockZ))->IsClickedThrough())
+ {
+ a_BlockX = BlockX;
+ a_BlockY = BlockY;
+ a_BlockZ = BlockZ;
+ }
+
+ if (
+ ((Diff(m_Player->GetPosX(), (double)a_BlockX) > 6) ||
+ (Diff(m_Player->GetPosY(), (double)a_BlockY) > 6) ||
+ (Diff(m_Player->GetPosZ(), (double)a_BlockZ) > 6))
+ )
+ {
+ m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
+ return;
}
- return;
}
cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
@@ -932,10 +959,6 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
{
// A plugin doesn't agree with the action, replace the block on the client and quit:
m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
- if (cBlockInfo::GetHandler(m_Player->GetWorld()->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ))->IsClickedThrough())
- {
- m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, m_Player);
- }
return;
}
@@ -1036,7 +1059,8 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
if (
m_Player->IsGameModeCreative() &&
- ItemCategory::IsSword(m_Player->GetInventory().GetEquippedItem().m_ItemType)
+ ItemCategory::IsSword(m_Player->GetInventory().GetEquippedItem().m_ItemType) &&
+ (m_Player->GetWorld()->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_FIRE)
)
{
// Players can't destroy blocks with a Sword in the hand.
@@ -1059,26 +1083,6 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
m_LastDigBlockY = a_BlockY;
m_LastDigBlockZ = a_BlockZ;
- // Check for clickthrough-blocks:
- /* When the user breaks a fire block, the client send the wrong block location.
- We must find the right block with the face direction. */
- if (a_BlockFace != BLOCK_FACE_NONE)
- {
- int pX = a_BlockX;
- int pY = a_BlockY;
- int pZ = a_BlockZ;
-
- AddFaceDirection(pX, pY, pZ, a_BlockFace); // Get the block in front of the clicked coordinates (m_bInverse defaulted to false)
- cBlockHandler * Handler = cBlockInfo::GetHandler(m_Player->GetWorld()->GetBlock(pX, pY, pZ));
-
- if (Handler->IsClickedThrough())
- {
- cChunkInterface ChunkInterface(m_Player->GetWorld()->GetChunkMap());
- Handler->OnDigging(ChunkInterface, *m_Player->GetWorld(), m_Player, pX, pY, pZ);
- return;
- }
- }
-
if (
(m_Player->IsGameModeCreative()) || // In creative mode, digging is done immediately
cBlockInfo::IsOneHitDig(a_OldBlock) // One-hit blocks get destroyed immediately, too
@@ -2730,7 +2734,7 @@ bool cClientHandle::HasPluginChannel(const AString & a_PluginChannel)
-bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkZ)
{
if (m_State >= csDestroying)
{
@@ -2738,7 +2742,7 @@ bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
}
cCSLock Lock(m_CSChunkLists);
- return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)) != m_ChunksToSend.end());
+ return (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkZ)) != m_ChunksToSend.end());
}
@@ -2754,9 +2758,9 @@ void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ)
LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, this);
cCSLock Lock(m_CSChunkLists);
- if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)) == m_ChunksToSend.end())
+ if (std::find(m_ChunksToSend.begin(), m_ChunksToSend.end(), cChunkCoords(a_ChunkX, a_ChunkZ)) == m_ChunksToSend.end())
{
- m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ));
+ m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
}
}
@@ -2854,11 +2858,27 @@ void cClientHandle::SocketClosed(void)
-void cClientHandle::HandleEnchantItem(Byte & WindowID, Byte & Enchantment)
+void cClientHandle::HandleEnchantItem(Byte & a_WindowID, Byte & a_Enchantment)
{
- cEnchantingWindow * Window = (cEnchantingWindow*)m_Player->GetWindow();
+ if (a_Enchantment > 2)
+ {
+ LOGWARNING("%s attempt to crash the server with invalid enchanting selection!", GetUsername().c_str());
+ Kick("Invalid enchanting!");
+ return;
+ }
+
+ if (
+ (m_Player->GetWindow() == NULL) ||
+ (m_Player->GetWindow()->GetWindowID() != a_WindowID) ||
+ (m_Player->GetWindow()->GetWindowType() != cWindow::wtEnchantment)
+ )
+ {
+ return;
+ }
+
+ cEnchantingWindow * Window = (cEnchantingWindow*) m_Player->GetWindow();
cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player);
- int BaseEnchantmentLevel = Window->GetPropertyValue(Enchantment);
+ int BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment);
if (Item.EnchantByXPLevels(BaseEnchantmentLevel))
{
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 057521a25..f5ee357b3 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -209,7 +209,7 @@ public:
// tolua_end
/** Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) */
- bool WantsSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ bool WantsSendChunk(int a_ChunkX, int a_ChunkZ);
/** Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) */
void AddWantedChunk(int a_ChunkX, int a_ChunkZ);
@@ -269,7 +269,7 @@ public:
void RemoveFromWorld(void);
/** Called when the player will enchant a Item */
- void HandleEnchantItem(Byte & WindowID, Byte & Enchantment);
+ void HandleEnchantItem(Byte & a_WindowID, Byte & a_Enchantment);
private:
diff --git a/src/CraftingRecipes.cpp b/src/CraftingRecipes.cpp
index 1a31a6e90..ed3409207 100644
--- a/src/CraftingRecipes.cpp
+++ b/src/CraftingRecipes.cpp
@@ -1,4 +1,4 @@
-
+
// CraftingRecipes.cpp
// Interfaces to the cCraftingRecipes class representing the storage of crafting recipes
@@ -83,7 +83,7 @@ cItem & cCraftingGrid::GetItem(int x, int y) const
-void cCraftingGrid::SetItem(int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth)
+void cCraftingGrid::SetItem(int x, int y, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
{
// Accessible through scripting, must verify parameters:
if ((x < 0) || (x >= m_Width) || (y < 0) || (y >= m_Height))
@@ -228,7 +228,7 @@ void cCraftingRecipe::Clear(void)
-void cCraftingRecipe::SetResult(ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth)
+void cCraftingRecipe::SetResult(ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
{
m_Result = cItem(a_ItemType, a_ItemCount, a_ItemHealth);
}
@@ -324,7 +324,11 @@ void cCraftingRecipes::LoadRecipes(void)
return;
}
AString Everything;
- f.ReadRestOfFile(Everything);
+ if (!f.ReadRestOfFile(Everything))
+ {
+ LOGWARNING("Cannot read file \"crafting.txt\", no crafting recipes will be available!");
+ return;
+ }
f.Close();
// Split it into lines, then process each line as a single recipe:
@@ -362,7 +366,10 @@ void cCraftingRecipes::ClearRecipes(void)
void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine)
{
- AStringVector Sides = StringSplit(a_RecipeLine, "=");
+ AString RecipeLine(a_RecipeLine);
+ RecipeLine.erase(std::remove_if(RecipeLine.begin(), RecipeLine.end(), isspace), RecipeLine.end());
+
+ AStringVector Sides = StringSplit(RecipeLine, "=");
if (Sides.size() != 2)
{
LOGWARNING("crafting.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
@@ -388,8 +395,7 @@ void cCraftingRecipes::AddRecipeLine(int a_LineNum, const AString & a_RecipeLine
}
if (ResultSplit.size() > 1)
{
- Recipe->m_Result.m_ItemCount = atoi(ResultSplit[1].c_str());
- if (Recipe->m_Result.m_ItemCount == 0)
+ if (!StringToInteger<char>(ResultSplit[1].c_str(), Recipe->m_Result.m_ItemCount))
{
LOGWARNING("crafting.txt: line %d: Cannot parse result count, ignoring the recipe.", a_LineNum);
LOGINFO("Offending line: \"%s\"", a_RecipeLine.c_str());
@@ -441,8 +447,7 @@ bool cCraftingRecipes::ParseItem(const AString & a_String, cItem & a_Item)
if (Split.size() > 1)
{
AString Damage = TrimString(Split[1]);
- a_Item.m_ItemDamage = atoi(Damage.c_str());
- if ((a_Item.m_ItemDamage == 0) && (Damage.compare("0") != 0))
+ if (!StringToInteger<short>(Damage.c_str(), a_Item.m_ItemDamage))
{
// Parsing the number failed
return false;
diff --git a/src/CraftingRecipes.h b/src/CraftingRecipes.h
index 0250d2f68..fe1e15817 100644
--- a/src/CraftingRecipes.h
+++ b/src/CraftingRecipes.h
@@ -33,7 +33,7 @@ public:
int GetWidth (void) const {return m_Width; }
int GetHeight(void) const {return m_Height; }
cItem & GetItem (int x, int y) const;
- void SetItem (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth);
+ void SetItem (int x, int y, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth);
void SetItem (int x, int y, const cItem & a_Item);
void Clear (void);
@@ -72,13 +72,13 @@ public:
int GetIngredientsHeight(void) const {return m_Ingredients.GetHeight(); }
cItem & GetIngredient (int x, int y) const {return m_Ingredients.GetItem(x, y); }
const cItem & GetResult (void) const {return m_Result; }
- void SetResult (ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth);
+ void SetResult (ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth);
void SetResult (const cItem & a_Item)
{
m_Result = a_Item;
}
- void SetIngredient (int x, int y, ENUM_ITEM_ID a_ItemType, int a_ItemCount, short a_ItemHealth)
+ void SetIngredient (int x, int y, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemHealth)
{
m_Ingredients.SetItem(x, y, a_ItemType, a_ItemCount, a_ItemHealth);
}
diff --git a/src/DeadlockDetect.cpp b/src/DeadlockDetect.cpp
index 1384dd1b6..d8c1ffd34 100644
--- a/src/DeadlockDetect.cpp
+++ b/src/DeadlockDetect.cpp
@@ -21,7 +21,8 @@ const int CYCLE_MILLISECONDS = 100;
cDeadlockDetect::cDeadlockDetect(void) :
- super("DeadlockDetect")
+ super("DeadlockDetect"),
+ m_IntervalSec(1000)
{
}
@@ -136,6 +137,7 @@ void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
void cDeadlockDetect::DeadlockDetected(void)
{
+ LOGERROR("Deadlock detected, aborting the server");
ASSERT(!"Deadlock detected");
abort();
}
diff --git a/src/Defines.h b/src/Defines.h
index 0981077c4..78c58034e 100644
--- a/src/Defines.h
+++ b/src/Defines.h
@@ -528,7 +528,7 @@ inline float GetSpecialSignf( float a_Val)
-template<class T> inline T Diff(T a_Val1, T a_Val2)
+template <class T> inline T Diff(T a_Val1, T a_Val2)
{
return std::abs(a_Val1 - a_Val2);
}
diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp
index 913519c4c..954e0a267 100644
--- a/src/Entities/ArrowEntity.cpp
+++ b/src/Entities/ArrowEntity.cpp
@@ -90,6 +90,13 @@ void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFa
// Broadcast arrow hit sound
m_World->BroadcastSoundEffect("random.bowhit", (double)X, (double)Y, (double)Z, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+
+ if ((m_World->GetBlock(Hit) == E_BLOCK_TNT) && IsOnFire())
+ {
+ m_World->SetBlock(X, Y, Z, E_BLOCK_AIR, 0);
+ m_World->SpawnPrimedTNT(X, Y, Z);
+ }
+
}
@@ -103,8 +110,36 @@ void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
Damage += m_World->GetTickRandomNumber(Damage / 2 + 2);
}
- a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1);
+
+ int PowerLevel = m_CreatorData.m_Enchantments.GetLevel(cEnchantments::enchPower);
+ if (PowerLevel > 0)
+ {
+ int ExtraDamage = (int)ceil(0.25 * (PowerLevel + 1));
+ Damage += ExtraDamage;
+ }
+
+ int KnockbackAmount = 1;
+ int PunchLevel = m_CreatorData.m_Enchantments.GetLevel(cEnchantments::enchPunch);
+ if (PunchLevel > 0)
+ {
+ Vector3d LookVector = GetLookVector();
+ Vector3f FinalSpeed = Vector3f(0, 0, 0);
+ switch (PunchLevel)
+ {
+ case 1: FinalSpeed = LookVector * Vector3d(5, 0.3, 5); break;
+ case 2: FinalSpeed = LookVector * Vector3d(8, 0.3, 8); break;
+ default: break;
+ }
+ a_EntityHit.SetSpeed(FinalSpeed);
+ }
+
+ a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, KnockbackAmount);
+ if (IsOnFire() && !a_EntityHit.IsSubmerged() && !a_EntityHit.IsSwimming())
+ {
+ a_EntityHit.StartBurning(100);
+ }
+
// Broadcast successful hit sound
GetWorld()->BroadcastSoundEffect("random.successful_hit", GetPosX(), GetPosY(), GetPosZ(), 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
diff --git a/src/Entities/ArrowEntity.h b/src/Entities/ArrowEntity.h
index e35feef2a..10d530a55 100644
--- a/src/Entities/ArrowEntity.h
+++ b/src/Entities/ArrowEntity.h
@@ -10,6 +10,7 @@
+
// tolua_begin
class cArrowEntity :
@@ -46,7 +47,7 @@ public:
/// Returns the damage modifier coeff.
double GetDamageCoeff(void) const { return m_DamageCoeff; }
-
+
/// Sets the damage modifier coeff
void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; }
@@ -89,7 +90,7 @@ protected:
/// If true, the arrow is in the process of being collected - don't go to anyone else
bool m_bIsCollected;
-
+
/// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air
Vector3i m_HitBlockPos;
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 32f220897..9bcdcffeb 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -13,6 +13,7 @@
#include "../Tracer.h"
#include "Player.h"
#include "Items/ItemHandler.h"
+#include "../FastRandom.h"
@@ -316,6 +317,105 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
// IsOnGround() only is false if the player is moving downwards
// TODO: Better damage increase, and check for enchantments (and use magic critical instead of plain)
+ const cEnchantments & Enchantments = Player->GetEquippedItem().m_Enchantments;
+
+ int SharpnessLevel = Enchantments.GetLevel(cEnchantments::enchSharpness);
+ int SmiteLevel = Enchantments.GetLevel(cEnchantments::enchSmite);
+ int BaneOfArthropodsLevel = Enchantments.GetLevel(cEnchantments::enchBaneOfArthropods);
+
+ if (SharpnessLevel > 0)
+ {
+ a_TDI.FinalDamage += (int)ceil(1.25 * SharpnessLevel);
+ }
+ else if (SmiteLevel > 0)
+ {
+ if (IsMob())
+ {
+ cMonster * Monster = (cMonster *)this;
+ switch (Monster->GetMobType())
+ {
+ case cMonster::mtSkeleton:
+ case cMonster::mtZombie:
+ case cMonster::mtWither:
+ case cMonster::mtZombiePigman:
+ {
+ a_TDI.FinalDamage += (int)ceil(2.5 * SmiteLevel);
+ break;
+ }
+ }
+ }
+ }
+ else if (BaneOfArthropodsLevel > 0)
+ {
+ if (IsMob())
+ {
+ cMonster * Monster = (cMonster *)this;
+ switch (Monster->GetMobType())
+ {
+ case cMonster::mtSpider:
+ case cMonster::mtCaveSpider:
+ case cMonster::mtSilverfish:
+ {
+ a_TDI.RawDamage += (int)ceil(2.5 * BaneOfArthropodsLevel);
+ // TODO: Add slowness effect
+
+ break;
+ };
+ default: break;
+ }
+ }
+ }
+
+ int FireAspectLevel = Enchantments.GetLevel(cEnchantments::enchFireAspect);
+ if (FireAspectLevel > 0)
+ {
+ int BurnTicks = 3;
+
+ if (FireAspectLevel > 1)
+ {
+ BurnTicks += 4 * (FireAspectLevel - 1);
+ }
+ if (!IsMob() && !IsSubmerged() && !IsSwimming())
+ {
+ StartBurning(BurnTicks * 20);
+ }
+ else if (IsMob() && !IsSubmerged() && !IsSwimming())
+ {
+ cMonster * Monster = (cMonster *)this;
+ switch (Monster->GetMobType())
+ {
+ case cMonster::mtGhast:
+ case cMonster::mtZombiePigman:
+ case cMonster::mtMagmaCube:
+ {
+ break;
+ };
+ default: StartBurning(BurnTicks * 20);
+ }
+ }
+ }
+
+ int ThornsLevel = 0;
+ const cItem ArmorItems[] = { GetEquippedHelmet(), GetEquippedChestplate(), GetEquippedLeggings(), GetEquippedBoots() };
+ for (size_t i = 0; i < ARRAYCOUNT(ArmorItems); i++)
+ {
+ const cItem & Item = ArmorItems[i];
+ ThornsLevel = std::max(ThornsLevel, Item.m_Enchantments.GetLevel(cEnchantments::enchThorns));
+ }
+
+ if (ThornsLevel > 0)
+ {
+ int Chance = ThornsLevel * 15;
+
+ cFastRandom Random;
+ int RandomValue = Random.GenerateRandomInteger(0, 100);
+
+ if (RandomValue <= Chance)
+ {
+ a_TDI.Attacker->TakeDamage(dtAttack, this, 0, Random.GenerateRandomInteger(1, 4), 0);
+ }
+ }
+
if (!Player->IsOnGround())
{
if ((a_TDI.DamageType == dtAttack) || (a_TDI.DamageType == dtArrowAttack))
@@ -328,13 +428,123 @@ bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
Player->GetStatManager().AddValue(statDamageDealt, (StatValue)floor(a_TDI.FinalDamage * 10 + 0.5));
}
+ if (IsPlayer())
+ {
+ double TotalEPF = 0.0;
+ double EPFProtection = 0.00;
+ double EPFFireProtection = 0.00;
+ double EPFBlastProtection = 0.00;
+ double EPFProjectileProtection = 0.00;
+ double EPFFeatherFalling = 0.00;
+
+ const cItem ArmorItems[] = { GetEquippedHelmet(), GetEquippedChestplate(), GetEquippedLeggings(), GetEquippedBoots() };
+ for (size_t i = 0; i < ARRAYCOUNT(ArmorItems); i++)
+ {
+ const cItem & Item = ArmorItems[i];
+ int Level = Item.m_Enchantments.GetLevel(cEnchantments::enchProtection);
+ if (Level > 0)
+ {
+ EPFProtection += (6 + Level * Level) * 0.75 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchFireProtection);
+ if (Level > 0)
+ {
+ EPFFireProtection += (6 + Level * Level) * 1.25 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchFeatherFalling);
+ if (Level > 0)
+ {
+ EPFFeatherFalling += (6 + Level * Level) * 2.5 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchBlastProtection);
+ if (Level > 0)
+ {
+ EPFBlastProtection += (6 + Level * Level) * 1.5 / 3;
+ }
+
+ Level = Item.m_Enchantments.GetLevel(cEnchantments::enchProjectileProtection);
+ if (Level > 0)
+ {
+ EPFProjectileProtection += (6 + Level * Level) * 1.5 / 3;
+ }
+
+ }
+
+ TotalEPF = EPFProtection + EPFFireProtection + EPFFeatherFalling + EPFBlastProtection + EPFProjectileProtection;
+
+ EPFProtection = EPFProtection / TotalEPF;
+ EPFFireProtection = EPFFireProtection / TotalEPF;
+ EPFFeatherFalling = EPFFeatherFalling / TotalEPF;
+ EPFBlastProtection = EPFBlastProtection / TotalEPF;
+ EPFProjectileProtection = EPFProjectileProtection / TotalEPF;
+
+ if (TotalEPF > 25)
+ {
+ TotalEPF = 25;
+ }
+
+ cFastRandom Random;
+ float RandomValue = Random.GenerateRandomInteger(50, 100) * 0.01f;
+
+ TotalEPF = ceil(TotalEPF * RandomValue);
+
+ if (TotalEPF > 20)
+ {
+ TotalEPF = 20;
+ }
+
+ EPFProtection = TotalEPF * EPFProtection;
+ EPFFireProtection = TotalEPF * EPFFireProtection;
+ EPFFeatherFalling = TotalEPF * EPFFeatherFalling;
+ EPFBlastProtection = TotalEPF * EPFBlastProtection;
+ EPFProjectileProtection = TotalEPF * EPFProjectileProtection;
+
+ int RemovedDamage = 0;
+
+ if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtAdmin))
+ {
+ RemovedDamage += (int)ceil(EPFProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if ((a_TDI.DamageType == dtFalling) || (a_TDI.DamageType == dtFall) || (a_TDI.DamageType == dtEnderPearl))
+ {
+ RemovedDamage += (int)ceil(EPFFeatherFalling * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.DamageType == dtBurning)
+ {
+ RemovedDamage += (int)ceil(EPFFireProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.DamageType == dtExplosion)
+ {
+ RemovedDamage += (int)ceil(EPFBlastProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.DamageType == dtProjectile)
+ {
+ RemovedDamage += (int)ceil(EPFBlastProtection * 0.04 * a_TDI.FinalDamage);
+ }
+
+ if (a_TDI.FinalDamage < RemovedDamage)
+ {
+ RemovedDamage = 0;
+ }
+
+ a_TDI.FinalDamage -= RemovedDamage;
+ }
+
m_Health -= (short)a_TDI.FinalDamage;
// TODO: Apply damage to armor
m_Health = std::max(m_Health, 0);
- if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL)) // Knockback for only players and mobs
+ // Add knockback:
+ if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL))
{
int KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchKnockback); // More common enchantment
if (KnockbackLevel < 1)
@@ -1252,6 +1462,8 @@ void cEntity::HandleAir(void)
// See if the entity is /submerged/ water (block above is water)
// Get the type of block the entity is standing in:
+ int RespirationLevel = GetEquippedHelmet().m_Enchantments.GetLevel(cEnchantments::enchRespiration);
+
if (IsSubmerged())
{
if (!IsPlayer()) // Players control themselves
@@ -1259,6 +1471,11 @@ void cEntity::HandleAir(void)
SetSpeedY(1); // Float in the water
}
+ if (RespirationLevel > 0)
+ {
+ ((cPawn *)this)->AddEntityEffect(cEntityEffect::effNightVision, 200, 5, 0);
+ }
+
if (m_AirLevel <= 0)
{
// Runs the air tick timer to check whether the player should be damaged
@@ -1285,6 +1502,12 @@ void cEntity::HandleAir(void)
// Set the air back to maximum
m_AirLevel = MAX_AIR_LEVEL;
m_AirTickTimer = DROWNING_TICKS;
+
+ if (RespirationLevel > 0)
+ {
+ m_AirTickTimer = DROWNING_TICKS + (RespirationLevel * 15 * 20);
+ }
+
}
}
diff --git a/src/Entities/EntityEffect.cpp b/src/Entities/EntityEffect.cpp
index 3e28392f4..f08755674 100644
--- a/src/Entities/EntityEffect.cpp
+++ b/src/Entities/EntityEffect.cpp
@@ -34,14 +34,14 @@ cEntityEffect::eType cEntityEffect::GetPotionEffectType(short a_ItemDamage)
case 0x08: return cEntityEffect::effWeakness;
case 0x09: return cEntityEffect::effStrength;
case 0x0a: return cEntityEffect::effSlowness;
+ case 0x0b: return cEntityEffect::effJumpBoost;
case 0x0c: return cEntityEffect::effInstantDamage;
case 0x0d: return cEntityEffect::effWaterBreathing;
case 0x0e: return cEntityEffect::effInvisibility;
-
+
// No effect potions
case 0x00:
case 0x07:
- case 0x0b: // Will be potion of leaping in 1.8
case 0x0f:
{
break;
diff --git a/src/Entities/ItemFrame.cpp b/src/Entities/ItemFrame.cpp
index f0b0c8c65..0bc10ec60 100644
--- a/src/Entities/ItemFrame.cpp
+++ b/src/Entities/ItemFrame.cpp
@@ -55,7 +55,6 @@ void cItemFrame::KilledBy(TakeDamageInfo & a_TDI)
{
if (m_Item.IsEmpty())
{
- SetHealth(0);
super::KilledBy(a_TDI);
Destroy();
return;
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
index 58736f040..d1f56867d 100644
--- a/src/Entities/Minecart.cpp
+++ b/src/Entities/Minecart.cpp
@@ -871,11 +871,101 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
return true;
}
case E_META_RAIL_CURVED_ZM_XM:
+ case E_META_RAIL_CURVED_ZP_XP:
+ {
+ Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());
+
+ // Prevent division by small numbers
+ if (abs(Distance.z) < 0.001)
+ {
+ Distance.z = 0.001;
+ }
+
+ /* Check to which side the minecart is to be pushed.
+ Let's consider a z-x-coordinate system where the minecart is the center (0/0).
+ The minecart moves along the line x = -z, the perpendicular line to this is x = z.
+ In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
+ if (
+ ((Distance.z > 0) && ((Distance.x / Distance.z) >= 1)) ||
+ ((Distance.z < 0) && ((Distance.x / Distance.z) <= 1))
+ )
+ {
+ // Moving -X +Z
+ if ((-GetSpeedX() * 0.4 / sqrt(2.0)) < 0.01)
+ {
+ // ~ SpeedX >= 0 Immobile or not moving in the "right" direction. Give it a bump!
+ AddSpeedX(-4 / sqrt(2.0));
+ AddSpeedZ(4 / sqrt(2.0));
+ }
+ else
+ {
+ // ~ SpeedX < 0 Moving in the "right" direction. Only accelerate it a bit.
+ SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
+ SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
+ }
+ }
+ else if ((GetSpeedX() * 0.4 / sqrt(2.0)) < 0.01)
+ {
+ // Moving +X -Z
+ // ~ SpeedX <= 0 Immobile or not moving in the "right" direction
+ AddSpeedX(4 / sqrt(2.0));
+ AddSpeedZ(-4 / sqrt(2.0));
+ }
+ else
+ {
+ // ~ SpeedX > 0 Moving in the "right" direction
+ SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
+ SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
+ }
+ break;
+ }
case E_META_RAIL_CURVED_ZM_XP:
case E_META_RAIL_CURVED_ZP_XM:
- case E_META_RAIL_CURVED_ZP_XP:
{
- // TODO - simply can't be bothered right now
+ Vector3d Distance = MinecartCollisionCallback.GetCollidedEntityPosition() - Vector3d(GetPosX(), 0, GetPosZ());
+
+ // Prevent division by small numbers
+ if (abs(Distance.z) < 0.001)
+ {
+ Distance.z = 0.001;
+ }
+
+ /* Check to which side the minecart is to be pushed.
+ Let's consider a z-x-coordinate system where the minecart is the center (0/0).
+ The minecart moves along the line x = z, the perpendicular line to this is x = -z.
+ In order to decide to which side the minecart is to be pushed, it must be checked on what side of the perpendicular line the pushing entity is located. */
+ if (
+ ((Distance.z > 0) && ((Distance.x / Distance.z) <= -1)) ||
+ ((Distance.z < 0) && ((Distance.x / Distance.z) >= -1))
+ )
+ {
+ // Moving +X +Z
+ if ((GetSpeedX() * 0.4) < 0.01)
+ {
+ // ~ SpeedX <= 0 Immobile or not moving in the "right" direction
+ AddSpeedX(4 / sqrt(2.0));
+ AddSpeedZ(4 / sqrt(2.0));
+ }
+ else
+ {
+ // ~ SpeedX > 0 Moving in the "right" direction
+ SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
+ SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
+ }
+ }
+ else if ((-GetSpeedX() * 0.4) < 0.01)
+ {
+ // Moving -X -Z
+ // ~ SpeedX >= 0 Immobile or not moving in the "right" direction
+ AddSpeedX(-4 / sqrt(2.0));
+ AddSpeedZ(-4 / sqrt(2.0));
+ }
+ else
+ {
+ // ~ SpeedX < 0 Moving in the "right" direction
+ SetSpeedX(GetSpeedX() * 0.4 / sqrt(2.0));
+ SetSpeedZ(GetSpeedZ() * 0.4 / sqrt(2.0));
+ }
break;
}
default: break;
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index 9f07d2fa0..20d0f2c6e 100644
--- a/src/Entities/Pickup.cpp
+++ b/src/Entities/Pickup.cpp
@@ -150,10 +150,14 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
}
}
- if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine into an already full pickup
+ // Try to combine the pickup with adjacent same-item pickups:
+ if (!IsDestroyed() && (m_Item.m_ItemCount < m_Item.GetMaxStackSize())) // Don't combine if already full
{
+ // By using a_Chunk's ForEachEntity() instead of cWorld's, pickups don't combine across chunk boundaries.
+ // That is a small price to pay for not having to traverse the entire world for each entity.
+ // The speedup in the tick thread is quite considerable.
cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this);
- m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries
+ a_Chunk.ForEachEntity(PickupCombiningCallback);
if (PickupCombiningCallback.FoundMatchingPickup())
{
m_World->BroadcastEntityMetadata(*this);
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 7dd46b73c..ee06b49e9 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -10,13 +10,12 @@
#include "../Bindings/PluginManager.h"
#include "../BlockEntities/BlockEntity.h"
#include "../BlockEntities/EnderChestEntity.h"
-#include "../GroupManager.h"
-#include "../Group.h"
#include "../Root.h"
#include "../OSSupport/Timer.h"
#include "../Chunk.h"
#include "../Items/ItemHandler.h"
#include "../Vector3.h"
+#include "../FastRandom.h"
#include "../WorldStorage/StatSerializer.h"
#include "../CompositeChat.h"
@@ -59,7 +58,6 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) :
m_EnderChestContents(9, 3),
m_CurrentWindow(NULL),
m_InventoryWindow(NULL),
- m_Color('-'),
m_GameMode(eGameMode_NotSet),
m_IP(""),
m_ClientHandle(a_Client),
@@ -1367,48 +1365,6 @@ void cPlayer::SetVisible(bool a_bVisible)
-void cPlayer::AddToGroup( const AString & a_GroupName)
-{
- cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName);
- m_Groups.push_back( Group);
- LOGD("Added %s to group %s", GetName().c_str(), a_GroupName.c_str());
- ResolveGroups();
- ResolvePermissions();
-}
-
-
-
-
-
-void cPlayer::RemoveFromGroup( const AString & a_GroupName)
-{
- bool bRemoved = false;
- for (GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr)
- {
- if ((*itr)->GetName().compare(a_GroupName) == 0)
- {
- m_Groups.erase( itr);
- bRemoved = true;
- break;
- }
- }
-
- if (bRemoved)
- {
- LOGD("Removed %s from group %s", GetName().c_str(), a_GroupName.c_str());
- ResolveGroups();
- ResolvePermissions();
- }
- else
- {
- LOGWARN("Tried to remove %s from group %s but was not in that group", GetName().c_str(), a_GroupName.c_str());
- }
-}
-
-
-
-
-
bool cPlayer::HasPermission(const AString & a_Permission)
{
if (a_Permission.empty())
@@ -1417,33 +1373,18 @@ bool cPlayer::HasPermission(const AString & a_Permission)
return true;
}
- AStringVector Split = StringSplit( a_Permission, ".");
- PermissionMap Possibilities = m_ResolvedPermissions;
- // Now search the namespaces
- while (Possibilities.begin() != Possibilities.end())
+ AStringVector Split = StringSplit(a_Permission, ".");
+
+ // Iterate over all granted permissions; if any matches, then return success:
+ for (AStringVectorVector::const_iterator itr = m_SplitPermissions.begin(), end = m_SplitPermissions.end(); itr != end; ++itr)
{
- PermissionMap::iterator itr = Possibilities.begin();
- if (itr->second)
+ if (PermissionMatches(Split, *itr))
{
- AStringVector OtherSplit = StringSplit( itr->first, ".");
- if (OtherSplit.size() <= Split.size())
- {
- unsigned int i;
- for (i = 0; i < OtherSplit.size(); ++i)
- {
- if (OtherSplit[i].compare( Split[i]) != 0)
- {
- if (OtherSplit[i].compare("*") == 0) return true; // WildCard man!! WildCard!
- break;
- }
- }
- if (i == Split.size()) return true;
- }
+ return true;
}
- Possibilities.erase( itr);
- }
+ } // for itr - m_SplitPermissions[]
- // Nothing that matched :(
+ // No granted permission matches
return false;
}
@@ -1451,82 +1392,35 @@ bool cPlayer::HasPermission(const AString & a_Permission)
-bool cPlayer::IsInGroup( const AString & a_Group)
+bool cPlayer::PermissionMatches(const AStringVector & a_Permission, const AStringVector & a_Template)
{
- for (GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr)
+ // Check the sub-items if they are the same or there's a wildcard:
+ size_t lenP = a_Permission.size();
+ size_t lenT = a_Template.size();
+ size_t minLen = std::min(lenP, lenT);
+ for (size_t i = 0; i < minLen; i++)
{
- if (a_Group.compare( (*itr)->GetName().c_str()) == 0)
+ if (a_Template[i] == "*")
+ {
+ // Has matched so far and now there's a wildcard in the template, so the permission matches:
return true;
- }
- return false;
-}
-
-
-
-
-
-void cPlayer::ResolvePermissions()
-{
- m_ResolvedPermissions.clear(); // Start with an empty map
-
- // Copy all player specific permissions into the resolved permissions map
- for (PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr)
- {
- m_ResolvedPermissions[ itr->first ] = itr->second;
- }
-
- for (GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr)
- {
- const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions();
- for (cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr)
+ }
+ if (a_Permission[i] != a_Template[i])
{
- m_ResolvedPermissions[ itr->first ] = itr->second;
+ // Found a mismatch
+ return false;
}
}
-}
-
-
-
-
-void cPlayer::ResolveGroups()
-{
- // Clear resolved groups first
- m_ResolvedGroups.clear();
-
- // Get a complete resolved list of all groups the player is in
- std::map< cGroup*, bool > AllGroups; // Use a map, because it's faster than iterating through a list to find duplicates
- GroupList ToIterate;
- for (GroupList::iterator GroupItr = m_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr)
+ // So far all the sub-items have matched
+ // If the sub-item count is the same, then the permission matches:
+ if (lenP == lenT)
{
- ToIterate.push_back( *GroupItr);
- }
- while (ToIterate.begin() != ToIterate.end())
- {
- cGroup* CurrentGroup = *ToIterate.begin();
- if (AllGroups.find( CurrentGroup) != AllGroups.end())
- {
- LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!",
- GetName().c_str(), CurrentGroup->GetName().c_str()
- );
- }
- else
- {
- AllGroups[ CurrentGroup ] = true;
- m_ResolvedGroups.push_back( CurrentGroup); // Add group to resolved list
- const cGroup::GroupList & Inherits = CurrentGroup->GetInherits();
- for (cGroup::GroupList::const_iterator itr = Inherits.begin(); itr != Inherits.end(); ++itr)
- {
- if (AllGroups.find( *itr) != AllGroups.end())
- {
- LOGERROR("ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!", GetName().c_str(), (*itr)->GetName().c_str());
- continue;
- }
- ToIterate.push_back( *itr);
- }
- }
- ToIterate.erase( ToIterate.begin());
+ return true;
}
+
+ // There are more sub-items in either the permission or the template, not a match:
+ return false;
}
@@ -1535,17 +1429,14 @@ void cPlayer::ResolveGroups()
AString cPlayer::GetColor(void) const
{
- if (m_Color != '-')
- {
- return cChatColor::Delimiter + m_Color;
- }
-
- if (m_Groups.size() < 1)
+ if (m_MsgNameColorCode.empty() || (m_MsgNameColorCode == "-"))
{
- return cChatColor::White;
+ // Color has not been assigned, return an empty string:
+ return AString();
}
- return (*m_Groups.begin())->GetColor();
+ // Return the color, including the delimiter:
+ return cChatColor::Delimiter + m_MsgNameColorCode;
}
@@ -1661,48 +1552,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
-void cPlayer::LoadPermissionsFromDisk()
-{
- m_Groups.clear();
- m_Permissions.clear();
-
- cIniFile IniFile;
- if (IniFile.ReadFile("users.ini"))
- {
- AString Groups = IniFile.GetValueSet(GetName(), "Groups", "Default");
- AStringVector Split = StringSplitAndTrim(Groups, ",");
-
- for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr)
- {
- if (!cRoot::Get()->GetGroupManager()->ExistsGroup(*itr))
- {
- LOGWARNING("The group %s for player %s was not found!", itr->c_str(), GetName().c_str());
- }
- AddToGroup(*itr);
- }
-
- AString Color = IniFile.GetValue(GetName(), "Color", "-");
- if (!Color.empty())
- {
- m_Color = Color[0];
- }
- }
- else
- {
- cGroupManager::GenerateDefaultUsersIni(IniFile);
- IniFile.AddValue("Groups", GetName(), "Default");
- AddToGroup("Default");
- }
- IniFile.WriteFile("users.ini");
- ResolvePermissions();
-}
-
-
-
-
bool cPlayer::LoadFromDisk(cWorldPtr & a_World)
{
- LoadPermissionsFromDisk();
+ LoadRank();
// Load from the UUID file:
if (LoadFromFile(GetUUIDFileName(m_UUID), a_World))
@@ -1937,31 +1789,33 @@ bool cPlayer::SaveToDisk()
-cPlayer::StringList cPlayer::GetResolvedPermissions()
+void cPlayer::UseEquippedItem(int a_Amount)
{
- StringList Permissions;
+ if (IsGameModeCreative()) // No damage in creative
+ {
+ return;
+ }
- const PermissionMap& ResolvedPermissions = m_ResolvedPermissions;
- for (PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr)
+ // If the item has an unbreaking enchantment, give it a random chance of not breaking:
+ cItem Item = GetEquippedItem();
+ int UnbreakingLevel = Item.m_Enchantments.GetLevel(cEnchantments::enchUnbreaking);
+ if (UnbreakingLevel > 0)
{
- if (itr->second)
+ int chance;
+ if (ItemCategory::IsArmor(Item.m_ItemType))
{
- Permissions.push_back( itr->first);
+ chance = 60 + (40 / (UnbreakingLevel + 1));
+ }
+ else
+ {
+ chance = 100 / (UnbreakingLevel + 1);
}
- }
-
- return Permissions;
-}
-
-
-
-
-void cPlayer::UseEquippedItem(int a_Amount)
-{
- if (IsGameModeCreative()) // No damage in creative
- {
- return;
+ cFastRandom Random;
+ if (Random.NextInt(101) <= chance)
+ {
+ return;
+ }
}
if (GetInventory().DamageEquippedItem(a_Amount))
@@ -2215,6 +2069,31 @@ void cPlayer::ApplyFoodExhaustionFromMovement()
+void cPlayer::LoadRank(void)
+{
+ // Load the values from cRankManager:
+ cRankManager & RankMgr = cRoot::Get()->GetRankManager();
+ m_Rank = RankMgr.GetPlayerRankName(m_UUID);
+ if (m_Rank.empty())
+ {
+ m_Rank = RankMgr.GetDefaultRank();
+ }
+ m_Permissions = RankMgr.GetPlayerPermissions(m_UUID);
+ RankMgr.GetRankVisuals(m_Rank, m_MsgPrefix, m_MsgSuffix, m_MsgNameColorCode);
+
+ // Break up the individual permissions on each dot, into m_SplitPermissions:
+ m_SplitPermissions.clear();
+ m_SplitPermissions.reserve(m_Permissions.size());
+ for (AStringVector::const_iterator itr = m_Permissions.begin(), end = m_Permissions.end(); itr != end; ++itr)
+ {
+ m_SplitPermissions.push_back(StringSplit(*itr, "."));
+ } // for itr - m_Permissions[]
+}
+
+
+
+
+
void cPlayer::Detach()
{
super::Detach();
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 553d12a80..9bcb0c2ef 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -13,7 +13,6 @@
-class cGroup;
class cWindow;
class cClientHandle;
class cTeam;
@@ -236,24 +235,20 @@ public:
// tolua_end
- typedef std::list< cGroup* > GroupList;
- typedef std::list< std::string > StringList;
+ bool HasPermission(const AString & a_Permission); // tolua_export
- /** Adds a player to existing group or creates a new group when it doesn't exist */
- void AddToGroup( const AString & a_GroupName); // tolua_export
-
- /** Removes a player from the group, resolves permissions and group inheritance (case sensitive) */
- void RemoveFromGroup( const AString & a_GroupName); // tolua_export
-
- bool HasPermission( const AString & a_Permission); // tolua_export
- const GroupList & GetGroups() { return m_Groups; } // >> EXPORTED IN MANUALBINDINGS <<
- StringList GetResolvedPermissions(); // >> EXPORTED IN MANUALBINDINGS <<
- bool IsInGroup( const AString & a_Group); // tolua_export
+ /** Returns true iff a_Permission matches the a_Template.
+ A match is defined by either being exactly the same, or each sub-item matches until there's a wildcard in a_Template.
+ Ie. {"a", "b", "c"} matches {"a", "b", "*"} but doesn't match {"a", "b"} */
+ static bool PermissionMatches(const AStringVector & a_Permission, const AStringVector & a_Template); // Exported in ManualBindings with AString params
+
+ /** Returns all the permissions that the player has assigned to them. */
+ const AStringVector & GetPermissions(void) { return m_Permissions; } // Exported in ManualBindings.cpp
// tolua_begin
- /** Returns the full color code to use for this player, based on their primary group or set in m_Color.
- The returned value includes the cChatColor::Delimiter. */
+ /** Returns the full color code to use for this player, based on their rank.
+ The returned value either is empty, or includes the cChatColor::Delimiter. */
AString GetColor(void) const;
/** tosses the item in the selected hotbar slot */
@@ -347,8 +342,6 @@ public:
*/
bool LoadFromFile(const AString & a_FileName, cWorldPtr & a_World);
- void LoadPermissionsFromDisk(void); // tolua_export
-
const AString & GetLoadedWorldName() { return m_LoadedWorldName; }
void UseEquippedItem(int a_Amount = 1);
@@ -422,6 +415,11 @@ public:
/** Returns the UUID (short format) that has been read from the client, or empty string if not available. */
const AString & GetUUID(void) const { return m_UUID; }
+ /** (Re)loads the rank and permissions from the cRankManager.
+ Expects the m_UUID member to be valid.
+ Loads the m_Rank, m_Permissions, m_MsgPrefix, m_MsgSuffix and m_MsgNameColorCode members. */
+ void LoadRank(void);
+
// tolua_end
// cEntity /*override*/s:
@@ -432,12 +430,22 @@ public:
virtual void Detach(void);
protected:
- typedef std::map< std::string, bool > PermissionMap;
- PermissionMap m_ResolvedPermissions;
- PermissionMap m_Permissions;
- GroupList m_ResolvedGroups;
- GroupList m_Groups;
+ typedef std::vector<std::vector<AString> > AStringVectorVector;
+
+ /** The name of the rank assigned to this player. */
+ AString m_Rank;
+
+ /** All the permissions that this player has, based on their rank. */
+ AStringVector m_Permissions;
+
+ /** All the permissions that this player has, based on their rank, split into individual dot-delimited parts.
+ This is used mainly by the HasPermission() function to optimize the lookup. */
+ AStringVectorVector m_SplitPermissions;
+
+ // Message visuals:
+ AString m_MsgPrefix, m_MsgSuffix;
+ AString m_MsgNameColorCode;
AString m_PlayerName;
AString m_LoadedWorldName;
@@ -482,8 +490,6 @@ protected:
/** The player's last saved bed position */
Vector3i m_LastBedPos;
- char m_Color;
-
eGameMode m_GameMode;
AString m_IP;
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index a359563f5..6356ab036 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -222,7 +222,8 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a
m_ProjectileKind(a_Kind),
m_CreatorData(
((a_Creator != NULL) ? a_Creator->GetUniqueID() : -1),
- ((a_Creator != NULL) ? (a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "") : "")
+ ((a_Creator != NULL) ? (a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "") : ""),
+ ((a_Creator != NULL) ? a_Creator->GetEquippedWeapon().m_Enchantments : cEnchantments())
),
m_IsInGround(false)
{
@@ -235,7 +236,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, double a
cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Vector3d & a_Pos, const Vector3d & a_Speed, double a_Width, double a_Height) :
super(etProjectile, a_Pos.x, a_Pos.y, a_Pos.z, a_Width, a_Height),
m_ProjectileKind(a_Kind),
- m_CreatorData(a_Creator->GetUniqueID(), a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : ""),
+ m_CreatorData(a_Creator->GetUniqueID(), a_Creator->IsPlayer() ? ((cPlayer *)a_Creator)->GetName() : "", a_Creator->GetEquippedWeapon().m_Enchantments),
m_IsInGround(false)
{
SetSpeed(a_Speed);
diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h
index 80c13cb82..46b61d097 100644
--- a/src/Entities/ProjectileEntity.h
+++ b/src/Entities/ProjectileEntity.h
@@ -94,14 +94,16 @@ protected:
*/
struct CreatorData
{
- CreatorData(int a_UniqueID, const AString & a_Name) :
+ CreatorData(int a_UniqueID, const AString & a_Name, const cEnchantments & a_Enchantments) :
m_UniqueID(a_UniqueID),
- m_Name(a_Name)
+ m_Name(a_Name),
+ m_Enchantments(a_Enchantments)
{
}
const int m_UniqueID;
AString m_Name;
+ cEnchantments m_Enchantments;
};
/** The type of projectile I am */
diff --git a/src/FastRandom.h b/src/FastRandom.h
index 2061a3958..cebebad96 100644
--- a/src/FastRandom.h
+++ b/src/FastRandom.h
@@ -45,7 +45,7 @@ public:
float NextFloat(float a_Range, int a_Salt);
/** Returns a random float between 0 and 1. */
- float NextFloat(void) { return NextFloat(1); };
+ float NextFloat(void) { return NextFloat(1); }
/** Returns a random int in the range [a_Begin .. a_End] */
int GenerateRandomInteger(int a_Begin, int a_End);
diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp
index ab772e97b..d200ef3d7 100644
--- a/src/FurnaceRecipe.cpp
+++ b/src/FurnaceRecipe.cpp
@@ -12,8 +12,8 @@
-typedef std::list< cFurnaceRecipe::Recipe > RecipeList;
-typedef std::list< cFurnaceRecipe::Fuel > FuelList;
+typedef std::list<cFurnaceRecipe::cRecipe> RecipeList;
+typedef std::list<cFurnaceRecipe::cFuel> FuelList;
@@ -30,7 +30,7 @@ struct cFurnaceRecipe::sFurnaceRecipeState
cFurnaceRecipe::cFurnaceRecipe()
- : m_pState( new sFurnaceRecipeState)
+ : m_pState(new sFurnaceRecipeState)
{
ReloadRecipes();
}
@@ -68,12 +68,18 @@ void cFurnaceRecipe::ReloadRecipes(void)
while (std::getline(f, ParsingLine))
{
LineNum++;
- ParsingLine.erase(std::remove_if(ParsingLine.begin(), ParsingLine.end(), isspace), ParsingLine.end()); // Remove ALL whitespace from the line
if (ParsingLine.empty())
{
continue;
}
+ // Remove comments from the line:
+ size_t FirstCommentSymbol = ParsingLine.find('#');
+ if ((FirstCommentSymbol != AString::npos) && (FirstCommentSymbol != 0))
+ {
+ ParsingLine.erase(ParsingLine.begin() + (const long)FirstCommentSymbol, ParsingLine.end());
+ }
+
switch (ParsingLine[0])
{
case '#':
@@ -103,159 +109,132 @@ void cFurnaceRecipe::ReloadRecipes(void)
-void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, int a_LineNum)
+void cFurnaceRecipe::AddFuelFromLine(const AString & a_Line, unsigned int a_LineNum)
{
- // Fuel
- int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0;
- AString::size_type BeginPos = 1; // Begin at one after exclamation mark (bang)
-
- if (
- !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID
- !ReadOptionalNumbers(BeginPos, ":", "=", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
- !ReadMandatoryNumber(BeginPos, "0123456789", a_Line, a_LineNum, IBurnTime, true) // Read item burn time - last value
- )
+ AString Line(a_Line);
+ Line.erase(Line.begin()); // Remove the beginning "!"
+ Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
+
+ std::auto_ptr<cItem> Item(new cItem);
+ int BurnTime;
+
+ const AStringVector & Sides = StringSplit(Line, "=");
+ if (Sides.size() != 2)
{
+ LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
- // Add to fuel list:
- Fuel F;
- F.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth);
- F.BurnTime = IBurnTime;
- m_pState->Fuel.push_back(F);
-}
-
-
-
-
+ if (!ParseItem(Sides[0], *Item))
+ {
+ LOGWARNING("furnace.txt: line %d: Cannot parse item \"%s\".", a_LineNum, Sides[0].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
+ }
-void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, int a_LineNum)
-{
- int IItemID = 0, IItemCount = 0, IItemHealth = 0, IBurnTime = 0;
- int OItemID = 0, OItemCount = 0, OItemHealth = 0;
- AString::size_type BeginPos = 0; // Begin at start of line
-
- if (
- !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, IItemID) || // Read item ID
- !ReadOptionalNumbers(BeginPos, ":", "@", a_Line, a_LineNum, IItemCount, IItemHealth) || // Read item count (and optionally health)
- !ReadMandatoryNumber(BeginPos, "=", a_Line, a_LineNum, IBurnTime) || // Read item burn time
- !ReadMandatoryNumber(BeginPos, ":", a_Line, a_LineNum, OItemID) || // Read result ID
- !ReadOptionalNumbers(BeginPos, ":", "012456789", a_Line, a_LineNum, OItemCount, OItemHealth, true) // Read result count (and optionally health) - last value
- )
+ if (!StringToInteger<int>(Sides[1], BurnTime))
{
+ LOGWARNING("furnace.txt: line %d: Cannot parse burn time.", a_LineNum);
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
return;
}
- // Add to recipe list
- Recipe R;
- R.In = new cItem((ENUM_ITEM_ID)IItemID, (char)IItemCount, (short)IItemHealth);
- R.Out = new cItem((ENUM_ITEM_ID)OItemID, (char)OItemCount, (short)OItemHealth);
- R.CookTime = IBurnTime;
- m_pState->Recipes.push_back(R);
+ // Add to fuel list:
+ cFuel Fuel;
+ Fuel.In = Item.release();
+ Fuel.BurnTime = BurnTime;
+ m_pState->Fuel.push_back(Fuel);
}
-void cFurnaceRecipe::PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing)
+void cFurnaceRecipe::AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum)
{
- LOGWARN("Error parsing furnace recipes at line %i pos " SIZE_T_FMT ": missing '%s'", a_Line, a_Position, a_CharactersMissing.c_str());
-}
-
-
+ AString Line(a_Line);
+ Line.erase(std::remove_if(Line.begin(), Line.end(), isspace), Line.end());
+ int CookTime = 200;
+ std::auto_ptr<cItem> InputItem(new cItem());
+ std::auto_ptr<cItem> OutputItem(new cItem());
+ const AStringVector & Sides = StringSplit(Line, "=");
+ if (Sides.size() != 2)
+ {
+ LOGWARNING("furnace.txt: line %d: A single '=' was expected, got %d", a_LineNum, (int)Sides.size() - 1);
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
+ }
-bool cFurnaceRecipe::ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue)
-{
- // TODO: replace atoi with std::stoi
- AString::size_type End;
- if (a_IsLastValue)
+ const AStringVector & InputSplit = StringSplit(Sides[0], "@");
+ if (!ParseItem(InputSplit[0], *InputItem))
{
- End = a_Text.find_first_not_of(a_Delimiter, a_Begin);
+ LOGWARNING("furnace.txt: line %d: Cannot parse input item \"%s\".", a_LineNum, InputSplit[0].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
}
- else
+
+ if (InputSplit.size() > 1)
{
- End = a_Text.find_first_of(a_Delimiter, a_Begin);
- if (End == AString::npos)
+ if (!StringToInteger<int>(InputSplit[1], CookTime))
{
- PrintParseError(a_Line, a_Begin, a_Delimiter);
- return false;
+ LOGWARNING("furnace.txt: line %d: Cannot parse cook time \"%s\".", a_LineNum, InputSplit[1].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
}
}
-
- // stoi won't throw an exception if the string is alphanumeric, we should check for this
- if (!DoesStringContainOnlyNumbers(a_Text.substr(a_Begin, End - a_Begin)))
+
+ if (!ParseItem(Sides[1], *OutputItem))
{
- PrintParseError(a_Line, a_Begin, "number");
- return false;
+ LOGWARNING("furnace.txt: line %d: Cannot parse output item \"%s\".", a_LineNum, Sides[1].c_str());
+ LOGINFO("Offending line: \"%s\"", a_Line.c_str());
+ return;
}
- a_Value = atoi(a_Text.substr(a_Begin, End - a_Begin).c_str());
- a_Begin = End + 1; // Jump over delimiter
- return true;
+ cRecipe Recipe;
+ Recipe.In = InputItem.release();
+ Recipe.Out = OutputItem.release();
+ Recipe.CookTime = CookTime;
+ m_pState->Recipes.push_back(Recipe);
}
-bool cFurnaceRecipe::ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue)
+bool cFurnaceRecipe::ParseItem(const AString & a_String, cItem & a_Item)
{
- // TODO: replace atoi with std::stoi
- AString::size_type End, Begin = a_Begin;
+ AString ItemString = a_String;
+
+ const AStringVector & SplitAmount = StringSplit(ItemString, ",");
+ ItemString = SplitAmount[0];
- End = a_Text.find_first_of(a_DelimiterOne, Begin);
- if (End != AString::npos)
- {
- if (DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin)))
- {
- a_ValueOne = std::atoi(a_Text.substr(Begin, End - Begin).c_str());
- Begin = End + 1;
+ const AStringVector & SplitMeta = StringSplit(ItemString, ":");
+ ItemString = SplitMeta[0];
- if (a_IsLastValue)
- {
- End = a_Text.find_first_not_of(a_DelimiterTwo, Begin);
- }
- else
- {
- End = a_Text.find_first_of(a_DelimiterTwo, Begin);
- if (End == AString::npos)
- {
- PrintParseError(a_Line, Begin, a_DelimiterTwo);
- return false;
- }
- }
-
- // stoi won't throw an exception if the string is alphanumeric, we should check for this
- if (!DoesStringContainOnlyNumbers(a_Text.substr(Begin, End - Begin)))
- {
- PrintParseError(a_Line, Begin, "number");
- return false;
- }
- a_ValueTwo = atoi(a_Text.substr(Begin, End - Begin).c_str());
+ if (!StringToItem(ItemString, a_Item))
+ {
+ return false;
+ }
- a_Begin = End + 1; // Jump over delimiter
- return true;
- }
- else
+ if (SplitAmount.size() > 1)
+ {
+ if (!StringToInteger<char>(SplitAmount[1].c_str(), a_Item.m_ItemCount))
{
- return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue);
+ return false;
}
}
-
- return ReadMandatoryNumber(a_Begin, a_DelimiterTwo, a_Text, a_Line, a_ValueOne, a_IsLastValue);
-}
-
-
-
-
-bool cFurnaceRecipe::DoesStringContainOnlyNumbers(const AString & a_String)
-{
- // TODO: replace this with std::all_of(a_String.begin(), a_String.end(), isdigit)
- return (a_String.find_first_not_of("0123456789") == AString::npos);
+ if (SplitMeta.size() > 1)
+ {
+ if (!StringToInteger<short>(SplitMeta[1].c_str(), a_Item.m_ItemDamage))
+ {
+ return false;
+ }
+ }
+ return true;
}
@@ -266,19 +245,19 @@ void cFurnaceRecipe::ClearRecipes(void)
{
for (RecipeList::iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
{
- Recipe R = *itr;
- delete R.In;
- R.In = NULL;
- delete R.Out;
- R.Out = NULL;
+ cRecipe Recipe = *itr;
+ delete Recipe.In;
+ Recipe.In = NULL;
+ delete Recipe.Out;
+ Recipe.Out = NULL;
}
m_pState->Recipes.clear();
for (FuelList::iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
{
- Fuel F = *itr;
- delete F.In;
- F.In = NULL;
+ cFuel Fuel = *itr;
+ delete Fuel.In;
+ Fuel.In = NULL;
}
m_pState->Fuel.clear();
}
@@ -287,21 +266,21 @@ void cFurnaceRecipe::ClearRecipes(void)
-const cFurnaceRecipe::Recipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
+const cFurnaceRecipe::cRecipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_Ingredient) const
{
- const Recipe * BestRecipe = 0;
+ const cRecipe * BestRecipe = 0;
for (RecipeList::const_iterator itr = m_pState->Recipes.begin(); itr != m_pState->Recipes.end(); ++itr)
{
- const Recipe & R = *itr;
- if ((R.In->m_ItemType == a_Ingredient.m_ItemType) && (R.In->m_ItemCount <= a_Ingredient.m_ItemCount))
+ const cRecipe & Recipe = *itr;
+ if ((Recipe.In->m_ItemType == a_Ingredient.m_ItemType) && (Recipe.In->m_ItemCount <= a_Ingredient.m_ItemCount))
{
- if (BestRecipe && (BestRecipe->In->m_ItemCount > R.In->m_ItemCount))
+ if (BestRecipe && (BestRecipe->In->m_ItemCount > Recipe.In->m_ItemCount))
{
continue;
}
else
{
- BestRecipe = &R;
+ BestRecipe = &Recipe;
}
}
}
@@ -317,16 +296,16 @@ int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const
int BestFuel = 0;
for (FuelList::const_iterator itr = m_pState->Fuel.begin(); itr != m_pState->Fuel.end(); ++itr)
{
- const Fuel & F = *itr;
- if ((F.In->m_ItemType == a_Fuel.m_ItemType) && (F.In->m_ItemCount <= a_Fuel.m_ItemCount))
+ const cFuel & Fuel = *itr;
+ if ((Fuel.In->m_ItemType == a_Fuel.m_ItemType) && (Fuel.In->m_ItemCount <= a_Fuel.m_ItemCount))
{
- if (BestFuel > 0 && (BestFuel > F.BurnTime))
+ if (BestFuel > 0 && (BestFuel > Fuel.BurnTime))
{
continue;
}
else
{
- BestFuel = F.BurnTime;
+ BestFuel = Fuel.BurnTime;
}
}
}
diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h
index 77ed35a57..6a1650695 100644
--- a/src/FurnaceRecipe.h
+++ b/src/FurnaceRecipe.h
@@ -19,23 +19,23 @@ public:
void ReloadRecipes(void);
- struct Fuel
+ struct cFuel
{
cItem * In;
int BurnTime; ///< How long this fuel burns, in ticks
};
- struct Recipe
+ struct cRecipe
{
cItem * In;
cItem * Out;
int CookTime; ///< How long this recipe takes to smelt, in ticks
};
- /// Returns a recipe for the specified input, NULL if no recipe found
- const Recipe * GetRecipeFrom(const cItem & a_Ingredient) const;
+ /** Returns a recipe for the specified input, NULL if no recipe found */
+ const cRecipe * GetRecipeFrom(const cItem & a_Ingredient) const;
- /// Returns the amount of time that the specified fuel burns, in ticks
+ /** Returns the amount of time that the specified fuel burns, in ticks */
int GetBurnTime(const cItem & a_Fuel) const;
private:
@@ -43,33 +43,14 @@ private:
/** Parses the fuel contained in the line, adds it to m_pState's fuels.
Logs a warning to the console on input error. */
- void AddFuelFromLine(const AString & a_Line, int a_LineNum);
+ void AddFuelFromLine(const AString & a_Line, unsigned int a_LineNum);
/** Parses the recipe contained in the line, adds it to m_pState's recipes.
Logs a warning to the console on input error. */
- void AddRecipeFromLine(const AString & a_Line, int a_LineNum);
-
- /** Calls LOGWARN with the line, position, and error */
- static void PrintParseError(unsigned int a_Line, size_t a_Position, const AString & a_CharactersMissing);
-
- /** Reads a number from a string given, starting at a given position and ending at a delimiter given
- Updates beginning position to the delimiter found + 1, and updates the value to the one read
- If it encounters a substring that is not fully numeric, it will call SetParseError() and return false; the caller should abort processing
- Otherwise, the function will return true
- */
- static bool ReadMandatoryNumber(AString::size_type & a_Begin, const AString & a_Delimiter, const AString & a_Text, unsigned int a_Line, int & a_Value, bool a_IsLastValue = false);
-
- /** Reads two numbers from a string given, starting at a given position and ending at the first delimiter given, then again (with an updated position) until the second delimiter given
- Updates beginning position to the second delimiter found + 1, and updates the values to the ones read
- If it encounters a substring that is not fully numeric whilst reading the second value, it will call SetParseError() and return false; the caller should abort processing
- If this happens whilst reading the first value, it will call ReadMandatoryNumber() with the appropriate position, as this may legitimately occur with the optional value and AString::find_first_of finding the incorrect delimiter. It will return the result of ReadMandatoryNumber()
- True will be returned definitively for an optional value that is valid
- */
- static bool ReadOptionalNumbers(AString::size_type & a_Begin, const AString & a_DelimiterOne, const AString & a_DelimiterTwo, const AString & a_Text, unsigned int a_Line, int & a_ValueOne, int & a_ValueTwo, bool a_IsLastValue = false);
-
- /** Uses std::all_of to determine if a string contains only digits */
- static bool DoesStringContainOnlyNumbers(const AString & a_String);
+ void AddRecipeFromLine(const AString & a_Line, unsigned int a_LineNum);
+ /** Parses an item string in the format "<ItemType>[: <Damage>][, <Amount>]", returns true if successful. */
+ bool ParseItem(const AString & a_String, cItem & a_Item);
struct sFurnaceRecipeState;
sFurnaceRecipeState * m_pState;
diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp
index 8fad9f5c9..175e4ef33 100644
--- a/src/Generating/BioGen.cpp
+++ b/src/Generating/BioGen.cpp
@@ -151,7 +151,7 @@ void cBioGenCache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a
LOGD("BioGenCache: %d hits, %d misses, saved %.2f %%", m_NumHits, m_NumMisses, 100.0 * m_NumHits / (m_NumHits + m_NumMisses));
LOGD("BioGenCache: Avg cache chain length: %.2f", (float)m_TotalChain / m_NumHits);
}
-
+
for (int i = 0; i < m_CacheSize; i++)
{
if (
@@ -208,6 +208,59 @@ void cBioGenCache::InitializeBiomeGen(cIniFile & a_IniFile)
+////////////////////////////////////////////////////////////////////////////////
+// cBioGenMulticache:
+
+cBioGenMulticache::cBioGenMulticache(cBiomeGen * a_BioGenToCache, size_t a_CacheSize, size_t a_CachesLength) :
+ m_CachesLength(a_CachesLength)
+{
+ m_Caches.reserve(a_CachesLength);
+ for (size_t i = 0; i < a_CachesLength; i++)
+ {
+ m_Caches.push_back(new cBioGenCache(a_BioGenToCache, a_CacheSize));
+ }
+}
+
+
+
+
+
+cBioGenMulticache::~cBioGenMulticache()
+{
+ for (std::vector<cBiomeGen*>::iterator it = m_Caches.begin(); it != m_Caches.end(); it++)
+ {
+ delete *it;
+ }
+}
+
+
+
+
+
+void cBioGenMulticache::GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
+{
+ const size_t coefficient = 3;
+ const size_t cacheIdx = ((size_t)a_ChunkX + coefficient * (size_t)a_ChunkZ) % m_CachesLength;
+
+ m_Caches[cacheIdx]->GenBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
+}
+
+
+
+
+
+void cBioGenMulticache::InitializeBiomeGen(cIniFile & a_IniFile)
+{
+ for (std::vector<cBiomeGen*>::iterator it = m_Caches.begin(); it != m_Caches.end(); it++)
+ {
+ cBiomeGen * tmp = *it;
+ tmp->InitializeBiomeGen(a_IniFile);
+ }
+}
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cBiomeGenList:
diff --git a/src/Generating/BioGen.h b/src/Generating/BioGen.h
index 5f9c7ebc0..f6946a5a8 100644
--- a/src/Generating/BioGen.h
+++ b/src/Generating/BioGen.h
@@ -80,6 +80,32 @@ protected:
+class cBioGenMulticache :
+ public cBiomeGen
+{
+
+ typedef cBiomeGen super;
+
+public:
+ /*
+ a_CacheSize defines the size of each singular cache
+ a_CachesLength defines how many caches are used for the multicache
+ */
+ cBioGenMulticache(cBiomeGen * a_BioGenToCache, size_t a_CacheSize, size_t a_CachesLength); // Doesn't take ownership of a_BioGenToCache
+ ~cBioGenMulticache();
+
+protected:
+ size_t m_CachesLength;
+ std::vector<cBiomeGen*> m_Caches;
+
+ virtual void GenBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
+ virtual void InitializeBiomeGen(cIniFile & a_IniFile) override;
+};
+
+
+
+
+
/// Base class for generators that use a list of available biomes. This class takes care of the list.
class cBiomeGenList :
public cBiomeGen
diff --git a/src/Generating/CMakeLists.txt b/src/Generating/CMakeLists.txt
index 58df9d421..33d622b42 100644
--- a/src/Generating/CMakeLists.txt
+++ b/src/Generating/CMakeLists.txt
@@ -12,6 +12,7 @@ SET (SRCS
CompoGen.cpp
ComposableGenerator.cpp
DistortedHeightmap.cpp
+ DungeonRoomsFinisher.cpp
EndGen.cpp
FinishGen.cpp
GridStructGen.cpp
@@ -40,6 +41,7 @@ SET (HDRS
CompoGen.h
ComposableGenerator.h
DistortedHeightmap.h
+ DungeonRoomsFinisher.h
EndGen.h
FinishGen.h
GridStructGen.h
diff --git a/src/Generating/Caves.cpp b/src/Generating/Caves.cpp
index 3e4bb6edd..13fe2d3ed 100644
--- a/src/Generating/Caves.cpp
+++ b/src/Generating/Caves.cpp
@@ -166,6 +166,9 @@ cCaveTunnel::cCaveTunnel(
if ((a_BlockStartY <= 0) && (a_BlockEndY <= 0))
{
// Don't bother detailing this cave, it's under the world anyway
+ m_MinBlockX = m_MaxBlockX = 0;
+ m_MinBlockY = m_MaxBlockY = -1;
+ m_MinBlockZ = m_MaxBlockZ = 0;
return;
}
diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp
index 3d5af152c..4fa9729ec 100644
--- a/src/Generating/ChunkGenerator.cpp
+++ b/src/Generating/ChunkGenerator.cpp
@@ -27,6 +27,7 @@ const unsigned int QUEUE_SKIP_LIMIT = 500;
cChunkGenerator::cChunkGenerator(void) :
super("cChunkGenerator"),
+ m_Seed(0), // Will be overwritten by the actual generator
m_Generator(NULL),
m_PluginInterface(NULL),
m_ChunkSink(NULL)
@@ -51,10 +52,21 @@ bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a
m_PluginInterface = &a_PluginInterface;
m_ChunkSink = &a_ChunkSink;
- MTRand rnd;
- m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", (int)rnd.randInt());
+ // Get the seed; create a new one and log it if not found in the INI file:
+ if (a_IniFile.HasValue("Seed", "Seed"))
+ {
+ m_Seed = a_IniFile.GetValueI("Seed", "Seed");
+ }
+ else
+ {
+ MTRand rnd;
+ m_Seed = rnd.randInt();
+ LOGINFO("Chosen a new random seed for world: %d", m_Seed);
+ a_IniFile.SetValueI("Seed", "Seed", m_Seed);
+ }
+
+ // Get the generator engine based on the INI file settings:
AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable");
-
if (NoCaseCompare(GeneratorName, "Noise3D") == 0)
{
m_Generator = new cNoise3DGenerator(*this);
@@ -98,15 +110,15 @@ void cChunkGenerator::Stop(void)
-void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate)
{
{
cCSLock Lock(m_CS);
// Check if it is already in the queue:
- for (cChunkCoordsList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
+ for (cChunkCoordsWithBoolList::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
{
- if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ))
+ if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ))
{
// Already in the queue, bail out
return;
@@ -118,7 +130,7 @@ void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_Chunk
{
LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (" SIZE_T_FMT ")", a_ChunkX, a_ChunkZ, m_Queue.size());
}
- m_Queue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
+ m_Queue.push_back(cChunkCoordsWithBool(a_ChunkX, a_ChunkZ, a_ForceGenerate));
}
m_Event.Set();
@@ -228,9 +240,9 @@ void cChunkGenerator::Execute(void)
continue;
}
- cChunkCoords coords = m_Queue.front(); // Get next coord from queue
- m_Queue.erase( m_Queue.begin()); // Remove coordinate from queue
+ cChunkCoordsWithBool coords = m_Queue.front(); // Get next coord from queue
bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
+ m_Queue.erase(m_Queue.begin()); // Remove coordinate from queue
Lock.Unlock(); // Unlock ASAP
m_evtRemoved.Set();
@@ -244,8 +256,7 @@ void cChunkGenerator::Execute(void)
LastReportTick = clock();
}
- // Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set
- if ((coords.m_ChunkY == 0) && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
+ if (!coords.m_ForceGenerate && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
{
LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ);
// Already generated, ignore request
@@ -258,8 +269,8 @@ void cChunkGenerator::Execute(void)
continue;
}
- LOGD("Generating chunk [%d, %d, %d]", coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
- DoGenerate(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
+ LOGD("Generating chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
+ DoGenerate(coords.m_ChunkX, coords.m_ChunkZ);
NumChunksGenerated++;
} // while (!bStop)
@@ -268,7 +279,7 @@ void cChunkGenerator::Execute(void)
-void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ)
{
ASSERT(m_PluginInterface != NULL);
ASSERT(m_ChunkSink != NULL);
diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h
index 47c3ee108..07711eef2 100644
--- a/src/Generating/ChunkGenerator.h
+++ b/src/Generating/ChunkGenerator.h
@@ -116,7 +116,7 @@ public:
void Stop(void);
/// Queues the chunk for generation; removes duplicate requests
- void QueueGenerateChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate);
/// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
@@ -137,10 +137,10 @@ private:
int m_Seed;
- cCriticalSection m_CS;
- cChunkCoordsList m_Queue;
- cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate
- cEvent m_evtRemoved; ///< Set when an item is removed from the queue
+ cCriticalSection m_CS;
+ cChunkCoordsWithBoolList m_Queue;
+ cEvent m_Event; ///< Set when an item is added to the queue or the thread should terminate
+ cEvent m_evtRemoved; ///< Set when an item is removed from the queue
cGenerator * m_Generator; ///< The actual generator engine used to generate chunks
@@ -154,7 +154,7 @@ private:
// cIsThread /*override*/:
virtual void Execute(void) /*override*/;
- void DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ void DoGenerate(int a_ChunkX, int a_ChunkZ);
};
diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp
index 2f575fe27..d70438bf3 100644
--- a/src/Generating/ComposableGenerator.cpp
+++ b/src/Generating/ComposableGenerator.cpp
@@ -19,6 +19,7 @@
#include "Caves.h"
#include "DistortedHeightmap.h"
+#include "DungeonRoomsFinisher.h"
#include "EndGen.h"
#include "MineShafts.h"
#include "NetherFortGen.h"
@@ -229,6 +230,8 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
// Add a cache, if requested:
int CacheSize = a_IniFile.GetValueSetI("Generator", "BiomeGenCacheSize", CacheOffByDefault ? 0 : 64);
+ int MultiCacheLength = a_IniFile.GetValueSetI("Generator", "BiomeGenMultiCacheLength", 4);
+
if (CacheSize > 0)
{
if (CacheSize < 4)
@@ -240,7 +243,16 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
}
LOGD("Using a cache for biomegen of size %d.", CacheSize);
m_UnderlyingBiomeGen = m_BiomeGen;
- m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize);
+ if (MultiCacheLength > 0)
+ {
+ LOGD("Enabling multicache for biomegen of length %d.", MultiCacheLength);
+ m_BiomeGen = new cBioGenMulticache(m_UnderlyingBiomeGen, CacheSize, MultiCacheLength);
+ }
+ else
+ {
+ m_BiomeGen = new cBioGenCache(m_UnderlyingBiomeGen, CacheSize);
+ }
+
}
}
@@ -343,6 +355,14 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
float Threshold = (float)a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3);
m_FinishGens.push_back(new cStructGenDualRidgeCaves(Seed, Threshold));
}
+ else if (NoCaseCompare(*itr, "DungeonRooms") == 0)
+ {
+ int GridSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsGridSize", 48);
+ int MaxSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMaxSize", 7);
+ int MinSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMinSize", 5);
+ AString HeightDistrib = a_IniFile.GetValueSet ("Generator", "DungeonRoomsHeightDistrib", "0, 0; 10, 10; 11, 500; 40, 500; 60, 40; 90, 1");
+ m_FinishGens.push_back(new cDungeonRoomsFinisher(*m_HeightGen, Seed, GridSize, MaxSize, MinSize, HeightDistrib));
+ }
else if (NoCaseCompare(*itr, "Ice") == 0)
{
m_FinishGens.push_back(new cFinishGenIce);
diff --git a/src/Generating/DungeonRoomsFinisher.cpp b/src/Generating/DungeonRoomsFinisher.cpp
new file mode 100644
index 000000000..f213455d6
--- /dev/null
+++ b/src/Generating/DungeonRoomsFinisher.cpp
@@ -0,0 +1,279 @@
+
+// DungeonRoomsFinisher.cpp
+
+// Declares the cDungeonRoomsFinisher class representing the finisher that generates dungeon rooms
+
+#include "Globals.h"
+#include "DungeonRoomsFinisher.h"
+#include "../FastRandom.h"
+
+
+
+
+
+/** Height, in blocks, of the internal dungeon room open space. This many air blocks Y-wise. */
+static const int ROOM_HEIGHT = 4;
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cDungeonRoom:
+
+class cDungeonRoom :
+ public cGridStructGen::cStructure
+{
+ typedef cGridStructGen::cStructure super;
+
+public:
+
+ cDungeonRoom(
+ int a_GridX, int a_GridZ,
+ int a_OriginX, int a_OriginZ,
+ int a_HalfSizeX, int a_HalfSizeZ,
+ int a_FloorHeight,
+ cNoise & a_Noise
+ ) :
+ super(a_GridX, a_GridZ, a_OriginX, a_OriginZ),
+ m_StartX(a_OriginX - a_HalfSizeX),
+ m_EndX(a_OriginX + a_HalfSizeX),
+ m_StartZ(a_OriginZ - a_HalfSizeZ),
+ m_EndZ(a_OriginZ + a_HalfSizeZ),
+ m_FloorHeight(a_FloorHeight)
+ {
+ /*
+ Pick coords next to the wall for the chests.
+ This is done by indexing the possible coords, picking any one for the first chest
+ and then picking another position for the second chest that is not adjacent to the first pos
+ */
+ int rnd = a_Noise.IntNoise2DInt(a_OriginX, a_OriginZ) / 7;
+ int SizeX = m_EndX - m_StartX - 1;
+ int SizeZ = m_EndZ - m_StartZ - 1;
+ int NumPositions = 2 * SizeX + 2 * SizeZ;
+ int FirstChestPos = rnd % NumPositions; // The corner positions are a bit more likely, but we don't mind
+ rnd = rnd / 512;
+ int SecondChestPos = (FirstChestPos + 2 + (rnd % (NumPositions - 3))) % NumPositions;
+ m_Chest1 = DecodeChestCoords(FirstChestPos, SizeX, SizeZ);
+ m_Chest2 = DecodeChestCoords(SecondChestPos, SizeX, SizeZ);
+ }
+
+protected:
+
+ // The X range of the room, start inclusive, end exclusive:
+ int m_StartX, m_EndX;
+
+ // The Z range of the room, start inclusive, end exclusive:
+ int m_StartZ, m_EndZ;
+
+ /** The Y coord of the floor of the room */
+ int m_FloorHeight;
+
+ /** The (absolute) coords of the first chest. The Y coord represents the chest's Meta value (facing). */
+ Vector3i m_Chest1;
+
+ /** The (absolute) coords of the second chest. The Y coord represents the chest's Meta value (facing). */
+ Vector3i m_Chest2;
+
+
+
+ /** Decodes the position index along the room walls into a proper 2D position for a chest. */
+ Vector3i DecodeChestCoords(int a_PosIdx, int a_SizeX, int a_SizeZ)
+ {
+ if (a_PosIdx < a_SizeX)
+ {
+ // Return a coord on the ZM side of the room:
+ return Vector3i(m_StartX + a_PosIdx + 1, E_META_CHEST_FACING_ZP, m_StartZ + 1);
+ }
+ a_PosIdx -= a_SizeX;
+ if (a_PosIdx < a_SizeZ)
+ {
+ // Return a coord on the XP side of the room:
+ return Vector3i(m_EndX - 1, E_META_CHEST_FACING_XM, m_StartZ + a_PosIdx + 1);
+ }
+ a_PosIdx -= a_SizeZ;
+ if (a_PosIdx < a_SizeX)
+ {
+ // Return a coord on the ZP side of the room:
+ return Vector3i(m_StartX + a_PosIdx + 1, E_META_CHEST_FACING_ZM, m_StartZ + 1);
+ }
+ a_PosIdx -= a_SizeX;
+ // Return a coord on the XM side of the room:
+ return Vector3i(m_StartX + 1, E_META_CHEST_FACING_XP, m_StartZ + a_PosIdx + 1);
+ }
+
+
+
+ /** Fills the specified area of blocks in the chunk with the specified blocktype if they are one of the overwritten block types.
+ The coords are absolute, start coords are inclusive, end coords are exclusive. */
+ void ReplaceCuboid(cChunkDesc & a_ChunkDesc, int a_StartX, int a_StartY, int a_StartZ, int a_EndX, int a_EndY, int a_EndZ, BLOCKTYPE a_DstBlockType)
+ {
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width - 1);
+ int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1);
+ int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width);
+ int RelEndZ = Clamp(a_EndZ - BlockZ, 0, cChunkDef::Width);
+ for (int y = a_StartY; y < a_EndY; y++)
+ {
+ for (int z = RelStartZ; z < RelEndZ; z++)
+ {
+ for (int x = RelStartX; x < RelEndX; x++)
+ {
+ if (cBlockInfo::CanBeTerraformed(a_ChunkDesc.GetBlockType(x, y, z)))
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, a_DstBlockType);
+ }
+ } // for x
+ } // for z
+ } // for z
+ }
+
+
+
+ /** Fills the specified area of blocks in the chunk with a random pattern of the specified blocktypes, if they are one of the overwritten block types.
+ The coords are absolute, start coords are inclusive, end coords are exclusive. The first blocktype uses 75% chance, the second 25% chance. */
+ void ReplaceCuboidRandom(cChunkDesc & a_ChunkDesc, int a_StartX, int a_StartY, int a_StartZ, int a_EndX, int a_EndY, int a_EndZ, BLOCKTYPE a_DstBlockType1, BLOCKTYPE a_DstBlockType2)
+ {
+ int BlockX = a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int BlockZ = a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ int RelStartX = Clamp(a_StartX - BlockX, 0, cChunkDef::Width - 1);
+ int RelStartZ = Clamp(a_StartZ - BlockZ, 0, cChunkDef::Width - 1);
+ int RelEndX = Clamp(a_EndX - BlockX, 0, cChunkDef::Width);
+ int RelEndZ = Clamp(a_EndZ - BlockZ, 0, cChunkDef::Width);
+ cFastRandom rnd;
+ for (int y = a_StartY; y < a_EndY; y++)
+ {
+ for (int z = RelStartZ; z < RelEndZ; z++)
+ {
+ for (int x = RelStartX; x < RelEndX; x++)
+ {
+ if (cBlockInfo::CanBeTerraformed(a_ChunkDesc.GetBlockType(x, y, z)))
+ {
+ BLOCKTYPE BlockType = (rnd.NextInt(101) < 75) ? a_DstBlockType1 : a_DstBlockType2;
+ a_ChunkDesc.SetBlockType(x, y, z, BlockType);
+ }
+ } // for x
+ } // for z
+ } // for z
+ }
+
+
+
+ /** Tries to place a chest at the specified (absolute) coords.
+ Does nothing if the coords are outside the chunk. */
+ void TryPlaceChest(cChunkDesc & a_ChunkDesc, const Vector3i & a_Chest)
+ {
+ int RelX = a_Chest.x - a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int RelZ = a_Chest.z - a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ if (
+ (RelX < 0) || (RelX >= cChunkDef::Width) || // The X coord is not in this chunk
+ (RelZ < 0) || (RelZ >= cChunkDef::Width) // The Z coord is not in this chunk
+ )
+ {
+ return;
+ }
+ a_ChunkDesc.SetBlockTypeMeta(RelX, m_FloorHeight + 1, RelZ, E_BLOCK_CHEST, (NIBBLETYPE)a_Chest.y);
+
+ // TODO: Fill the chest with random loot
+ }
+
+
+
+ // cGridStructGen::cStructure override:
+ virtual void DrawIntoChunk(cChunkDesc & a_ChunkDesc) override
+ {
+ if (
+ (m_EndX < a_ChunkDesc.GetChunkX() * cChunkDef::Width) ||
+ (m_StartX >= a_ChunkDesc.GetChunkX() * cChunkDef::Width + cChunkDef::Width) ||
+ (m_EndZ < a_ChunkDesc.GetChunkZ() * cChunkDef::Width) ||
+ (m_StartZ >= a_ChunkDesc.GetChunkZ() * cChunkDef::Width + cChunkDef::Width)
+ )
+ {
+ // The chunk is not intersecting the room at all, bail out
+ return;
+ }
+ int b = m_FloorHeight + 1; // Bottom
+ int t = m_FloorHeight + 1 + ROOM_HEIGHT; // Top
+ ReplaceCuboidRandom(a_ChunkDesc, m_StartX, m_FloorHeight, m_StartZ, m_EndX + 1, b, m_EndZ + 1, E_BLOCK_MOSSY_COBBLESTONE, E_BLOCK_COBBLESTONE); // Floor
+ ReplaceCuboid(a_ChunkDesc, m_StartX + 1, b, m_StartZ + 1, m_EndX, t, m_EndZ, E_BLOCK_AIR); // Insides
+
+ // Walls:
+ ReplaceCuboid(a_ChunkDesc, m_StartX, b, m_StartZ, m_StartX + 1, t, m_EndZ, E_BLOCK_COBBLESTONE); // XM wall
+ ReplaceCuboid(a_ChunkDesc, m_EndX, b, m_StartZ, m_EndX + 1, t, m_EndZ, E_BLOCK_COBBLESTONE); // XP wall
+ ReplaceCuboid(a_ChunkDesc, m_StartX, b, m_StartZ, m_EndX + 1, t, m_StartZ + 1, E_BLOCK_COBBLESTONE); // ZM wall
+ ReplaceCuboid(a_ChunkDesc, m_StartX, b, m_EndZ, m_EndX + 1, t, m_EndZ + 1, E_BLOCK_COBBLESTONE); // ZP wall
+
+ // Place chests:
+ TryPlaceChest(a_ChunkDesc, m_Chest1);
+ TryPlaceChest(a_ChunkDesc, m_Chest2);
+
+ // Place the spawner:
+ int CenterX = (m_StartX + m_EndX) / 2 - a_ChunkDesc.GetChunkX() * cChunkDef::Width;
+ int CenterZ = (m_StartZ + m_EndZ) / 2 - a_ChunkDesc.GetChunkZ() * cChunkDef::Width;
+ if (
+ (CenterX >= 0) && (CenterX < cChunkDef::Width) &&
+ (CenterZ >= 0) && (CenterZ < cChunkDef::Width)
+ )
+ {
+ a_ChunkDesc.SetBlockTypeMeta(CenterX, b, CenterZ, E_BLOCK_MOB_SPAWNER, 0);
+ // TODO: Set the spawned mob
+ }
+ }
+} ;
+
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cDungeonRoomsFinisher:
+
+cDungeonRoomsFinisher::cDungeonRoomsFinisher(cTerrainHeightGen & a_HeightGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib) :
+ super(a_Seed + 100, a_GridSize, a_GridSize, a_GridSize, a_GridSize, a_MaxSize, a_MaxSize, 1024),
+ m_HeightGen(a_HeightGen),
+ m_MaxHalfSize((a_MaxSize + 1) / 2),
+ m_MinHalfSize((a_MinSize + 1) / 2),
+ m_HeightProbability(cChunkDef::Height)
+{
+ // Initialize the height probability distribution:
+ m_HeightProbability.SetDefString(a_HeightDistrib);
+
+ // Normalize the min and max size:
+ if (m_MinHalfSize > m_MaxHalfSize)
+ {
+ std::swap(m_MinHalfSize, m_MaxHalfSize);
+ }
+}
+
+
+
+
+
+
+cDungeonRoomsFinisher::cStructurePtr cDungeonRoomsFinisher::CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ)
+{
+ // Select a random room size in each direction:
+ int rnd = m_Noise.IntNoise2DInt(a_OriginX, a_OriginZ) / 7;
+ int HalfSizeX = m_MinHalfSize + (rnd % (m_MaxHalfSize - m_MinHalfSize + 1));
+ rnd = rnd / 32;
+ int HalfSizeZ = m_MinHalfSize + (rnd % (m_MaxHalfSize - m_MinHalfSize + 1));
+ rnd = rnd / 32;
+
+ // Select a random floor height for the room, based on the height generator:
+ int ChunkX, ChunkZ;
+ int RelX = a_OriginX, RelY = 0, RelZ = a_OriginZ;
+ cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ);
+ cChunkDef::HeightMap HeightMap;
+ m_HeightGen.GenHeightMap(ChunkX, ChunkZ, HeightMap);
+ int Height = cChunkDef::GetHeight(HeightMap, RelX, RelZ); // Max room height at {a_OriginX, a_OriginZ}
+ Height = Clamp(m_HeightProbability.MapValue(rnd % m_HeightProbability.GetSum()), 10, Height - 5);
+
+ // Create the dungeon room descriptor:
+ return cStructurePtr(new cDungeonRoom(a_GridX, a_GridZ, a_OriginX, a_OriginZ, HalfSizeX, HalfSizeZ, Height, m_Noise));
+}
+
+
+
+
diff --git a/src/Generating/DungeonRoomsFinisher.h b/src/Generating/DungeonRoomsFinisher.h
new file mode 100644
index 000000000..2b52c9de6
--- /dev/null
+++ b/src/Generating/DungeonRoomsFinisher.h
@@ -0,0 +1,52 @@
+
+// DungeonRoomsFinisher.h
+
+// Declares the cDungeonRoomsFinisher class representing the finisher that generates dungeon rooms
+
+
+
+
+
+#pragma once
+
+#include "GridStructGen.h"
+#include "../ProbabDistrib.h"
+
+
+
+
+
+class cDungeonRoomsFinisher :
+ public cGridStructGen
+{
+ typedef cGridStructGen super;
+
+public:
+ /** Creates a new dungeon room finisher.
+ a_HeightGen is the underlying height generator, so that the rooms can always be placed under the terrain.
+ a_MaxSize and a_MinSize are the maximum and minimum sizes of the room's internal (air) area, in blocks across.
+ a_HeightDistrib is the string defining the height distribution for the rooms (cProbabDistrib format). */
+ cDungeonRoomsFinisher(cTerrainHeightGen & a_HeightGen, int a_Seed, int a_GridSize, int a_MaxSize, int a_MinSize, const AString & a_HeightDistrib);
+
+protected:
+
+ /** The height gen that is used for limiting the rooms' Y coords */
+ cTerrainHeightGen & m_HeightGen;
+
+ /** Maximum half-size (from center to wall) of the dungeon room's inner (air) area. Default is 3 (vanilla). */
+ int m_MaxHalfSize;
+
+ /** Minimum half-size (from center to wall) of the dungeon room's inner (air) area. Default is 2 (vanilla). */
+ int m_MinHalfSize;
+
+ /** The height probability distribution to make the spawners more common in layers 10 - 40, less common outside this range. */
+ cProbabDistrib m_HeightProbability;
+
+
+ // cGridStructGen overrides:
+ virtual cStructurePtr CreateStructure(int a_GridX, int a_GridZ, int a_OriginX, int a_OriginZ) override;
+} ;
+
+
+
+
diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp
index c3f3b38a9..79d529a6a 100644
--- a/src/Generating/HeiGen.cpp
+++ b/src/Generating/HeiGen.cpp
@@ -239,7 +239,13 @@ bool cHeiGenCache::GetHeightAt(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_Rel
cHeiGenClassic::cHeiGenClassic(int a_Seed) :
m_Seed(a_Seed),
- m_Noise(a_Seed)
+ m_Noise(a_Seed),
+ m_HeightFreq1(1.0f),
+ m_HeightAmp1(1.0f),
+ m_HeightFreq2(0.5f),
+ m_HeightAmp2(0.5f),
+ m_HeightFreq3(0.1f),
+ m_HeightAmp3(0.1f)
{
}
diff --git a/src/Group.cpp b/src/Group.cpp
deleted file mode 100644
index def585618..000000000
--- a/src/Group.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "Group.h"
-
-
-
-
-
-void cGroup::AddCommand( const AString & a_Command)
-{
- m_Commands[ a_Command ] = true;
-}
-
-
-
-
-
-void cGroup::AddPermission( const AString & a_Permission)
-{
- m_Permissions[ a_Permission ] = true;
-}
-
-
-
-
-
-void cGroup::InheritFrom( cGroup* a_Group)
-{
- m_Inherits.remove( a_Group);
- m_Inherits.push_back( a_Group);
-}
-
-
-
-
-
-void cGroup::ClearPermission()
-{
- m_Permissions.clear();
-}
diff --git a/src/Group.h b/src/Group.h
deleted file mode 100644
index 5816f8a06..000000000
--- a/src/Group.h
+++ /dev/null
@@ -1,44 +0,0 @@
-
-#pragma once
-
-
-
-
-
-// tolua_begin
-class cGroup
-{
-public:
- // tolua_end
- cGroup() {}
- ~cGroup() {}
-
- // tolua_begin
- void SetName( const AString & a_Name) { m_Name = a_Name; }
- const AString & GetName() const { return m_Name; }
- void SetColor( const AString & a_Color) { m_Color = a_Color; }
- void AddCommand( const AString & a_Command);
- void AddPermission( const AString & a_Permission);
- void InheritFrom( cGroup* a_Group);
- // tolua_end
-
- typedef std::map< AString, bool > PermissionMap;
- const PermissionMap & GetPermissions() const { return m_Permissions; }
-
- void ClearPermission(void);
-
- typedef std::map< AString, bool > CommandMap;
- const CommandMap & GetCommands() const { return m_Commands; }
-
- const AString & GetColor() const { return m_Color; } // tolua_export
-
- typedef std::list< cGroup* > GroupList;
- const GroupList & GetInherits() const { return m_Inherits; }
-private:
- AString m_Name;
- AString m_Color;
-
- PermissionMap m_Permissions;
- CommandMap m_Commands;
- GroupList m_Inherits;
-}; // tolua_export
diff --git a/src/GroupManager.cpp b/src/GroupManager.cpp
deleted file mode 100644
index 4c3dfc6f0..000000000
--- a/src/GroupManager.cpp
+++ /dev/null
@@ -1,227 +0,0 @@
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "GroupManager.h"
-#include "Group.h"
-#include "inifile/iniFile.h"
-#include "ChatColor.h"
-#include "Root.h"
-
-
-
-
-
-typedef std::map< AString, cGroup* > GroupMap;
-
-
-
-
-
-struct cGroupManager::sGroupManagerState
-{
- GroupMap Groups;
-};
-
-
-
-
-
-cGroupManager::~cGroupManager()
-{
- for (GroupMap::iterator itr = m_pState->Groups.begin(); itr != m_pState->Groups.end(); ++itr)
- {
- delete itr->second;
- itr->second = NULL;
- }
- m_pState->Groups.clear();
-
- delete m_pState;
- m_pState = NULL;
-}
-
-
-
-
-
-cGroupManager::cGroupManager()
- : m_pState( new sGroupManagerState)
-{
- LOGD("-- Loading Groups --");
-
- if (!LoadGroups())
- {
- LOGWARNING("ERROR: Groups could not load!");
- }
- if (!CheckUsers())
- {
- LOGWARNING("ERROR: User file could not be found!");
- }
-
- LOGD("-- Groups Successfully Loaded --");
-}
-
-
-
-
-
-void cGroupManager::GenerateDefaultUsersIni(cIniFile & a_IniFile)
-{
- LOGWARN("Regenerating users.ini, all users will be reset");
- a_IniFile.AddHeaderComment(" This file stores the players' groups.");
- a_IniFile.AddHeaderComment(" The format is:");
- a_IniFile.AddHeaderComment(" [PlayerName]");
- a_IniFile.AddHeaderComment(" Groups = GroupName1, GroupName2, ...");
-
- a_IniFile.WriteFile("users.ini");
-}
-
-
-
-
-
-bool cGroupManager::CheckUsers()
-{
- cIniFile IniFile;
- if (!IniFile.ReadFile("users.ini"))
- {
- GenerateDefaultUsersIni(IniFile);
- return true;
- }
-
- int NumKeys = IniFile.GetNumKeys();
- for (int i = 0; i < NumKeys; i++)
- {
- AString Player = IniFile.GetKeyName(i);
- AString Groups = IniFile.GetValue(Player, "Groups", "");
- if (Groups.empty())
- {
- continue;
- }
- AStringVector Split = StringSplitAndTrim(Groups, ",");
- for (AStringVector::const_iterator itr = Split.begin(), end = Split.end(); itr != end; ++itr)
- {
- if (!ExistsGroup(*itr))
- {
- LOGWARNING("The group %s for player %s was not found!", Split[i].c_str(), Player.c_str());
- }
- } // for itr - Split[]
- } // for i - ini file keys
- // Always return true for now, just but we can handle writefile fails later.
- return true;
-}
-
-
-
-
-
-bool cGroupManager::LoadGroups()
-{
- cIniFile IniFile;
- if (!IniFile.ReadFile("groups.ini"))
- {
- LOGWARNING("Regenerating groups.ini, all groups will be reset");
- IniFile.AddHeaderComment(" This is the MCServer permissions manager groups file");
- IniFile.AddHeaderComment(" It stores all defined groups such as Administrators, Players, or Moderators");
-
- IniFile.SetValue("Owner", "Permissions", "*", true);
- IniFile.SetValue("Owner", "Color", "2", true);
-
- IniFile.SetValue("Moderator", "Permissions", "core.time, core.item, core.tpa, core.tpaccept, core.ban, core.unban, core.save-all, core.toggledownfall");
- IniFile.SetValue("Moderator", "Color", "2", true);
- IniFile.SetValue("Moderator", "Inherits", "Player", true);
-
- IniFile.SetValue("Player", "Permissions", "core.portal", true);
- IniFile.SetValue("Player", "Color", "f", true);
- IniFile.SetValue("Player", "Inherits", "Default", true);
-
- IniFile.SetValue("Default", "Permissions", "core.help, core.plugins, core.spawn, core.worlds, core.back, core.motd, core.build, core.locate, core.viewdistance", true);
- IniFile.SetValue("Default", "Color", "f", true);
-
- IniFile.WriteFile("groups.ini");
- }
-
- int NumKeys = IniFile.GetNumKeys();
- for (int i = 0; i < NumKeys; i++)
- {
- AString KeyName = IniFile.GetKeyName(i);
- cGroup * Group = GetGroup(KeyName.c_str());
-
- Group->ClearPermission(); // Needed in case the groups are reloaded.
-
- LOGD("Loading group %s", KeyName.c_str());
-
- Group->SetName(KeyName);
- AString Color = IniFile.GetValue(KeyName, "Color", "-");
- if ((Color != "-") && (Color.length() >= 1))
- {
- Group->SetColor(AString(cChatColor::Delimiter) + Color[0]);
- }
- else
- {
- Group->SetColor(cChatColor::White);
- }
-
- AString Commands = IniFile.GetValue(KeyName, "Commands", "");
- if (!Commands.empty())
- {
- AStringVector Split = StringSplitAndTrim(Commands, ",");
- for (size_t i = 0; i < Split.size(); i++)
- {
- Group->AddCommand(Split[i]);
- }
- }
-
- AString Permissions = IniFile.GetValue(KeyName, "Permissions", "");
- if (!Permissions.empty())
- {
- AStringVector Split = StringSplitAndTrim(Permissions, ",");
- for (size_t i = 0; i < Split.size(); i++)
- {
- Group->AddPermission(Split[i]);
- }
- }
-
- AString Groups = IniFile.GetValue(KeyName, "Inherits", "");
- if (!Groups.empty())
- {
- AStringVector Split = StringSplitAndTrim(Groups, ",");
- for (size_t i = 0; i < Split.size(); i++)
- {
- Group->InheritFrom(GetGroup(Split[i].c_str()));
- }
- }
- }
- // Always return true, we can handle writefile fails later.
- return true;
-}
-
-
-
-
-
-bool cGroupManager::ExistsGroup( const AString & a_Name)
-{
- GroupMap::iterator itr = m_pState->Groups.find( a_Name);
- return ( itr != m_pState->Groups.end());
-}
-
-
-
-
-
-cGroup* cGroupManager::GetGroup( const AString & a_Name)
-{
- GroupMap::iterator itr = m_pState->Groups.find( a_Name);
- if (itr != m_pState->Groups.end())
- {
- return itr->second;
- }
-
- cGroup* Group = new cGroup();
- m_pState->Groups[a_Name] = Group;
-
- return Group;
-}
-
-
-
-
diff --git a/src/GroupManager.h b/src/GroupManager.h
deleted file mode 100644
index d42b55c4a..000000000
--- a/src/GroupManager.h
+++ /dev/null
@@ -1,36 +0,0 @@
-
-#pragma once
-
-
-
-
-
-class cGroup;
-
-
-
-
-
-class cGroupManager
-{
-public:
- bool ExistsGroup(const AString & a_Name);
- cGroup * GetGroup(const AString & a_Name);
- bool LoadGroups();
- bool CheckUsers();
-
- /** Writes the default header to the specified ini file, and saves it as "users.ini". */
- static void GenerateDefaultUsersIni(cIniFile & a_IniFile);
-
-private:
- friend class cRoot;
- cGroupManager();
- ~cGroupManager();
-
- struct sGroupManagerState;
- sGroupManagerState * m_pState;
-} ;
-
-
-
-
diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp
index b9c762e7c..bf46bb241 100644
--- a/src/HTTPServer/HTTPConnection.cpp
+++ b/src/HTTPServer/HTTPConnection.cpp
@@ -15,7 +15,8 @@
cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) :
m_HTTPServer(a_HTTPServer),
m_State(wcsRecvHeaders),
- m_CurrentRequest(NULL)
+ m_CurrentRequest(NULL),
+ m_CurrentRequestBodyRemaining(0)
{
// LOGD("HTTP: New connection at %p", this);
}
diff --git a/src/HTTPServer/HTTPFormParser.cpp b/src/HTTPServer/HTTPFormParser.cpp
index 9ddfb82f1..c50c6dcf2 100644
--- a/src/HTTPServer/HTTPFormParser.cpp
+++ b/src/HTTPServer/HTTPFormParser.cpp
@@ -15,7 +15,9 @@
cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callbacks) :
m_Callbacks(a_Callbacks),
- m_IsValid(true)
+ m_IsValid(true),
+ m_IsCurrentPartFile(false),
+ m_FileHasBeenAnnounced(false)
{
if (a_Request.GetMethod() == "GET")
{
@@ -55,7 +57,9 @@ cHTTPFormParser::cHTTPFormParser(cHTTPRequest & a_Request, cCallbacks & a_Callba
cHTTPFormParser::cHTTPFormParser(eKind a_Kind, const char * a_Data, size_t a_Size, cCallbacks & a_Callbacks) :
m_Callbacks(a_Callbacks),
m_Kind(a_Kind),
- m_IsValid(true)
+ m_IsValid(true),
+ m_IsCurrentPartFile(false),
+ m_FileHasBeenAnnounced(false)
{
Parse(a_Data, a_Size);
}
diff --git a/src/Inventory.cpp b/src/Inventory.cpp
index 8da3dea5f..832a079bc 100644
--- a/src/Inventory.cpp
+++ b/src/Inventory.cpp
@@ -106,7 +106,7 @@ int cInventory::AddItem(const cItem & a_Item, bool a_AllowNewStacks, bool a_tryT
// When the item is a armor, try to set it directly to the armor slot.
if (ItemCategory::IsArmor(a_Item.m_ItemType))
{
- for (size_t i = 0; i < (size_t)m_ArmorSlots.GetNumSlots(); i++)
+ for (int i = 0; i < m_ArmorSlots.GetNumSlots(); i++)
{
if (m_ArmorSlots.GetSlot(i).IsEmpty() && cSlotAreaArmor::CanPlaceArmorInSlot(i, a_Item))
{
diff --git a/src/Item.cpp b/src/Item.cpp
index 2c5deaddf..ebdf99ca5 100644
--- a/src/Item.cpp
+++ b/src/Item.cpp
@@ -190,31 +190,35 @@ void cItem::FromJson(const Json::Value & a_Value)
-bool cItem::IsEnchantable(short item)
+bool cItem::IsEnchantable(short a_ItemType, bool a_WithBook)
{
- if ((item >= 256) && (item <= 259))
+ if (
+ ItemCategory::IsAxe(a_ItemType) ||
+ ItemCategory::IsSword(a_ItemType) ||
+ ItemCategory::IsShovel(a_ItemType) ||
+ ItemCategory::IsPickaxe(a_ItemType) ||
+ (a_WithBook && ItemCategory::IsHoe(a_ItemType)) ||
+ ItemCategory::IsArmor(a_ItemType)
+ )
{
return true;
}
- if ((item >= 267) && (item <= 279))
- {
- return true;
- }
- if ((item >= 283) && (item <= 286))
- {
- return true;
- }
- if ((item >= 290) && (item <= 294))
- {
- return true;
- }
- if ((item >= 298) && (item <= 317))
- {
- return true;
- }
- if ((item == 346) || (item == 359) || (item == 261))
+
+ switch (a_ItemType)
{
- return true;
+ case E_ITEM_BOOK:
+ case E_ITEM_BOW:
+ case E_ITEM_FISHING_ROD:
+ {
+ return true;
+ }
+
+ case E_ITEM_CARROT_ON_STICK:
+ case E_ITEM_SHEARS:
+ case E_ITEM_FLINT_AND_STEEL:
+ {
+ return a_WithBook;
+ }
}
return false;
@@ -299,73 +303,77 @@ int cItem::GetEnchantability()
bool cItem::EnchantByXPLevels(int a_NumXPLevels)
{
- if (!cItem::IsEnchantable(m_ItemType) && (m_ItemType != E_ITEM_BOOK))
+ if (!cItem::IsEnchantable(m_ItemType))
{
return false;
}
int Enchantability = GetEnchantability();
+ if (Enchantability == 0)
+ {
+ return false;
+ }
cFastRandom Random;
int ModifiedEnchantmentLevel = a_NumXPLevels + (int)Random.NextFloat((float)Enchantability / 4) + (int)Random.NextFloat((float)Enchantability / 4) + 1;
float RandomBonus = 1.0F + (Random.NextFloat(1) + Random.NextFloat(1) - 1.0F) * 0.15F;
int FinalEnchantmentLevel = (int)(ModifiedEnchantmentLevel * RandomBonus + 0.5F);
- cWeightedEnchantments enchantments;
- cEnchantments::AddItemEnchantmentWeights(enchantments, m_ItemType, FinalEnchantmentLevel);
+ cWeightedEnchantments Enchantments;
+ cEnchantments::AddItemEnchantmentWeights(Enchantments, m_ItemType, FinalEnchantmentLevel);
if (m_ItemType == E_ITEM_BOOK)
{
m_ItemType = E_ITEM_ENCHANTED_BOOK;
}
- cEnchantments Enchantment1 = cEnchantments::GetRandomEnchantmentFromVector(enchantments);
+ cEnchantments Enchantment1 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
m_Enchantments.AddFromString(Enchantment1.ToString());
- cEnchantments::RemoveEnchantmentWeightFromVector(enchantments, Enchantment1);
+ cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment1);
// Checking for conflicting enchantments
- cEnchantments::CheckEnchantmentConflictsFromVector(enchantments, Enchantment1);
+ cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment1);
float NewEnchantmentLevel = (float)a_NumXPLevels;
// Next Enchantment (Second)
NewEnchantmentLevel = NewEnchantmentLevel / 2;
float SecondEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100;
- if (enchantments.empty() || (Random.NextFloat(100) > SecondEnchantmentChance))
+ if (Enchantments.empty() || (Random.NextFloat(100) > SecondEnchantmentChance))
{
return true;
}
- cEnchantments Enchantment2 = cEnchantments::GetRandomEnchantmentFromVector(enchantments);
+ cEnchantments Enchantment2 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
m_Enchantments.AddFromString(Enchantment2.ToString());
- cEnchantments::RemoveEnchantmentWeightFromVector(enchantments, Enchantment2);
+ cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment2);
// Checking for conflicting enchantments
- cEnchantments::CheckEnchantmentConflictsFromVector(enchantments, Enchantment2);
+ cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment2);
// Next Enchantment (Third)
NewEnchantmentLevel = NewEnchantmentLevel / 2;
float ThirdEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100;
- if (enchantments.empty() || (Random.NextFloat(100) > ThirdEnchantmentChance))
+ if (Enchantments.empty() || (Random.NextFloat(100) > ThirdEnchantmentChance))
{
return true;
}
- cEnchantments Enchantment3 = cEnchantments::GetRandomEnchantmentFromVector(enchantments);
+ cEnchantments Enchantment3 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
m_Enchantments.AddFromString(Enchantment3.ToString());
- cEnchantments::RemoveEnchantmentWeightFromVector(enchantments, Enchantment3);
+ cEnchantments::RemoveEnchantmentWeightFromVector(Enchantments, Enchantment3);
// Checking for conflicting enchantments
- cEnchantments::CheckEnchantmentConflictsFromVector(enchantments, Enchantment3);
+ cEnchantments::CheckEnchantmentConflictsFromVector(Enchantments, Enchantment3);
// Next Enchantment (Fourth)
NewEnchantmentLevel = NewEnchantmentLevel / 2;
float FourthEnchantmentChance = (NewEnchantmentLevel + 1) / 50 * 100;
- if (enchantments.empty() || (Random.NextFloat(100) > FourthEnchantmentChance))
+ if (Enchantments.empty() || (Random.NextFloat(100) > FourthEnchantmentChance))
{
return true;
}
- cEnchantments Enchantment4 = cEnchantments::GetRandomEnchantmentFromVector(enchantments);
+ cEnchantments Enchantment4 = cEnchantments::GetRandomEnchantmentFromVector(Enchantments);
m_Enchantments.AddFromString(Enchantment4.ToString());
return true;
diff --git a/src/Item.h b/src/Item.h
index d8b9e78a0..056b5eb8a 100644
--- a/src/Item.h
+++ b/src/Item.h
@@ -183,8 +183,10 @@ public:
/** Loads the item data from JSON representation */
void FromJson(const Json::Value & a_Value);
- /** Returns true if the specified item type is enchantable (as per 1.2.5 protocol requirements) */
- static bool IsEnchantable(short a_ItemType); // tolua_export
+ /** Returns true if the specified item type is enchantable.
+ If WithBook is true, the function is used in the anvil inventory with book enchantments.
+ So it checks the "only book enchantments" too. Example: You can only enchant a hoe with a book. */
+ static bool IsEnchantable(short a_ItemType, bool a_WithBook = false); // tolua_export
/** Returns the enchantability of the item. When the item hasn't a enchantability, it will returns 0 */
int GetEnchantability(); // tolua_export
diff --git a/src/Items/ItemBow.h b/src/Items/ItemBow.h
index 618b0828f..639442481 100644
--- a/src/Items/ItemBow.h
+++ b/src/Items/ItemBow.h
@@ -75,7 +75,6 @@ public:
Arrow = NULL;
return;
}
-
a_Player->GetWorld()->BroadcastSoundEffect("random.bow", a_Player->GetPosX(), a_Player->GetPosY(), a_Player->GetPosZ(), 0.5, (float)Force);
if (!a_Player->IsGameModeCreative())
{
@@ -83,8 +82,19 @@ public:
{
a_Player->GetInventory().RemoveItem(cItem(E_ITEM_ARROW));
}
+ else
+ {
+ Arrow->SetPickupState(cArrowEntity::psNoPickup);
+ }
+
+
a_Player->UseEquippedItem();
}
+
+ if (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchFlame) > 0)
+ {
+ Arrow->StartBurning(100);
+ }
}
} ;
diff --git a/src/Items/ItemFood.h b/src/Items/ItemFood.h
index fc5607b11..3f67463ed 100644
--- a/src/Items/ItemFood.h
+++ b/src/Items/ItemFood.h
@@ -29,7 +29,7 @@ public:
switch (m_ItemType)
{
// Please keep alpha-sorted.
- case E_ITEM_BAKED_POTATO: return FoodInfo(6, 7.2);
+ case E_ITEM_BAKED_POTATO: return FoodInfo(5, 7.2);
case E_ITEM_BREAD: return FoodInfo(5, 6);
// Carrots handled in ItemSeeds
case E_ITEM_COOKED_CHICKEN: return FoodInfo(6, 7.2);
@@ -43,14 +43,16 @@ public:
case E_ITEM_POISONOUS_POTATO: return FoodInfo(2, 1.2);
// Potatoes handled in ItemSeeds
case E_ITEM_PUMPKIN_PIE: return FoodInfo(8, 4.8);
+ case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4);
case E_ITEM_RAW_BEEF: return FoodInfo(3, 1.8);
case E_ITEM_RAW_CHICKEN: return FoodInfo(2, 1.2);
case E_ITEM_RAW_FISH: return FoodInfo(2, 1.2);
+ case E_ITEM_RAW_MUTTON: return FoodInfo(2, 1.2);
case E_ITEM_RAW_PORKCHOP: return FoodInfo(3, 1.8);
- case E_ITEM_RED_APPLE: return FoodInfo(4, 2.4);
case E_ITEM_ROTTEN_FLESH: return FoodInfo(4, 0.8);
case E_ITEM_SPIDER_EYE: return FoodInfo(2, 3.2);
case E_ITEM_STEAK: return FoodInfo(8, 12.8);
+ case E_ITEM_MUTTON: return FoodInfo(6, 9.6);
}
LOGWARNING("%s: Unknown food item (%d), returning zero nutrition", __FUNCTION__, m_ItemType);
return FoodInfo(0, 0.f);
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index bceedaf69..67c945ce4 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -207,7 +207,7 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
}
// Food (please keep alpha-sorted):
- // (carrots and potatoes handled in SeedHandler as both seed and food
+ // (carrots and potatoes handled separately in SeedHandler as they're both seed and food)
case E_ITEM_BAKED_POTATO:
case E_ITEM_BREAD:
case E_ITEM_COOKED_CHICKEN:
@@ -217,13 +217,15 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
case E_ITEM_GOLDEN_CARROT:
case E_ITEM_MELON_SLICE:
case E_ITEM_MUSHROOM_SOUP:
+ case E_ITEM_MUTTON:
case E_ITEM_POISONOUS_POTATO:
case E_ITEM_PUMPKIN_PIE:
+ case E_ITEM_RED_APPLE:
case E_ITEM_RAW_BEEF:
case E_ITEM_RAW_CHICKEN:
case E_ITEM_RAW_FISH:
+ case E_ITEM_RAW_MUTTON:
case E_ITEM_RAW_PORKCHOP:
- case E_ITEM_RED_APPLE:
case E_ITEM_ROTTEN_FLESH:
case E_ITEM_SPIDER_EYE:
case E_ITEM_STEAK:
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index 8b554ee34..67c250a97 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -75,7 +75,7 @@ public:
int FoodLevel;
double Saturation;
- FoodInfo(int a_FoodLevel, double a_Saturation, int a_PoisonChance = 0) :
+ FoodInfo(int a_FoodLevel, double a_Saturation) :
FoodLevel(a_FoodLevel),
Saturation(a_Saturation)
{
diff --git a/src/Items/ItemSeeds.h b/src/Items/ItemSeeds.h
index 249966531..5b5b91626 100644
--- a/src/Items/ItemSeeds.h
+++ b/src/Items/ItemSeeds.h
@@ -37,7 +37,7 @@ public:
{
switch (m_ItemType)
{
- case E_ITEM_CARROT: return FoodInfo(4, 4.8);
+ case E_ITEM_CARROT: return FoodInfo(3, 4.8);
case E_ITEM_POTATO: return FoodInfo(1, 0.6);
default: return FoodInfo(0, 0);
}
diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp
index eb2393e5c..281e5ab1a 100644
--- a/src/LightingThread.cpp
+++ b/src/LightingThread.cpp
@@ -73,6 +73,8 @@ public:
HEIGHTTYPE * m_HeightMap; // 3x3 chunks of height map, organized as a single XZY blob of data (instead of 3x3 XZY blobs)
cReader(BLOCKTYPE * a_BlockTypes, HEIGHTTYPE * a_HeightMap) :
+ m_ReadingChunkX(0),
+ m_ReadingChunkZ(0),
m_MaxHeight(0),
m_BlockTypes(a_BlockTypes),
m_HeightMap(a_HeightMap)
@@ -89,7 +91,9 @@ public:
cLightingThread::cLightingThread(void) :
super("cLightingThread"),
- m_World(NULL)
+ m_World(NULL),
+ m_MaxHeight(0),
+ m_NumSeeds(0)
{
}
diff --git a/src/LinearUpscale.h b/src/LinearUpscale.h
index 0b04408cf..a49f4bdf9 100644
--- a/src/LinearUpscale.h
+++ b/src/LinearUpscale.h
@@ -31,7 +31,7 @@ Linearly interpolates values in the array between the equidistant anchor points
Works in-place (input is already present at the correct output coords)
Uses templates to make it possible for the compiler to further optimizer the loops
*/
-template<
+template <
int SizeX, int SizeY, // Dimensions of the array
int AnchorStepX, int AnchorStepY,
typename TYPE
@@ -83,7 +83,7 @@ void LinearUpscale2DArrayInPlace(TYPE * a_Array)
Linearly interpolates values in the array between the equidistant anchor points (upscales).
Works on two arrays, input is packed and output is to be completely constructed.
*/
-template<typename TYPE> void LinearUpscale2DArray(
+template <typename TYPE> void LinearUpscale2DArray(
TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY
int a_SrcSizeX, int a_SrcSizeY, ///< Dimensions of the src array
TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1)
@@ -153,7 +153,7 @@ template<typename TYPE> void LinearUpscale2DArray(
Linearly interpolates values in the array between the equidistant anchor points (upscales).
Works on two arrays, input is packed and output is to be completely constructed.
*/
-template<typename TYPE> void LinearUpscale3DArray(
+template <typename TYPE> void LinearUpscale3DArray(
TYPE * a_Src, ///< Source array of size a_SrcSizeX x a_SrcSizeY x a_SrcSizeZ
int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ, ///< Dimensions of the src array
TYPE * a_Dst, ///< Dest array, of size (a_SrcSizeX * a_UpscaleX + 1) x (a_SrcSizeY * a_UpscaleY + 1) x (a_SrcSizeZ * a_UpscaleZ + 1)
diff --git a/src/LoggerListeners.cpp b/src/LoggerListeners.cpp
index 181439979..6ad94b7bd 100644
--- a/src/LoggerListeners.cpp
+++ b/src/LoggerListeners.cpp
@@ -65,7 +65,7 @@
{
case cLogger::llRegular:
{
- // Gray on black
+ // Gray on black
Attrib = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
break;
}
@@ -93,7 +93,7 @@
virtual void SetDefaultLogColour() /*override*/
- {
+ {
SetConsoleTextAttribute(m_Console, m_DefaultConsoleAttrib);
}
@@ -119,9 +119,9 @@
{
case cLogger::llRegular:
{
- // Whatever the console default is
+ // Whatever the console default is
printf("\x1b[0m");
- break;
+ break;
}
case cLogger::llInfo:
{
diff --git a/src/Mobs/CMakeLists.txt b/src/Mobs/CMakeLists.txt
index 572dab593..e0a55a17b 100644
--- a/src/Mobs/CMakeLists.txt
+++ b/src/Mobs/CMakeLists.txt
@@ -69,4 +69,5 @@ SET (HDRS
if(NOT MSVC)
add_library(Mobs ${SRCS} ${HDRS})
+ target_link_libraries(Mobs Components)
endif()
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 1af44271b..029930266 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -1,3 +1,4 @@
+
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Monster.h"
#include "IncludeAllMonsters.h"
@@ -73,7 +74,8 @@ cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString
m_DropChanceBoots = 0.0f;
}
-void cMonster::Tick(float a_Dt, cChunk & a_Chunk) {
+void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
+{
super::Tick(a_Dt, a_Chunk);
if (m_Health <= 0)
@@ -106,7 +108,6 @@ void cMonster::SpawnOn(cClientHandle & a_Client)
-
void cMonster::AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth)
{
MTRand r1;
@@ -132,9 +133,6 @@ void cMonster::AddRandomUncommonDropItem(cItems & a_Drops, float a_Chance, short
}
-
-
-
void cMonster::AddRandomRareDropItem(cItems & a_Drops, cItems & a_Items, short a_LootingLevel)
{
MTRand r1;
@@ -176,8 +174,6 @@ void cMonster::AddRandomArmorDropItem(cItems & a_Drops, short a_LootingLevel)
-
-
void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel)
{
MTRand r1;
@@ -191,6 +187,7 @@ void cMonster::AddRandomWeaponDropItem(cItems & a_Drops, short a_LootingLevel)
+
cMonster::eFamily cMonster::FamilyFromType(eType a_Type)
{
// Passive-agressive mobs are counted in mob spawning code as passive
diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp
index e370a7639..a3a873b37 100644
--- a/src/Mobs/Sheep.cpp
+++ b/src/Mobs/Sheep.cpp
@@ -39,6 +39,13 @@ void cSheep::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
a_Drops.push_back(cItem(E_BLOCK_WOOL, 1, m_WoolColor));
}
+
+ int LootingLevel = 0;
+ if (a_Killer != NULL)
+ {
+ LootingLevel = a_Killer->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchLooting);
+ }
+ AddRandomDropItem(a_Drops, 1, 3 + LootingLevel, IsOnFire() ? E_ITEM_MUTTON : E_ITEM_RAW_MUTTON);
}
diff --git a/src/Noise.cpp b/src/Noise.cpp
index 507d05ea5..71e801f30 100644
--- a/src/Noise.cpp
+++ b/src/Noise.cpp
@@ -146,6 +146,8 @@ cCubicCell2D::cCubicCell2D(
) :
m_Noise(a_Noise),
m_WorkRnds(&m_Workspace1),
+ m_CurFloorX(0),
+ m_CurFloorY(0),
m_Array(a_Array),
m_SizeX(a_SizeX),
m_SizeY(a_SizeY),
@@ -300,6 +302,9 @@ cCubicCell3D::cCubicCell3D(
) :
m_Noise(a_Noise),
m_WorkRnds(&m_Workspace1),
+ m_CurFloorX(0),
+ m_CurFloorY(0),
+ m_CurFloorZ(0),
m_Array(a_Array),
m_SizeX(a_SizeX),
m_SizeY(a_SizeY),
diff --git a/src/OSSupport/Queue.h b/src/OSSupport/Queue.h
index bf4d7f004..8d096fe29 100644
--- a/src/OSSupport/Queue.h
+++ b/src/OSSupport/Queue.h
@@ -20,7 +20,7 @@ cQueueFuncs and is used as the default behavior.
*/
/// This empty struct allows for the callback functions to be inlined
-template<class T>
+template <class T>
struct cQueueFuncs
{
public:
diff --git a/src/PolarSSL++/SslContext.cpp b/src/PolarSSL++/SslContext.cpp
index c3074f197..482470c3a 100644
--- a/src/PolarSSL++/SslContext.cpp
+++ b/src/PolarSSL++/SslContext.cpp
@@ -16,6 +16,7 @@ cSslContext::cSslContext(void) :
m_IsValid(false),
m_HasHandshaken(false)
{
+ memset(&m_Ssl, 0, sizeof(m_Ssl));
}
diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp
index 823ff5469..28da83c31 100644
--- a/src/Protocol/MojangAPI.cpp
+++ b/src/Protocol/MojangAPI.cpp
@@ -10,6 +10,7 @@
#include "inifile/iniFile.h"
#include "json/json.h"
#include "PolarSSL++/BlockingSslClientSocket.h"
+#include "../RankManager.h"
@@ -157,7 +158,8 @@ cMojangAPI::cMojangAPI(void) :
m_NameToUUIDServer(DEFAULT_NAME_TO_UUID_SERVER),
m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS),
m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER),
- m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS)
+ m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS),
+ m_RankMgr(NULL)
{
}
@@ -300,6 +302,7 @@ void cMojangAPI::AddPlayerNameToUUIDMapping(const AString & a_PlayerName, const
cCSLock Lock(m_CSUUIDToName);
m_UUIDToName[UUID] = sProfile(a_PlayerName, UUID, "", "", Now);
}
+ NotifyNameUUID(a_PlayerName, a_UUID);
}
@@ -322,6 +325,7 @@ void cMojangAPI::AddPlayerProfile(const AString & a_PlayerName, const AString &
cCSLock Lock(m_CSUUIDToProfile);
m_UUIDToProfile[UUID] = sProfile(a_PlayerName, UUID, a_Properties, Now);
}
+ NotifyNameUUID(a_PlayerName, a_UUID);
}
@@ -655,11 +659,11 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames)
}
// Store the returned results into cache:
- size_t JsonCount = root.size();
+ Json::Value::UInt JsonCount = root.size();
Int64 Now = time(NULL);
{
cCSLock Lock(m_CSNameToUUID);
- for (size_t idx = 0; idx < JsonCount; ++idx)
+ for (Json::Value::UInt idx = 0; idx < JsonCount; ++idx)
{
Json::Value & Val = root[idx];
AString JsonName = Val.get("name", "").asString();
@@ -669,13 +673,14 @@ void cMojangAPI::CacheNamesToUUIDs(const AStringVector & a_PlayerNames)
continue;
}
m_NameToUUID[StrToLower(JsonName)] = sProfile(JsonName, JsonUUID, "", "", Now);
+ NotifyNameUUID(JsonName, JsonUUID);
} // for idx - root[]
} // cCSLock (m_CSNameToUUID)
// Also cache the UUIDToName:
{
cCSLock Lock(m_CSUUIDToName);
- for (size_t idx = 0; idx < JsonCount; ++idx)
+ for (Json::Value::UInt idx = 0; idx < JsonCount; ++idx)
{
Json::Value & Val = root[idx];
AString JsonName = Val.get("name", "").asString();
@@ -792,6 +797,21 @@ void cMojangAPI::CacheUUIDToProfile(const AString & a_UUID)
cCSLock Lock(m_CSNameToUUID);
m_NameToUUID[StrToLower(PlayerName)] = sProfile(PlayerName, a_UUID, Properties, Now);
}
+ NotifyNameUUID(PlayerName, a_UUID);
+}
+
+
+
+
+
+void cMojangAPI::NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID)
+{
+ // Notify the rank manager:
+ cCSLock Lock(m_CSRankMgr);
+ if (m_RankMgr != NULL)
+ {
+ m_RankMgr->NotifyNameUUID(a_PlayerName, a_UUID);
+ }
}
diff --git a/src/Protocol/MojangAPI.h b/src/Protocol/MojangAPI.h
index 6ed37625e..252d32543 100644
--- a/src/Protocol/MojangAPI.h
+++ b/src/Protocol/MojangAPI.h
@@ -11,6 +11,13 @@
#include <time.h>
+
+
+
+
+// fwd: ../RankManager.h"
+class cRankManager;
+
namespace Json
{
class Value;
@@ -38,8 +45,6 @@ public:
Returns true if all was successful, false on failure. */
static bool SecureRequest(const AString & a_ServerName, const AString & a_Request, AString & a_Response);
- // tolua_begin
-
/** Normalizes the given UUID to its short form (32 bytes, no dashes, lowercase).
Logs a warning and returns empty string if not a UUID.
Note: only checks the string's length, not the actual content. */
@@ -50,8 +55,6 @@ public:
Note: only checks the string's length, not the actual content. */
static AString MakeUUIDDashed(const AString & a_UUID);
- // tolua_end
-
/** Converts a player name into a UUID.
The UUID will be empty on error.
If a_UseOnlyCached is true, the function only consults the cached values.
@@ -85,7 +88,10 @@ public:
/** Called by the Authenticator to add a profile that it has received from authenticating a user. Adds
the profile to the respective mapping caches and updtes their datetime stamp to now. */
void AddPlayerProfile(const AString & a_PlayerName, const AString & a_UUID, const Json::Value & a_Properties);
-
+
+ /** Sets the m_RankMgr that is used for name-uuid notifications. Accepts NULL to remove the binding. */
+ void SetRankManager(cRankManager * a_RankManager) { m_RankMgr = a_RankManager; }
+
protected:
/** Holds data for a single player profile. */
struct sProfile
@@ -165,6 +171,12 @@ protected:
/** Protects m_UUIDToProfile against simultaneous multi-threaded access. */
cCriticalSection m_CSUUIDToProfile;
+
+ /** The rank manager that is notified of the name-uuid pairings. May be NULL. Protected by m_CSRankMgr. */
+ cRankManager * m_RankMgr;
+
+ /** Protects m_RankMgr agains simultaneous multi-threaded access. */
+ cCriticalSection m_CSRankMgr;
/** Loads the caches from a disk storage. */
@@ -182,6 +194,10 @@ protected:
UUIDs that are not valid will not be added into the cache.
ASSUMEs that a_UUID is a lowercased short UUID. */
void CacheUUIDToProfile(const AString & a_UUID);
+
+ /** Called for each name-uuid pairing that is discovered.
+ If assigned, notifies the m_RankManager of the event. */
+ void NotifyNameUUID(const AString & a_PlayerName, const AString & a_PlayerUUID);
} ; // tolua_export
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 56e73c1c1..1091b877f 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -41,6 +41,7 @@ Implements the 1.7.x protocol classes:
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/MobHeadEntity.h"
#include "../BlockEntities/FlowerPotEntity.h"
+#include "Bindings/PluginManager.h"
@@ -1720,21 +1721,41 @@ void cProtocol172::HandlePacketStatusPing(cByteBuffer & a_ByteBuffer)
void cProtocol172::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
{
- // Send the response:
- AString Response = "{\"version\":{\"name\":\"1.7.2\", \"protocol\":4}, \"players\":{";
cServer * Server = cRoot::Get()->GetServer();
- AppendPrintf(Response, "\"max\":%u, \"online\":%u, \"sample\":[]},",
- Server->GetMaxPlayers(),
- Server->GetNumPlayers()
- );
- AppendPrintf(Response, "\"description\":{\"text\":\"%s\"},",
- Server->GetDescription().c_str()
- );
- AppendPrintf(Response, "\"favicon\": \"data:image/png;base64,%s\"",
- Server->GetFaviconData().c_str()
- );
- Response.append("}");
-
+ AString ServerDescription = Server->GetDescription();
+ int NumPlayers = Server->GetNumPlayers();
+ int MaxPlayers = Server->GetMaxPlayers();
+ AString Favicon = Server->GetFaviconData();
+ cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, ServerDescription, NumPlayers, MaxPlayers, Favicon);
+
+ // Version:
+ Json::Value Version;
+ Version["name"] = "1.7.2";
+ Version["protocol"] = 4;
+
+ // Players:
+ Json::Value Players;
+ Players["online"] = NumPlayers;
+ Players["max"] = MaxPlayers;
+ // TODO: Add "sample"
+
+ // Description:
+ Json::Value Description;
+ Description["text"] = ServerDescription.c_str();
+
+ // Create the response:
+ Json::Value ResponseValue;
+ ResponseValue["version"] = Version;
+ ResponseValue["players"] = Players;
+ ResponseValue["description"] = Description;
+ if (!Favicon.empty())
+ {
+ ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
+ }
+
+ Json::StyledWriter Writer;
+ AString Response = Writer.write(ResponseValue);
+
cPacketizer Pkt(*this, 0x00); // Response packet
Pkt.WriteString(Response);
}
@@ -1803,7 +1824,11 @@ void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffe
void cProtocol172::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer)
{
AString Username;
- a_ByteBuffer.ReadVarUTF8String(Username);
+ if (!a_ByteBuffer.ReadVarUTF8String(Username))
+ {
+ m_Client->Kick("Bad username");
+ return;
+ }
if (!m_Client->HandleHandshake(Username))
{
@@ -3070,20 +3095,41 @@ void cProtocol176::SendPlayerSpawn(const cPlayer & a_Player)
void cProtocol176::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
{
- // Send the response:
- AString Response = "{\"version\": {\"name\": \"1.7.6\", \"protocol\":5}, \"players\": {";
- AppendPrintf(Response, "\"max\": %u, \"online\": %u, \"sample\": []},",
- cRoot::Get()->GetServer()->GetMaxPlayers(),
- cRoot::Get()->GetServer()->GetNumPlayers()
- );
- AppendPrintf(Response, "\"description\": {\"text\": \"%s\"},",
- cRoot::Get()->GetServer()->GetDescription().c_str()
- );
- AppendPrintf(Response, "\"favicon\": \"data:image/png;base64,%s\"",
- cRoot::Get()->GetServer()->GetFaviconData().c_str()
- );
- Response.append("}");
-
+ cServer * Server = cRoot::Get()->GetServer();
+ AString Motd = Server->GetDescription();
+ int NumPlayers = Server->GetNumPlayers();
+ int MaxPlayers = Server->GetMaxPlayers();
+ AString Favicon = Server->GetFaviconData();
+ cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, Motd, NumPlayers, MaxPlayers, Favicon);
+
+ // Version:
+ Json::Value Version;
+ Version["name"] = "1.7.6";
+ Version["protocol"] = 5;
+
+ // Players:
+ Json::Value Players;
+ Players["online"] = NumPlayers;
+ Players["max"] = MaxPlayers;
+ // TODO: Add "sample"
+
+ // Description:
+ Json::Value Description;
+ Description["text"] = Motd.c_str();
+
+ // Create the response:
+ Json::Value ResponseValue;
+ ResponseValue["version"] = Version;
+ ResponseValue["players"] = Players;
+ ResponseValue["description"] = Description;
+ if (!Favicon.empty())
+ {
+ ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
+ }
+
+ Json::StyledWriter Writer;
+ AString Response = Writer.write(ResponseValue);
+
cPacketizer Pkt(*this, 0x00); // Response packet
Pkt.WriteString(Response);
}
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index 18694572a..8b395230a 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -18,6 +18,7 @@
#include "../Server.h"
#include "../World.h"
#include "../ChatColor.h"
+#include "Bindings/PluginManager.h"
@@ -978,9 +979,18 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
AString ServerAddress;
short ServerPort;
UInt32 NextState;
- m_Buffer.ReadVarUTF8String(ServerAddress);
- m_Buffer.ReadBEShort(ServerPort);
- m_Buffer.ReadVarInt(NextState);
+ if (!m_Buffer.ReadVarUTF8String(ServerAddress))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadBEShort(ServerPort))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadVarInt(NextState))
+ {
+ break;
+ }
m_Buffer.CommitRead();
m_Protocol = new cProtocol172(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
return true;
@@ -990,9 +1000,18 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
AString ServerAddress;
short ServerPort;
UInt32 NextState;
- m_Buffer.ReadVarUTF8String(ServerAddress);
- m_Buffer.ReadBEShort(ServerPort);
- m_Buffer.ReadVarInt(NextState);
+ if (!m_Buffer.ReadVarUTF8String(ServerAddress))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadBEShort(ServerPort))
+ {
+ break;
+ }
+ if (!m_Buffer.ReadVarInt(NextState))
+ {
+ break;
+ }
m_Buffer.CommitRead();
m_Protocol = new cProtocol176(m_Client, ServerAddress, (UInt16)ServerPort, NextState);
return true;
@@ -1013,6 +1032,13 @@ void cProtocolRecognizer::SendLengthlessServerPing(void)
{
AString Reply;
cServer * Server = cRoot::Get()->GetServer();
+
+ AString ServerDescription = Server->GetDescription();
+ int NumPlayers = Server->GetNumPlayers();
+ int MaxPlayers = Server->GetMaxPlayers();
+ AString Favicon = Server->GetFaviconData();
+ cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, ServerDescription, NumPlayers, MaxPlayers, Favicon);
+
switch (cRoot::Get()->GetPrimaryServerVersion())
{
case PROTO_VERSION_1_2_5:
@@ -1020,11 +1046,11 @@ void cProtocolRecognizer::SendLengthlessServerPing(void)
{
// http://wiki.vg/wiki/index.php?title=Protocol&oldid=3099#Server_List_Ping_.280xFE.29
Printf(Reply, "%s%s%i%s%i",
- Server->GetDescription().c_str(),
+ ServerDescription.c_str(),
cChatColor::Delimiter,
- Server->GetNumPlayers(),
+ NumPlayers,
cChatColor::Delimiter,
- Server->GetMaxPlayers()
+ MaxPlayers
);
break;
}
@@ -1051,13 +1077,7 @@ void cProtocolRecognizer::SendLengthlessServerPing(void)
m_Buffer.ReadByte(val); // 0x01 magic value
ASSERT(val == 0x01);
}
-
- // http://wiki.vg/wiki/index.php?title=Server_List_Ping&oldid=3100
- AString NumPlayers;
- Printf(NumPlayers, "%d", Server->GetNumPlayers());
- AString MaxPlayers;
- Printf(MaxPlayers, "%d", Server->GetMaxPlayers());
-
+
AString ProtocolVersionNum;
Printf(ProtocolVersionNum, "%d", cRoot::Get()->GetPrimaryServerVersion());
AString ProtocolVersionTxt(GetVersionTextFromInt(cRoot::Get()->GetPrimaryServerVersion()));
@@ -1070,11 +1090,11 @@ void cProtocolRecognizer::SendLengthlessServerPing(void)
Reply.push_back(0);
Reply.append(ProtocolVersionTxt);
Reply.push_back(0);
- Reply.append(Server->GetDescription());
+ Reply.append(ServerDescription);
Reply.push_back(0);
- Reply.append(NumPlayers);
+ Reply.append(Printf("%d", NumPlayers));
Reply.push_back(0);
- Reply.append(MaxPlayers);
+ Reply.append(Printf("%d", MaxPlayers));
break;
}
} // switch (m_PrimaryServerVersion)
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index 32243d090..206d30e1f 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -18,7 +18,7 @@
// Adjust these if a new protocol is added or an old one is removed:
-#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2, 1.7.4, 1.7.5, 1.7.6, 1.7.7, 1.7.8, 1.7.9"
+#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2, 1.7.4, 1.7.5, 1.7.6, 1.7.7, 1.7.8, 1.7.9, 1.7.10"
#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78, 4, 5"
diff --git a/src/RankManager.cpp b/src/RankManager.cpp
new file mode 100644
index 000000000..e5896f8f3
--- /dev/null
+++ b/src/RankManager.cpp
@@ -0,0 +1,1837 @@
+
+// RankManager.cpp
+
+// Implements the cRankManager class that represents the rank manager responsible for assigning permissions and message visuals to players
+
+#include "Globals.h"
+#include "RankManager.h"
+#include "inifile/iniFile.h"
+#include "Protocol/MojangAPI.h"
+#include "ClientHandle.h"
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cRankManagerIniMigrator:
+
+/** Migrates from groups.ini and users.ini into the rankmanager DB */
+class cRankManagerIniMigrator
+{
+public:
+ cRankManagerIniMigrator(cRankManager & a_RankManager, cMojangAPI & a_MojangAPI) :
+ m_RankManager(a_RankManager),
+ m_MojangAPI(a_MojangAPI)
+ {
+ }
+
+
+
+ /** Performs the complete migration from INI files to DB. */
+ bool Migrate(void)
+ {
+ cRankManager::cMassChangeLock Lock(m_RankManager);
+
+ LOGD("Reading groups...");
+ if (!ReadGroups())
+ {
+ return false;
+ }
+ LOGD("Cleaning groups inheritance...");
+ CleanGroupInheritance();
+ LOGD("Creating groups...");
+ CreateGroups();
+
+ LOGD("Reading users...");
+ if (!ReadUsers())
+ {
+ return false;
+ }
+ LOGD("Cleaning user groups...");
+ CleanUserGroups();
+ LOGD("Resolving user UUIDs...");
+ ResolveUserUUIDs();
+
+ LOGD("Setting ranks...");
+ SetRanks();
+
+ LOGD("Creating defaults...");
+ CreateDefaults();
+
+ return true;
+ }
+
+protected:
+
+ /** Container for a group read from an INI file. */
+ struct sGroup
+ {
+ AString m_Name;
+ AString m_Color;
+ AStringVector m_Inherits;
+ AStringVector m_Permissions;
+
+ sGroup(void) {}
+
+ sGroup(const AString & a_Name, const AString & a_Color, const AStringVector & a_Inherits, const AStringVector & a_Permissions):
+ m_Name(a_Name),
+ m_Color(a_Color),
+ m_Inherits(a_Inherits),
+ m_Permissions(a_Permissions)
+ {
+ }
+ };
+ typedef std::map<AString, sGroup> sGroupMap;
+
+
+ /** Container for a single user read from an INI file. */
+ struct sUser
+ {
+ AString m_Name;
+ AStringVector m_Groups;
+
+ /** Assigned by ResolveUserUUIDs(), contains the online (Mojang) UUID of the player. */
+ AString m_UUID;
+
+ /** Assigned by ResolveUserUUIDs(), contains the offline (generated) UUID of the player. */
+ AString m_OfflineUUID;
+
+
+ sUser(void) {}
+
+ sUser(const AString & a_Name, const AStringVector & a_Groups):
+ m_Name(a_Name),
+ m_Groups(a_Groups)
+ {
+ }
+ };
+ typedef std::map<AString, sUser> sUserMap;
+
+ typedef std::map<AString, AString> cStringMap;
+
+
+ /** The parent Rank manager where we will create the groups, ranks and players */
+ cRankManager & m_RankManager;
+
+ /** The player name to UUID resolver */
+ cMojangAPI & m_MojangAPI;
+
+ /** List of all groups read from the ini file */
+ sGroupMap m_Groups;
+
+ /** List of all players read from the ini file. */
+ sUserMap m_Users;
+
+ /** Maps lists of groups to rank names.
+ Each group list is either a simple "<Group>" if there's only one group,
+ or "<PrimaryGroup>, <FirstSecondaryGroup>, <SecondSecondaryGroup>...", where the secondary groups are
+ lowercased and alpha-sorted. This makes the group lists comparable for equivalence, simply by comparing
+ their string names.
+ The ranks are named "<Group>" for single-group players, and "AutoMigratedRank_N" for the composite ranks,
+ where N is a unique number. */
+ cStringMap m_GroupsToRanks;
+
+
+
+ /** Reads the groups from the "groups.ini" file into m_Groups */
+ bool ReadGroups(void)
+ {
+ // Read the file:
+ cIniFile Groups;
+ if (!Groups.ReadFile("groups.ini"))
+ {
+ return false;
+ }
+
+ // Read all the groups into a map:
+ int NumGroups = Groups.GetNumKeys();
+ for (int i = 0; i < NumGroups; i++)
+ {
+ AString GroupName = Groups.GetKeyName(i);
+ AString lcGroupName = StrToLower(GroupName);
+ if (m_Groups.find(lcGroupName) != m_Groups.end())
+ {
+ LOGINFO("groups.ini contains a duplicate definition of group %s, ignoring the latter.", GroupName.c_str());
+ continue;
+ }
+ m_Groups[lcGroupName] = sGroup(
+ GroupName,
+ Groups.GetValue(GroupName, "Color", ""),
+ StringSplitAndTrim(Groups.GetValue(GroupName, "Inherits"), ","),
+ StringSplitAndTrim(Groups.GetValue(GroupName, "Permissions"), ",")
+ );
+ } // for i - Groups' keys
+ return true;
+ }
+
+
+
+ /** Removes non-existent groups from all the groups' inheritance */
+ void CleanGroupInheritance(void)
+ {
+ for (sGroupMap::iterator itrG = m_Groups.begin(), endG = m_Groups.end(); itrG != endG; ++itrG)
+ {
+ AStringVector & Inherits = itrG->second.m_Inherits;
+ for (AStringVector::iterator itrI = Inherits.begin(); itrI != Inherits.end();)
+ {
+ AString lcInherits = StrToLower(*itrI);
+ if (m_Groups.find(lcInherits) != m_Groups.end())
+ {
+ // Inherited group exists, continue checking the next one
+ ++itrI;
+ continue;
+ }
+ // Inherited group doesn't exist, remove it from the list:
+ LOGWARNING("RankMigrator: Group \"%s\" inherits from a non-existent group \"%s\", this inheritance will be ignored.",
+ itrG->second.m_Name.c_str(), itrI->c_str()
+ );
+ AStringVector::iterator itrI2 = itrI;
+ ++itrI2;
+ Inherits.erase(itrI);
+ itrI = itrI2;
+ } // for itrI - Inherits[]
+ } // for itrG - m_Groups[]
+ }
+
+
+
+ /** Reads the users from the "users.ini" file into m_Users */
+ bool ReadUsers(void)
+ {
+ // Read the file:
+ cIniFile Users;
+ if (!Users.ReadFile("users.ini"))
+ {
+ return false;
+ }
+
+ // Read all the users into a map:
+ int NumUsers = Users.GetNumKeys();
+ for (int i = 0; i < NumUsers; i++)
+ {
+ AString UserName = Users.GetKeyName(i);
+ AString lcUserName = StrToLower(UserName);
+ if (m_Users.find(lcUserName) != m_Users.end())
+ {
+ LOGINFO("users.ini contains a duplicate definition of user %s, ignoring the latter.", UserName.c_str());
+ continue;
+ }
+ m_Users[lcUserName] = sUser(
+ UserName,
+ StringSplitAndTrim(Users.GetValue(UserName, "Groups", ""), ",")
+ );
+ } // for i - Users' keys
+ return true;
+ }
+
+
+
+ /** Removes non-existent groups from each user's definition. */
+ void CleanUserGroups(void)
+ {
+ for (sUserMap::iterator itrU = m_Users.begin(), endU = m_Users.end(); itrU != endU; ++itrU)
+ {
+ AStringVector & Groups = itrU->second.m_Groups;
+ for (AStringVector::iterator itrG = Groups.begin(); itrG != Groups.end();)
+ {
+ AString lcGroup = StrToLower(*itrG);
+ if (m_Groups.find(lcGroup) != m_Groups.end())
+ {
+ // Assigned group exists, continue checking the next one
+ ++itrG;
+ continue;
+ }
+ // Assigned group doesn't exist, remove it from the list:
+ LOGWARNING("RankMigrator: User \"%s\" is assigned a non-existent group \"%s\", this assignment will be ignored.",
+ itrU->second.m_Name.c_str(), itrG->c_str()
+ );
+ AStringVector::iterator itrG2 = itrG;
+ ++itrG2;
+ Groups.erase(itrG);
+ itrG = itrG2;
+ } // for itrG - Groups[]
+ } // for itrU - m_Users[]
+ }
+
+
+
+ /** Creates groups based on m_Groups.
+ Ignores group inheritance. */
+ void CreateGroups(void)
+ {
+ // Create each group, with its permissions:
+ for (sGroupMap::const_iterator itr = m_Groups.begin(), end = m_Groups.end(); itr != end; ++itr)
+ {
+ m_RankManager.AddGroup(itr->second.m_Name);
+ m_RankManager.AddPermissionsToGroup(itr->second.m_Permissions, itr->second.m_Name);
+ } // for itr - m_Groups[]
+ }
+
+
+ /** Resolves the UUID of each user in m_Users.
+ If a user doesn't resolve, they are removed and logged in the console. */
+ void ResolveUserUUIDs(void)
+ {
+ // Resolve all PlayerNames at once (the API doesn't like single-name queries):
+ AStringVector PlayerNames;
+ for (sUserMap::const_iterator itr = m_Users.begin(), end = m_Users.end(); itr != end; ++itr)
+ {
+ PlayerNames.push_back(itr->second.m_Name);
+ }
+ m_MojangAPI.GetUUIDsFromPlayerNames(PlayerNames);
+
+ // Assign the UUIDs back to players, remove those not resolved:
+ for (sUserMap::iterator itr = m_Users.begin(); itr != m_Users.end(); ++itr)
+ {
+ AString UUID = m_MojangAPI.GetUUIDFromPlayerName(itr->second.m_Name);
+ if (UUID.empty())
+ {
+ LOGWARNING("RankMigrator: Cannot resolve player %s to online UUID, player will be left unranked in online mode", itr->second.m_Name.c_str());
+ }
+ itr->second.m_UUID = UUID;
+ itr->second.m_OfflineUUID = cClientHandle::GenerateOfflineUUID(itr->second.m_Name);
+ }
+ }
+
+
+
+ /** Adds the specified groups to the specified ranks. Recurses on the groups' inheritance. */
+ void AddGroupsToRank(const AStringVector & a_Groups, const AString & a_RankName)
+ {
+ for (AStringVector::const_iterator itr = a_Groups.begin(), end = a_Groups.end(); itr != end; ++itr)
+ {
+ // Normalize the group name:
+ sGroup & Group = m_Groups[StrToLower(*itr)];
+
+ // Avoid loops, check if the group is already added:
+ if (m_RankManager.IsGroupInRank(Group.m_Name, a_RankName))
+ {
+ continue;
+ }
+
+ // Add the group, and all the groups it inherits from recursively:
+ m_RankManager.AddGroupToRank(Group.m_Name, a_RankName);
+ AddGroupsToRank(Group.m_Inherits, a_RankName);
+ } // for itr - a_Groups[]
+ }
+
+
+
+ /** Creates a rank for each player, based on the master groups they are assigned. */
+ void SetRanks(void)
+ {
+ for (sUserMap::const_iterator itr = m_Users.begin(), end = m_Users.end(); itr != end; ++itr)
+ {
+ // Ignore users with no groups:
+ const AStringVector & Groups = itr->second.m_Groups;
+ if (Groups.empty())
+ {
+ LOGWARNING("RankMigrator: Player %s has no groups assigned to them, skipping the player.", itr->second.m_Name.c_str());
+ continue;
+ }
+
+ // Compose the rank name out of group names:
+ AString RankName;
+ for (AStringVector::const_iterator itrG = Groups.begin(), endG = Groups.end(); itrG != endG; ++itrG)
+ {
+ AString GroupName = m_Groups[StrToLower(*itrG)].m_Name; // Normalize group name
+ if (!RankName.empty())
+ {
+ RankName.push_back(',');
+ }
+ RankName.append(GroupName);
+ } // for itrG - Groups[]
+
+ // Create the rank, with al its groups:
+ if (!m_RankManager.RankExists(RankName))
+ {
+ m_RankManager.AddRank(RankName, "", "", m_Groups[StrToLower(Groups[0])].m_Color);
+ AddGroupsToRank(Groups, RankName);
+ }
+
+ // Set the rank to the user, using both the online and offline UUIDs:
+ m_RankManager.SetPlayerRank(itr->second.m_UUID, itr->second.m_Name, RankName);
+ m_RankManager.SetPlayerRank(itr->second.m_OfflineUUID, itr->second.m_Name, RankName);
+ } // for itr - m_Users[]
+ }
+
+
+
+ /** Creates the Default rank that contains the Default group, if it exists.
+ Sets the RankManager's default rank. */
+ void CreateDefaults(void)
+ {
+ if (!m_RankManager.RankExists("Default"))
+ {
+ m_RankManager.AddRank("Default", "", "", "");
+ if (!m_RankManager.IsGroupInRank("Default", "Default"))
+ {
+ m_RankManager.AddGroupToRank("Default", "Default");
+ }
+ }
+ m_RankManager.SetDefaultRank("Default");
+ }
+};
+
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// cRankManager:
+
+cRankManager::cRankManager(void) :
+ m_DB("Ranks.sqlite", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE),
+ m_IsInitialized(false),
+ m_MojangAPI(NULL)
+{
+}
+
+
+
+
+
+cRankManager::~cRankManager()
+{
+ if (m_MojangAPI != NULL)
+ {
+ m_MojangAPI->SetRankManager(NULL);
+ }
+}
+
+
+
+
+
+void cRankManager::Initialize(cMojangAPI & a_MojangAPI)
+{
+ ASSERT(!m_IsInitialized); // Calling Initialize for the second time?
+
+ // Create the DB tables, if they don't exist:
+ m_DB.exec("CREATE TABLE IF NOT EXISTS Rank (RankID INTEGER PRIMARY KEY, Name, MsgPrefix, MsgSuffix, MsgNameColorCode)");
+ m_DB.exec("CREATE TABLE IF NOT EXISTS PlayerRank (PlayerUUID, PlayerName, RankID INTEGER)");
+ m_DB.exec("CREATE TABLE IF NOT EXISTS PermGroup (PermGroupID INTEGER PRIMARY KEY, Name)");
+ m_DB.exec("CREATE TABLE IF NOT EXISTS RankPermGroup (RankID INTEGER, PermGroupID INTEGER)");
+ m_DB.exec("CREATE TABLE IF NOT EXISTS PermissionItem (PermGroupID INTEGER, Permission)");
+ m_DB.exec("CREATE TABLE IF NOT EXISTS DefaultRank (RankID INTEGER)");
+
+ m_IsInitialized = true;
+
+ a_MojangAPI.SetRankManager(this);
+
+ // Check if tables empty, migrate from ini files then
+ if (AreDBTablesEmpty())
+ {
+ LOGINFO("There are no ranks, migrating old-style INI files to new DB ranks...");
+ LOGINFO("(This might take a while)");
+ cRankManagerIniMigrator Migrator(*this, a_MojangAPI);
+ if (Migrator.Migrate())
+ {
+ LOGINFO("Ranks migrated.");
+ // The default rank has been set by the migrator
+ return;
+ }
+
+ // Migration failed. Add some defaults
+ LOGINFO("Rank migration failed, creating default ranks...");
+ CreateDefaults();
+ LOGINFO("Default ranks created.");
+ }
+
+ // Load the default rank:
+ try
+ {
+ SQLite::Statement stmt(m_DB,
+ "SELECT Rank.Name FROM Rank "
+ "LEFT JOIN DefaultRank ON Rank.RankID = DefaultRank.RankID"
+ );
+ if (stmt.executeStep())
+ {
+ m_DefaultRank = stmt.getColumn(0).getText();
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Cannot load default rank: %s", __FUNCTION__, ex.what());
+ return;
+ }
+
+ // If the default rank cannot be loaded, use the first rank:
+ if (m_DefaultRank.empty())
+ {
+ SetDefaultRank(GetAllRanks()[0]);
+ }
+}
+
+
+
+
+
+AString cRankManager::GetPlayerRankName(const AString & a_PlayerUUID)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ SQLite::Statement stmt(m_DB, "SELECT Rank.Name FROM Rank LEFT JOIN PlayerRank ON Rank.RankID = PlayerRank.RankID WHERE PlayerRank.PlayerUUID = ?");
+ stmt.bind(1, a_PlayerUUID);
+ // executeStep returns false on no data
+ if (!stmt.executeStep())
+ {
+ // No data returned from the DB
+ return AString();
+ }
+ return stmt.getColumn(0).getText();
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Cannot get player rank name: %s", __FUNCTION__, ex.what());
+ }
+ return AString();
+}
+
+
+
+
+
+AStringVector cRankManager::GetPlayerGroups(const AString & a_PlayerUUID)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ AStringVector res;
+ try
+ {
+ // Prepare the DB statement:
+ SQLite::Statement stmt(m_DB,
+ "SELECT PermGroup.Name FROM PermGroup "
+ "LEFT JOIN RankPermGroup ON PermGroup.PermGroupID = RankPermGroup.PermGroupID "
+ "LEFT JOIN PlayerRank ON PlayerRank.RankID = RankPermGroup.RankID "
+ "WHERE PlayerRank.PlayerUUID = ?"
+ );
+ stmt.bind(1, a_PlayerUUID);
+
+ // Execute and get results:
+ while (stmt.executeStep())
+ {
+ res.push_back(stmt.getColumn(0).getText());
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Cannot get player groups: %s", __FUNCTION__, ex.what());
+ }
+ return res;
+}
+
+
+
+
+
+AStringVector cRankManager::GetPlayerPermissions(const AString & a_PlayerUUID)
+{
+ AString Rank = GetPlayerRankName(a_PlayerUUID);
+ if (Rank.empty())
+ {
+ Rank = m_DefaultRank;
+ }
+ return GetRankPermissions(Rank);
+}
+
+
+
+
+
+AStringVector cRankManager::GetRankGroups(const AString & a_RankName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ AStringVector res;
+ try
+ {
+ SQLite::Statement stmt(m_DB,
+ "SELECT PermGroup.Name FROM PermGroup "
+ "LEFT JOIN RankPermGroup ON RankPermGroup.PermGroupID = PermGroup.PermGroupID "
+ "LEFT JOIN Rank ON Rank.RankID = RankPermGroup.RankID "
+ "WHERE Rank.Name = ?"
+ );
+ stmt.bind(1, a_RankName);
+ while (stmt.executeStep())
+ {
+ res.push_back(stmt.getColumn(0).getText());
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to get rank groups from DB: %s", __FUNCTION__, ex.what());
+ }
+ return res;
+}
+
+
+
+
+
+AStringVector cRankManager::GetGroupPermissions(const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ AStringVector res;
+ try
+ {
+ SQLite::Statement stmt(m_DB,
+ "SELECT PermissionItem.Permission FROM PermissionItem "
+ "LEFT JOIN PermGroup ON PermGroup.PermGroupID = PermissionItem.PermGroupID "
+ "WHERE PermGroup.Name = ?"
+ );
+ stmt.bind(1, a_GroupName);
+ while (stmt.executeStep())
+ {
+ res.push_back(stmt.getColumn(0).getText());
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to get group permissions from DB: %s", __FUNCTION__, ex.what());
+ }
+ return res;
+}
+
+
+
+
+
+AStringVector cRankManager::GetRankPermissions(const AString & a_RankName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ AStringVector res;
+ try
+ {
+ SQLite::Statement stmt(m_DB,
+ "SELECT PermissionItem.Permission FROM PermissionItem "
+ "LEFT JOIN RankPermGroup ON RankPermGroup.PermGroupID = PermissionItem.PermGroupID "
+ "LEFT JOIN Rank ON Rank.RankID = RankPermGroup.RankID "
+ "WHERE Rank.Name = ?"
+ );
+ stmt.bind(1, a_RankName);
+ while (stmt.executeStep())
+ {
+ res.push_back(stmt.getColumn(0).getText());
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to get rank permissions from DB: %s", __FUNCTION__, ex.what());
+ }
+ return res;
+}
+
+
+
+
+
+AStringVector cRankManager::GetAllRanks(void)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ AStringVector res;
+ try
+ {
+ SQLite::Statement stmt(m_DB, "SELECT Name FROM Rank");
+ while (stmt.executeStep())
+ {
+ res.push_back(stmt.getColumn(0).getText());
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to get ranks from DB: %s", __FUNCTION__, ex.what());
+ }
+ return res;
+}
+
+
+
+
+
+AStringVector cRankManager::GetAllGroups(void)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ AStringVector res;
+ try
+ {
+ SQLite::Statement stmt(m_DB, "SELECT Name FROM PermGroup");
+ while (stmt.executeStep())
+ {
+ res.push_back(stmt.getColumn(0).getText());
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to get groups from DB: %s", __FUNCTION__, ex.what());
+ }
+ return res;
+}
+
+
+
+
+
+AStringVector cRankManager::GetAllPermissions(void)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ AStringVector res;
+ try
+ {
+ SQLite::Statement stmt(m_DB, "SELECT DISTINCT(Permission) FROM PermissionItem");
+ while (stmt.executeStep())
+ {
+ res.push_back(stmt.getColumn(0).getText());
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to get permissions from DB: %s", __FUNCTION__, ex.what());
+ }
+ return res;
+}
+
+
+
+
+
+bool cRankManager::GetPlayerMsgVisuals(
+ const AString & a_PlayerUUID,
+ AString & a_MsgPrefix,
+ AString & a_MsgSuffix,
+ AString & a_MsgNameColorCode
+)
+{
+ AString Rank = GetPlayerRankName(a_PlayerUUID);
+ if (Rank.empty())
+ {
+ // Rank not found, return failure:
+ a_MsgPrefix.clear();
+ a_MsgSuffix.clear();
+ a_MsgNameColorCode.clear();
+ return false;
+ }
+ return GetRankVisuals(Rank, a_MsgPrefix, a_MsgSuffix, a_MsgNameColorCode);
+}
+
+
+
+
+
+void cRankManager::AddRank(
+ const AString & a_RankName,
+ const AString & a_MsgPrefix,
+ const AString & a_MsgSuffix,
+ const AString & a_MsgNameColorCode
+)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Check if such a rank name is already used:
+ {
+ SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM Rank WHERE Name = ?");
+ stmt.bind(1, a_RankName);
+ if (stmt.executeStep())
+ {
+ if (stmt.getColumn(0).getInt() > 0)
+ {
+ // Rank already exists, do nothing:
+ return;
+ }
+ }
+ }
+
+ // Insert a new rank:
+ SQLite::Statement stmt(m_DB, "INSERT INTO Rank (Name, MsgPrefix, MsgSuffix, MsgNameColorCode) VALUES (?, ?, ?, ?)");
+ stmt.bind(1, a_RankName);
+ stmt.bind(2, a_MsgPrefix);
+ stmt.bind(3, a_MsgSuffix);
+ stmt.bind(4, a_MsgNameColorCode);
+ if (stmt.exec() <= 0)
+ {
+ LOGWARNING("%s: Failed to add a new rank \"%s\".", __FUNCTION__, a_RankName.c_str());
+ return;
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to add a new rank \"%s\": %s", __FUNCTION__, a_RankName.c_str(), ex.what());
+ }
+}
+
+
+
+
+
+void cRankManager::AddGroup(const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Check if such a group name is already used:
+ {
+ SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, a_GroupName);
+ if (stmt.executeStep())
+ {
+ if (stmt.getColumn(0).getInt() > 0)
+ {
+ // Group already exists, do nothing:
+ return;
+ }
+ }
+ }
+
+ // Insert a new group:
+ SQLite::Statement stmt(m_DB, "INSERT INTO PermGroup (Name) VALUES (?)");
+ stmt.bind(1, a_GroupName);
+ if (stmt.exec() <= 0)
+ {
+ LOGWARNING("%s: Failed to add a new group \"%s\".", __FUNCTION__, a_GroupName.c_str());
+ return;
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to add a new group \"%s\": %s", __FUNCTION__, a_GroupName.c_str(), ex.what());
+ }
+}
+
+
+
+
+
+void cRankManager::AddGroups(const AStringVector & a_GroupNames)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ for (AStringVector::const_iterator itr = a_GroupNames.begin(), end = a_GroupNames.end(); itr != end; ++itr)
+ {
+ // Check if such the group name is already used:
+ {
+ SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, *itr);
+ if (stmt.executeStep())
+ {
+ if (stmt.getColumn(0).getInt() > 0)
+ {
+ // Group already exists, do nothing:
+ return;
+ }
+ }
+ }
+
+ // Insert a new group:
+ SQLite::Statement stmt(m_DB, "INSERT INTO PermGroup (Name) VALUES (?)");
+ stmt.bind(1, *itr);
+ if (stmt.exec() <= 0)
+ {
+ LOGWARNING("%s: Failed to add a new group \"%s\".", __FUNCTION__, itr->c_str());
+ return;
+ }
+ } // for itr - a_GroupNames[]
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to add new groups: %s", __FUNCTION__, ex.what());
+ }
+}
+
+
+
+
+
+bool cRankManager::AddGroupToRank(const AString & a_GroupName, const AString & a_RankName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Get the group's ID:
+ int GroupID;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, a_GroupName);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: No such group (%s), aborting.", __FUNCTION__, a_GroupName.c_str());
+ return false;
+ }
+ GroupID = stmt.getColumn(0);
+ }
+
+ // Get the rank's ID:
+ int RankID;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?");
+ stmt.bind(1, a_RankName);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: No such rank (%s), aborting.", __FUNCTION__, a_RankName.c_str());
+ return false;
+ }
+ RankID = stmt.getColumn(0);
+ }
+
+ // Check if the group is already there:
+ {
+ SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM RankPermGroup WHERE RankID = ? AND PermGroupID = ?");
+ stmt.bind(1, RankID);
+ stmt.bind(2, GroupID);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: Failed to check binding between rank %s and group %s, aborting.", __FUNCTION__, a_RankName.c_str(), a_GroupName.c_str());
+ return false;
+ }
+ if (stmt.getColumn(0).getInt() > 0)
+ {
+ LOGD("%s: Group %s already present in rank %s, skipping and returning success.",
+ __FUNCTION__, a_GroupName.c_str(), a_RankName.c_str()
+ );
+ return true;
+ }
+ }
+
+ // Add the group:
+ {
+ SQLite::Statement stmt(m_DB, "INSERT INTO RankPermGroup (RankID, PermGroupID) VALUES (?, ?)");
+ stmt.bind(1, RankID);
+ stmt.bind(2, GroupID);
+ if (stmt.exec() <= 0)
+ {
+ LOGWARNING("%s: Failed to add group %s to rank %s, aborting.", __FUNCTION__, a_GroupName.c_str(), a_RankName.c_str());
+ return false;
+ }
+ }
+
+ // Adding succeeded:
+ return true;
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to add group %s to rank %s: %s", __FUNCTION__, a_GroupName.c_str(), a_RankName.c_str(), ex.what());
+ }
+ return false;
+}
+
+
+
+
+
+bool cRankManager::AddPermissionToGroup(const AString & a_Permission, const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Get the group's ID:
+ int GroupID;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, a_GroupName);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: No such group (%s), aborting.", __FUNCTION__, a_GroupName.c_str());
+ return false;
+ }
+ GroupID = stmt.getColumn(0).getInt();
+ }
+
+ // Check if the permission is already present:
+ {
+ SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM PermissionItem WHERE PermGroupID = ? AND Permission = ?");
+ stmt.bind(1, GroupID);
+ stmt.bind(2, a_Permission);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: Failed to check binding between permission %s and group %s, aborting.", __FUNCTION__, a_Permission.c_str(), a_GroupName.c_str());
+ return false;
+ }
+ if (stmt.getColumn(0).getInt() > 0)
+ {
+ LOGD("%s: Permission %s is already present in group %s, skipping and returning success.",
+ __FUNCTION__, a_Permission.c_str(), a_GroupName.c_str()
+ );
+ return true;
+ }
+ }
+
+ // Add the permission:
+ {
+ SQLite::Statement stmt(m_DB, "INSERT INTO PermissionItem (Permission, PermGroupID) VALUES (?, ?)");
+ stmt.bind(1, a_Permission);
+ stmt.bind(2, GroupID);
+ if (stmt.exec() <= 0)
+ {
+ LOGWARNING("%s: Failed to add permission %s to group %s, aborting.", __FUNCTION__, a_Permission.c_str(), a_GroupName.c_str());
+ return false;
+ }
+ }
+
+ // Adding succeeded:
+ return true;
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to add permission %s to group %s: %s",
+ __FUNCTION__, a_Permission.c_str(), a_GroupName.c_str(), ex.what()
+ );
+ }
+ return false;
+}
+
+
+
+
+
+bool cRankManager::AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Get the group's ID:
+ int GroupID;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, a_GroupName);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: No such group (%s), aborting.", __FUNCTION__, a_GroupName.c_str());
+ return false;
+ }
+ GroupID = stmt.getColumn(0).getInt();
+ }
+
+ for (AStringVector::const_iterator itr = a_Permissions.begin(), end = a_Permissions.end(); itr != end; ++itr)
+ {
+ // Check if the permission is already present:
+ {
+ SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM PermissionItem WHERE PermGroupID = ? AND Permission = ?");
+ stmt.bind(1, GroupID);
+ stmt.bind(2, *itr);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: Failed to check binding between permission %s and group %s, aborting.", __FUNCTION__, itr->c_str(), a_GroupName.c_str());
+ return false;
+ }
+ if (stmt.getColumn(0).getInt() > 0)
+ {
+ LOGD("%s: Permission %s is already present in group %s, skipping and returning success.",
+ __FUNCTION__, itr->c_str(), a_GroupName.c_str()
+ );
+ continue;
+ }
+ }
+
+ // Add the permission:
+ {
+ SQLite::Statement stmt(m_DB, "INSERT INTO PermissionItem (Permission, PermGroupID) VALUES (?, ?)");
+ stmt.bind(1, *itr);
+ stmt.bind(2, GroupID);
+ if (stmt.exec() <= 0)
+ {
+ LOGWARNING("%s: Failed to add permission %s to group %s, skipping.", __FUNCTION__, itr->c_str(), a_GroupName.c_str());
+ continue;
+ }
+ }
+ } // for itr - a_Permissions[]
+
+ // Adding succeeded:
+ return true;
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to add permissions to group %s: %s",
+ __FUNCTION__, a_GroupName.c_str(), ex.what()
+ );
+ }
+ return false;
+}
+
+
+
+
+
+void cRankManager::RemoveRank(const AString & a_RankName, const AString & a_ReplacementRankName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ // Check if the default rank is being removed with a proper replacement:
+ if ((a_RankName == m_DefaultRank) && !RankExists(a_ReplacementRankName))
+ {
+ LOGWARNING("%s: Cannot remove rank %s, it is the default rank and the replacement rank doesn't exist.", __FUNCTION__, a_RankName.c_str());
+ return;
+ }
+
+ AStringVector res;
+ try
+ {
+ // Get the RankID for the rank being removed:
+ int RemoveRankID;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?");
+ stmt.bind(1, a_RankName);
+ if (!stmt.executeStep())
+ {
+ LOGINFO("%s: Rank %s was not found. Skipping.", __FUNCTION__, a_RankName.c_str());
+ return;
+ }
+ RemoveRankID = stmt.getColumn(0).getInt();
+ }
+
+ // Get the RankID for the replacement rank:
+ int ReplacementRankID = -1;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?");
+ stmt.bind(1, a_ReplacementRankName);
+ if (stmt.executeStep())
+ {
+ ReplacementRankID = stmt.getColumn(0).getInt();
+ }
+ }
+
+ // Remove the rank's bindings to groups:
+ {
+ SQLite::Statement stmt(m_DB, "DELETE FROM RankPermGroup WHERE RankID = ?");
+ stmt.bind(1, RemoveRankID);
+ stmt.exec();
+ }
+
+ // Adjust players:
+ if (ReplacementRankID == -1)
+ {
+ // No replacement, just delete all the players that have the rank:
+ SQLite::Statement stmt(m_DB, "DELETE FROM PlayerRank WHERE RankID = ?");
+ stmt.bind(1, RemoveRankID);
+ stmt.exec();
+ }
+ else
+ {
+ // Replacement available, change all the player records:
+ SQLite::Statement stmt(m_DB, "UPDATE PlayerRank SET RankID = ? WHERE RankID = ?");
+ stmt.bind(1, ReplacementRankID);
+ stmt.bind(2, RemoveRankID);
+ stmt.exec();
+ }
+
+ // Remove the rank from the DB:
+ {
+ SQLite::Statement stmt(m_DB, "DELETE FROM Rank WHERE RankID = ?");
+ stmt.bind(1, RemoveRankID);
+ stmt.exec();
+ }
+
+ // Update the default rank, if it was the one being removed:
+ if (a_RankName == m_DefaultRank)
+ {
+ m_DefaultRank = a_RankName;
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to remove rank from DB: %s", __FUNCTION__, ex.what());
+ }
+}
+
+
+
+
+
+void cRankManager::RemoveGroup(const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Get the ID of the group:
+ int GroupID;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, a_GroupName);
+ if (!stmt.executeStep())
+ {
+ LOGINFO("%s: Group %s was not found, skipping.", __FUNCTION__, a_GroupName.c_str());
+ return;
+ }
+ GroupID = stmt.getColumn(0).getInt();
+ }
+
+ // Remove all permissions from the group:
+ {
+ SQLite::Statement stmt(m_DB, "DELETE FROM PermissionItem WHERE PermGroupID = ?");
+ stmt.bind(1, GroupID);
+ stmt.exec();
+ }
+
+ // Remove the group from all ranks that contain it:
+ {
+ SQLite::Statement stmt(m_DB, "DELETE FROM RankPermGroup WHERE PermGroupID = ?");
+ stmt.bind(1, GroupID);
+ stmt.exec();
+ }
+
+ // Remove the group itself:
+ {
+ SQLite::Statement stmt(m_DB, "DELETE FROM PermGroup WHERE PermGroupID = ?");
+ stmt.bind(1, GroupID);
+ stmt.exec();
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to remove group %s from DB: %s", __FUNCTION__, a_GroupName.c_str(), ex.what());
+ }
+}
+
+
+
+
+
+void cRankManager::RemoveGroupFromRank(const AString & a_GroupName, const AString & a_RankName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Get the IDs of the group and the rank:
+ int GroupID, RankID;
+ {
+ SQLite::Statement stmt(m_DB,
+ "SELECT PermGroup.PermGroupID, Rank.RankID FROM PermGroup "
+ "LEFT JOIN RankPermGroup ON RankPermGroup.PermGroupID = PermGroup.PermGroupID "
+ "LEFT JOIN Rank ON Rank.RankID = RankPermGroup.RankID "
+ "WHERE PermGroup.Name = ? AND Rank.Name = ?"
+ );
+ stmt.bind(1, a_GroupName);
+ stmt.bind(2, a_RankName);
+ if (!stmt.executeStep())
+ {
+ LOGINFO("%s: Group %s was not found in rank %s, skipping.", __FUNCTION__, a_GroupName.c_str(), a_RankName.c_str());
+ return;
+ }
+ GroupID = stmt.getColumn(0).getInt();
+ RankID = stmt.getColumn(1).getInt();
+ }
+
+ // Remove the group from all ranks that contain it:
+ {
+ SQLite::Statement stmt(m_DB, "DELETE FROM RankPermGroup WHERE PermGroupID = ?");
+ stmt.bind(1, GroupID);
+ stmt.exec();
+ }
+
+ // Remove the group-to-rank binding:
+ {
+ SQLite::Statement stmt(m_DB, "DELETE FROM RankPermGroup WHERE PermGroupID = ? AND RankID = ?");
+ stmt.bind(1, GroupID);
+ stmt.bind(1, RankID);
+ stmt.exec();
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to remove group %s from rank %s in the DB: %s", __FUNCTION__, a_GroupName.c_str(), a_RankName.c_str(), ex.what());
+ }
+}
+
+
+
+
+
+void cRankManager::RemovePermissionFromGroup(const AString & a_Permission, const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Get the ID of the group:
+ int GroupID;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, a_GroupName);
+ if (!stmt.executeStep())
+ {
+ LOGINFO("%s: Group %s was not found, skipping.", __FUNCTION__, a_GroupName.c_str());
+ return;
+ }
+ GroupID = stmt.getColumn(0).getInt();
+ }
+
+ // Remove the permission from the group:
+ {
+ SQLite::Statement stmt(m_DB, "DELETE FROM PermissionItem WHERE PermGroupID = ? AND Permission = ?");
+ stmt.bind(1, GroupID);
+ stmt.bind(2, a_Permission);
+ stmt.exec();
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to remove permission %s from group %s in DB: %s",
+ __FUNCTION__, a_Permission.c_str(), a_GroupName.c_str(), ex.what()
+ );
+ }
+}
+
+
+
+
+
+bool cRankManager::RenameRank(const AString & a_OldName, const AString & a_NewName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Check that NewName doesn't exist:
+ {
+ SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?");
+ stmt.bind(1, a_NewName);
+ if (stmt.executeStep())
+ {
+ LOGINFO("%s: Rank %s is already present, cannot rename %s", __FUNCTION__, a_NewName.c_str(), a_OldName.c_str());
+ return false;
+ }
+ }
+
+ // Rename:
+ {
+ SQLite::Statement stmt(m_DB, "UPDATE Rank SET Name = ? WHERE Name = ?");
+ stmt.bind(1, a_NewName);
+ stmt.bind(2, a_OldName);
+ if (stmt.exec() <= 0)
+ {
+ LOGINFO("%s: There is no rank %s, cannot rename to %s.", __FUNCTION__, a_OldName.c_str(), a_NewName.c_str());
+ return false;
+ }
+ }
+
+ // Update the default rank, if it was the one being renamed:
+ if (a_OldName == m_DefaultRank)
+ {
+ m_DefaultRank = a_NewName;
+ }
+
+ return true;
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to rename rank %s to %s in DB: %s",
+ __FUNCTION__, a_OldName.c_str(), a_NewName.c_str(), ex.what());
+ }
+ return false;
+}
+
+
+
+
+
+bool cRankManager::RenameGroup(const AString & a_OldName, const AString & a_NewName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Check that NewName doesn't exist:
+ {
+ SQLite::Statement stmt(m_DB, "SELECT PermGroupID FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, a_NewName);
+ if (stmt.executeStep())
+ {
+ LOGD("%s: Group %s is already present, cannot rename %s", __FUNCTION__, a_NewName.c_str(), a_OldName.c_str());
+ return false;
+ }
+ }
+
+ // Rename:
+ bool res;
+ {
+ SQLite::Statement stmt(m_DB, "UPDATE PermGroup SET Name = ? WHERE Name = ?");
+ stmt.bind(1, a_NewName);
+ stmt.bind(2, a_OldName);
+ res = (stmt.exec() > 0);
+ }
+
+ return res;
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to rename group %s to %s in DB: %s",
+ __FUNCTION__, a_OldName.c_str(), a_NewName.c_str(), ex.what());
+ }
+ return false;
+}
+
+
+
+
+
+void cRankManager::SetPlayerRank(const AString & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Get the rank ID:
+ int RankID;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?");
+ stmt.bind(1, a_RankName);
+ if (!stmt.executeStep())
+ {
+ LOGWARNING("%s: There is no rank %s, aborting.", __FUNCTION__, a_RankName.c_str());
+ return;
+ }
+ RankID = stmt.getColumn(0).getInt();
+ }
+
+ // Update the player's rank, if already in DB:
+ {
+ SQLite::Statement stmt(m_DB, "UPDATE PlayerRank SET RankID = ?, PlayerName = ? WHERE PlayerUUID = ?");
+ stmt.bind(1, RankID);
+ stmt.bind(2, a_PlayerName);
+ stmt.bind(3, a_PlayerUUID);
+ if (stmt.exec() > 0)
+ {
+ // Successfully updated the player's rank
+ return;
+ }
+ }
+
+ // The player is not yet in the DB, add them:
+ SQLite::Statement stmt(m_DB, "INSERT INTO PlayerRank (RankID, PlayerUUID, PlayerName) VALUES (?, ?, ?)");
+ stmt.bind(1, RankID);
+ stmt.bind(2, a_PlayerUUID);
+ stmt.bind(3, a_PlayerName);
+ if (stmt.exec() > 0)
+ {
+ // Successfully added the player
+ return;
+ }
+
+ LOGWARNING("%s: Failed to set player UUID %s to rank %s.",
+ __FUNCTION__, a_PlayerUUID.c_str(), a_RankName.c_str()
+ );
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to set player UUID %s to rank %s: %s",
+ __FUNCTION__, a_PlayerUUID.c_str(), a_RankName.c_str(), ex.what()
+ );
+ }
+}
+
+
+
+
+
+void cRankManager::RemovePlayerRank(const AString & a_PlayerUUID)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ SQLite::Statement stmt(m_DB, "DELETE FROM PlayerRank WHERE PlayerUUID = ?");
+ stmt.bind(1, a_PlayerUUID);
+ stmt.exec();
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to remove rank from player UUID %s: %s",
+ __FUNCTION__, a_PlayerUUID.c_str(), ex.what()
+ );
+ }
+}
+
+
+
+
+
+void cRankManager::SetRankVisuals(
+ const AString & a_RankName,
+ const AString & a_MsgPrefix,
+ const AString & a_MsgSuffix,
+ const AString & a_MsgNameColorCode
+)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ SQLite::Statement stmt(m_DB, "UPDATE Rank SET MsgPrefix = ?, MsgSuffix = ?, MsgNameColorCode = ? WHERE Name = ?");
+ stmt.bind(1, a_MsgPrefix);
+ stmt.bind(2, a_MsgSuffix);
+ stmt.bind(3, a_MsgNameColorCode);
+ stmt.bind(4, a_RankName);
+ if (!stmt.executeStep())
+ {
+ LOGINFO("%s: Rank %s not found, visuals not set.", __FUNCTION__, a_RankName.c_str());
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to get ranks from DB: %s", __FUNCTION__, ex.what());
+ }
+}
+
+
+
+
+
+bool cRankManager::GetRankVisuals(
+ const AString & a_RankName,
+ AString & a_MsgPrefix,
+ AString & a_MsgSuffix,
+ AString & a_MsgNameColorCode
+)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ SQLite::Statement stmt(m_DB, "SELECT MsgPrefix, MsgSuffix, MsgNameColorCode FROM Rank WHERE Name = ?");
+ stmt.bind(1, a_RankName);
+ if (!stmt.executeStep())
+ {
+ // Rank not found
+ return false;
+ }
+ a_MsgPrefix = stmt.getColumn(0).getText();
+ a_MsgSuffix = stmt.getColumn(1).getText();
+ a_MsgNameColorCode = stmt.getColumn(2).getText();
+ return true;
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to get ranks from DB: %s", __FUNCTION__, ex.what());
+ }
+ return false;
+}
+
+
+
+
+
+bool cRankManager::RankExists(const AString & a_RankName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ SQLite::Statement stmt(m_DB, "SELECT * FROM Rank WHERE Name = ?");
+ stmt.bind(1, a_RankName);
+ if (stmt.executeStep())
+ {
+ // The rank was found
+ return true;
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to query DB for rank %s: %s", __FUNCTION__, a_RankName.c_str(), ex.what());
+ }
+ return false;
+}
+
+
+
+
+
+bool cRankManager::GroupExists(const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ SQLite::Statement stmt(m_DB, "SELECT * FROM PermGroup WHERE Name = ?");
+ stmt.bind(1, a_GroupName);
+ if (stmt.executeStep())
+ {
+ // The group was found
+ return true;
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to query DB for group %s: %s", __FUNCTION__, a_GroupName.c_str(), ex.what());
+ }
+ return false;
+}
+
+
+
+
+
+bool cRankManager::IsPlayerRankSet(const AString & a_PlayerUUID)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ SQLite::Statement stmt(m_DB, "SELECT * FROM PlayerRank WHERE PlayerUUID = ?");
+ stmt.bind(1, a_PlayerUUID);
+ if (stmt.executeStep())
+ {
+ // The player UUID was found, they have a rank
+ return true;
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to query DB for player UUID %s: %s", __FUNCTION__, a_PlayerUUID.c_str(), ex.what());
+ }
+ return false;
+}
+
+
+
+
+
+bool cRankManager::IsGroupInRank(const AString & a_GroupName, const AString & a_RankName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ SQLite::Statement stmt(m_DB,
+ "SELECT * FROM Rank "
+ "LEFT JOIN RankPermGroup ON Rank.RankID = RankPermGroup.RankID "
+ "LEFT JOIN PermGroup ON PermGroup.PermGroupID = RankPermGroup.PermGroupID "
+ "WHERE Rank.Name = ? AND PermGroup.Name = ?"
+ );
+ stmt.bind(1, a_RankName);
+ stmt.bind(2, a_GroupName);
+ if (stmt.executeStep())
+ {
+ // The group is in the rank
+ return true;
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, ex.what());
+ }
+ return false;
+}
+
+
+
+
+
+bool cRankManager::IsPermissionInGroup(const AString & a_Permission, const AString & a_GroupName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ SQLite::Statement stmt(m_DB,
+ "SELECT * FROM PermissionItem "
+ "LEFT JOIN PermGroup ON PermGroup.PermGroupID = PermissionItem.PermGroupID "
+ "WHERE PermissionItem.Permission = ? AND PermGroup.Name = ?"
+ );
+ stmt.bind(1, a_Permission);
+ stmt.bind(2, a_GroupName);
+ if (stmt.executeStep())
+ {
+ // The permission is in the group
+ return true;
+ }
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, ex.what());
+ }
+ return false;
+}
+
+
+
+
+
+void cRankManager::NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ SQLite::Statement stmt(m_DB, "UPDATE PlayerRank SET PlayerName = ? WHERE PlayerUUID = ?");
+ stmt.bind(1, a_PlayerName);
+ stmt.bind(2, a_UUID);
+ stmt.exec();
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to update DB: %s", __FUNCTION__, ex.what());
+ }
+}
+
+
+
+
+
+bool cRankManager::SetDefaultRank(const AString & a_RankName)
+{
+ ASSERT(m_IsInitialized);
+ cCSLock Lock(m_CS);
+
+ try
+ {
+ // Find the rank's ID:
+ int RankID = 0;
+ {
+ SQLite::Statement stmt(m_DB, "SELECT RankID FROM Rank WHERE Name = ?");
+ stmt.bind(1, a_RankName);
+ if (!stmt.executeStep())
+ {
+ LOGINFO("%s: Cannot set rank %s as the default, it does not exist.", __FUNCTION__, a_RankName.c_str());
+ return false;
+ }
+ }
+
+ // Set the rank as the default:
+ {
+ SQLite::Statement stmt(m_DB, "UPDATE DefaultRank SET RankID = ?");
+ stmt.bind(1, RankID);
+ if (stmt.exec() < 1)
+ {
+ // Failed to update, there might be none in the DB, try inserting:
+ SQLite::Statement stmt2(m_DB, "INSERT INTO DefaultRank (RankID) VALUES (?)");
+ stmt2.bind(1, RankID);
+ if (stmt2.exec() < 1)
+ {
+ LOGINFO("%s: Cannot update the default rank in the DB to %s.", __FUNCTION__, a_RankName.c_str());
+ return false;
+ }
+ }
+ }
+
+ // Set the internal cache:
+ m_DefaultRank = a_RankName;
+ return true;
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to update DB: %s", __FUNCTION__, ex.what());
+ return false;
+ }
+}
+
+
+
+
+
+bool cRankManager::AreDBTablesEmpty(void)
+{
+ return (
+ IsDBTableEmpty("Rank") &&
+ IsDBTableEmpty("PlayerRank") &&
+ IsDBTableEmpty("PermGroup") &&
+ IsDBTableEmpty("RankPermGroup") &&
+ IsDBTableEmpty("PermissionItem") &&
+ IsDBTableEmpty("DefaultRank")
+ );
+}
+
+
+
+
+
+bool cRankManager::IsDBTableEmpty(const AString & a_TableName)
+{
+ try
+ {
+ SQLite::Statement stmt(m_DB, "SELECT COUNT(*) FROM " + a_TableName);
+ return (stmt.executeStep() && (stmt.getColumn(0).getInt() == 0));
+ }
+ catch (const SQLite::Exception & ex)
+ {
+ LOGWARNING("%s: Failed to query DB: %s", __FUNCTION__, ex.what());
+ }
+ return false;
+}
+
+
+
+
+
+void cRankManager::CreateDefaults(void)
+{
+ // Wrap everything in a big transaction to speed things up:
+ cMassChangeLock Lock(*this);
+
+ // Create ranks:
+ AddRank("Default", "", "", "");
+ AddRank("VIP", "", "", "");
+ AddRank("Operator", "", "", "");
+ AddRank("Admin", "", "", "");
+
+ // Create groups:
+ AddGroup("Default");
+ AddGroup("Kick");
+ AddGroup("Teleport");
+ AddGroup("Everything");
+
+ // Add groups to ranks:
+ AddGroupToRank("Default", "Default");
+ AddGroupToRank("Teleport", "VIP");
+ AddGroupToRank("Teleport", "Operator");
+ AddGroupToRank("Kick", "Operator");
+ AddGroupToRank("Everything", "Admin");
+
+ // Add permissions to groups:
+ AddPermissionToGroup("core.build", "Default");
+ AddPermissionToGroup("core.tp", "Teleport");
+ AddPermissionToGroup("core.kick", "Kick");
+ AddPermissionToGroup("*", "Everything");
+
+ // Set the default rank:
+ SetDefaultRank("Default");
+}
+
+
+
+
diff --git a/src/RankManager.h b/src/RankManager.h
new file mode 100644
index 000000000..f364bba6a
--- /dev/null
+++ b/src/RankManager.h
@@ -0,0 +1,246 @@
+
+// RankManager.h
+
+// Declares the cRankManager class that represents the rank manager responsible for assigning permissions and message visuals to players
+
+
+
+
+#pragma once
+
+#include "SQLiteCpp/Database.h"
+#include "SQLiteCpp/Transaction.h"
+
+
+
+
+
+class cMojangAPI;
+
+
+
+
+
+class cRankManager
+{
+public:
+ /** Acquire this lock to perform mass changes.
+ Improves performance by wrapping everything into a transaction.
+ Makes sure that no other thread is accessing the DB. */
+ class cMassChangeLock
+ {
+ public:
+ cMassChangeLock(cRankManager & a_RankManager) :
+ m_Lock(a_RankManager.m_CS),
+ m_Transaction(a_RankManager.m_DB)
+ {
+ }
+
+ ~cMassChangeLock()
+ {
+ m_Transaction.commit();
+ }
+
+ protected:
+ cCSLock m_Lock;
+ SQLite::Transaction m_Transaction;
+ };
+
+
+ /** Creates the rank manager. Needs to be initialized before other use. */
+ cRankManager(void);
+
+ ~cRankManager();
+
+ /** Initializes the rank manager. Performs migration and default-setting if no data is found in the DB.
+ The a_MojangAPI param is used when migrating from old ini files, to look up player UUIDs. */
+ void Initialize(cMojangAPI & a_MojangAPI);
+
+ /** Returns the name of the rank that the specified player has assigned to them.
+ If the player has no rank assigned, returns an empty string (NOT the default rank). */
+ AString GetPlayerRankName(const AString & a_PlayerUUID);
+
+ /** Returns the names of Groups that the specified player has assigned to them. */
+ AStringVector GetPlayerGroups(const AString & a_PlayerUUID);
+
+ /** Returns the permissions that the specified player has assigned to them.
+ If the player has no rank assigned to them, returns the default rank's permissions. */
+ AStringVector GetPlayerPermissions(const AString & a_PlayerUUID);
+
+ /** Returns the names of groups that the specified rank has assigned to it.
+ Returns an empty vector if the rank doesn't exist. */
+ AStringVector GetRankGroups(const AString & a_RankName);
+
+ /** Returns the permissions that the specified group has assigned to it.
+ Returns an empty vector if the group doesn't exist. */
+ AStringVector GetGroupPermissions(const AString & a_GroupName);
+
+ /** Returns all permissions that the specified rank has assigned to it, through all its groups.
+ Returns an empty vector if the rank doesn't exist. Any non-existent groups are ignored. */
+ AStringVector GetRankPermissions(const AString & a_RankName);
+
+ /** Returns the names of all defined ranks. */
+ AStringVector GetAllRanks(void);
+
+ /** Returns the names of all permission groups. */
+ AStringVector GetAllGroups(void);
+
+ /** Returns all the distinct permissions that are stored in the DB. */
+ AStringVector GetAllPermissions(void);
+
+ /** Returns the message visuals (prefix, postfix, color) for the specified player.
+ Returns true if the visuals were read from the DB, false if not (player not found etc). */
+ bool GetPlayerMsgVisuals(
+ const AString & a_PlayerUUID,
+ AString & a_MsgPrefix,
+ AString & a_MsgSuffix,
+ AString & a_MsgNameColorCode
+ );
+
+ /** Adds a new rank. No action if the rank already exists. */
+ void AddRank(
+ const AString & a_RankName,
+ const AString & a_MsgPrefix,
+ const AString & a_MsgSuffix,
+ const AString & a_MsgNameColorCode
+ );
+
+ /** Adds a new permission group. No action if such a group already exists. */
+ void AddGroup(const AString & a_GroupName);
+
+ /** Bulk-adds groups. Group names that already exist are silently skipped. */
+ void AddGroups(const AStringVector & a_GroupNames);
+
+ /** Adds the specified permission group to the specified rank.
+ Fails if the rank or group names are not found.
+ Returns true if successful, false on error. */
+ bool AddGroupToRank(const AString & a_GroupName, const AString & a_RankName);
+
+ /** Adds the specified permission to the specified permission group.
+ Fails if the permission group name is not found.
+ Returns true if successful, false on error. */
+ bool AddPermissionToGroup(const AString & a_Permission, const AString & a_GroupName);
+
+ /** Adds the specified permissions to the specified permission group.
+ Fails if the permission group name is not found.
+ Returns true if successful, false on error. */
+ bool AddPermissionsToGroup(const AStringVector & a_Permissions, const AString & a_GroupName);
+
+ /** Removes the specified rank.
+ All players assigned to that rank will be re-assigned to a_ReplacementRankName.
+ If a_ReplacementRankName is empty or not a valid rank, the player will be removed from the DB,
+ which means they will receive the default rank the next time they are queried.
+ If the rank being removed is the default rank, the default will be changed to the replacement
+ rank; the operation fails if there's no replacement. */
+ void RemoveRank(const AString & a_RankName, const AString & a_ReplacementRankName);
+
+ /** Removes the specified group completely.
+ The group will first be removed from all ranks using it, and then removed itself. */
+ void RemoveGroup(const AString & a_GroupName);
+
+ /** Removes the specified group from the specified rank.
+ The group will stay defined, even if no rank is using it. */
+ void RemoveGroupFromRank(const AString & a_GroupName, const AString & a_RankName);
+
+ /** Removes the specified permission from the specified group. */
+ void RemovePermissionFromGroup(const AString & a_Permission, const AString & a_GroupName);
+
+ /** Renames the specified rank. No action if the rank name is not found.
+ Fails if the new name is already used.
+ Updates the cached m_DefaultRank if the default rank is being renamed.
+ Returns true on success, false on failure. */
+ bool RenameRank(const AString & a_OldName, const AString & a_NewName);
+
+ /** Renames the specified group. No action if the rank name is not found.
+ Fails if the new name is already used.
+ Returns true on success, false on failure. */
+ bool RenameGroup(const AString & a_OldName, const AString & a_NewName);
+
+ /** Sets the specified player's rank.
+ If the player already had rank assigned to them, it is overwritten with the new rank and name.
+ Note that this doesn't change the cPlayer if the player is already connected, you need to update all the
+ cPlayer instances manually.
+ The PlayerName is provided for reference, so that GetRankPlayerNames() can work. */
+ void SetPlayerRank(const AString & a_PlayerUUID, const AString & a_PlayerName, const AString & a_RankName);
+
+ /** Removes the player's rank assignment. The player is left without a rank.
+ Note that this doesn't change the cPlayer instances for the already connected players, you need to update
+ all the instances manually.
+ No action if the player has no rank assigned to them already. */
+ void RemovePlayerRank(const AString & a_PlayerUUID);
+
+ /** Sets the message visuals of an existing rank. No action if the rank name is not found. */
+ void SetRankVisuals(
+ const AString & a_RankName,
+ const AString & a_MsgPrefix,
+ const AString & a_MsgSuffix,
+ const AString & a_MsgNameColorCode
+ );
+
+ /** Returns the message visuals of an existing rank.
+ Returns true if successful, false on error (rank doesn't exist). */
+ bool GetRankVisuals(
+ const AString & a_RankName,
+ AString & a_MsgPrefix,
+ AString & a_MsgSuffix,
+ AString & a_MsgNameColorCode
+ );
+
+ /** Returns true iff the specified rank exists in the DB. */
+ bool RankExists(const AString & a_RankName);
+
+ /** Returns true iff the specified group exists in the DB. */
+ bool GroupExists(const AString & a_GroupName);
+
+ /** Returns true iff the specified player has a rank assigned to them in the DB. */
+ bool IsPlayerRankSet(const AString & a_PlayerUUID);
+
+ /** Returns true iff the specified rank contains the specified group. */
+ bool IsGroupInRank(const AString & a_GroupName, const AString & a_RankName);
+
+ /** Returns true iff the specified group contains the specified permission. */
+ bool IsPermissionInGroup(const AString & a_Permission, const AString & a_GroupName);
+
+ /** Called by cMojangAPI whenever the playername-uuid pairing is discovered. Updates the DB. */
+ void NotifyNameUUID(const AString & a_PlayerName, const AString & a_UUID);
+
+ /** Sets the specified rank as the default rank.
+ Returns true on success, false on failure (rank not found). */
+ bool SetDefaultRank(const AString & a_RankName);
+
+ /** Returns the name of the default rank. */
+ const AString & GetDefaultRank(void) const { return m_DefaultRank; }
+
+protected:
+
+ /** The database storage for all the data. Protected by m_CS. */
+ SQLite::Database m_DB;
+
+ /** The name of the default rank. Kept as a cache so that queries for it don't need to go through the DB. */
+ AString m_DefaultRank;
+
+ /** The mutex protecting m_DB and m_DefaultRank against multi-threaded access. */
+ cCriticalSection m_CS;
+
+ /** Set to true once the manager is initialized. */
+ bool m_IsInitialized;
+
+ /** The MojangAPI instance that is used for translating playernames to UUIDs.
+ Set in Initialize(), may be NULL. */
+ cMojangAPI * m_MojangAPI;
+
+
+ /** Returns true if all the DB tables are empty, indicating a fresh new install. */
+ bool AreDBTablesEmpty(void);
+
+ /** Returns true iff the specified DB table is empty.
+ If there's an error while querying, returns false. */
+ bool IsDBTableEmpty(const AString & a_TableName);
+
+ /** Creates a default set of ranks / groups / permissions. */
+ void CreateDefaults(void);
+} ;
+
+
+
+
diff --git a/src/Root.cpp b/src/Root.cpp
index ee0d9b835..f04cbf08b 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -6,7 +6,6 @@
#include "World.h"
#include "WebAdmin.h"
#include "FurnaceRecipe.h"
-#include "GroupManager.h"
#include "CraftingRecipes.h"
#include "Bindings/PluginManager.h"
#include "MonsterConfig.h"
@@ -47,7 +46,6 @@ cRoot::cRoot(void) :
m_InputThread(NULL),
m_Server(NULL),
m_MonsterConfig(NULL),
- m_GroupManager(NULL),
m_CraftingRecipes(NULL),
m_FurnaceRecipe(NULL),
m_WebAdmin(NULL),
@@ -161,7 +159,7 @@ void cRoot::Start(void)
m_WebAdmin->Init();
LOGD("Loading settings...");
- m_GroupManager = new cGroupManager();
+ m_RankManager.Initialize(m_MojangAPI);
m_CraftingRecipes = new cCraftingRecipes;
m_FurnaceRecipe = new cFurnaceRecipe();
@@ -240,8 +238,6 @@ void cRoot::Start(void)
LOGD("Unloading recipes...");
delete m_FurnaceRecipe; m_FurnaceRecipe = NULL;
delete m_CraftingRecipes; m_CraftingRecipes = NULL;
- LOGD("Forgetting groups...");
- delete m_GroupManager; m_GroupManager = NULL;
LOGD("Unloading worlds...");
UnloadWorlds();
@@ -472,16 +468,6 @@ void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCall
void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
{
- // Some commands are built-in:
- if (a_Cmd == "stop")
- {
- m_bStop = true;
- }
- else if (a_Cmd == "restart")
- {
- m_bRestart = true;
- }
-
// Put the command into a queue (Alleviates FS #363):
cCSLock Lock(m_CSPendingCommands);
m_PendingCommands.push_back(cCommand(a_Cmd, new cLogCommandDeleteSelfOutputCallback));
@@ -493,14 +479,16 @@ void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
{
- // Some commands are built-in:
+ // cRoot handles stopping and restarting due to our access to controlling variables
if (a_Cmd == "stop")
{
m_bStop = true;
+ return;
}
else if (a_Cmd == "restart")
{
m_bRestart = true;
+ return;
}
LOG("Executing console command: \"%s\"", a_Cmd.c_str());
@@ -555,17 +543,6 @@ void cRoot::SaveAllChunks(void)
-void cRoot::ReloadGroups(void)
-{
- LOG("Reload groups ...");
- m_GroupManager->LoadGroups();
- m_GroupManager->CheckUsers();
-}
-
-
-
-
-
void cRoot::BroadcastChat(const AString & a_Message, eMessageType a_ChatPrefix)
{
for (WorldMap::iterator itr = m_WorldsByName.begin(), end = m_WorldsByName.end(); itr != end; ++itr)
diff --git a/src/Root.h b/src/Root.h
index 6840efcbe..9bc975889 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -5,6 +5,7 @@
#include "Protocol/MojangAPI.h"
#include "HTTPServer/HTTPServer.h"
#include "Defines.h"
+#include "RankManager.h"
@@ -13,7 +14,6 @@
// fwd:
class cThread;
class cMonsterConfig;
-class cGroupManager;
class cCraftingRecipes;
class cFurnaceRecipe;
class cWebAdmin;
@@ -78,7 +78,6 @@ public:
cMonsterConfig * GetMonsterConfig(void) { return m_MonsterConfig; }
- cGroupManager * GetGroupManager (void) { return m_GroupManager; } // tolua_export
cCraftingRecipes * GetCraftingRecipes(void) { return m_CraftingRecipes; } // tolua_export
cFurnaceRecipe * GetFurnaceRecipe (void) { return m_FurnaceRecipe; } // Exported in ManualBindings.cpp with quite a different signature
@@ -89,6 +88,7 @@ public:
cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export
cAuthenticator & GetAuthenticator (void) { return m_Authenticator; }
cMojangAPI & GetMojangAPI (void) { return m_MojangAPI; }
+ cRankManager & GetRankManager (void) { return m_RankManager; }
/** Queues a console command for execution through the cServer class.
The command will be executed in the tick thread
@@ -122,9 +122,6 @@ public:
/// Saves all chunks in all worlds
void SaveAllChunks(void); // tolua_export
- /// Reloads all the groups
- void ReloadGroups(void); // tolua_export
-
/// Calls the callback for each player in all worlds
bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
@@ -187,13 +184,13 @@ private:
cServer * m_Server;
cMonsterConfig * m_MonsterConfig;
- cGroupManager * m_GroupManager;
cCraftingRecipes * m_CraftingRecipes;
cFurnaceRecipe * m_FurnaceRecipe;
cWebAdmin * m_WebAdmin;
cPluginManager * m_PluginManager;
cAuthenticator m_Authenticator;
cMojangAPI m_MojangAPI;
+ cRankManager m_RankManager;
cHTTPServer m_HTTPServer;
bool m_bStop;
diff --git a/src/Server.cpp b/src/Server.cpp
index 4640716eb..f3a1404aa 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -11,7 +11,6 @@
#include "World.h"
#include "ChunkDef.h"
#include "Bindings/PluginManager.h"
-#include "GroupManager.h"
#include "ChatColor.h"
#include "Entities/Player.h"
#include "Inventory.h"
@@ -117,7 +116,9 @@ cServer::cServer(void) :
m_MaxPlayers(0),
m_bIsHardcore(false),
m_TickThread(*this),
- m_ShouldAuthenticate(false)
+ m_ShouldAuthenticate(false),
+ m_ShouldLoadOfflinePlayerData(false),
+ m_ShouldLoadNamedPlayerData(true)
{
}
@@ -457,83 +458,98 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
return;
}
- // Special handling: "stop" and "restart" are built in
- if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0))
- {
- return;
- }
+ // "stop" and "restart" are handled in cRoot::ExecuteConsoleCommand, our caller, due to its access to controlling variables
// "help" and "reload" are to be handled by MCS, so that they work no matter what
if (split[0] == "help")
{
PrintHelp(split, a_Output);
+ a_Output.Finished();
return;
}
- if (split[0] == "reload")
+ else if (split[0] == "reload")
{
cPluginManager::Get()->ReloadPlugins();
- cRoot::Get()->ReloadGroups();
+ a_Output.Finished();
return;
}
- if (split[0] == "reloadplugins")
+ else if (split[0] == "reloadplugins")
{
cPluginManager::Get()->ReloadPlugins();
- return;
- }
- if (split[0] == "reloadgroups")
- {
- cRoot::Get()->ReloadGroups();
- a_Output.Out("Groups reloaded!");
+ a_Output.Out("Plugins reloaded");
a_Output.Finished();
return;
}
- if (split[0] == "load")
+ else if (split[0] == "load")
{
if (split.size() > 1)
{
- cPluginManager::Get()->LoadPlugin(split[1]);
-
- return;
+ a_Output.Out(cPluginManager::Get()->LoadPlugin(split[1]) ? "Plugin loaded" : "Error occurred loading plugin");
}
else
{
- a_Output.Out("No plugin given! Command: load <pluginname>");
- a_Output.Finished();
- return;
+ a_Output.Out("Usage: load <pluginname>");
}
+ a_Output.Finished();
+ return;
}
-
- if (split[0] == "unload")
+ else if (split[0] == "unload")
{
if (split.size() > 1)
{
cPluginManager::Get()->RemovePlugin(cPluginManager::Get()->GetPlugin(split[1]));
- return;
+ a_Output.Out("Plugin unloaded");
}
else
{
- a_Output.Out("No plugin given! Command: unload <pluginname>");
- a_Output.Finished();
- return;
+ a_Output.Out("Usage: unload <pluginname>");
}
+ a_Output.Finished();
+ return;
+ }
+ if (split[0] == "destroyentities")
+ {
+ class WorldCallback : public cWorldListCallback
+ {
+ virtual bool Item(cWorld * a_World) override
+ {
+ class EntityCallback : public cEntityCallback
+ {
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ if (!a_Entity->IsPlayer())
+ {
+ a_Entity->Destroy();
+ }
+ return false;
+ }
+ } EC;
+ a_World->ForEachEntity(EC);
+ return false;
+ }
+ } WC;
+ cRoot::Get()->ForEachWorld(WC);
+ a_Output.Out("Destroyed all entities");
+ a_Output.Finished();
+ return;
}
// There is currently no way a plugin can do these (and probably won't ever be):
- if (split[0].compare("chunkstats") == 0)
+ else if (split[0].compare("chunkstats") == 0)
{
cRoot::Get()->LogChunkStats(a_Output);
a_Output.Finished();
return;
}
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
- if (split[0].compare("dumpmem") == 0)
+ else if (split[0].compare("dumpmem") == 0)
{
LeakFinderXmlOutput Output("memdump.xml");
DumpUsedMemory(&Output);
return;
}
- if (split[0].compare("killmem") == 0)
+ else if (split[0].compare("killmem") == 0)
{
for (;;)
{
@@ -542,7 +558,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
}
#endif
- if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output))
+ else if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output))
{
a_Output.Finished();
return;
@@ -610,6 +626,7 @@ void cServer::BindBuiltInConsoleCommands(void)
PlgMgr->BindConsoleCommand("chunkstats", NULL, " - Displays detailed chunk memory statistics");
PlgMgr->BindConsoleCommand("load <pluginname>", NULL, " - Adds and enables the specified plugin");
PlgMgr->BindConsoleCommand("unload <pluginname>", NULL, " - Disables the specified plugin");
+ PlgMgr->BindConsoleCommand("destroyentities", NULL, " - Destroys all entities in all worlds");
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
PlgMgr->BindConsoleCommand("dumpmem", NULL, " - Dumps all used memory blocks together with their callstacks into memdump.xml");
diff --git a/src/Server.h b/src/Server.h
index 2165c2e34..8e190fdca 100644
--- a/src/Server.h
+++ b/src/Server.h
@@ -120,7 +120,7 @@ public: // tolua_export
const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; }
/** Returns true if authentication has been turned on in server settings. */
- bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; }
+ bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; } // tolua_export
/** Returns true if offline UUIDs should be used to load data for players whose normal UUIDs cannot be found.
Loaded from the settings.ini [PlayerData].LoadOfflinePlayerData setting. */
diff --git a/src/SetChunkData.cpp b/src/SetChunkData.cpp
index 6e0c2733e..bfe59fbcb 100644
--- a/src/SetChunkData.cpp
+++ b/src/SetChunkData.cpp
@@ -5,6 +5,7 @@
#include "Globals.h"
#include "SetChunkData.h"
+#include "BlockEntities/BlockEntity.h"
@@ -13,6 +14,9 @@
cSetChunkData::cSetChunkData(int a_ChunkX, int a_ChunkZ, bool a_ShouldMarkDirty) :
m_ChunkX(a_ChunkX),
m_ChunkZ(a_ChunkZ),
+ m_IsLightValid(false),
+ m_IsHeightMapValid(false),
+ m_AreBiomesValid(false),
m_ShouldMarkDirty(a_ShouldMarkDirty)
{
}
@@ -113,3 +117,35 @@ void cSetChunkData::CalculateHeightMap(void)
+
+void cSetChunkData::RemoveInvalidBlockEntities(void)
+{
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end();)
+ {
+ BLOCKTYPE EntityBlockType = (*itr)->GetBlockType();
+ BLOCKTYPE WorldBlockType = cChunkDef::GetBlock(m_BlockTypes, (*itr)->GetRelX(), (*itr)->GetPosY(), (*itr)->GetRelZ());
+ if (EntityBlockType != WorldBlockType)
+ {
+ // Bad blocktype, remove the block entity:
+ LOGD("Block entity blocktype mismatch at {%d, %d, %d}: entity for blocktype %s(%d) in block %s(%d). Deleting the block entity.",
+ (*itr)->GetPosX(), (*itr)->GetPosY(), (*itr)->GetPosZ(),
+ ItemTypeToString(EntityBlockType).c_str(), EntityBlockType,
+ ItemTypeToString(WorldBlockType).c_str(), WorldBlockType
+ );
+ cBlockEntityList::iterator itr2 = itr;
+ itr2++;
+ delete *itr;
+ m_BlockEntities.erase(itr);
+ itr = itr2;
+ }
+ else
+ {
+ // Good blocktype, keep the block entity:
+ ++itr;
+ }
+ } // for itr - m_BlockEntities[]
+}
+
+
+
+
diff --git a/src/SetChunkData.h b/src/SetChunkData.h
index a2f776f6b..03e9ef4d9 100644
--- a/src/SetChunkData.h
+++ b/src/SetChunkData.h
@@ -92,6 +92,9 @@ public:
/** Calculates the heightmap based on the contained blocktypes and marks it valid. */
void CalculateHeightMap(void);
+ /** Removes the block entities that don't have a proper blocktype at their corresponding coords. */
+ void RemoveInvalidBlockEntities(void);
+
protected:
int m_ChunkX;
int m_ChunkZ;
diff --git a/src/Simulator/VanillaFluidSimulator.cpp b/src/Simulator/VanillaFluidSimulator.cpp
index 18d9b07e1..6df75eebb 100644
--- a/src/Simulator/VanillaFluidSimulator.cpp
+++ b/src/Simulator/VanillaFluidSimulator.cpp
@@ -98,7 +98,7 @@ int cVanillaFluidSimulator::CalculateFlowCost(cChunk * a_Chunk, int a_RelX, int
}
// Check if block below is passable
- if (!a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta))
+ if ((a_RelY > 0) && !a_Chunk->UnboundedRelGetBlock(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta))
{
return Cost;
}
diff --git a/src/StringCompression.cpp b/src/StringCompression.cpp
index 71d64e71e..af9f1687f 100644
--- a/src/StringCompression.cpp
+++ b/src/StringCompression.cpp
@@ -180,3 +180,65 @@ extern int UncompressStringGZIP(const char * a_Data, size_t a_Length, AString &
+
+extern int InflateString(const char * a_Data, size_t a_Length, AString & a_Uncompressed)
+{
+ a_Uncompressed.reserve(a_Length);
+
+ char Buffer[64 KiB];
+ z_stream strm;
+ memset(&strm, 0, sizeof(strm));
+ strm.next_in = (Bytef *)a_Data;
+ strm.avail_in = (uInt)a_Length;
+ strm.next_out = (Bytef *)Buffer;
+ strm.avail_out = sizeof(Buffer);
+
+ int res = inflateInit(&strm); // Force GZIP decoding
+ if (res != Z_OK)
+ {
+ LOG("%s: inflation initialization failed: %d (\"%s\").", __FUNCTION__, res, strm.msg);
+ return res;
+ }
+
+ for (;;)
+ {
+ res = inflate(&strm, Z_NO_FLUSH);
+ switch (res)
+ {
+ case Z_OK:
+ {
+ // Some data has been uncompressed. Consume the buffer and continue uncompressing
+ a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out);
+ strm.next_out = (Bytef *)Buffer;
+ strm.avail_out = sizeof(Buffer);
+ if (strm.avail_in == 0)
+ {
+ // All data has been uncompressed
+ inflateEnd(&strm);
+ return Z_OK;
+ }
+ break;
+ }
+
+ case Z_STREAM_END:
+ {
+ // Finished uncompressing. Consume the rest of the buffer and return
+ a_Uncompressed.append(Buffer, sizeof(Buffer) - strm.avail_out);
+ inflateEnd(&strm);
+ return Z_OK;
+ }
+
+ default:
+ {
+ // An error has occurred, log it and return the error value
+ LOG("%s: inflation failed: %d (\"%s\").", __FUNCTION__, res, strm.msg);
+ inflateEnd(&strm);
+ return res;
+ }
+ } // switch (res)
+ } // while (true)
+}
+
+
+
+
diff --git a/src/StringCompression.h b/src/StringCompression.h
index 038240797..dde17ce30 100644
--- a/src/StringCompression.h
+++ b/src/StringCompression.h
@@ -21,5 +21,7 @@ extern int CompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_
/// Uncompresses a_Data into a_Uncompressed using GZIP; returns Z_OK for success or Z_XXX error constants same as zlib
extern int UncompressStringGZIP(const char * a_Data, size_t a_Length, AString & a_Uncompressed);
+/** Uncompresses a_Data into a_Uncompressed using Inflate; returns Z_OK for success or Z_XXX error constants same as zlib */
+extern int InflateString(const char * a_Data, size_t a_Length, AString & a_Uncompressed);
diff --git a/src/StringUtils.h b/src/StringUtils.h
index 3d4379352..72a90a8c2 100644
--- a/src/StringUtils.h
+++ b/src/StringUtils.h
@@ -9,6 +9,7 @@
#pragma once
#include <string>
+#include <limits>
@@ -98,6 +99,68 @@ extern int GetBEInt(const char * a_Mem);
/// Writes four bytes to the specified memory location so that they interpret as BigEndian int
extern void SetBEInt(char * a_Mem, Int32 a_Value);
+/// Parses any integer type. Checks bounds and returns errors out of band.
+template <class T>
+bool StringToInteger(const AString & a_str, T & a_Num)
+{
+ size_t i = 0;
+ bool positive = true;
+ T result = 0;
+ if (a_str[0] == '+')
+ {
+ i++;
+ }
+ else if (a_str[0] == '-')
+ {
+ i++;
+ positive = false;
+ }
+ if (positive)
+ {
+ for (size_t size = a_str.size(); i < size; i++)
+ {
+ if ((a_str[i] < '0') || (a_str[i] > '9'))
+ {
+ return false;
+ }
+ if (std::numeric_limits<T>::max() / 10 < result)
+ {
+ return false;
+ }
+ result *= 10;
+ T digit = a_str[i] - '0';
+ if (std::numeric_limits<T>::max() - digit < result)
+ {
+ return false;
+ }
+ result += digit;
+ }
+ }
+ else
+ {
+ for (size_t size = a_str.size(); i < size; i++)
+ {
+ if ((a_str[i] < '0') || (a_str[i] > '9'))
+ {
+ return false;
+ }
+ if (std::numeric_limits<T>::min() / 10 > result)
+ {
+ return false;
+ }
+ result *= 10;
+ T digit = a_str[i] - '0';
+ if (std::numeric_limits<T>::min() + digit > result)
+ {
+ return false;
+ }
+ result -= digit;
+ }
+ }
+ a_Num = result;
+ return true;
+}
+
// If you have any other string helper functions, declare them here
diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp
index 4199bbf56..b4facb2d3 100644
--- a/src/UI/SlotArea.cpp
+++ b/src/UI/SlotArea.cpp
@@ -207,7 +207,7 @@ void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_
m_ParentWindow.DistributeStack(Slot, a_Player, this, true);
if (Slot.IsEmpty())
{
- // Empty the slot completely, the cilent doesn't like left-over ItemType with zero count
+ // Empty the slot completely, the client doesn't like left-over ItemType with zero count
Slot.Empty();
}
SetSlot(a_SlotNum, a_Player, Slot);
@@ -1389,8 +1389,11 @@ void cSlotAreaBeacon::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
////////////////////////////////////////////////////////////////////////////////
// cSlotAreaEnchanting:
-cSlotAreaEnchanting::cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow) :
- cSlotAreaTemporary(1, a_ParentWindow)
+cSlotAreaEnchanting::cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ) :
+ cSlotAreaTemporary(1, a_ParentWindow),
+ m_BlockX(a_BlockX),
+ m_BlockY(a_BlockY),
+ m_BlockZ(a_BlockZ)
{
a_ParentWindow.m_SlotArea = this;
}
@@ -1409,7 +1412,7 @@ void cSlotAreaEnchanting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickActio
LOGWARNING("GetSlot(%d) returned NULL! Ignoring click", a_SlotNum);
return;
}
-
+
switch (a_ClickAction)
{
case caShiftLeftClick:
@@ -1428,6 +1431,25 @@ void cSlotAreaEnchanting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickActio
MiddleClicked(a_Player, a_SlotNum);
return;
}
+ case caDropKey:
+ case caCtrlDropKey:
+ {
+ DropClicked(a_Player, a_SlotNum, false);
+ return;
+ }
+ case caNumber1:
+ case caNumber2:
+ case caNumber3:
+ case caNumber4:
+ case caNumber5:
+ case caNumber6:
+ case caNumber7:
+ case caNumber8:
+ case caNumber9:
+ {
+ NumberClicked(a_Player, a_SlotNum, a_ClickAction);
+ return;
+ }
default:
{
break;
@@ -1443,106 +1465,36 @@ void cSlotAreaEnchanting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickActio
bAsync = true;
}
cItem & DraggingItem = a_Player.GetDraggingItem();
- switch (a_ClickAction)
+
+ if (DraggingItem.IsEmpty())
{
- case caRightClick:
+ // DraggingItem is empty -> Switch draggingitem and slot
+ if (!Slot.IsEmpty())
{
- // Right-clicked
- if (DraggingItem.IsEmpty())
- {
- DraggingItem = Slot.CopyOne();
- Slot.Empty();
- break;
- }
-
- if (Slot.IsEmpty())
- {
- Slot = DraggingItem.CopyOne();
- DraggingItem.m_ItemCount -= 1;
- if (DraggingItem.m_ItemCount <= 0)
- {
- DraggingItem.Empty();
- }
- }
- else if ((!DraggingItem.IsEqual(Slot)) && (DraggingItem.m_ItemCount == 1))
- {
- // Swap contents
- cItem tmp(DraggingItem);
- DraggingItem = Slot;
- Slot = tmp;
- }
- break;
+ std::swap(DraggingItem, Slot);
}
-
- case caLeftClick:
- {
- // Left-clicked
- if (DraggingItem.IsEmpty())
- {
- DraggingItem = Slot.CopyOne();
- Slot.Empty();
- break;
- }
-
- if (DraggingItem.IsEqual(Slot))
- {
- // Do nothing
- break;
- }
-
- if (!Slot.IsEmpty())
- {
- if (DraggingItem.m_ItemCount == 1)
- {
- // Swap contents
- cItem tmp(DraggingItem);
- DraggingItem = Slot;
- Slot = tmp;
- }
- }
- else
- {
- Slot = DraggingItem.CopyOne();
- DraggingItem.m_ItemCount -= 1;
- if (DraggingItem.m_ItemCount <= 0)
- {
- DraggingItem.Empty();
- }
- }
- break;
- }
- default:
+ }
+ else if (Slot.IsEmpty())
+ {
+ // DraggingItem isn't empty and slot is empty -> Set one dragging item in the slot
+ Slot = DraggingItem.CopyOne();
+ DraggingItem.m_ItemCount -= 1;
+
+ if (DraggingItem.m_ItemCount <= 0)
{
- LOGWARNING("SlotArea: Unhandled click action: %d (%s)", a_ClickAction, ClickActionToString(a_ClickAction));
- m_ParentWindow.BroadcastWholeWindow();
- return;
+ DraggingItem.Empty();
}
- } // switch (a_ClickAction
-
- SetSlot(a_SlotNum, a_Player, Slot);
- if (bAsync)
- {
- m_ParentWindow.BroadcastWholeWindow();
}
- UpdateResult(a_Player);
-}
-
-
-
-
-
-void cSlotAreaEnchanting::DblClicked(cPlayer & a_Player, int a_SlotNum)
-{
- cItem & Dragging = a_Player.GetDraggingItem();
- if ((!Dragging.IsEmpty()) || (a_SlotNum != 0))
+ else if ((DraggingItem.m_ItemCount == 1) && !DraggingItem.IsEqual(Slot))
{
- return;
+ // DraggingItem and slot aren't empty -> Switch items
+ std::swap(DraggingItem, Slot);
}
-
- cItem Item = *GetSlot(0, a_Player);
- if (!m_ParentWindow.CollectItemsToHand(Item, *this, a_Player, false))
+
+ SetSlot(a_SlotNum, a_Player, Slot);
+ if (bAsync)
{
- m_ParentWindow.CollectItemsToHand(Item, *this, a_Player, true);
+ m_ParentWindow.BroadcastWholeWindow();
}
}
@@ -1567,7 +1519,15 @@ void cSlotAreaEnchanting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Playe
{
a_ItemStack.Empty();
}
+}
+
+
+
+
+void cSlotAreaEnchanting::OnPlayerAdded(cPlayer & a_Player)
+{
+ super::OnPlayerAdded(a_Player);
UpdateResult(a_Player);
}
@@ -1587,29 +1547,33 @@ void cSlotAreaEnchanting::OnPlayerRemoved(cPlayer & a_Player)
+void cSlotAreaEnchanting::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item)
+{
+ super::SetSlot(a_SlotNum, a_Player, a_Item);
+ UpdateResult(a_Player);
+}
+
+
+
+
+
void cSlotAreaEnchanting::UpdateResult(cPlayer & a_Player)
{
cItem Item = *GetSlot(0, a_Player);
- if (Item.IsEmpty() || !Item.m_Enchantments.IsEmpty())
- {
- m_ParentWindow.SetProperty(0, 0, a_Player);
- m_ParentWindow.SetProperty(1, 0, a_Player);
- m_ParentWindow.SetProperty(2, 0, a_Player);
- }
- else if (cItem::IsEnchantable(Item.m_ItemType) || Item.m_ItemType == E_ITEM_BOOK)
+ if (cItem::IsEnchantable(Item.m_ItemType) && Item.m_Enchantments.IsEmpty())
{
int Bookshelves = std::min(GetBookshelvesCount(a_Player.GetWorld()), 15);
cFastRandom Random;
- int base = (Random.GenerateRandomInteger(1, 8) + (int)floor((float)Bookshelves / 2) + Random.GenerateRandomInteger(0, Bookshelves));
- int topSlot = std::max(base / 3, 1);
- int middleSlot = (base * 2) / 3 + 1;
- int bottomSlot = std::max(base, Bookshelves * 2);
+ int Base = (Random.GenerateRandomInteger(1, 8) + (int)floor((float)Bookshelves / 2) + Random.GenerateRandomInteger(0, Bookshelves));
+ int TopSlot = std::max(Base / 3, 1);
+ int MiddleSlot = (Base * 2) / 3 + 1;
+ int BottomSlot = std::max(Base, Bookshelves * 2);
- m_ParentWindow.SetProperty(0, topSlot, a_Player);
- m_ParentWindow.SetProperty(1, middleSlot, a_Player);
- m_ParentWindow.SetProperty(2, bottomSlot, a_Player);
+ m_ParentWindow.SetProperty(0, TopSlot, a_Player);
+ m_ParentWindow.SetProperty(1, MiddleSlot, a_Player);
+ m_ParentWindow.SetProperty(2, BottomSlot, a_Player);
}
else
{
@@ -1625,12 +1589,9 @@ void cSlotAreaEnchanting::UpdateResult(cPlayer & a_Player)
int cSlotAreaEnchanting::GetBookshelvesCount(cWorld * a_World)
{
- int PosX, PosY, PosZ;
- ((cEnchantingWindow*)&m_ParentWindow)->GetBlockPos(PosX, PosY, PosZ);
-
int Bookshelves = 0;
cBlockArea Area;
- Area.Read(a_World, PosX - 2, PosX + 2, PosY, PosY + 1, PosZ - 2, PosZ + 2);
+ Area.Read(a_World, m_BlockX - 2, m_BlockX + 2, m_BlockY, m_BlockY + 1, m_BlockZ - 2, m_BlockZ + 2);
static const struct
{
@@ -1678,7 +1639,7 @@ int cSlotAreaEnchanting::GetBookshelvesCount(cWorld * a_World)
if (
(Area.GetRelBlockType(CheckCoords[i].m_AirX, CheckCoords[i].m_AirY, CheckCoords[i].m_AirZ) == E_BLOCK_AIR) && // There's air in the checkspot
(Area.GetRelBlockType(CheckCoords[i].m_BookX, CheckCoords[i].m_BookY, CheckCoords[i].m_BookZ) == E_BLOCK_BOOKCASE) // There's bookcase in the wanted place
- )
+ )
{
Bookshelves++;
}
diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h
index 0cdb38680..cf0a129df 100644
--- a/src/UI/SlotArea.h
+++ b/src/UI/SlotArea.h
@@ -349,14 +349,15 @@ class cSlotAreaEnchanting :
typedef cSlotAreaTemporary super;
public:
- cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow);
+ cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ);
// cSlotArea /*override*/s:
virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) /*override*/;
- virtual void DblClicked(cPlayer & a_Player, int a_SlotNum) /*override*/;
virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) /*override*/;
+ virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override;
// cSlotAreaTemporary /*override*/s:
+ virtual void OnPlayerAdded (cPlayer & a_Player) override;
virtual void OnPlayerRemoved(cPlayer & a_Player) /*override*/;
/* Get the count of bookshelves who stand in the near of the enchanting table */
@@ -365,6 +366,8 @@ public:
protected:
/** Handles a click in the item slot. */
void UpdateResult(cPlayer & a_Player);
+
+ int m_BlockX, m_BlockY, m_BlockZ;
};
diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp
index 8f4913030..66900269f 100644
--- a/src/UI/Window.cpp
+++ b/src/UI/Window.cpp
@@ -881,7 +881,7 @@ cEnchantingWindow::cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
m_BlockY(a_BlockY),
m_BlockZ(a_BlockZ)
{
- m_SlotAreas.push_back(new cSlotAreaEnchanting(*this));
+ m_SlotAreas.push_back(new cSlotAreaEnchanting(*this, m_BlockX, m_BlockY, m_BlockZ));
m_SlotAreas.push_back(new cSlotAreaInventory(*this));
m_SlotAreas.push_back(new cSlotAreaHotBar(*this));
}
@@ -892,8 +892,13 @@ cEnchantingWindow::cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) :
void cEnchantingWindow::SetProperty(int a_Property, int a_Value)
{
- m_PropertyValue[a_Property] = a_Value;
+ if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue)))
+ {
+ ASSERT(!"a_Property is invalid");
+ return;
+ }
+ m_PropertyValue[a_Property] = a_Value;
super::SetProperty(a_Property, a_Value);
}
@@ -903,8 +908,13 @@ void cEnchantingWindow::SetProperty(int a_Property, int a_Value)
void cEnchantingWindow::SetProperty(int a_Property, int a_Value, cPlayer & a_Player)
{
- m_PropertyValue[a_Property] = a_Value;
+ if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue)))
+ {
+ ASSERT(!"a_Property is invalid");
+ return;
+ }
+ m_PropertyValue[a_Property] = a_Value;
super::SetProperty(a_Property, a_Value, a_Player);
}
@@ -914,18 +924,13 @@ void cEnchantingWindow::SetProperty(int a_Property, int a_Value, cPlayer & a_Pla
int cEnchantingWindow::GetPropertyValue(int a_Property)
{
- return m_PropertyValue[a_Property];
-}
-
-
-
-
+ if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue)))
+ {
+ ASSERT(!"a_Property is invalid");
+ return 0;
+ }
-void cEnchantingWindow::GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ)
-{
- a_PosX = m_BlockX;
- a_PosY = m_BlockY;
- a_PosZ = m_BlockZ;
+ return m_PropertyValue[a_Property];
}
diff --git a/src/UI/Window.h b/src/UI/Window.h
index ae1c5a6b3..4546fb9ad 100644
--- a/src/UI/Window.h
+++ b/src/UI/Window.h
@@ -291,9 +291,6 @@ public:
/** Return the Value of a Property */
int GetPropertyValue(int a_Property);
- /** Get the Position from the Enchantment Table */
- void GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ);
-
cSlotArea * m_SlotArea;
protected:
diff --git a/src/World.cpp b/src/World.cpp
index a2a926596..50bc6224d 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -251,8 +251,38 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin
m_TimeOfDay(0),
m_LastTimeUpdate(0),
m_SkyDarkness(0),
+ m_GameMode(gmNotSet),
+ m_bEnabledPVP(false),
+ m_IsDeepSnowEnabled(false),
+ m_ShouldLavaSpawnFire(true),
+ m_VillagersShouldHarvestCrops(true),
+ m_SimulatorManager(NULL),
+ m_SandSimulator(NULL),
+ m_WaterSimulator(NULL),
+ m_LavaSimulator(NULL),
+ m_FireSimulator(NULL),
+ m_RedstoneSimulator(NULL),
+ m_MaxPlayers(10),
+ m_ChunkMap(NULL),
+ m_bAnimals(true),
m_Weather(eWeather_Sunny),
m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :)
+ m_MaxCactusHeight(3),
+ m_MaxSugarcaneHeight(4),
+ m_IsCactusBonemealable(false),
+ m_IsCarrotsBonemealable(true),
+ m_IsCropsBonemealable(true),
+ m_IsGrassBonemealable(true),
+ m_IsMelonStemBonemealable(true),
+ m_IsMelonBonemealable(true),
+ m_IsPotatoesBonemealable(true),
+ m_IsPumpkinStemBonemealable(true),
+ m_IsPumpkinBonemealable(true),
+ m_IsSaplingBonemealable(true),
+ m_IsSugarcaneBonemealable(false),
+ m_bCommandBlocksEnabled(true),
+ m_bUseChatPrefixes(false),
+ m_TNTShrapnelLevel(slNone),
m_Scoreboard(this),
m_MapManager(this),
m_GeneratorCallbacks(*this),
@@ -407,12 +437,12 @@ void cWorld::InitializeSpawn(void)
int ViewDist = IniFile.GetValueSetI("SpawnPosition", "PregenerateDistance", DefaultViewDist);
IniFile.WriteFile(m_IniFileName);
- LOG("Preparing spawn area in world \"%s\"...", m_WorldName.c_str());
+ LOG("Preparing spawn area in world \"%s\", %d x %d chunks, total %d chunks...", m_WorldName.c_str(), ViewDist, ViewDist, ViewDist * ViewDist);
for (int x = 0; x < ViewDist; x++)
{
for (int z = 0; z < ViewDist; z++)
{
- m_ChunkMap->TouchChunk(x + ChunkX-(ViewDist - 1) / 2, ZERO_CHUNK_Y, z + ChunkZ-(ViewDist - 1) / 2); // Queue the chunk in the generator / loader
+ m_ChunkMap->TouchChunk(x + ChunkX-(ViewDist - 1) / 2, z + ChunkZ-(ViewDist - 1) / 2); // Queue the chunk in the generator / loader
}
}
@@ -1196,24 +1226,26 @@ void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_Blo
return;
}
- // TODO: Add damage to entities and implement block hardiness
+ // TODO: Implement block hardiness
Vector3d explosion_pos = Vector3d(a_BlockX, a_BlockY, a_BlockZ);
cVector3iArray BlocksAffected;
m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected);
BroadcastSoundEffect("random.explode", (double)a_BlockX, (double)a_BlockY, (double)a_BlockZ, 1.0f, 0.6f);
+
{
cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
cClientHandle * ch = (*itr)->GetClientHandle();
- if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ if (ch == NULL)
{
continue;
}
+
Vector3d distance_explosion = (*itr)->GetPosition() - explosion_pos;
if (distance_explosion.SqrLength() < 4096.0)
{
- double real_distance = std::max(0.004, sqrt(distance_explosion.SqrLength()));
+ double real_distance = std::max(0.004, distance_explosion.Length());
double power = a_ExplosionSize / real_distance;
if (power <= 1)
{
@@ -1225,6 +1257,7 @@ void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_Blo
}
}
}
+
cPluginManager::Get()->CallHookExploded(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData);
}
@@ -2391,7 +2424,7 @@ void cWorld::SetChunkData(cSetChunkData & a_SetChunkData)
// Save the chunk right after generating, so that we don't have to generate it again on next run
if (a_SetChunkData.ShouldMarkDirty())
{
- m_Storage.QueueSaveChunk(ChunkX, 0, ChunkZ);
+ m_Storage.QueueSaveChunk(ChunkX, ChunkZ);
}
}
@@ -2663,6 +2696,15 @@ bool cWorld::ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback &
+bool cWorld::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback)
+{
+ return m_ChunkMap->ForEachEntityInBox(a_Box, a_Callback);
+}
+
+
+
+
+
bool cWorld::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback)
{
return m_ChunkMap->DoWithEntityByID(a_UniqueID, a_Callback);
@@ -2736,18 +2778,18 @@ void cWorld::RemoveClientFromChunkSender(cClientHandle * a_Client)
-void cWorld::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cWorld::TouchChunk(int a_ChunkX, int a_ChunkZ)
{
- m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+ m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkZ);
}
-bool cWorld::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+bool cWorld::LoadChunk(int a_ChunkX, int a_ChunkZ)
{
- return m_ChunkMap->LoadChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
+ return m_ChunkMap->LoadChunk(a_ChunkX, a_ChunkZ);
}
@@ -2763,9 +2805,9 @@ void cWorld::LoadChunks(const cChunkCoordsList & a_Chunks)
-void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cWorld::ChunkLoadFailed(int a_ChunkX, int a_ChunkZ)
{
- m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ);
+ m_ChunkMap->ChunkLoadFailed(a_ChunkX, a_ChunkZ);
}
@@ -2870,8 +2912,7 @@ void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ)
{
m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ);
- // Trick: use Y=1 to force the chunk generation even though the chunk data is already present
- m_Generator.QueueGenerateChunk(a_ChunkX, 1, a_ChunkZ);
+ m_Generator.QueueGenerateChunk(a_ChunkX, a_ChunkZ, true);
}
@@ -2880,7 +2921,7 @@ void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ)
void cWorld::GenerateChunk(int a_ChunkX, int a_ChunkZ)
{
- m_Generator.QueueGenerateChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ);
+ m_Generator.QueueGenerateChunk(a_ChunkX, a_ChunkZ, false);
}
@@ -3454,14 +3495,16 @@ void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc
cChunkDef::BlockNibbles BlockMetas;
a_ChunkDesc.CompressBlockMetas(BlockMetas);
- m_World->QueueSetChunkData(cSetChunkDataPtr(new cSetChunkData(
+ cSetChunkDataPtr SetChunkData(new cSetChunkData(
a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(),
a_ChunkDesc.GetBlockTypes(), BlockMetas,
NULL, NULL, // We don't have lighting, chunk will be lighted when needed
&a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(),
a_ChunkDesc.GetEntities(), a_ChunkDesc.GetBlockEntities(),
true
- )));
+ ));
+ SetChunkData->RemoveInvalidBlockEntities();
+ m_World->QueueSetChunkData(SetChunkData);
}
diff --git a/src/World.h b/src/World.h
index 572d9502f..b9191caef 100644
--- a/src/World.h
+++ b/src/World.h
@@ -324,6 +324,11 @@ public:
/** Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
+ /** Calls the callback for each entity that has a nonempty intersection with the specified boundingbox.
+ Returns true if all entities processed, false if the callback aborted by returning true.
+ If any chunk in the box is missing, ignores the entities in that chunk silently. */
+ bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
+
/** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false. */
bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
@@ -351,16 +356,16 @@ public:
void RemoveClientFromChunkSender(cClientHandle * a_Client);
/** Touches the chunk, causing it to be loaded or generated */
- void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ void TouchChunk(int a_ChunkX, int a_ChunkZ);
/** Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) */
- bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ bool LoadChunk(int a_ChunkX, int a_ChunkZ);
/** Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() */
void LoadChunks(const cChunkCoordsList & a_Chunks);
/** Marks the chunk as failed-to-load: */
- void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ);
/** Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be NULL. Returns true if sign text changed. Same as UpdateSign() */
bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); // Exported in ManualBindings.cpp
@@ -380,7 +385,7 @@ public:
/** Regenerate the given chunk: */
void RegenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export
- /** Generates the given chunk, if not already generated */
+ /** Generates the given chunk */
void GenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export
/** Queues a chunk for lighting; a_Callback is called after the chunk is lighted */
diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h
index 4ef72e379..ebf99103f 100644
--- a/src/WorldStorage/FastNBT.h
+++ b/src/WorldStorage/FastNBT.h
@@ -76,7 +76,9 @@ public:
cFastNBTTag(eTagType a_Type, int a_Parent) :
m_Type(a_Type),
+ m_NameStart(0),
m_NameLength(0),
+ m_DataStart(0),
m_DataLength(0),
m_Parent(a_Parent),
m_PrevSibling(-1),
@@ -88,7 +90,9 @@ public:
cFastNBTTag(eTagType a_Type, int a_Parent, int a_PrevSibling) :
m_Type(a_Type),
+ m_NameStart(0),
m_NameLength(0),
+ m_DataStart(0),
m_DataLength(0),
m_Parent(a_Parent),
m_PrevSibling(a_PrevSibling),
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index e435a1b1f..68e541eba 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -615,7 +615,6 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile)
{
m_Writer.BeginCompound("");
AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName());
- Vector3d Pos = a_Projectile->GetPosition();
m_Writer.AddByte("inGround", a_Projectile->IsInGround() ? 1 : 0);
switch (a_Projectile->GetProjectileKind())
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index a9c9ae4b5..4d2f92173 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -66,8 +66,17 @@ Since only the header is actually in the memory, this number can be high, but st
*/
#define MAX_MCA_FILES 32
-/// The maximum size of an inflated chunk; raw chunk data is 192 KiB, allow 64 KiB more of entities
-#define CHUNK_INFLATE_MAX 256 KiB
+#define LOAD_FAILED(CHX, CHZ) \
+ { \
+ const int RegionX = FAST_FLOOR_DIV(CHX, 32); \
+ const int RegionZ = FAST_FLOOR_DIV(CHZ, 32); \
+ LOGERROR("%s (%d): Loading chunk [%d, %d] from file r.%d.%d.mca failed. " \
+ "The server will now abort in order to avoid further data loss. " \
+ "Please add the reported file and this message to the issue report.", \
+ __FUNCTION__, __LINE__, CHX, CHZ, RegionX, RegionZ \
+ ); \
+ *((volatile int *)0) = 0; /* Crash intentionally */ \
+ }
@@ -248,29 +257,22 @@ cWSSAnvil::cMCAFile * cWSSAnvil::LoadMCAFile(const cChunkCoords & a_Chunk)
bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & a_Data)
{
- // Decompress the data:
- char Uncompressed[CHUNK_INFLATE_MAX];
- z_stream strm;
- strm.zalloc = (alloc_func)NULL;
- strm.zfree = (free_func)NULL;
- strm.opaque = NULL;
- inflateInit(&strm);
- strm.next_out = (Bytef *)Uncompressed;
- strm.avail_out = sizeof(Uncompressed);
- strm.next_in = (Bytef *)a_Data.data();
- strm.avail_in = (uInt)a_Data.size();
- int res = inflate(&strm, Z_FINISH);
- inflateEnd(&strm);
- if (res != Z_STREAM_END)
+ // Uncompress the data:
+ AString Uncompressed;
+ int res = InflateString(a_Data.data(), a_Data.size(), Uncompressed);
+ if (res != Z_OK)
{
+ LOGWARNING("Uncompressing chunk [%d, %d] failed: %d", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, res);
+ LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false;
}
// Parse the NBT data:
- cParsedNBT NBT(Uncompressed, strm.total_out);
+ cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
if (!NBT.IsValid())
{
// NBT Parsing failed
+ LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false;
}
@@ -317,11 +319,13 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
int Level = a_NBT.FindChildByName(0, "Level");
if (Level < 0)
{
+ LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false;
}
int Sections = a_NBT.FindChildByName(Level, "Sections");
if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List) || (a_NBT.GetChildrenType(Sections) != TAG_Compound))
{
+ LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false;
}
for (int Child = a_NBT.GetFirstChild(Sections); Child >= 0; Child = a_NBT.GetNextSibling(Child))
@@ -396,7 +400,7 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
} // for y
//*/
- m_World->QueueSetChunkData(cSetChunkDataPtr(new cSetChunkData(
+ cSetChunkDataPtr SetChunkData(new cSetChunkData(
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ,
BlockTypes, MetaData,
IsLightValid ? BlockLight : NULL,
@@ -404,7 +408,8 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
NULL, Biomes,
Entities, BlockEntities,
false
- )));
+ ));
+ m_World->QueueSetChunkData(SetChunkData);
return true;
}
@@ -581,64 +586,28 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
{
continue;
}
- int sID = a_NBT.FindChildByName(Child, "id");
- if (sID < 0)
+
+ // Get the BlockEntity's position
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, Child, x, y, z))
{
+ LOGWARNING("Bad block entity, missing the coords. Will be ignored.");
continue;
}
- if (strncmp(a_NBT.GetData(sID), "Beacon", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadBeaconFromNBT(a_BlockEntities, a_NBT, Child);
- }
- else if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadChestFromNBT(a_BlockEntities, a_NBT, Child, E_BLOCK_CHEST);
- }
- else if (strncmp(a_NBT.GetData(sID), "Control", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadCommandBlockFromNBT(a_BlockEntities, a_NBT, Child);
- }
- else if (strncmp(a_NBT.GetData(sID), "Dropper", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadDropperFromNBT(a_BlockEntities, a_NBT, Child);
- }
- else if (strncmp(a_NBT.GetData(sID), "FlowerPot", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadFlowerPotFromNBT(a_BlockEntities, a_NBT, Child);
- }
- else if (strncmp(a_NBT.GetData(sID), "Furnace", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child, a_BlockTypes, a_BlockMetas);
- }
- else if (strncmp(a_NBT.GetData(sID), "Hopper", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadHopperFromNBT(a_BlockEntities, a_NBT, Child);
- }
- else if (strncmp(a_NBT.GetData(sID), "Music", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadNoteFromNBT(a_BlockEntities, a_NBT, Child);
- }
- else if (strncmp(a_NBT.GetData(sID), "RecordPlayer", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadJukeboxFromNBT(a_BlockEntities, a_NBT, Child);
- }
- else if (strncmp(a_NBT.GetData(sID), "Sign", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadSignFromNBT(a_BlockEntities, a_NBT, Child);
- }
- else if (strncmp(a_NBT.GetData(sID), "Skull", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadMobHeadFromNBT(a_BlockEntities, a_NBT, Child);
- }
- else if (strncmp(a_NBT.GetData(sID), "Trap", a_NBT.GetDataLength(sID)) == 0)
+ int RelX = x, RelY = y, RelZ = z, ChunkX, ChunkZ;
+ cChunkDef::AbsoluteToRelative(RelX, RelY, RelZ, ChunkX, ChunkZ);
+
+ // Load the proper BlockEntity type based on the block type:
+ BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, RelY, RelZ);
+ NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, RelY, RelZ);
+ std::auto_ptr<cBlockEntity> be(LoadBlockEntityFromNBT(a_NBT, Child, x, y, z, BlockType, BlockMeta));
+ if (be.get() == NULL)
{
- LoadDispenserFromNBT(a_BlockEntities, a_NBT, Child);
- }
- else if (strncmp(a_NBT.GetData(sID), "TrappedChest", a_NBT.GetDataLength(sID)) == 0)
- {
- LoadChestFromNBT(a_BlockEntities, a_NBT, Child, E_BLOCK_TRAPPED_CHEST);
+ continue;
}
- // TODO: Other block entities
+
+ // Add the BlockEntity to the loaded data:
+ a_BlockEntities.push_back(be.release());
} // for Child - tag children
}
@@ -646,6 +615,52 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
+cBlockEntity * cWSSAnvil::LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a_Tag, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
+{
+ // Load the specific BlockEntity type:
+ switch (a_BlockType)
+ {
+ // Specific entity loaders:
+ case E_BLOCK_BEACON: return LoadBeaconFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
+ case E_BLOCK_CHEST: return LoadChestFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_CHEST);
+ case E_BLOCK_COMMAND_BLOCK: return LoadCommandBlockFromNBT(a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
+ case E_BLOCK_DISPENSER: return LoadDispenserFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
+ case E_BLOCK_DROPPER: return LoadDropperFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
+ case E_BLOCK_FLOWER_POT: return LoadFlowerPotFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
+ case E_BLOCK_FURNACE: return LoadFurnaceFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FURNACE, a_BlockMeta);
+ case E_BLOCK_HEAD: return LoadMobHeadFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
+ case E_BLOCK_HOPPER: return LoadHopperFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
+ case E_BLOCK_JUKEBOX: return LoadJukeboxFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
+ case E_BLOCK_LIT_FURNACE: return LoadFurnaceFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LIT_FURNACE, a_BlockMeta);
+ case E_BLOCK_NOTE_BLOCK: return LoadNoteBlockFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ);
+ case E_BLOCK_SIGN_POST: return LoadSignFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_SIGN_POST);
+ case E_BLOCK_TRAPPED_CHEST: return LoadChestFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_TRAPPED_CHEST);
+ case E_BLOCK_WALLSIGN: return LoadSignFromNBT (a_NBT, a_Tag, a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WALLSIGN);
+
+ // Blocktypes that have block entities but don't load their contents from disk:
+ case E_BLOCK_ENDER_CHEST: return NULL;
+ }
+
+ // All the other blocktypes should have no entities assigned to them. Report an error:
+ // Get the "id" tag:
+ int TagID = a_NBT.FindChildByName(a_Tag, "id");
+ AString TypeName("<unknown>");
+ if (TagID >= 0)
+ {
+ TypeName.assign(a_NBT.GetData(TagID), (size_t)a_NBT.GetDataLength(TagID));
+ }
+ LOGINFO("WorldLoader(%s): Block entity mismatch: block type %s (%d), type \"%s\", at {%d, %d, %d}; the entity will be lost.",
+ m_World->GetName().c_str(),
+ ItemTypeToString(a_BlockType).c_str(), a_BlockType, TypeName.c_str(),
+ a_BlockX, a_BlockY, a_BlockZ
+ );
+ return NULL;
+}
+
+
+
+
+
bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx)
{
int Type = a_NBT.FindChildByName(a_TagIdx, "id");
@@ -656,7 +671,6 @@ bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_
a_Item.m_ItemType = a_NBT.GetShort(Type);
if (a_Item.m_ItemType < 0)
{
- LOGD("Encountered an item with negative type (%d). Replacing with an empty item.", a_NBT.GetShort(Type));
a_Item.Empty();
return true;
}
@@ -754,16 +768,46 @@ void cWSSAnvil::LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a
-void cWSSAnvil::LoadBeaconFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+bool cWSSAnvil::CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, const char * a_ExpectedType)
{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ // Check if the given tag is a compound:
+ if (a_NBT.GetType(a_TagIdx) != TAG_Compound)
{
- return;
+ return false;
}
- std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(x, y, z, m_World));
+ // Get the "id" tag:
+ int TagID = a_NBT.FindChildByName(a_TagIdx, "id");
+ if (TagID < 0)
+ {
+ return false;
+ }
+
+ // Compare the value:
+ if (strncmp(a_NBT.GetData(TagID), a_ExpectedType, (size_t)a_NBT.GetDataLength(TagID)) == 0)
+ {
+ return true;
+ }
+ LOGWARNING("Block entity type mismatch: exp \"%s\", got \"%s\".",
+ a_ExpectedType,
+ AString(a_NBT.GetData(TagID), (size_t)a_NBT.GetDataLength(TagID)).c_str()
+ );
+ return false;
+}
+
+
+
+
+
+cBlockEntity * cWSSAnvil::LoadBeaconFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Beacon"))
+ {
+ return NULL;
+ }
+
+ std::auto_ptr<cBeaconEntity> Beacon(new cBeaconEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Levels");
if (CurrentLine >= 0)
@@ -790,88 +834,128 @@ void cWSSAnvil::LoadBeaconFromNBT(cBlockEntityList & a_BlockEntities, const cPar
LoadItemGridFromNBT(Beacon->GetContents(), a_NBT, Items);
}
- a_BlockEntities.push_back(Beacon.release());
+ return Beacon.release();
}
-void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType)
+cBlockEntity * cWSSAnvil::LoadChestFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_ChestBlockType)
{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ // Check if the data has a proper type:
+ // TODO: Does vanilla use "TrappedChest" or not? MCWiki says no, but previous code says yes
+ // Ref.: http://minecraft.gamepedia.com/Trapped_Chest
+ // https://github.com/mc-server/MCServer/blob/d0551e2e0a98a28f31a88d489d17b408e4a7d38d/src/WorldStorage/WSSAnvil.cpp#L637
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Chest") && !CheckBlockEntityType(a_NBT, a_TagIdx, "TrappedChest"))
{
- return;
+ return NULL;
}
+
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
- return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
+ return NULL; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this
}
- std::auto_ptr<cChestEntity> Chest(new cChestEntity(x, y, z, m_World, a_ChestType));
+ std::auto_ptr<cChestEntity> Chest(new cChestEntity(a_BlockX, a_BlockY, a_BlockZ, m_World, a_ChestBlockType));
LoadItemGridFromNBT(Chest->GetContents(), a_NBT, Items);
- a_BlockEntities.push_back(Chest.release());
+ return Chest.release();
}
-void cWSSAnvil::LoadDispenserFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+cBlockEntity * cWSSAnvil::LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Control"))
{
- return;
+ return NULL;
+ }
+
+ std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+
+ int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command");
+ if (currentLine >= 0)
+ {
+ CmdBlock->SetCommand(a_NBT.GetString(currentLine));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "SuccessCount");
+ if (currentLine >= 0)
+ {
+ CmdBlock->SetResult(a_NBT.GetInt(currentLine));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "LastOutput");
+ if (currentLine >= 0)
+ {
+ CmdBlock->SetLastOutput(a_NBT.GetString(currentLine));
+ }
+
+ // TODO 2014-01-18 xdot: Figure out what TrackOutput is and parse it.
+
+ return CmdBlock.release();
+}
+
+
+
+
+
+cBlockEntity * cWSSAnvil::LoadDispenserFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Trap"))
+ {
+ return NULL;
}
+
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
- return; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this
+ return NULL; // Make it an empty dispenser - the chunk loader will provide an empty cDispenserEntity for this
}
- std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(x, y, z, m_World));
+ std::auto_ptr<cDispenserEntity> Dispenser(new cDispenserEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Dispenser->GetContents(), a_NBT, Items);
- a_BlockEntities.push_back(Dispenser.release());
+ return Dispenser.release();
}
-void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+cBlockEntity * cWSSAnvil::LoadDropperFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Dropper"))
{
- return;
+ return NULL;
}
+
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
- return; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this
+ return NULL; // Make it an empty dropper - the chunk loader will provide an empty cDropperEntity for this
}
- std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(x, y, z, m_World));
+ std::auto_ptr<cDropperEntity> Dropper(new cDropperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Dropper->GetContents(), a_NBT, Items);
- a_BlockEntities.push_back(Dropper.release());
+ return Dropper.release();
}
-void cWSSAnvil::LoadFlowerPotFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+cBlockEntity * cWSSAnvil::LoadFlowerPotFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "FlowerPot"))
{
- return;
+ return NULL;
}
- std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(x, y, z, m_World));
+
+ std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
short ItemType = 0, ItemData = 0;
int currentLine = a_NBT.FindChildByName(a_TagIdx, "Item");
@@ -887,37 +971,28 @@ void cWSSAnvil::LoadFlowerPotFromNBT(cBlockEntityList & a_BlockEntities, const c
}
FlowerPot->SetItem(cItem(ItemType, 1, ItemData));
- a_BlockEntities.push_back(FlowerPot.release());
+ return FlowerPot.release();
}
-void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas)
+cBlockEntity * cWSSAnvil::LoadFurnaceFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Furnace"))
{
- return;
+ return NULL;
}
+
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
- return; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this
+ return NULL; // Make it an empty furnace - the chunk loader will provide an empty cFurnaceEntity for this
}
- // Convert coords to relative:
- int RelX = x;
- int RelZ = z;
- int ChunkX, ChunkZ;
- cChunkDef::AbsoluteToRelative(RelX, y, RelZ, ChunkX, ChunkZ);
-
- // Create the furnace entity, with proper BlockType and BlockMeta info:
- BLOCKTYPE BlockType = cChunkDef::GetBlock(a_BlockTypes, RelX, y, RelZ);
- NIBBLETYPE BlockMeta = cChunkDef::GetNibble(a_BlockMetas, RelX, y, RelZ);
- std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(x, y, z, BlockType, BlockMeta, m_World));
+ std::auto_ptr<cFurnaceEntity> Furnace(new cFurnaceEntity(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, m_World));
// Load slots:
for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child))
@@ -954,184 +1029,147 @@ void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cPa
// Restart cooking:
Furnace->ContinueCooking();
- a_BlockEntities.push_back(Furnace.release());
+ return Furnace.release();
}
-void cWSSAnvil::LoadHopperFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+cBlockEntity * cWSSAnvil::LoadHopperFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Hopper"))
{
- return;
+ return NULL;
}
+
int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List))
{
- return; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this
+ return NULL; // Make it an empty hopper - the chunk loader will provide an empty cHopperEntity for this
}
- std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(x, y, z, m_World));
+ std::auto_ptr<cHopperEntity> Hopper(new cHopperEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
LoadItemGridFromNBT(Hopper->GetContents(), a_NBT, Items);
- a_BlockEntities.push_back(Hopper.release());
+ return Hopper.release();
}
-void cWSSAnvil::LoadJukeboxFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+cBlockEntity * cWSSAnvil::LoadJukeboxFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "RecordPlayer"))
{
- return;
+ return NULL;
}
- std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(x, y, z, m_World));
+
+ std::auto_ptr<cJukeboxEntity> Jukebox(new cJukeboxEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
int Record = a_NBT.FindChildByName(a_TagIdx, "Record");
if (Record >= 0)
{
Jukebox->SetRecord(a_NBT.GetInt(Record));
}
- a_BlockEntities.push_back(Jukebox.release());
+ return Jukebox.release();
}
-void cWSSAnvil::LoadNoteFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+cBlockEntity * cWSSAnvil::LoadMobHeadFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Skull"))
{
- return;
- }
- std::auto_ptr<cNoteEntity> Note(new cNoteEntity(x, y, z, m_World));
- int note = a_NBT.FindChildByName(a_TagIdx, "note");
- if (note >= 0)
- {
- Note->SetPitch(a_NBT.GetByte(note));
- }
- a_BlockEntities.push_back(Note.release());
-}
-
-
-
-
-
-void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
-{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
- {
- return;
+ return NULL;
}
- std::auto_ptr<cSignEntity> Sign(new cSignEntity(E_BLOCK_SIGN_POST, x, y, z, m_World));
- int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1");
- if (currentLine >= 0)
- {
- Sign->SetLine(0, a_NBT.GetString(currentLine));
- }
+ std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
- currentLine = a_NBT.FindChildByName(a_TagIdx, "Text2");
+ int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType");
if (currentLine >= 0)
{
- Sign->SetLine(1, a_NBT.GetString(currentLine));
+ MobHead->SetType(static_cast<eMobHeadType>(a_NBT.GetByte(currentLine)));
}
- currentLine = a_NBT.FindChildByName(a_TagIdx, "Text3");
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Rot");
if (currentLine >= 0)
{
- Sign->SetLine(2, a_NBT.GetString(currentLine));
+ MobHead->SetRotation(static_cast<eMobHeadRotation>(a_NBT.GetByte(currentLine)));
}
- currentLine = a_NBT.FindChildByName(a_TagIdx, "Text4");
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "ExtraType");
if (currentLine >= 0)
{
- Sign->SetLine(3, a_NBT.GetString(currentLine));
+ MobHead->SetOwner(a_NBT.GetString(currentLine));
}
- a_BlockEntities.push_back(Sign.release());
+ return MobHead.release();
}
-void cWSSAnvil::LoadMobHeadFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+cBlockEntity * cWSSAnvil::LoadNoteBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Music"))
{
- return;
- }
- std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(x, y, z, m_World));
-
- int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType");
- if (currentLine >= 0)
- {
- MobHead->SetType(static_cast<eMobHeadType>(a_NBT.GetByte(currentLine)));
- }
-
- currentLine = a_NBT.FindChildByName(a_TagIdx, "Rot");
- if (currentLine >= 0)
- {
- MobHead->SetRotation(static_cast<eMobHeadRotation>(a_NBT.GetByte(currentLine)));
+ return NULL;
}
- currentLine = a_NBT.FindChildByName(a_TagIdx, "ExtraType");
- if (currentLine >= 0)
+ std::auto_ptr<cNoteEntity> NoteBlock(new cNoteEntity(a_BlockX, a_BlockY, a_BlockZ, m_World));
+ int note = a_NBT.FindChildByName(a_TagIdx, "note");
+ if (note >= 0)
{
- MobHead->SetOwner(a_NBT.GetString(currentLine));
+ NoteBlock->SetPitch(a_NBT.GetByte(note));
}
-
- a_BlockEntities.push_back(MobHead.release());
+ return NoteBlock.release();
}
-void cWSSAnvil::LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+cBlockEntity * cWSSAnvil::LoadSignFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
{
- ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
- int x, y, z;
- if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ // Check if the data has a proper type:
+ if (!CheckBlockEntityType(a_NBT, a_TagIdx, "Sign"))
{
- return;
+ return NULL;
}
- std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(x, y, z, m_World));
- int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command");
+ std::auto_ptr<cSignEntity> Sign(new cSignEntity(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, m_World));
+
+ int currentLine = a_NBT.FindChildByName(a_TagIdx, "Text1");
if (currentLine >= 0)
{
- CmdBlock->SetCommand(a_NBT.GetString(currentLine));
+ Sign->SetLine(0, a_NBT.GetString(currentLine));
}
- currentLine = a_NBT.FindChildByName(a_TagIdx, "SuccessCount");
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Text2");
if (currentLine >= 0)
{
- CmdBlock->SetResult(a_NBT.GetInt(currentLine));
+ Sign->SetLine(1, a_NBT.GetString(currentLine));
}
- currentLine = a_NBT.FindChildByName(a_TagIdx, "LastOutput");
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Text3");
if (currentLine >= 0)
{
- CmdBlock->SetLastOutput(a_NBT.GetString(currentLine));
+ Sign->SetLine(2, a_NBT.GetString(currentLine));
}
- // TODO 2014-01-18 xdot: Figure out what TrackOutput is and parse it.
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Text4");
+ if (currentLine >= 0)
+ {
+ Sign->SetLine(3, a_NBT.GetString(currentLine));
+ }
- a_BlockEntities.push_back(CmdBlock.release());
+ return Sign.release();
}
@@ -2777,30 +2815,42 @@ bool cWSSAnvil::cMCAFile::GetChunkData(const cChunkCoords & a_Chunk, AString & a
}
unsigned ChunkLocation = ntohl(m_Header[LocalX + 32 * LocalZ]);
unsigned ChunkOffset = ChunkLocation >> 8;
+ if (ChunkOffset <= 2)
+ {
+ return false;
+ }
m_File.Seek((int)ChunkOffset * 4096);
int ChunkSize = 0;
if (m_File.Read(&ChunkSize, 4) != 4)
{
+ LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false;
}
ChunkSize = ntohl((u_long)ChunkSize);
char CompressionType = 0;
if (m_File.Read(&CompressionType, 1) != 1)
{
+ LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false;
}
if (CompressionType != 2)
{
// Chunk is in an unknown compression
+ LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false;
}
ChunkSize--;
// HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly
a_Data.assign(ChunkSize, '\0');
- return (m_File.Read((void *)a_Data.data(), ChunkSize) == ChunkSize);
+ if (m_File.Read((void *)a_Data.data(), ChunkSize) == ChunkSize)
+ {
+ return true;
+ }
+ LOAD_FAILED(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
+ return false;
}
@@ -2855,7 +2905,13 @@ bool cWSSAnvil::cMCAFile::SetChunkData(const cChunkCoords & a_Chunk, const AStri
// Store the header:
ChunkSize = ((u_long)a_Data.size() + MCA_CHUNK_HEADER_LENGTH + 4095) / 4096; // Round data size *up* to nearest 4KB sector, make it a sector number
- ASSERT(ChunkSize < 256);
+ if (ChunkSize > 255)
+ {
+ LOGWARNING("Cannot save chunk [%d, %d], the data is too large (%u KiB, maximum is 1024 KiB). Remove some entities and retry.",
+ a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, (unsigned)(ChunkSize * 4)
+ );
+ return false;
+ }
m_Header[LocalX + 32 * LocalZ] = htonl((ChunkSector << 8) | ChunkSize);
if (m_File.Seek(0) < 0)
{
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
index 8ddf72115..a1f9b293b 100644
--- a/src/WorldStorage/WSSAnvil.h
+++ b/src/WorldStorage/WSSAnvil.h
@@ -125,6 +125,10 @@ protected:
/// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1)
void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
+ /** Loads the data for a block entity from the specified NBT tag.
+ Returns the loaded block entity, or NULL upon failure. */
+ cBlockEntity * LoadBlockEntityFromNBT(const cParsedNBT & a_NBT, int a_Tag, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+
/// Loads a cItem contents from the specified NBT tag; returns true if successful. Doesn't load the Slot tag
bool LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx);
@@ -134,18 +138,21 @@ protected:
*/
void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0);
- void LoadBeaconFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE a_ChestType);
- void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadFlowerPotFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
- void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadMobHeadFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ /** Returns true iff the "id" child tag inside the specified tag equals the specified expected type. */
+ bool CheckBlockEntityType(const cParsedNBT & a_NBT, int a_TagIdx, const char * a_ExpectedType);
+
+ cBlockEntity * LoadBeaconFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
+ cBlockEntity * LoadChestFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_ChestBlockType);
+ cBlockEntity * LoadCommandBlockFromNBT(const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
+ cBlockEntity * LoadDispenserFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
+ cBlockEntity * LoadDropperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
+ cBlockEntity * LoadFlowerPotFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
+ cBlockEntity * LoadFurnaceFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
+ cBlockEntity * LoadHopperFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
+ cBlockEntity * LoadJukeboxFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
+ cBlockEntity * LoadMobHeadFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
+ cBlockEntity * LoadNoteBlockFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ);
+ cBlockEntity * LoadSignFromNBT (const cParsedNBT & a_NBT, int a_TagIdx, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SignBlockType);
void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, size_t a_IDTagLength);
diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp
index 58f9e3cab..6760186b2 100644
--- a/src/WorldStorage/WSSCompact.cpp
+++ b/src/WorldStorage/WSSCompact.cpp
@@ -980,7 +980,7 @@ bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld
if (!a_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer))
{
// Chunk not valid
- LOG("cWSSCompact: Trying to save chunk [%d, %d, %d] that has no data, ignoring request.", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ);
+ LOG("cWSSCompact: Trying to save chunk [%d, %d] that has no data, ignoring request.", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false;
}
@@ -999,7 +999,7 @@ bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld
int errorcode = CompressString(Data.data(), Data.size(), CompressedData, m_CompressionFactor);
if (errorcode != Z_OK)
{
- LOGERROR("Error %i compressing data for chunk [%d, %d, %d]", errorcode, a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ);
+ LOGERROR("Error %i compressing data for chunk [%d, %d]", errorcode, a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false;
}
@@ -1010,7 +1010,7 @@ bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld
sChunkHeader * Header = new sChunkHeader;
if (Header == NULL)
{
- LOGWARNING("Cannot create a new chunk header to save chunk [%d, %d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ);
+ LOGWARNING("Cannot create a new chunk header to save chunk [%d, %d]", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
return false;
}
Header->m_CompressedSize = (int)CompressedData.size();
diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp
index 6e0d36eed..c35bbbad0 100644
--- a/src/WorldStorage/WorldStorage.cpp
+++ b/src/WorldStorage/WorldStorage.cpp
@@ -141,9 +141,9 @@ size_t cWorldStorage::GetSaveQueueLength(void)
-void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate)
+void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ, bool a_Generate)
{
- m_LoadQueue.EnqueueItem(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, a_Generate));
+ m_LoadQueue.EnqueueItem(sChunkLoad(a_ChunkX, a_ChunkZ, a_Generate));
m_Event.Set();
}
@@ -151,9 +151,9 @@ void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, boo
-void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ)
{
- m_SaveQueue.EnqueueItemIfNotPresent(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
+ m_SaveQueue.EnqueueItemIfNotPresent(cChunkCoords(a_ChunkX, a_ChunkZ));
m_Event.Set();
}
@@ -161,9 +161,9 @@ void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
-void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkZ)
{
- m_LoadQueue.Remove(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, true));
+ m_LoadQueue.Remove(sChunkLoad(a_ChunkX, a_ChunkZ, true));
}
@@ -242,19 +242,19 @@ void cWorldStorage::Execute(void)
bool cWorldStorage::LoadOneChunk(void)
{
- sChunkLoad ToLoad(0, 0, 0, false);
+ sChunkLoad ToLoad(0, 0, false);
bool ShouldLoad = m_LoadQueue.TryDequeueItem(ToLoad);
- if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ))
+ if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkZ))
{
if (ToLoad.m_Generate)
{
// The chunk couldn't be loaded, generate it:
- m_World->GetGenerator().QueueGenerateChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ);
+ m_World->GetGenerator().QueueGenerateChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkZ, false);
}
else
{
// TODO: Notify the world that the load has failed:
- // m_World->ChunkLoadFailed(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ);
+ // m_World->ChunkLoadFailed(ToLoad.m_ChunkX, ToLoad.m_ChunkZ);
}
}
return ShouldLoad;
@@ -266,7 +266,7 @@ bool cWorldStorage::LoadOneChunk(void)
bool cWorldStorage::SaveOneChunk(void)
{
- cChunkCoords ToSave(0, 0, 0);
+ cChunkCoords ToSave(0, 0);
bool ShouldSave = m_SaveQueue.TryDequeueItem(ToSave);
if (ShouldSave && m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ))
{
@@ -283,7 +283,7 @@ bool cWorldStorage::SaveOneChunk(void)
-bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
+bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkZ)
{
if (m_World->IsChunkValid(a_ChunkX, a_ChunkZ))
{
@@ -291,7 +291,7 @@ bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
return true;
}
- cChunkCoords Coords(a_ChunkX, a_ChunkY, a_ChunkZ);
+ cChunkCoords Coords(a_ChunkX, a_ChunkZ);
// First try the schema that is used for saving
if (m_SaveSchema->LoadChunk(Coords))
@@ -309,7 +309,7 @@ bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
}
// Notify the chunk owner that the chunk failed to load (sets cChunk::m_HasLoadFailed to true):
- m_World->ChunkLoadFailed(a_ChunkX, a_ChunkY, a_ChunkZ);
+ m_World->ChunkLoadFailed(a_ChunkX, a_ChunkZ);
return false;
}
diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h
index 148dc97d0..21f5ca25d 100644
--- a/src/WorldStorage/WorldStorage.h
+++ b/src/WorldStorage/WorldStorage.h
@@ -64,13 +64,13 @@ public:
cWorldStorage(void);
~cWorldStorage();
- void QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate); // Queues the chunk for loading; if not loaded, the chunk will be generated if a_Generate is true
- void QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ void QueueLoadChunk(int a_ChunkX, int a_ChunkZ, bool a_Generate); // Queues the chunk for loading; if not loaded, the chunk will be generated if a_Generate is true
+ void QueueSaveChunk(int a_ChunkX, int a_ChunkZ);
/// Loads the chunk specified; returns true on success, false on failure
- bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ bool LoadChunk(int a_ChunkX, int a_ChunkZ);
- void UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
+ void UnqueueLoad(int a_ChunkX, int a_ChunkZ);
void UnqueueSave(const cChunkCoords & a_Chunk);
bool Start(cWorld * a_World, const AString & a_StorageSchemaName, int a_StorageCompressionFactor); // Hide the cIsThread's Start() method, we need to provide args
@@ -87,17 +87,15 @@ protected:
struct sChunkLoad
{
int m_ChunkX;
- int m_ChunkY;
int m_ChunkZ;
bool m_Generate; // If true, the chunk will be generated if it cannot be loaded
- sChunkLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ), m_Generate(a_Generate) {}
+ sChunkLoad(int a_ChunkX, int a_ChunkZ, bool a_Generate) : m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ), m_Generate(a_Generate) {}
bool operator ==(const sChunkLoad other) const
{
return (
(this->m_ChunkX == other.m_ChunkX) &&
- (this->m_ChunkY == other.m_ChunkY) &&
(this->m_ChunkZ == other.m_ChunkZ)
);
}