1
0
Fork 0

Spectators added (#2852)

This commit is contained in:
bibo38 2016-10-12 14:38:45 +02:00 committed by Mattes D
parent 4e33569110
commit cb640ffea4
38 changed files with 396 additions and 36 deletions

View File

@ -2220,6 +2220,24 @@ return
}, },
Notes = "Returns whether the specified block is transparent.", Notes = "Returns whether the specified block is transparent.",
}, },
IsUseableBySpectator =
{
IsStatic = true,
Params =
{
{
Name = "BlockType",
Type = "number",
},
},
Returns =
{
{
Type = "boolean",
},
},
Notes = "Returns whether a spectator can interact with the specified block.",
},
RequiresSpecialTool = RequiresSpecialTool =
{ {
IsStatic = true, IsStatic = true,
@ -10949,6 +10967,16 @@ a_Player:OpenWindow(Window);
}, },
Notes = "Returns if the player is able to fly.", Notes = "Returns if the player is able to fly.",
}, },
CanMobsTarget =
{
Returns =
{
{
Type = "boolean",
},
},
Notes = "Returns if the player can be targeted by mobs.",
},
CloseWindow = CloseWindow =
{ {
Params = Params =

View File

@ -492,6 +492,15 @@ void cBlockInfo::Initialize(cBlockInfoArray & a_Info)
a_Info[E_BLOCK_WOODEN_PRESSURE_PLATE].m_IsSolid = false; 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: // 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_BARRIER ].m_FullyOccupiesVoxel = true;
a_Info[E_BLOCK_BEDROCK ].m_FullyOccupiesVoxel = true; a_Info[E_BLOCK_BEDROCK ].m_FullyOccupiesVoxel = true;

View File

@ -55,6 +55,9 @@ public:
/** Is this block solid (player cannot walk through)? */ /** Is this block solid (player cannot walk through)? */
bool m_IsSolid; 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? */ /** Does this block fully occupy its voxel - is it a 'full' block? */
bool m_FullyOccupiesVoxel; bool m_FullyOccupiesVoxel;
@ -81,6 +84,7 @@ public:
inline static bool IsPistonBreakable (BLOCKTYPE a_Type) { return Get(a_Type).m_PistonBreakable; } 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 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 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 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 bool CanBeTerraformed (BLOCKTYPE a_Type) { return Get(a_Type).m_CanBeTerraformed; }
inline static float GetBlockHeight (BLOCKTYPE a_Type) { return Get(a_Type).m_BlockHeight; } inline static float GetBlockHeight (BLOCKTYPE a_Type) { return Get(a_Type).m_BlockHeight; }
@ -103,6 +107,7 @@ protected:
, m_PistonBreakable(false) , m_PistonBreakable(false)
, m_IsSnowable(false) , m_IsSnowable(false)
, m_IsSolid(true) , m_IsSolid(true)
, m_UseableBySpectator(false)
, m_FullyOccupiesVoxel(false) , m_FullyOccupiesVoxel(false)
, m_CanBeTerraformed(false) , m_CanBeTerraformed(false)
, m_BlockHeight(1.0) , m_BlockHeight(1.0)

View File

@ -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 #ifdef DEBUG_SINGLE_THREAD_ACCESS
/** Simple RAII class that is used for checking that no two threads are using an object simultanously. /** 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) bool cByteBuffer::WriteBEInt8(Int8 a_Value)
{ {
CHECK_THREAD CHECK_THREAD

View File

@ -68,6 +68,7 @@ public:
bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8 bool ReadVarUTF8String (AString & a_Value); // string length as VarInt, then string as UTF-8
bool ReadLEInt (int & a_Value); bool ReadLEInt (int & a_Value);
bool ReadPosition64 (int & a_BlockX, int & a_BlockY, int & a_BlockZ); 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, ...) */ /** 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) template <typename T> bool ReadVarInt(T & a_Value)

View File

@ -2051,13 +2051,22 @@ bool cChunk::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_
bool cChunk::DoWithEntityByID(UInt32 a_EntityID, cEntityCallback & a_Callback, bool & a_CallbackResult) 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 // 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) for (cEntityList::iterator itr = m_Entities.begin(), end = m_Entities.end(); itr != end; ++itr)
{ {
if (((*itr)->GetUniqueID() == a_EntityID) && ((*itr)->IsTicking())) if (((*itr)->GetUniqueID() == a_EntityID) && ((*itr)->IsTicking()))
{ {
a_CallbackResult = a_Callback.Item(*itr); a_CallbackResult = a_Callback(*itr);
return true; return true;
} }
} // for itr - m_Entitites[] } // for itr - m_Entitites[]

View File

@ -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. */ /** 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, 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 */ /** 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 bool ForEachBlockEntity(cBlockEntityCallback & a_Callback); // Lua-accessible

View File

@ -1873,6 +1873,15 @@ void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_
bool cChunkMap::DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback & a_Callback) 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); cCSLock Lock(m_CSChunks);
bool res = false; bool res = false;

View File

@ -56,6 +56,8 @@ typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
typedef cItemCallback<cMobHeadEntity> cMobHeadCallback; typedef cItemCallback<cMobHeadEntity> cMobHeadCallback;
typedef cItemCallback<cChunk> cChunkCallback; 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. /** 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. */ Returns true if entity found and callback returned false. */
bool DoWithEntityByID(UInt32 a_EntityID, cEntityCallback & a_Callback); // Lua-accessible 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. /** 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. */ Returns true if all block entities processed, false if the callback aborted by returning true. */

View File

@ -7,6 +7,7 @@
#include "Entities/Pickup.h" #include "Entities/Pickup.h"
#include "Bindings/PluginManager.h" #include "Bindings/PluginManager.h"
#include "Entities/Player.h" #include "Entities/Player.h"
#include "Entities/Minecart.h"
#include "Inventory.h" #include "Inventory.h"
#include "EffectID.h" #include "EffectID.h"
#include "BlockEntities/BeaconEntity.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); World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType); 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)) 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; short EquippedDamage = Equipped.m_ItemDamage;
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(Equipped.m_ItemType); 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) void cClientHandle::HandleSteerVehicle(float a_Forward, float a_Sideways)
{ {
m_Player->SteerVehicle(a_Forward, 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 // 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 it is a right click, call the entity's OnRightClicked() handler:
if (!a_IsLeftClick) if (!a_IsLeftClick)
{ {
@ -1625,7 +1656,19 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
cPlayer & m_Player; cPlayer & m_Player;
virtual bool Item(cEntity * a_Entity) override 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; 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) void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
{ {
cWorld * World = GetPlayer()->GetWorld(); cWorld * World = GetPlayer()->GetWorld();

View File

@ -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 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 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 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 AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = "");
void SendChat (const cCompositeChat & a_Message); void SendChat (const cCompositeChat & a_Message);
void SendChatAboveActionBar (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = ""); void SendChatAboveActionBar (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = "");
@ -336,6 +337,7 @@ public: // tolua_export
void HandleRespawn (void); 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 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 HandleSlotSelected (Int16 a_SlotNum);
void HandleSpectate (const AString & a_PlayerUUID);
void HandleSteerVehicle (float Forward, float Sideways); void HandleSteerVehicle (float Forward, float Sideways);
void HandleTabCompletion (const AString & a_Text); void HandleTabCompletion (const AString & a_Text);
void HandleUpdateSign ( void HandleUpdateSign (

View File

@ -429,7 +429,7 @@ public:
cEntity * GetAttached(); cEntity * GetAttached();
/** Attaches to the specified entity; detaches from any previous one first */ /** 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 */ /** Detaches from the currently attached entity, if any */
virtual void Detach(void); virtual void Detach(void);

View File

@ -45,7 +45,7 @@ void cExpOrb::SpawnOn(cClientHandle & a_Client)
void cExpOrb::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cExpOrb::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
{ {
cPlayer * a_ClosestPlayer(m_World->FindClosestPlayer(Vector3f(GetPosition()), 5)); 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()); Vector3f a_PlayerPos(a_ClosestPlayer->GetPosition());
a_PlayerPos.y++; a_PlayerPos.y++;

View File

@ -39,7 +39,15 @@ public:
{ {
ASSERT(a_Entity != nullptr); 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; return false;
} }

View File

@ -108,7 +108,11 @@ void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
} }
} Callback(this); } 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); super::Tick(a_Dt, a_Chunk);
if (!IsTicking()) if (!IsTicking())

View File

@ -208,6 +208,12 @@ bool cPickup::CollectedBy(cPlayer & a_Dest)
return false; // Not old enough 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)) 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()); // LOG("Pickup %d cannot be collected by \"%s\", because a plugin has said no.", m_UniqueID, a_Dest->GetName().c_str());

View File

@ -250,6 +250,20 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
m_Stats.AddValue(statMinutesPlayed, 1); 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 // Handle a frozen player
TickFreezeCode(); TickFreezeCode();
if (m_IsFrozen) 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) void cPlayer::SetTeam(cTeam * a_Team)
{ {
if (m_Team == a_Team) if (m_Team == a_Team)
@ -1344,6 +1368,12 @@ void cPlayer::SetGameMode(eGameMode a_GameMode)
return; 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_GameMode = a_GameMode;
m_ClientHandle->SendGameMode(a_GameMode); m_ClientHandle->SendGameMode(a_GameMode);
@ -1379,6 +1409,13 @@ void cPlayer::SetCapabilities()
{ {
SetVisible(false); SetVisible(false);
SetCanFly(true); SetCanFly(true);
// Clear the current dragging item of the player
if (GetWindow() != nullptr)
{
m_DraggingItem.Empty();
GetClientHandle()->SendInventorySlot(-1, -1, m_DraggingItem);
}
} }
else 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() 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(); super::Detach();
int PosX = POSX_TOINT; int PosX = POSX_TOINT;
int PosY = POSY_TOINT; int PosY = POSY_TOINT;

View File

@ -193,6 +193,9 @@ public:
/** Returns true if the player is in Spectator mode, either explicitly, or by inheriting from current world */ /** Returns true if the player is in Spectator mode, either explicitly, or by inheriting from current world */
bool IsGameModeSpectator(void) const; 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 AString GetIP(void) const { return m_IP; } // tolua_export
/** Returns the associated team, nullptr if none */ /** Returns the associated team, nullptr if none */
@ -518,6 +521,7 @@ public:
virtual bool IsSprinting(void) const override { return m_IsSprinting; } virtual bool IsSprinting(void) const override { return m_IsSprinting; }
virtual bool IsRclking (void) const override { return IsEating() || IsChargingBow(); } virtual bool IsRclking (void) const override { return IsEating() || IsChargingBow(); }
virtual void AttachTo(cEntity * a_AttachTo) override;
virtual void Detach(void) override; virtual void Detach(void) override;
/** Called by cClientHandle when the client is being destroyed. /** Called by cClientHandle when the client is being destroyed.

View File

@ -167,7 +167,15 @@ public:
return false; 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 // Not an entity that interacts with a projectile
return false; return false;

View File

@ -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); return;
m_EMState = CHASING;
} }
super::EventSeePlayer(a_Player, a_Chunk);
m_EMState = CHASING;
} }

View File

@ -19,7 +19,8 @@ public:
virtual void Tick (std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; 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 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 /** 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 returns true if attack was deemed successful (hit player, fired projectile, creeper exploded, etc.) even if it didn't actually do damage

View File

@ -23,8 +23,8 @@ public:
virtual bool Item(cPlayer * a_Player) override virtual bool Item(cPlayer * a_Player) override
{ {
// Don't check players who are in creative gamemode // Don't check players who cannot be targeted
if (a_Player->IsGameModeCreative()) if (!a_Player->CanMobsTarget())
{ {
return false; return false;
} }
@ -124,13 +124,16 @@ void cEnderman::CheckEventSeePlayer(cChunk & a_Chunk)
return; return;
} }
if (!Callback.GetPlayer()->IsGameModeCreative()) if (!Callback.GetPlayer()->CanMobsTarget())
{ {
cMonster::EventSeePlayer(Callback.GetPlayer(), a_Chunk); return;
m_EMState = CHASING;
m_bIsScreaming = true;
GetWorld()->BroadcastEntityMetadata(*this);
} }
// Target the player
cMonster::EventSeePlayer(Callback.GetPlayer(), a_Chunk);
m_EMState = CHASING;
m_bIsScreaming = true;
GetWorld()->BroadcastEntityMetadata(*this);
} }

View File

@ -265,7 +265,7 @@ void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
if (GetTarget()->IsPlayer()) if (GetTarget()->IsPlayer())
{ {
if (static_cast<cPlayer *>(GetTarget())->IsGameModeCreative()) if (!static_cast<cPlayer *>(GetTarget())->CanMobsTarget())
{ {
SetTarget(nullptr); SetTarget(nullptr);
m_EMState = IDLE; m_EMState = IDLE;
@ -471,7 +471,13 @@ bool cMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
if ((a_TDI.Attacker != nullptr) && a_TDI.Attacker->IsPawn()) 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; m_TicksSinceLastDamaged = 0;
} }
return true; return true;
@ -617,11 +623,10 @@ void cMonster::CheckEventLostPlayer(void)
// What to do if player is seen // What to do if player is seen
// default to change state to chasing // 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); UNUSED(a_Chunk);
ASSERT(a_SeenPlayer->IsPlayer()); SetTarget(a_SeenPlayer);
SetTarget(static_cast<cPawn*>(a_SeenPlayer));
} }

View File

@ -72,7 +72,7 @@ public:
// tolua_end // tolua_end
virtual void CheckEventSeePlayer(cChunk & a_Chunk); 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. */ /** Reads the monster configuration for the specified monster name and assigns it to this object. */
void GetMonsterConfig(const AString & a_Name); void GetMonsterConfig(const AString & a_Name);

View File

@ -28,7 +28,7 @@ bool cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer())) if ((GetTarget() != nullptr) && (GetTarget()->IsPlayer()))
{ {
if (!static_cast<cPlayer *>(GetTarget())->IsGameModeCreative()) if (static_cast<cPlayer *>(GetTarget())->CanMobsTarget())
{ {
m_EMState = CHASING; 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 // don't do anything, neutral mobs don't react to just seeing the player
} }

View File

@ -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); 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 bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
virtual void EventSeePlayer(cEntity *, cChunk & a_Chunk) override; virtual void EventSeePlayer(cPlayer *, cChunk & a_Chunk) override;
} ; } ;

View File

@ -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())) if (!GetWorld()->IsChunkLighted(GetChunkX(), GetChunkZ()))
{ {
@ -48,9 +48,9 @@ void cSpider::EventSeePlayer(cEntity * a_Entity, cChunk & a_Chunk)
return; 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);
} }
} }

View File

@ -18,7 +18,7 @@ public:
CLASS_PROTODEF(cSpider) CLASS_PROTODEF(cSpider)
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = nullptr) override; 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; virtual bool DoTakeDamage(TakeDamageInfo & a_TDI) override;
} ; } ;

View File

@ -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 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 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 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 AString & a_Message, eChatType a_Type) = 0;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) = 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; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) = 0;

View File

@ -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) void cProtocol180::SendChat(const AString & a_Message, eChatType a_Type)
{ {
ASSERT(m_State == 3); // In game mode? 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 0x15: HandlePacketClientSettings (a_ByteBuffer); return true;
case 0x16: HandlePacketClientStatus (a_ByteBuffer); return true; case 0x16: HandlePacketClientStatus (a_ByteBuffer); return true;
case 0x17: HandlePacketPluginMessage (a_ByteBuffer); return true; case 0x17: HandlePacketPluginMessage (a_ByteBuffer); return true;
case 0x18: HandlePacketSpectate (a_ByteBuffer); return true;
} }
break; 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) void cProtocol180::HandlePacketSteerVehicle(cByteBuffer & a_ByteBuffer)
{ {
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Sideways); HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Sideways);

View File

@ -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 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 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 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 AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) 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; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
@ -210,6 +211,7 @@ protected:
void HandlePacketPlayerPosLook (cByteBuffer & a_ByteBuffer); void HandlePacketPlayerPosLook (cByteBuffer & a_ByteBuffer);
void HandlePacketPluginMessage (cByteBuffer & a_ByteBuffer); void HandlePacketPluginMessage (cByteBuffer & a_ByteBuffer);
void HandlePacketSlotSelect (cByteBuffer & a_ByteBuffer); void HandlePacketSlotSelect (cByteBuffer & a_ByteBuffer);
void HandlePacketSpectate (cByteBuffer & a_ByteBuffer);
void HandlePacketSteerVehicle (cByteBuffer & a_ByteBuffer); void HandlePacketSteerVehicle (cByteBuffer & a_ByteBuffer);
void HandlePacketTabComplete (cByteBuffer & a_ByteBuffer); void HandlePacketTabComplete (cByteBuffer & a_ByteBuffer);
void HandlePacketUpdateSign (cByteBuffer & a_ByteBuffer); void HandlePacketUpdateSign (cByteBuffer & a_ByteBuffer);

View File

@ -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) void cProtocol190::SendChat(const AString & a_Message, eChatType a_Type)
{ {
ASSERT(m_State == 3); // In game mode? 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 0x18: HandlePacketCreativeInventoryAction(a_ByteBuffer); return true;
case 0x19: HandlePacketUpdateSign (a_ByteBuffer); return true; case 0x19: HandlePacketUpdateSign (a_ByteBuffer); return true;
case 0x1a: HandlePacketAnimation (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 0x1c: HandlePacketBlockPlace (a_ByteBuffer); return true;
case 0x1d: HandlePacketUseItem (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) void cProtocol190::HandlePacketSteerVehicle(cByteBuffer & a_ByteBuffer)
{ {
HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Sideways); HANDLE_READ(a_ByteBuffer, ReadBEFloat, float, Sideways);

View File

@ -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 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 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 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 AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) 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; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;
@ -219,6 +220,7 @@ protected:
void HandlePacketPluginMessage (cByteBuffer & a_ByteBuffer); void HandlePacketPluginMessage (cByteBuffer & a_ByteBuffer);
void HandlePacketSlotSelect (cByteBuffer & a_ByteBuffer); void HandlePacketSlotSelect (cByteBuffer & a_ByteBuffer);
void HandlePacketSteerVehicle (cByteBuffer & a_ByteBuffer); void HandlePacketSteerVehicle (cByteBuffer & a_ByteBuffer);
void HandlePacketSpectate (cByteBuffer & a_ByteBuffer);
void HandlePacketTabComplete (cByteBuffer & a_ByteBuffer); void HandlePacketTabComplete (cByteBuffer & a_ByteBuffer);
void HandlePacketUpdateSign (cByteBuffer & a_ByteBuffer); void HandlePacketUpdateSign (cByteBuffer & a_ByteBuffer);
void HandlePacketUseEntity (cByteBuffer & a_ByteBuffer); void HandlePacketUseEntity (cByteBuffer & a_ByteBuffer);

View File

@ -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) void cProtocolRecognizer::SendChat(const AString & a_Message, eChatType a_Type)
{ {

View File

@ -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 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 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 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 AString & a_Message, eChatType a_Type) override;
virtual void SendChat (const cCompositeChat & a_Message, eChatType a_Type, bool a_ShouldUseChatPrefixes) 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; virtual void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) override;

View File

@ -58,6 +58,14 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickA
return; 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) switch (a_ClickAction)
{ {
case caShiftLeftClick: case caShiftLeftClick:

View File

@ -3132,6 +3132,15 @@ bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCa
bool cWorld::DoWithPlayerByUUID(const AString & a_PlayerUUID, cPlayerListCallback & a_Callback) 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); cCSLock Lock(m_CSPlayers);
for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) 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) if ((*itr)->GetUUID() == a_PlayerUUID)
{ {
return a_Callback.Item(*itr); return a_Callback(*itr);
} }
} }
return false; return false;
@ -3240,6 +3249,15 @@ bool cWorld::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_
bool cWorld::DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback & a_Callback) 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: // First check the entities-to-add:
{ {
@ -3248,7 +3266,7 @@ bool cWorld::DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback & a_Callback)
{ {
if (ent->GetUniqueID() == a_UniqueID) if (ent->GetUniqueID() == a_UniqueID)
{ {
a_Callback.Item(ent); a_Callback(ent);
return true; return true;
} }
} // for ent - m_EntitiesToAdd[] } // for ent - m_EntitiesToAdd[]

View File

@ -78,6 +78,8 @@ typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
typedef cItemCallback<cMobHeadEntity> cMobHeadCallback; typedef cItemCallback<cMobHeadEntity> cMobHeadCallback;
typedef cItemCallback<cFlowerPotEntity> cFlowerPotCallback; 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 */ /** 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, 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 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. /** 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. */ 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, 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 */ /** 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); void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback);