summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/BlockInfo.cpp9
-rw-r--r--src/BlockInfo.h5
-rw-r--r--src/ByteBuffer.cpp33
-rw-r--r--src/ByteBuffer.h1
-rw-r--r--src/Chunk.cpp11
-rw-r--r--src/Chunk.h1
-rw-r--r--src/ChunkMap.cpp9
-rw-r--r--src/ChunkMap.h3
-rw-r--r--src/ClientHandle.cpp56
-rw-r--r--src/ClientHandle.h2
-rw-r--r--src/Entities/Entity.h2
-rw-r--r--src/Entities/ExpOrb.cpp2
-rw-r--r--src/Entities/Minecart.cpp10
-rw-r--r--src/Entities/Pawn.cpp6
-rw-r--r--src/Entities/Pickup.cpp6
-rw-r--r--src/Entities/Player.cpp69
-rw-r--r--src/Entities/Player.h4
-rw-r--r--src/Entities/ProjectileEntity.cpp10
-rw-r--r--src/Mobs/AggressiveMonster.cpp11
-rw-r--r--src/Mobs/AggressiveMonster.h3
-rw-r--r--src/Mobs/Enderman.cpp17
-rw-r--r--src/Mobs/Monster.cpp15
-rw-r--r--src/Mobs/Monster.h2
-rw-r--r--src/Mobs/PassiveAggressiveMonster.cpp5
-rw-r--r--src/Mobs/PassiveAggressiveMonster.h2
-rw-r--r--src/Mobs/Spider.cpp6
-rw-r--r--src/Mobs/Spider.h2
-rw-r--r--src/Protocol/Protocol.h1
-rw-r--r--src/Protocol/Protocol18x.cpp26
-rw-r--r--src/Protocol/Protocol18x.h2
-rw-r--r--src/Protocol/Protocol19x.cpp27
-rw-r--r--src/Protocol/Protocol19x.h2
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp9
-rw-r--r--src/Protocol/ProtocolRecognizer.h1
-rw-r--r--src/UI/SlotArea.cpp8
-rw-r--r--src/World.cpp22
-rw-r--r--src/World.h4
37 files changed, 368 insertions, 36 deletions
diff --git a/src/BlockInfo.cpp b/src/BlockInfo.cpp
index 2afb5d6f2..34ccc2378 100644
--- a/src/BlockInfo.cpp
+++ b/src/BlockInfo.cpp
@@ -492,6 +492,15 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_WOODEN_PRESSURE_PLATE].m_IsSolid = false;
+ // Blocks, which a spectator is allowed to interact with
+ a_Info[E_BLOCK_BEACON ].m_UseableBySpectator = true;
+ a_Info[E_BLOCK_BREWING_STAND ].m_UseableBySpectator = true;
+ a_Info[E_BLOCK_CHEST ].m_UseableBySpectator = true;
+ a_Info[E_BLOCK_DISPENSER ].m_UseableBySpectator = true;
+ a_Info[E_BLOCK_DROPPER ].m_UseableBySpectator = true;
+ a_Info[E_BLOCK_HOPPER ].m_UseableBySpectator = true;
+
+
// Blocks that fully occupy their voxel - used as a guide for torch placeable blocks, amongst other things:
a_Info[E_BLOCK_BARRIER ].m_FullyOccupiesVoxel = true;
a_Info[E_BLOCK_BEDROCK ].m_FullyOccupiesVoxel = true;
diff --git a/src/BlockInfo.h b/src/BlockInfo.h
index c1eb926d1..d42987794 100644
--- a/src/BlockInfo.h
+++ b/src/BlockInfo.h
@@ -55,6 +55,9 @@ public:
/** Is this block solid (player cannot walk through)? */
bool m_IsSolid;
+ /** Can a spectator interact with this block */
+ bool m_UseableBySpectator;
+
/** Does this block fully occupy its voxel - is it a 'full' block? */
bool m_FullyOccupiesVoxel;
@@ -81,6 +84,7 @@ public:
inline static bool IsPistonBreakable (BLOCKTYPE a_Type) { return Get(a_Type).m_PistonBreakable; }
inline static bool IsSnowable (BLOCKTYPE a_Type) { return Get(a_Type).m_IsSnowable; }
inline static bool IsSolid (BLOCKTYPE a_Type) { return Get(a_Type).m_IsSolid; }
+ inline static bool IsUseableBySpectator (BLOCKTYPE a_Type) { return Get(a_Type).m_UseableBySpectator; }
inline static bool FullyOccupiesVoxel (BLOCKTYPE a_Type) { return Get(a_Type).m_FullyOccupiesVoxel; }
inline static bool CanBeTerraformed (BLOCKTYPE a_Type) { return Get(a_Type).m_CanBeTerraformed; }
inline static float GetBlockHeight (BLOCKTYPE a_Type) { return Get(a_Type).m_BlockHeight; }
@@ -103,6 +107,7 @@ protected:
, m_PistonBreakable(false)
, m_IsSnowable(false)
, m_IsSolid(true)
+ , m_UseableBySpectator(false)
, m_FullyOccupiesVoxel(false)
, m_CanBeTerraformed(false)
, m_BlockHeight(1.0)
diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp
index b5f862a73..6cfb66f9a 100644
--- a/src/ByteBuffer.cpp
+++ b/src/ByteBuffer.cpp
@@ -53,6 +53,16 @@ Unfortunately it is very slow, so it is disabled even for regular DEBUG builds.
+static char ValueToHexDigit(UInt8 digit)
+{
+ ASSERT(digit < 16);
+ return "0123456789abcdef"[digit];
+}
+
+
+
+
+
#ifdef DEBUG_SINGLE_THREAD_ACCESS
/** Simple RAII class that is used for checking that no two threads are using an object simultanously.
@@ -517,6 +527,29 @@ bool cByteBuffer::ReadPosition64(int & a_BlockX, int & a_BlockY, int & a_BlockZ)
+bool cByteBuffer::ReadUUID(AString & a_Value)
+{
+ CHECK_THREAD
+
+ if (!ReadString(a_Value, 16))
+ {
+ return false;
+ }
+
+ a_Value.resize(32);
+ for (unsigned int i = 15; i < 16; i--)
+ {
+ a_Value[i * 2 + 1] = ValueToHexDigit(a_Value[i] & 0xf);
+ a_Value[i * 2] = ValueToHexDigit(static_cast<UInt8>(a_Value[i]) >> 4);
+ }
+
+ return true;
+}
+
+
+
+
+
bool cByteBuffer::WriteBEInt8(Int8 a_Value)
{
CHECK_THREAD
diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h
index 128a907b2..761112721 100644
--- a/src/ByteBuffer.h
+++ b/src/ByteBuffer.h
@@ -68,6 +68,7 @@ public:
bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8
bool ReadLEInt (int & a_Value);
bool ReadPosition64 (int & a_BlockX, int & a_BlockY, int & a_BlockZ);
+ bool ReadUUID (AString & a_Value); // UUID without dashes
/** Reads VarInt, assigns it to anything that can be assigned from an UInt64 (unsigned short, char, Byte, double, ...) */
template <typename T> bool ReadVarInt(T & a_Value)
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index 508fe355e..28a7c7272 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -2052,12 +2052,21 @@ bool cChunk::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_
bool cChunk::DoWithEntityByID(UInt32 a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult)
{
+ return DoWithEntityByID(a_EntityID, std::bind(&cEntityCallback::Item, &a_Callback, std::placeholders::_1), a_CallbackResult);
+}
+
+
+
+
+
+bool cChunk::DoWithEntityByID(UInt32 a_EntityID, cLambdaEntityCallback a_Callback, bool & a_CallbackResult)
+{
// The entity list is locked by the parent chunkmap's CS
for (cEntityList::iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr)
{
if (((*itr)->GetUniqueID() == a_EntityID) && ((*itr)->IsTicking()))
{
- a_CallbackResult = a_Callback.Item(*itr);
+ a_CallbackResult = a_Callback(*itr);
return true;
}
} // for itr - m_Entitites[]
diff --git a/src/Chunk.h b/src/Chunk.h
index 398d33a5f..aae3b98f0 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -274,6 +274,7 @@ public:
/** 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(UInt32 a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult); // Lua-accessible
+ bool DoWithEntityByID(UInt32 a_EntityID, cLambdaEntityCallback a_Callback, bool & a_CallbackResult); // Lambda version
/** Calls the callback for each block entity; returns true if all block entities processed, false if the callback aborted by returning true */
bool ForEachBlockEntity(cBlockEntityCallback & a_Callback); // Lua-accessible
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index c8e485cdd..e608f6b30 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -1874,6 +1874,15 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
bool cChunkMap::DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback & a_Callback)
{
+ return DoWithEntityByID(a_UniqueID, std::bind(&cEntityCallback::Item, &a_Callback, std::placeholders::_1));
+}
+
+
+
+
+
+bool cChunkMap::DoWithEntityByID(UInt32 a_UniqueID, cLambdaEntityCallback a_Callback)
+{
cCSLock Lock(m_CSChunks);
bool res = false;
for (const auto & Chunk : m_Chunks)
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index ff8f82f91..871881483 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -56,6 +56,8 @@ typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
typedef cItemCallback<cMobHeadEntity> cMobHeadCallback;
typedef cItemCallback<cChunk> cChunkCallback;
+typedef std::function<bool (cEntity *)> cLambdaEntityCallback;
+
@@ -237,6 +239,7 @@ public:
/** 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(UInt32 a_EntityID, cEntityCallback & a_Callback); // Lua-accessible
+ bool DoWithEntityByID(UInt32 a_EntityID, cLambdaEntityCallback a_Callback); // Lambda version
/** Calls the callback for each block entity in the specified chunk.
Returns true if all block entities processed, false if the callback aborted by returning true. */
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 8df60ad40..469095b19 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -7,6 +7,7 @@
#include "Entities/Pickup.h"
#include "Bindings/PluginManager.h"
#include "Entities/Player.h"
+#include "Entities/Minecart.h"
#include "Inventory.h"
#include "EffectID.h"
#include "BlockEntities/BeaconEntity.h"
@@ -1412,7 +1413,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType);
- if (BlockHandler->IsUseable() && !m_Player->IsCrouched())
+ if (BlockHandler->IsUseable() && !m_Player->IsCrouched() && (!m_Player->IsGameModeSpectator() || cBlockInfo::IsUseableBySpectator(BlockType)))
{
if (!PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
{
@@ -1427,6 +1428,12 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
}
}
+ // Players, who spectate cannot use their items
+ if (m_Player->IsGameModeSpectator())
+ {
+ return;
+ }
+
short EquippedDamage = Equipped.m_ItemDamage;
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType);
@@ -1557,6 +1564,19 @@ void cClientHandle::HandleSlotSelected(Int16 a_SlotNum)
+void cClientHandle::HandleSpectate(const AString & a_PlayerUUID)
+{
+ m_Player->GetWorld()->DoWithPlayerByUUID(a_PlayerUUID, [=](cPlayer * a_ToSpectate)
+ {
+ m_Player->TeleportToEntity(*a_ToSpectate);
+ return true;
+ });
+}
+
+
+
+
+
void cClientHandle::HandleSteerVehicle(float a_Forward, float a_Sideways)
{
m_Player->SteerVehicle(a_Forward, a_Sideways);
@@ -1617,6 +1637,17 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
{
// TODO: Let plugins interfere via a hook
+ // If the player is a spectator, let him spectate
+ if (m_Player->IsGameModeSpectator() && a_IsLeftClick)
+ {
+ m_Player->GetWorld()->DoWithEntityByID(a_TargetEntityID, [=](cEntity * a_Entity)
+ {
+ m_Player->AttachTo(a_Entity);
+ return true;
+ });
+ return;
+ }
+
// If it is a right click, call the entity's OnRightClicked() handler:
if (!a_IsLeftClick)
{
@@ -1625,7 +1656,19 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
cPlayer & m_Player;
virtual bool Item(cEntity * a_Entity) override
{
- if (cPluginManager::Get()->CallHookPlayerRightClickingEntity(m_Player, *a_Entity))
+ if (
+ cPluginManager::Get()->CallHookPlayerRightClickingEntity(m_Player, *a_Entity) ||
+ (
+ m_Player.IsGameModeSpectator() && // Spectators cannot interact with every entity
+ (
+ !a_Entity->IsMinecart() || // They can only interact with minecarts
+ (
+ (reinterpret_cast<cMinecart *>(a_Entity)->GetPayload() != cMinecart::mpChest) && // And only if the type matches a minecart with a chest or
+ (reinterpret_cast<cMinecart *>(a_Entity)->GetPayload() != cMinecart::mpHopper) // a minecart with a hopper
+ )
+ )
+ )
+ )
{
return false;
}
@@ -2178,6 +2221,15 @@ void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlock
+void cClientHandle::SendCameraSetTo(const cEntity & a_Entity)
+{
+ m_Protocol->SendCameraSetTo(a_Entity);
+}
+
+
+
+
+
void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
{
cWorld * World = GetPlayer()->GetWorld();
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 7d829653b..4a4c9553d 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -150,6 +150,7 @@ public: // tolua_export
void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage);
void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export
void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes);
+ void SendCameraSetTo (const cEntity & a_Entity);
void SendChat (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = "");
void SendChat (const cCompositeChat & a_Message);
void SendChatAboveActionBar (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = "");
@@ -336,6 +337,7 @@ public: // tolua_export
void HandleRespawn (void);
void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem);
void HandleSlotSelected (Int16 a_SlotNum);
+ void HandleSpectate (const AString & a_PlayerUUID);
void HandleSteerVehicle (float Forward, float Sideways);
void HandleTabCompletion (const AString & a_Text);
void HandleUpdateSign (
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index c543fd9c1..4833f8b5c 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -429,7 +429,7 @@ public:
cEntity * GetAttached();
/** Attaches to the specified entity; detaches from any previous one first */
- void AttachTo(cEntity * a_AttachTo);
+ virtual void AttachTo(cEntity * a_AttachTo);
/** Detaches from the currently attached entity, if any */
virtual void Detach(void);
diff --git a/src/Entities/ExpOrb.cpp b/src/Entities/ExpOrb.cpp
index f51bbb300..290f9e665 100644
--- a/src/Entities/ExpOrb.cpp
+++ b/src/Entities/ExpOrb.cpp
@@ -45,7 +45,7 @@ void cExpOrb::SpawnOn(cClientHandle & a_Client)
void cExpOrb::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{
cPlayer * a_ClosestPlayer(m_World->FindClosestPlayer(Vector3f(GetPosition()), 5));
- if (a_ClosestPlayer != nullptr)
+ if ((a_ClosestPlayer != nullptr) && (!a_ClosestPlayer->IsGameModeSpectator()))
{
Vector3f a_PlayerPos(a_ClosestPlayer->GetPosition());
a_PlayerPos.y++;
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
index 43291bdc8..88b5959d5 100644
--- a/src/Entities/Minecart.cpp
+++ b/src/Entities/Minecart.cpp
@@ -39,7 +39,15 @@ public:
{
ASSERT(a_Entity != nullptr);
- if (!a_Entity->IsPlayer() && !a_Entity->IsMob() && !a_Entity->IsMinecart() && !a_Entity->IsBoat())
+ if (
+ (
+ !a_Entity->IsPlayer() ||
+ reinterpret_cast<cPlayer *>(a_Entity)->IsGameModeSpectator() // Spectators doesn't collide with anything
+ ) &&
+ !a_Entity->IsMob() &&
+ !a_Entity->IsMinecart() &&
+ !a_Entity->IsBoat()
+ )
{
return false;
}
diff --git a/src/Entities/Pawn.cpp b/src/Entities/Pawn.cpp
index dbcaba591..4b42dbb57 100644
--- a/src/Entities/Pawn.cpp
+++ b/src/Entities/Pawn.cpp
@@ -108,7 +108,11 @@ void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
}
} Callback(this);
- m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), GetWidth(), GetHeight()), Callback);
+ // Spectators cannot push entities around
+ if ((!IsPlayer()) || (!reinterpret_cast<cPlayer *>(this)->IsGameModeSpectator()))
+ {
+ m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), GetWidth(), GetHeight()), Callback);
+ }
super::Tick(a_Dt, a_Chunk);
if (!IsTicking())
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index 12d535f84..b1892e4cc 100644
--- a/src/Entities/Pickup.cpp
+++ b/src/Entities/Pickup.cpp
@@ -208,6 +208,12 @@ bool cPickup::CollectedBy(cPlayer & a_Dest)
return false; // Not old enough
}
+ // If the player is a spectator, he cannot collect anything
+ if (a_Dest.IsGameModeSpectator())
+ {
+ return false;
+ }
+
if (cRoot::Get()->GetPluginManager()->CallHookCollectingPickup(a_Dest, *this))
{
// LOG("Pickup %d cannot be collected by \"%s\", because a plugin has said no.", m_UniqueID, a_Dest->GetName().c_str());
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 5ab5e4567..db4b07553 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -250,6 +250,20 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_Stats.AddValue(statMinutesPlayed, 1);
+ // Handle the player detach, when the player is in spectator mode
+ if (
+ (IsGameModeSpectator()) &&
+ (m_AttachedTo != nullptr) &&
+ (
+ (m_AttachedTo->IsDestroyed()) || // Watching entity destruction
+ (m_AttachedTo->GetHealth() <= 0) || // Watching entity dead
+ (IsCrouched()) // Or the player wants to be detached
+ )
+ )
+ {
+ Detach();
+ }
+
// Handle a frozen player
TickFreezeCode();
if (m_IsFrozen)
@@ -1233,6 +1247,16 @@ bool cPlayer::IsGameModeSpectator(void) const
+
+bool cPlayer::CanMobsTarget(void) const
+{
+ return IsGameModeSurvival() || IsGameModeAdventure();
+}
+
+
+
+
+
void cPlayer::SetTeam(cTeam * a_Team)
{
if (m_Team == a_Team)
@@ -1344,6 +1368,12 @@ void cPlayer::SetGameMode(eGameMode a_GameMode)
return;
}
+ // Detach, if the player is switching from or to the spectator mode
+ if ((m_GameMode == gmSpectator) || (a_GameMode == gmSpectator))
+ {
+ Detach();
+ }
+
m_GameMode = a_GameMode;
m_ClientHandle->SendGameMode(a_GameMode);
@@ -1379,6 +1409,13 @@ void cPlayer::SetCapabilities()
{
SetVisible(false);
SetCanFly(true);
+
+ // Clear the current dragging item of the player
+ if (GetWindow() != nullptr)
+ {
+ m_DraggingItem.Empty();
+ GetClientHandle()->SendInventorySlot(-1, -1, m_DraggingItem);
+ }
}
else
{
@@ -2476,8 +2513,40 @@ bool cPlayer::PlaceBlocks(const sSetBlockVector & a_Blocks)
+void cPlayer::AttachTo(cEntity * a_AttachTo)
+{
+ // Different attach, if this is a spectator
+ if (IsGameModeSpectator())
+ {
+ m_AttachedTo = a_AttachTo;
+ GetClientHandle()->SendCameraSetTo(*m_AttachedTo);
+ return;
+ }
+
+ super::AttachTo(a_AttachTo);
+}
+
+
+
+
+
void cPlayer::Detach()
{
+ if (m_AttachedTo == nullptr)
+ {
+ // The player is not attached to anything. Bail out.
+ return;
+ }
+
+ // Different detach, if this is a spectator
+ if (IsGameModeSpectator())
+ {
+ GetClientHandle()->SendCameraSetTo(*this);
+ TeleportToEntity(*m_AttachedTo);
+ m_AttachedTo = nullptr;
+ return;
+ }
+
super::Detach();
int PosX = POSX_TOINT;
int PosY = POSY_TOINT;
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 25796ee50..04cb5232b 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -193,6 +193,9 @@ public:
/** Returns true if the player is in Spectator mode, either explicitly, or by inheriting from current world */
bool IsGameModeSpectator(void) const;
+ /** Returns true if the player can be targeted by Mobs */
+ bool CanMobsTarget(void) const;
+
AString GetIP(void) const { return m_IP; } // tolua_export
/** Returns the associated team, nullptr if none */
@@ -518,6 +521,7 @@ public:
virtual bool IsSprinting(void) const override { return m_IsSprinting; }
virtual bool IsRclking (void) const override { return IsEating() || IsChargingBow(); }
+ virtual void AttachTo(cEntity * a_AttachTo) override;
virtual void Detach(void) override;
/** Called by cClientHandle when the client is being destroyed.
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index fb7da85a1..3bded2b56 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -167,7 +167,15 @@ public:
return false;
}
- if (!a_Entity->IsMob() && !a_Entity->IsMinecart() && !a_Entity->IsPlayer() && !a_Entity->IsBoat())
+ if (
+ !a_Entity->IsMob() &&
+ !a_Entity->IsMinecart() &&
+ (
+ !a_Entity->IsPlayer() ||
+ static_cast<cPlayer *>(a_Entity)->IsGameModeSpectator()
+ ) &&
+ !a_Entity->IsBoat()
+ )
{
// Not an entity that interacts with a projectile
return false;
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
index 109ad274c..d8bdc4af5 100644
--- a/src/Mobs/AggressiveMonster.cpp
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -36,13 +36,16 @@ void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt, cChunk &
-void cAggressiveMonster::EventSeePlayer(cEntity * a_Entity, cChunk & a_Chunk)
+
+void cAggressiveMonster::EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk)
{
- if (!static_cast<cPlayer *>(a_Entity)->IsGameModeCreative())
+ if (!a_Player->CanMobsTarget())
{
- super::EventSeePlayer(a_Entity, a_Chunk);
- m_EMState = CHASING;
+ return;
}
+
+ super::EventSeePlayer(a_Player, a_Chunk);
+ m_EMState = CHASING;
}
diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h
index f2d6366e2..9ab8df06f 100644
--- a/src/Mobs/AggressiveMonster.h
+++ b/src/Mobs/AggressiveMonster.h
@@ -19,7 +19,8 @@ public:
virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
virtual void InStateChasing(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override;
- virtual void EventSeePlayer(cEntity * a_Player, cChunk & a_Chunk) override;
+
+ virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk) override;
/** Try to perform attack
returns true if attack was deemed successful (hit player, fired projectile, creeper exploded, etc.) even if it didn't actually do damage
diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp
index 2ff547c3c..4e2e67f8a 100644
--- a/src/Mobs/Enderman.cpp
+++ b/src/Mobs/Enderman.cpp
@@ -23,8 +23,8 @@ public:
virtual bool Item(cPlayer * a_Player) override
{
- // Don't check players who are in creative gamemode
- if (a_Player->IsGameModeCreative())
+ // Don't check players who cannot be targeted
+ if (!a_Player->CanMobsTarget())
{
return false;
}
@@ -124,13 +124,16 @@ void cEnderman::CheckEventSeePlayer(cChunk & a_Chunk)
return;
}
- if (!Callback.GetPlayer()->IsGameModeCreative())
+ if (!Callback.GetPlayer()->CanMobsTarget())
{
- cMonster::EventSeePlayer(Callback.GetPlayer(), a_Chunk);
- m_EMState = CHASING;
- m_bIsScreaming = true;
- GetWorld()->BroadcastEntityMetadata(*this);
+ return;
}
+
+ // Target the player
+ cMonster::EventSeePlayer(Callback.GetPlayer(), a_Chunk);
+ m_EMState = CHASING;
+ m_bIsScreaming = true;
+ GetWorld()->BroadcastEntityMetadata(*this);
}
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index acd8f0145..ece59828e 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -265,7 +265,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (GetTarget()->IsPlayer())
{
- if (static_cast<cPlayer *>(GetTarget())->IsGameModeCreative())
+ if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget())
{
SetTarget(nullptr);
m_EMState = IDLE;
@@ -471,7 +471,13 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn())
{
- SetTarget(static_cast<cPawn*>(a_TDI.Attacker));
+ if (
+ (!a_TDI.Attacker->IsPlayer()) ||
+ (static_cast<cPlayer *>(a_TDI.Attacker)->CanMobsTarget())
+ )
+ {
+ SetTarget(static_cast<cPawn*>(a_TDI.Attacker));
+ }
m_TicksSinceLastDamaged = 0;
}
return true;
@@ -617,11 +623,10 @@ void cMonster::CheckEventLostPlayer(void)
// What to do if player is seen
// default to change state to chasing
-void cMonster::EventSeePlayer(cEntity * a_SeenPlayer, cChunk & a_Chunk)
+void cMonster::EventSeePlayer(cPlayer * a_SeenPlayer, cChunk & a_Chunk)
{
UNUSED(a_Chunk);
- ASSERT(a_SeenPlayer->IsPlayer());
- SetTarget(static_cast<cPawn*>(a_SeenPlayer));
+ SetTarget(a_SeenPlayer);
}
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index 03382e28e..1c3d9c37a 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -72,7 +72,7 @@ public:
// tolua_end
virtual void CheckEventSeePlayer(cChunk & a_Chunk);
- virtual void EventSeePlayer(cEntity * a_Entity, cChunk & a_Chunk);
+ virtual void EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk);
/** Reads the monster configuration for the specified monster name and assigns it to this object. */
void GetMonsterConfig(const AString & a_Name);
diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp
index a1bb1138f..8715ba9c2 100644
--- a/src/Mobs/PassiveAggressiveMonster.cpp
+++ b/src/Mobs/PassiveAggressiveMonster.cpp
@@ -28,7 +28,7 @@ bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer()))
{
- if (!static_cast<cPlayer *>(GetTarget())->IsGameModeCreative())
+ if (static_cast<cPlayer *>(GetTarget())->CanMobsTarget())
{
m_EMState = CHASING;
}
@@ -39,7 +39,8 @@ bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
-void cPassiveAggressiveMonster::EventSeePlayer(cEntity *, cChunk & a_Chunk)
+
+void cPassiveAggressiveMonster::EventSeePlayer(cPlayer *, cChunk & a_Chunk)
{
// don't do anything, neutral mobs don't react to just seeing the player
}
diff --git a/src/Mobs/PassiveAggressiveMonster.h b/src/Mobs/PassiveAggressiveMonster.h
index 00db75385..764e27779 100644
--- a/src/Mobs/PassiveAggressiveMonster.h
+++ b/src/Mobs/PassiveAggressiveMonster.h
@@ -16,7 +16,7 @@ public:
cPassiveAggressiveMonster(const AString & a_ConfigName, eMonsterType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
- virtual void EventSeePlayer(cEntity *, cChunk & a_Chunk) override;
+ virtual void EventSeePlayer(cPlayer *, cChunk & a_Chunk) override;
} ;
diff --git a/src/Mobs/Spider.cpp b/src/Mobs/Spider.cpp
index a5f0d6a89..5ee3e3294 100644
--- a/src/Mobs/Spider.cpp
+++ b/src/Mobs/Spider.cpp
@@ -35,7 +35,7 @@ void cSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-void cSpider::EventSeePlayer(cEntity * a_Entity, cChunk & a_Chunk)
+void cSpider::EventSeePlayer(cPlayer * a_Player, cChunk & a_Chunk)
{
if (!GetWorld()->IsChunkLighted(GetChunkX(), GetChunkZ()))
{
@@ -48,9 +48,9 @@ void cSpider::EventSeePlayer(cEntity * a_Entity, cChunk & a_Chunk)
return;
}
- if (!static_cast<cPlayer *>(a_Entity)->IsGameModeCreative() && (Chunk->GetSkyLightAltered(Rel.x, Rel.y, Rel.z) <= 9))
+ if (a_Player->CanMobsTarget() && (Chunk->GetSkyLightAltered(Rel.x, Rel.y, Rel.z) <= 9))
{
- super::EventSeePlayer(a_Entity, a_Chunk);
+ super::EventSeePlayer(a_Player, a_Chunk);
}
}
diff --git a/src/Mobs/Spider.h b/src/Mobs/Spider.h
index 85cae92fc..af2753012 100644
--- a/src/Mobs/Spider.h
+++ b/src/Mobs/Spider.h
@@ -18,7 +18,7 @@ public:
CLASS_PROTODEF(cSpider)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override;
- virtual void EventSeePlayer(cEntity *, cChunk & a_Chunk) override;
+ virtual void EventSeePlayer(cPlayer *, cChunk & a_Chunk) override;
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
} ;
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index 1da2a6fd7..3874307de 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -69,6 +69,7 @@ public:
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) = 0;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) = 0;
+ virtual void SendCameraSetTo (const cEntity & a_Entity) = 0;
virtual void SendChat (const AString & a_Message, eChatType a_Type) = 0;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) = 0;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
index c1018324f..d75ab3a5c 100644
--- a/src/Protocol/Protocol18x.cpp
+++ b/src/Protocol/Protocol18x.cpp
@@ -246,6 +246,16 @@ void cProtocol180::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV
+void cProtocol180::SendCameraSetTo(const cEntity & a_Entity)
+{
+ cPacketizer Pkt(*this, 0x43); // Camera Packet (Attach the camera of a player at another entity in spectator mode)
+ Pkt.WriteVarInt32(a_Entity.GetUniqueID());
+}
+
+
+
+
+
void cProtocol180::SendChat(const AString & a_Message, eChatType a_Type)
{
ASSERT(m_State == 3); // In game mode?
@@ -2038,6 +2048,7 @@ bool cProtocol180::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
case 0x15: HandlePacketClientSettings (a_ByteBuffer); return true;
case 0x16: HandlePacketClientStatus (a_ByteBuffer); return true;
case 0x17: HandlePacketPluginMessage (a_ByteBuffer); return true;
+ case 0x18: HandlePacketSpectate (a_ByteBuffer); return true;
}
break;
}
@@ -2495,6 +2506,21 @@ void cProtocol180::HandlePacketSlotSelect(cByteBuffer & a_ByteBuffer)
+void cProtocol180::HandlePacketSpectate(cByteBuffer &a_ByteBuffer)
+{
+ AString playerUUID;
+ if (!a_ByteBuffer.ReadUUID(playerUUID))
+ {
+ return;
+ }
+
+ m_Client->HandleSpectate(playerUUID);
+}
+
+
+
+
+
void cProtocol180::HandlePacketSteerVehicle(cByteBuffer & a_ByteBuffer)
{
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Sideways);
diff --git a/src/Protocol/Protocol18x.h b/src/Protocol/Protocol18x.h
index b8f9675ba..6fc2647ed 100644
--- a/src/Protocol/Protocol18x.h
+++ b/src/Protocol/Protocol18x.h
@@ -65,6 +65,7 @@ public:
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendCameraSetTo (const cEntity & a_Entity) override;
virtual void SendChat (const AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) override;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
@@ -210,6 +211,7 @@ protected:
void HandlePacketPlayerPosLook (cByteBuffer & a_ByteBuffer);
void HandlePacketPluginMessage (cByteBuffer & a_ByteBuffer);
void HandlePacketSlotSelect (cByteBuffer & a_ByteBuffer);
+ void HandlePacketSpectate (cByteBuffer & a_ByteBuffer);
void HandlePacketSteerVehicle (cByteBuffer & a_ByteBuffer);
void HandlePacketTabComplete (cByteBuffer & a_ByteBuffer);
void HandlePacketUpdateSign (cByteBuffer & a_ByteBuffer);
diff --git a/src/Protocol/Protocol19x.cpp b/src/Protocol/Protocol19x.cpp
index 6791da8cd..6e26b2012 100644
--- a/src/Protocol/Protocol19x.cpp
+++ b/src/Protocol/Protocol19x.cpp
@@ -255,6 +255,16 @@ void cProtocol190::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV
+void cProtocol190::SendCameraSetTo(const cEntity & a_Entity)
+{
+ cPacketizer Pkt(*this, 0x36); // Camera Packet (Attach the camera of a player at another entity in spectator mode)
+ Pkt.WriteVarInt32(a_Entity.GetUniqueID());
+}
+
+
+
+
+
void cProtocol190::SendChat(const AString & a_Message, eChatType a_Type)
{
ASSERT(m_State == 3); // In game mode?
@@ -2058,7 +2068,7 @@ bool cProtocol190::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
case 0x18: HandlePacketCreativeInventoryAction(a_ByteBuffer); return true;
case 0x19: HandlePacketUpdateSign (a_ByteBuffer); return true;
case 0x1a: HandlePacketAnimation (a_ByteBuffer); return true;
- case 0x1b: break; // Spectate?
+ case 0x1b: HandlePacketSpectate (a_ByteBuffer); return true;
case 0x1c: HandlePacketBlockPlace (a_ByteBuffer); return true;
case 0x1d: HandlePacketUseItem (a_ByteBuffer); return true;
}
@@ -2551,6 +2561,21 @@ void cProtocol190::HandlePacketSlotSelect(cByteBuffer & a_ByteBuffer)
+void cProtocol190::HandlePacketSpectate(cByteBuffer & a_ByteBuffer)
+{
+ AString playerUUID;
+ if (!a_ByteBuffer.ReadUUID(playerUUID))
+ {
+ return;
+ }
+
+ m_Client->HandleSpectate(playerUUID);
+}
+
+
+
+
+
void cProtocol190::HandlePacketSteerVehicle(cByteBuffer & a_ByteBuffer)
{
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Sideways);
diff --git a/src/Protocol/Protocol19x.h b/src/Protocol/Protocol19x.h
index 79180e3a7..d46da2a0f 100644
--- a/src/Protocol/Protocol19x.h
+++ b/src/Protocol/Protocol19x.h
@@ -71,6 +71,7 @@ public:
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendCameraSetTo (const cEntity & a_Entity) override;
virtual void SendChat (const AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) override;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
@@ -219,6 +220,7 @@ protected:
void HandlePacketPluginMessage (cByteBuffer & a_ByteBuffer);
void HandlePacketSlotSelect (cByteBuffer & a_ByteBuffer);
void HandlePacketSteerVehicle (cByteBuffer & a_ByteBuffer);
+ void HandlePacketSpectate (cByteBuffer & a_ByteBuffer);
void HandlePacketTabComplete (cByteBuffer & a_ByteBuffer);
void HandlePacketUpdateSign (cByteBuffer & a_ByteBuffer);
void HandlePacketUseEntity (cByteBuffer & a_ByteBuffer);
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index ca0d05c51..be97279a9 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -170,6 +170,15 @@ void cProtocolRecognizer::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSe
+void cProtocolRecognizer::SendCameraSetTo(const cEntity & a_Entity)
+{
+ ASSERT(m_Protocol != nullptr);
+ m_Protocol->SendCameraSetTo(a_Entity);
+}
+
+
+
+
void cProtocolRecognizer::SendChat(const AString & a_Message, eChatType a_Type)
{
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index 6390b6289..24e3e214e 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -56,6 +56,7 @@ public:
virtual void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage) override;
virtual void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes) override;
+ virtual void SendCameraSetTo (const cEntity & a_Entity) override;
virtual void SendChat (const AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) override;
virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp
index 2cef9b06d..8878bb900 100644
--- a/src/UI/SlotArea.cpp
+++ b/src/UI/SlotArea.cpp
@@ -58,6 +58,14 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickA
return;
}
+ if (a_Player.IsGameModeSpectator())
+ {
+ // Block the action of the player and make sure, the inventory doesn't get out of sync
+ a_Player.GetClientHandle()->SendInventorySlot(-1, -1, cItem()); // Reset the dragged item
+ SetSlot(a_SlotNum, a_Player, *GetSlot(a_SlotNum, a_Player)); // Update the current slot
+ return;
+ }
+
switch (a_ClickAction)
{
case caShiftLeftClick:
diff --git a/src/World.cpp b/src/World.cpp
index d47d0832a..cee1f8643 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -3133,6 +3133,15 @@ bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCa
bool cWorld::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback)
{
+ return DoWithPlayerByUUID(a_PlayerUUID, std::bind(&cPlayerListCallback::Item, &a_Callback, std::placeholders::_1));
+}
+
+
+
+
+
+bool cWorld::DoWithPlayerByUUID(const AString & a_PlayerUUID, cLambdaPlayerCallback a_Callback)
+{
cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
@@ -3142,7 +3151,7 @@ bool cWorld::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallbac
}
if ((*itr)->GetUUID() == a_PlayerUUID)
{
- return a_Callback.Item(*itr);
+ return a_Callback(*itr);
}
}
return false;
@@ -3241,6 +3250,15 @@ bool cWorld::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_
bool cWorld::DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback & a_Callback)
{
+ return DoWithEntityByID(a_UniqueID, std::bind(&cEntityCallback::Item, &a_Callback, std::placeholders::_1));
+}
+
+
+
+
+
+bool cWorld::DoWithEntityByID(UInt32 a_UniqueID, cLambdaEntityCallback a_Callback)
+{
// First check the entities-to-add:
{
cCSLock Lock(m_CSEntitiesToAdd);
@@ -3248,7 +3266,7 @@ bool cWorld::DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback & a_Callback)
{
if (ent->GetUniqueID() == a_UniqueID)
{
- a_Callback.Item(ent);
+ a_Callback(ent);
return true;
}
} // for ent - m_EntitiesToAdd[]
diff --git a/src/World.h b/src/World.h
index de34af1d2..0bcd1f823 100644
--- a/src/World.h
+++ b/src/World.h
@@ -78,6 +78,8 @@ typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
typedef cItemCallback<cMobHeadEntity> cMobHeadCallback;
typedef cItemCallback<cFlowerPotEntity> cFlowerPotCallback;
+typedef std::function<bool (cPlayer *)> cLambdaPlayerCallback;
+typedef std::function<bool (cEntity *)> cLambdaEntityCallback;
@@ -288,6 +290,7 @@ public:
/** Finds the player over his uuid and calls the callback */
bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+ bool DoWithPlayerByUUID(const AString & a_PlayerUUID, cLambdaPlayerCallback a_Callback); // Lambda version
void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player
@@ -313,6 +316,7 @@ public:
/** 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(UInt32 a_UniqueID, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
+ bool DoWithEntityByID(UInt32 a_UniqueID, cLambdaEntityCallback a_Callback); // Lambda version
/** Compares clients of two chunks, calls the callback accordingly */
void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback);