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

View File

@ -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;

View File

@ -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)

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
/** 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

View File

@ -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)

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)
{
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[]

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. */
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

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)
{
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;

View File

@ -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. */

View File

@ -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();

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 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 (

View File

@ -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);

View File

@ -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++;

View File

@ -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;
}

View File

@ -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())

View File

@ -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());

View File

@ -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;

View File

@ -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.

View File

@ -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;

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);
m_EMState = CHASING;
return;
}
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 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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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
}

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);
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()))
{
@ -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);
}
}

View File

@ -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;
} ;

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 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;

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)
{
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);

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 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);

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)
{
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);

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 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);

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)
{

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 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;

View File

@ -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:

View File

@ -3132,6 +3132,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;
@ -3240,6 +3249,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:
{
@ -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[]

View File

@ -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);