Merge branch 'master' of https://github.com/cuberite/cuberite
This commit is contained in:
commit
f7029eddd4
@ -3,6 +3,7 @@ Many people have contributed to Cuberite, and this list attempts to broadcast at
|
|||||||
BasedDoge (Donated AlchemistVillage prefabs)
|
BasedDoge (Donated AlchemistVillage prefabs)
|
||||||
bearbin (Alexander Harkness)
|
bearbin (Alexander Harkness)
|
||||||
beeduck
|
beeduck
|
||||||
|
bibo38
|
||||||
birkett (Anthony Birkett)
|
birkett (Anthony Birkett)
|
||||||
derouinw
|
derouinw
|
||||||
Diusrex
|
Diusrex
|
||||||
|
@ -2582,6 +2582,7 @@ end
|
|||||||
GetMinNetherPortalWidth = { Params = "", Return = "number", Notes = "Returns the minimum width for a nether portal" },
|
GetMinNetherPortalWidth = { Params = "", Return = "number", Notes = "Returns the minimum width for a nether portal" },
|
||||||
GetName = { Params = "", Return = "string", Notes = "Returns the name of the world, as specified in the settings.ini file." },
|
GetName = { Params = "", Return = "string", Notes = "Returns the name of the world, as specified in the settings.ini file." },
|
||||||
GetNumChunks = { Params = "", Return = "number", Notes = "Returns the number of chunks currently loaded." },
|
GetNumChunks = { Params = "", Return = "number", Notes = "Returns the number of chunks currently loaded." },
|
||||||
|
GetNumUnusedDirtyChunks = { Params = "", Return = "number", Notes = "Returns the number of unused dirty chunks. That's the number of chunks that we can save and then unload." },
|
||||||
GetScoreBoard = { Params = "", Return = "{{cScoreBoard}}", Notes = "Returns the {{cScoreBoard|ScoreBoard}} object used by this world. " },
|
GetScoreBoard = { Params = "", Return = "{{cScoreBoard}}", Notes = "Returns the {{cScoreBoard|ScoreBoard}} object used by this world. " },
|
||||||
GetSeed = { Params = "", Return = "number", Notes = "Returns the seed of the world." },
|
GetSeed = { Params = "", Return = "number", Notes = "Returns the seed of the world." },
|
||||||
GetSignLines = { Params = "BlockX, BlockY, BlockZ", Return = "IsValid, [Line1, Line2, Line3, Line4]", Notes = "Returns true and the lines of a sign at the specified coords, or false if there is no sign at the coords." },
|
GetSignLines = { Params = "BlockX, BlockY, BlockZ", Return = "IsValid, [Line1, Line2, Line3, Line4]", Notes = "Returns true and the lines of a sign at the specified coords, or false if there is no sign at the coords." },
|
||||||
|
@ -38,8 +38,7 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
|
a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_WATER, 0);
|
||||||
// This is called later than the real destroying of this ice block
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,6 +223,19 @@ bool cChunk::CanUnload(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool cChunk::CanUnloadAfterSaving(void)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
m_LoadedByClient.empty() && // The chunk is not used by any client
|
||||||
|
m_IsDirty && // The chunk is dirty
|
||||||
|
(m_StayCount == 0) && // The chunk is not in a ChunkStay
|
||||||
|
(m_Presence != cpQueued) ; // The chunk is not queued for loading / generating (otherwise multi-load / multi-gen could occur)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cChunk::MarkSaving(void)
|
void cChunk::MarkSaving(void)
|
||||||
{
|
{
|
||||||
m_IsSaving = true;
|
m_IsSaving = true;
|
||||||
|
@ -112,6 +112,9 @@ public:
|
|||||||
|
|
||||||
bool CanUnload(void);
|
bool CanUnload(void);
|
||||||
|
|
||||||
|
/** Returns true if the chunk could have been unloaded if it weren't dirty */
|
||||||
|
bool CanUnloadAfterSaving(void);
|
||||||
|
|
||||||
bool IsLightValid(void) const {return m_IsLightValid; }
|
bool IsLightValid(void) const {return m_IsLightValid; }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2704,11 +2704,28 @@ void cChunkMap::SaveAllChunks(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
int cChunkMap::GetNumChunks(void)
|
size_t cChunkMap::GetNumChunks(void)
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CSChunks);
|
cCSLock Lock(m_CSChunks);
|
||||||
return static_cast<int>(m_Chunks.size()); // TODO: change return value to unsigned type
|
return m_Chunks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
size_t cChunkMap::GetNumUnusedDirtyChunks(void)
|
||||||
|
{
|
||||||
|
cCSLock Lock(m_CSChunks);
|
||||||
|
size_t res = 0;
|
||||||
|
for (const auto & Chunk : m_Chunks)
|
||||||
|
{
|
||||||
|
if (Chunk.second->IsValid() && Chunk.second->CanUnloadAfterSaving())
|
||||||
|
{
|
||||||
|
res += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -388,7 +388,10 @@ public:
|
|||||||
|
|
||||||
cWorld * GetWorld(void) { return m_World; }
|
cWorld * GetWorld(void) { return m_World; }
|
||||||
|
|
||||||
int GetNumChunks(void);
|
size_t GetNumChunks(void);
|
||||||
|
|
||||||
|
/** Returns the number of unused dirty chunks. Those are chunks that we can save and then unload */
|
||||||
|
size_t GetNumUnusedDirtyChunks(void);
|
||||||
|
|
||||||
void ChunkValidated(void); // Called by chunks that have become valid
|
void ChunkValidated(void); // Called by chunks that have become valid
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ int cClientHandle::s_ClientCount = 0;
|
|||||||
// cClientHandle:
|
// cClientHandle:
|
||||||
|
|
||||||
cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) :
|
cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) :
|
||||||
|
m_LastSentDimension(dimNotSet),
|
||||||
m_CurrentViewDistance(a_ViewDistance),
|
m_CurrentViewDistance(a_ViewDistance),
|
||||||
m_RequestedViewDistance(a_ViewDistance),
|
m_RequestedViewDistance(a_ViewDistance),
|
||||||
m_IPString(a_IPString),
|
m_IPString(a_IPString),
|
||||||
@ -368,6 +369,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID,
|
|||||||
|
|
||||||
// Return a server login packet
|
// Return a server login packet
|
||||||
m_Protocol->SendLogin(*m_Player, *World);
|
m_Protocol->SendLogin(*m_Player, *World);
|
||||||
|
m_LastSentDimension = World->GetDimension();
|
||||||
|
|
||||||
// Send Weather if raining:
|
// Send Weather if raining:
|
||||||
if ((World->GetWeather() == 1) || (World->GetWeather() == 2))
|
if ((World->GetWeather() == 1) || (World->GetWeather() == 2))
|
||||||
@ -601,7 +603,6 @@ void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ, cChunkSender::eChunk
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Removes the client from all chunks. Used when switching worlds or destroying the player
|
|
||||||
void cClientHandle::RemoveFromAllChunks()
|
void cClientHandle::RemoveFromAllChunks()
|
||||||
{
|
{
|
||||||
cWorld * World = m_Player->GetWorld();
|
cWorld * World = m_Player->GetWorld();
|
||||||
@ -1267,7 +1268,7 @@ void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_Blo
|
|||||||
BlockHandler(a_OldBlock)->OnDestroyedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ);
|
BlockHandler(a_OldBlock)->OnDestroyedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ);
|
||||||
World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, a_BlockX, a_BlockY, a_BlockZ, a_OldBlock, this);
|
World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, a_BlockX, a_BlockY, a_BlockZ, a_OldBlock, this);
|
||||||
// This call would remove the water, placed from the ice block handler
|
// This call would remove the water, placed from the ice block handler
|
||||||
if (a_OldBlock != E_BLOCK_ICE)
|
if (!((a_OldBlock == E_BLOCK_ICE) && (ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WATER)))
|
||||||
{
|
{
|
||||||
World->DigBlock(a_BlockX, a_BlockY, a_BlockZ);
|
World->DigBlock(a_BlockX, a_BlockY, a_BlockZ);
|
||||||
}
|
}
|
||||||
@ -1886,7 +1887,7 @@ void cClientHandle::RemoveFromWorld(void)
|
|||||||
}
|
}
|
||||||
for (auto && Chunk : Chunks)
|
for (auto && Chunk : Chunks)
|
||||||
{
|
{
|
||||||
m_Protocol->SendUnloadChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ);
|
SendUnloadChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ);
|
||||||
} // for itr - Chunks[]
|
} // for itr - Chunks[]
|
||||||
|
|
||||||
// Here, we set last streamed values to bogus ones so everything is resent
|
// Here, we set last streamed values to bogus ones so everything is resent
|
||||||
@ -2704,7 +2705,21 @@ void cClientHandle::SendResetTitle()
|
|||||||
|
|
||||||
void cClientHandle::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
|
void cClientHandle::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
|
||||||
{
|
{
|
||||||
m_Protocol->SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks);
|
// If a_ShouldIgnoreDimensionChecks is true, we must be traveling to the same dimension
|
||||||
|
ASSERT((!a_ShouldIgnoreDimensionChecks) || (a_Dimension == m_LastSentDimension));
|
||||||
|
|
||||||
|
if ((!a_ShouldIgnoreDimensionChecks) && (a_Dimension == m_LastSentDimension))
|
||||||
|
{
|
||||||
|
// The client goes crazy if we send a respawn packet with the dimension of the current world
|
||||||
|
// So we send a temporary one first.
|
||||||
|
// This is not needed when the player dies, hence the a_ShouldIgnoreDimensionChecks flag.
|
||||||
|
// a_ShouldIgnoreDimensionChecks is true only at cPlayer::respawn, which is called after
|
||||||
|
// the player dies.
|
||||||
|
eDimension TemporaryDimension = (a_Dimension == dimOverworld) ? dimNether : dimOverworld;
|
||||||
|
m_Protocol->SendRespawn(TemporaryDimension);
|
||||||
|
}
|
||||||
|
m_Protocol->SendRespawn(a_Dimension);
|
||||||
|
m_LastSentDimension = a_Dimension;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -125,7 +125,8 @@ public: // tolua_export
|
|||||||
/** Remove all loaded chunks that are no longer in range */
|
/** Remove all loaded chunks that are no longer in range */
|
||||||
void UnloadOutOfRangeChunks(void);
|
void UnloadOutOfRangeChunks(void);
|
||||||
|
|
||||||
// Removes the client from all chunks. Used when switching worlds or destroying the player
|
/** Removes the client from all chunks. Used when destroying the player.
|
||||||
|
When switching worlds, RemoveFromWorld does this function's job so it isn't called. */
|
||||||
void RemoveFromAllChunks(void);
|
void RemoveFromAllChunks(void);
|
||||||
|
|
||||||
inline bool IsLoggedIn(void) const { return (m_State >= csAuthenticating); }
|
inline bool IsLoggedIn(void) const { return (m_State >= csAuthenticating); }
|
||||||
@ -369,6 +370,9 @@ public: // tolua_export
|
|||||||
bool IsPlayerChunkSent();
|
bool IsPlayerChunkSent();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/** The dimension that was last sent to a player in a Respawn or Login packet.
|
||||||
|
Used to avoid Respawning into the same dimension, which confuses the client. */
|
||||||
|
eDimension m_LastSentDimension;
|
||||||
|
|
||||||
friend class cServer; // Needs access to SetSelf()
|
friend class cServer; // Needs access to SetSelf()
|
||||||
|
|
||||||
|
@ -180,6 +180,11 @@ void cArrowEntity::CollectedBy(cPlayer & a_Dest)
|
|||||||
void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_Timer += a_Dt;
|
m_Timer += a_Dt;
|
||||||
|
|
||||||
if (m_bIsCollected)
|
if (m_bIsCollected)
|
||||||
|
@ -102,6 +102,11 @@ void cBoat::OnRightClicked(cPlayer & a_Player)
|
|||||||
void cBoat::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cBoat::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
BroadcastMovementUpdate();
|
BroadcastMovementUpdate();
|
||||||
|
|
||||||
SetSpeed(GetSpeed() * 0.97); // Slowly decrease the speed
|
SetSpeed(GetSpeed() * 0.97); // Slowly decrease the speed
|
||||||
|
@ -1433,19 +1433,22 @@ bool cEntity::DetectPortal()
|
|||||||
}
|
}
|
||||||
m_PortalCooldownData.m_TicksDelayed = 0;
|
m_PortalCooldownData.m_TicksDelayed = 0;
|
||||||
|
|
||||||
|
// Nether portal in the nether
|
||||||
if (GetWorld()->GetDimension() == dimNether)
|
if (GetWorld()->GetDimension() == dimNether)
|
||||||
{
|
{
|
||||||
if (GetWorld()->GetLinkedOverworldName().empty())
|
if (GetWorld()->GetLinkedOverworldName().empty())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
cWorld * DestinationWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
|
||||||
|
eDimension DestionationDim = DestinationWorld->GetDimension();
|
||||||
|
|
||||||
m_PortalCooldownData.m_ShouldPreventTeleportation = true; // Stop portals from working on respawn
|
m_PortalCooldownData.m_ShouldPreventTeleportation = true; // Stop portals from working on respawn
|
||||||
|
|
||||||
if (IsPlayer())
|
if (IsPlayer())
|
||||||
{
|
{
|
||||||
// Send a respawn packet before world is loaded / generated so the client isn't left in limbo
|
// Send a respawn packet before world is loaded / generated so the client isn't left in limbo
|
||||||
(reinterpret_cast<cPlayer *>(this))->GetClientHandle()->SendRespawn(dimOverworld);
|
(reinterpret_cast<cPlayer *>(this))->GetClientHandle()->SendRespawn(DestionationDim);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3d TargetPos = GetPosition();
|
Vector3d TargetPos = GetPosition();
|
||||||
@ -1454,23 +1457,30 @@ bool cEntity::DetectPortal()
|
|||||||
|
|
||||||
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
|
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
|
||||||
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
||||||
LOGD("Jumping nether -> overworld");
|
LOGD("Jumping %s -> %s", DimensionToString(dimNether).c_str(), DimensionToString(DestionationDim).c_str());
|
||||||
new cNetherPortalScanner(this, TargetWorld, TargetPos, 256);
|
new cNetherPortalScanner(this, TargetWorld, TargetPos, 256);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
// Nether portal in the overworld
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (GetWorld()->GetLinkedNetherWorldName().empty())
|
if (GetWorld()->GetLinkedNetherWorldName().empty())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
cWorld * DestinationWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName());
|
||||||
|
eDimension DestionationDim = DestinationWorld->GetDimension();
|
||||||
|
|
||||||
m_PortalCooldownData.m_ShouldPreventTeleportation = true;
|
m_PortalCooldownData.m_ShouldPreventTeleportation = true;
|
||||||
|
|
||||||
if (IsPlayer())
|
if (IsPlayer())
|
||||||
|
{
|
||||||
|
if (DestionationDim == dimNether)
|
||||||
{
|
{
|
||||||
reinterpret_cast<cPlayer *>(this)->AwardAchievement(achEnterPortal);
|
reinterpret_cast<cPlayer *>(this)->AwardAchievement(achEnterPortal);
|
||||||
reinterpret_cast<cPlayer *>(this)->GetClientHandle()->SendRespawn(dimNether);
|
}
|
||||||
|
|
||||||
|
reinterpret_cast<cPlayer *>(this)->GetClientHandle()->SendRespawn(DestionationDim);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3d TargetPos = GetPosition();
|
Vector3d TargetPos = GetPosition();
|
||||||
@ -1479,7 +1489,7 @@ bool cEntity::DetectPortal()
|
|||||||
|
|
||||||
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName());
|
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedNetherWorldName());
|
||||||
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
||||||
LOGD("Jumping overworld -> nether");
|
LOGD("Jumping %s -> %s", DimensionToString(dimOverworld).c_str(), DimensionToString(DestionationDim).c_str());
|
||||||
new cNetherPortalScanner(this, TargetWorld, TargetPos, 128);
|
new cNetherPortalScanner(this, TargetWorld, TargetPos, 128);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1491,6 +1501,7 @@ bool cEntity::DetectPortal()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// End portal in the end
|
||||||
if (GetWorld()->GetDimension() == dimEnd)
|
if (GetWorld()->GetDimension() == dimEnd)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -1498,37 +1509,55 @@ bool cEntity::DetectPortal()
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
cWorld * DestinationWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
|
||||||
|
eDimension DestionationDim = DestinationWorld->GetDimension();
|
||||||
|
|
||||||
|
|
||||||
m_PortalCooldownData.m_ShouldPreventTeleportation = true;
|
m_PortalCooldownData.m_ShouldPreventTeleportation = true;
|
||||||
|
|
||||||
if (IsPlayer())
|
if (IsPlayer())
|
||||||
{
|
{
|
||||||
cPlayer * Player = reinterpret_cast<cPlayer *>(this);
|
cPlayer * Player = reinterpret_cast<cPlayer *>(this);
|
||||||
|
if (Player->GetBedWorld() == DestinationWorld)
|
||||||
|
{
|
||||||
Player->TeleportToCoords(Player->GetLastBedPos().x, Player->GetLastBedPos().y, Player->GetLastBedPos().z);
|
Player->TeleportToCoords(Player->GetLastBedPos().x, Player->GetLastBedPos().y, Player->GetLastBedPos().z);
|
||||||
Player->GetClientHandle()->SendRespawn(dimOverworld);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Player->TeleportToCoords(DestinationWorld->GetSpawnX(), DestinationWorld->GetSpawnY(), DestinationWorld->GetSpawnZ());
|
||||||
|
}
|
||||||
|
Player->GetClientHandle()->SendRespawn(DestionationDim);
|
||||||
}
|
}
|
||||||
|
|
||||||
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
|
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedOverworldName());
|
||||||
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
||||||
|
LOGD("Jumping %s -> %s", DimensionToString(dimEnd).c_str(), DimensionToString(DestionationDim).c_str());
|
||||||
return MoveToWorld(TargetWorld, false);
|
return MoveToWorld(TargetWorld, false);
|
||||||
}
|
}
|
||||||
|
// End portal in the overworld
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (GetWorld()->GetLinkedEndWorldName().empty())
|
if (GetWorld()->GetLinkedEndWorldName().empty())
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
cWorld * DestinationWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedEndWorldName());
|
||||||
|
eDimension DestionationDim = DestinationWorld->GetDimension();
|
||||||
|
|
||||||
m_PortalCooldownData.m_ShouldPreventTeleportation = true;
|
m_PortalCooldownData.m_ShouldPreventTeleportation = true;
|
||||||
|
|
||||||
if (IsPlayer())
|
if (IsPlayer())
|
||||||
|
{
|
||||||
|
if (DestionationDim == dimEnd)
|
||||||
{
|
{
|
||||||
reinterpret_cast<cPlayer *>(this)->AwardAchievement(achEnterTheEnd);
|
reinterpret_cast<cPlayer *>(this)->AwardAchievement(achEnterTheEnd);
|
||||||
reinterpret_cast<cPlayer *>(this)->GetClientHandle()->SendRespawn(dimEnd);
|
}
|
||||||
|
reinterpret_cast<cPlayer *>(this)->GetClientHandle()->SendRespawn(DestionationDim);
|
||||||
}
|
}
|
||||||
|
|
||||||
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedEndWorldName());
|
cWorld * TargetWorld = cRoot::Get()->GetWorld(GetWorld()->GetLinkedEndWorldName());
|
||||||
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
ASSERT(TargetWorld != nullptr); // The linkage checker should have prevented this at startup. See cWorld::start()
|
||||||
|
LOGD("Jumping %s -> %s", DimensionToString(dimOverworld).c_str(), DimensionToString(DestionationDim).c_str());
|
||||||
return MoveToWorld(TargetWorld, false);
|
return MoveToWorld(TargetWorld, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +65,11 @@ void cFireworkEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_C
|
|||||||
void cFireworkEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cFireworkEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_TicksToExplosion <= 0)
|
if (m_TicksToExplosion <= 0)
|
||||||
{
|
{
|
||||||
|
@ -1264,6 +1264,11 @@ void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player)
|
|||||||
void cMinecartWithFurnace::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cMinecartWithFurnace::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_IsFueled)
|
if (m_IsFueled)
|
||||||
{
|
{
|
||||||
|
@ -111,7 +111,11 @@ void cPawn::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), GetWidth(), GetHeight()), Callback);
|
m_World->ForEachEntityInBox(cBoundingBox(GetPosition(), GetWidth(), GetHeight()), Callback);
|
||||||
|
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
HandleFalling();
|
HandleFalling();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +116,11 @@ void cPickup::SpawnOn(cClientHandle & a_Client)
|
|||||||
void cPickup::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cPickup::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
BroadcastMovementUpdate(); // Notify clients of position
|
BroadcastMovementUpdate(); // Notify clients of position
|
||||||
|
|
||||||
m_Timer += a_Dt;
|
m_Timer += a_Dt;
|
||||||
|
@ -893,6 +893,15 @@ void cPlayer::SetBedPos(const Vector3i & a_Pos, cWorld * a_World)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cWorld * cPlayer::GetBedWorld()
|
||||||
|
{
|
||||||
|
return m_SpawnWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cPlayer::SetFlying(bool a_IsFlying)
|
void cPlayer::SetFlying(bool a_IsFlying)
|
||||||
{
|
{
|
||||||
if (a_IsFlying == m_IsFlying)
|
if (a_IsFlying == m_IsFlying)
|
||||||
@ -1771,13 +1780,15 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ask the plugins if the player is allowed to changing the world
|
// Ask the plugins if the player is allowed to change the world
|
||||||
if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World))
|
if (cRoot::Get()->GetPluginManager()->CallHookEntityChangingWorld(*this, *a_World))
|
||||||
{
|
{
|
||||||
// A Plugin doesn't allow the player to changing the world
|
// A Plugin doesn't allow the player to change the world
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetWorld()->QueueTask([this, a_World, a_ShouldSendRespawn, a_NewPosition](cWorld & a_OldWorld)
|
||||||
|
{
|
||||||
// The clienthandle caches the coords of the chunk we're standing at. Invalidate this.
|
// The clienthandle caches the coords of the chunk we're standing at. Invalidate this.
|
||||||
GetClientHandle()->InvalidateCachedSentChunk();
|
GetClientHandle()->InvalidateCachedSentChunk();
|
||||||
|
|
||||||
@ -1797,38 +1808,43 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn, Vector3d
|
|||||||
// Stop all mobs from targeting this player
|
// Stop all mobs from targeting this player
|
||||||
StopEveryoneFromTargetingMe();
|
StopEveryoneFromTargetingMe();
|
||||||
|
|
||||||
|
cClientHandle * ch = this->GetClientHandle();
|
||||||
|
if (ch != nullptr)
|
||||||
|
{
|
||||||
// Send the respawn packet:
|
// Send the respawn packet:
|
||||||
if (a_ShouldSendRespawn && (m_ClientHandle != nullptr))
|
if (a_ShouldSendRespawn)
|
||||||
{
|
{
|
||||||
m_ClientHandle->SendRespawn(a_World->GetDimension());
|
m_ClientHandle->SendRespawn(a_World->GetDimension());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Update the view distance.
|
// Update the view distance.
|
||||||
m_ClientHandle->SetViewDistance(m_ClientHandle->GetRequestedViewDistance());
|
ch->SetViewDistance(m_ClientHandle->GetRequestedViewDistance());
|
||||||
|
|
||||||
// Send current weather of target world to player
|
// Send current weather of target world to player
|
||||||
if (a_World->GetDimension() == dimOverworld)
|
if (a_World->GetDimension() == dimOverworld)
|
||||||
{
|
{
|
||||||
m_ClientHandle->SendWeather(a_World->GetWeather());
|
ch->SendWeather(a_World->GetWeather());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Broadcast the player into the new world.
|
// Broadcast the player into the new world.
|
||||||
a_World->BroadcastSpawnEntity(*this);
|
a_World->BroadcastSpawnEntity(*this);
|
||||||
|
|
||||||
// Queue add to new world and removal from the old one
|
// Queue add to new world and removal from the old one
|
||||||
cChunk * ParentChunk = GetParentChunk();
|
|
||||||
cWorld * OldWorld = GetWorld();
|
|
||||||
SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
|
SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
|
||||||
OldWorld->QueueTask([this, ParentChunk, a_World](cWorld & a_OldWorld)
|
cChunk * ParentChunk = this->GetParentChunk();
|
||||||
{
|
|
||||||
LOGD("Warping player \"%s\" from world \"%s\" to \"%s\". Source chunk: (%d, %d) ",
|
LOGD("Warping player \"%s\" from world \"%s\" to \"%s\". Source chunk: (%d, %d) ",
|
||||||
this->GetName().c_str(),
|
this->GetName().c_str(),
|
||||||
a_OldWorld.GetName().c_str(), a_World->GetName().c_str(),
|
a_OldWorld.GetName().c_str(), a_World->GetName().c_str(),
|
||||||
ParentChunk->GetPosX(), ParentChunk->GetPosZ()
|
ParentChunk->GetPosX(), ParentChunk->GetPosZ()
|
||||||
);
|
);
|
||||||
ParentChunk->RemoveEntity(this);
|
ParentChunk->RemoveEntity(this);
|
||||||
a_World->AddPlayer(this, &a_OldWorld); // New world will appropriate and announce client at his next tick
|
a_World->AddPlayer(this, &a_OldWorld); // New world will take over and announce client at its next tick
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,6 +467,9 @@ public:
|
|||||||
|
|
||||||
// tolua_end
|
// tolua_end
|
||||||
|
|
||||||
|
// TODO lua export GetBedPos and GetBedWorld
|
||||||
|
cWorld * GetBedWorld();
|
||||||
|
|
||||||
/** Update movement-related statistics. */
|
/** Update movement-related statistics. */
|
||||||
void UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround);
|
void UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround);
|
||||||
|
|
||||||
|
@ -369,6 +369,11 @@ AString cProjectileEntity::GetMCAClassName(void) const
|
|||||||
void cProjectileEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cProjectileEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
BroadcastMovementUpdate();
|
BroadcastMovementUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,11 @@ void cTNTEntity::Explode(void)
|
|||||||
void cTNTEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cTNTEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
BroadcastMovementUpdate();
|
BroadcastMovementUpdate();
|
||||||
|
|
||||||
m_FuseTicks -= 1;
|
m_FuseTicks -= 1;
|
||||||
|
@ -52,6 +52,11 @@ void cAggressiveMonster::EventSeePlayer(cEntity * a_Entity, cChunk & a_Chunk)
|
|||||||
void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_EMState == CHASING)
|
if (m_EMState == CHASING)
|
||||||
{
|
{
|
||||||
|
@ -19,6 +19,11 @@ cCaveSpider::cCaveSpider(void) :
|
|||||||
void cCaveSpider::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cCaveSpider::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE;
|
m_EMPersonality = (GetWorld()->GetTimeOfDay() < (12000 + 1000)) ? PASSIVE : AGGRESSIVE;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,11 @@ cChicken::cChicken(void) :
|
|||||||
void cChicken::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cChicken::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (IsBaby())
|
if (IsBaby())
|
||||||
{
|
{
|
||||||
|
@ -26,6 +26,11 @@ cCreeper::cCreeper(void) :
|
|||||||
void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cCreeper::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ((GetTarget() == nullptr) || (!TargetIsInRange() && !m_BurnedWithFlintAndSteel))
|
if ((GetTarget() == nullptr) || (!TargetIsInRange() && !m_BurnedWithFlintAndSteel))
|
||||||
{
|
{
|
||||||
|
@ -189,6 +189,11 @@ bool cEnderman::CheckLight()
|
|||||||
void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cEnderman::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO take damage in rain
|
// TODO take damage in rain
|
||||||
|
|
||||||
|
@ -35,6 +35,11 @@ cHorse::cHorse(int Type, int Color, int Style, int TameTimes) :
|
|||||||
void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cHorse::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_bIsMouthOpen)
|
if (!m_bIsMouthOpen)
|
||||||
{
|
{
|
||||||
|
@ -231,6 +231,11 @@ void cMonster::StopMovingToPosition()
|
|||||||
void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT);
|
GET_AND_VERIFY_CURRENT_CHUNK(Chunk, POSX_TOINT, POSZ_TOINT);
|
||||||
|
|
||||||
ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == GetWorld())));
|
ASSERT((GetTarget() == nullptr) || (GetTarget()->IsPawn() && (GetTarget()->GetWorld() == GetWorld())));
|
||||||
|
@ -81,6 +81,11 @@ void cPassiveMonster::Destroyed()
|
|||||||
void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cPassiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_EMState == ESCAPING)
|
if (m_EMState == ESCAPING)
|
||||||
{
|
{
|
||||||
|
@ -85,6 +85,11 @@ void cPig::OnRightClicked(cPlayer & a_Player)
|
|||||||
void cPig::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cPig::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If the attachee player is holding a carrot-on-stick, let them drive this pig:
|
// If the attachee player is holding a carrot-on-stick, let them drive this pig:
|
||||||
if (m_bIsSaddled && (m_Attachee != nullptr))
|
if (m_bIsSaddled && (m_Attachee != nullptr))
|
||||||
|
@ -88,6 +88,11 @@ void cSheep::OnRightClicked(cPlayer & a_Player)
|
|||||||
void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cSheep::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
int PosX = POSX_TOINT;
|
int PosX = POSX_TOINT;
|
||||||
int PosY = POSY_TOINT - 1;
|
int PosY = POSY_TOINT - 1;
|
||||||
int PosZ = POSZ_TOINT;
|
int PosZ = POSZ_TOINT;
|
||||||
|
@ -30,6 +30,11 @@ void cSnowGolem::GetDrops(cItems & a_Drops, cEntity * a_Killer)
|
|||||||
void cSnowGolem::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cSnowGolem::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (IsBiomeNoDownfall(m_World->GetBiomeAt(POSX_TOINT, POSZ_TOINT)))
|
if (IsBiomeNoDownfall(m_World->GetBiomeAt(POSX_TOINT, POSZ_TOINT)))
|
||||||
{
|
{
|
||||||
TakeDamage(*this);
|
TakeDamage(*this);
|
||||||
|
@ -54,6 +54,11 @@ bool cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
|
|||||||
void cVillager::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cVillager::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_ActionCountDown > -1)
|
if (m_ActionCountDown > -1)
|
||||||
{
|
{
|
||||||
|
@ -69,6 +69,11 @@ bool cWither::DoTakeDamage(TakeDamageInfo & a_TDI)
|
|||||||
void cWither::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
void cWither::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
||||||
{
|
{
|
||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_WitherInvulnerableTicks > 0)
|
if (m_WitherInvulnerableTicks > 0)
|
||||||
{
|
{
|
||||||
|
@ -263,6 +263,12 @@ void cWolf::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
|
|||||||
super::Tick(a_Dt, a_Chunk);
|
super::Tick(a_Dt, a_Chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!IsTicking())
|
||||||
|
{
|
||||||
|
// The base class tick destroyed us
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (GetTarget() == nullptr)
|
if (GetTarget() == nullptr)
|
||||||
{
|
{
|
||||||
cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
|
cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), static_cast<float>(m_SightDistance));
|
||||||
|
@ -9,6 +9,7 @@ SET (SRCS
|
|||||||
Packetizer.cpp
|
Packetizer.cpp
|
||||||
Protocol18x.cpp
|
Protocol18x.cpp
|
||||||
Protocol19x.cpp
|
Protocol19x.cpp
|
||||||
|
Protocol110x.cpp
|
||||||
ProtocolRecognizer.cpp
|
ProtocolRecognizer.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -20,12 +21,14 @@ SET (HDRS
|
|||||||
Protocol.h
|
Protocol.h
|
||||||
Protocol18x.h
|
Protocol18x.h
|
||||||
Protocol19x.h
|
Protocol19x.h
|
||||||
|
Protocol110x.h
|
||||||
ProtocolRecognizer.h
|
ProtocolRecognizer.h
|
||||||
)
|
)
|
||||||
|
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||||
set_source_files_properties(Protocol19x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum -Wno-error=switch")
|
set_source_files_properties(Protocol19x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum -Wno-error=switch")
|
||||||
set_source_files_properties(Protocol18x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum -Wno-error=switch")
|
set_source_files_properties(Protocol18x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch-enum -Wno-error=switch")
|
||||||
|
set_source_files_properties(Protocol110x.cpp PROPERTIES COMPILE_FLAGS "-Wno-error=switch")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (NOT MSVC)
|
if (NOT MSVC)
|
||||||
|
@ -114,7 +114,7 @@ public:
|
|||||||
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0;
|
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0;
|
||||||
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0;
|
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0;
|
||||||
virtual void SendResetTitle (void) = 0;
|
virtual void SendResetTitle (void) = 0;
|
||||||
virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) = 0;
|
virtual void SendRespawn (eDimension a_Dimension) = 0;
|
||||||
virtual void SendExperience (void) = 0;
|
virtual void SendExperience (void) = 0;
|
||||||
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0;
|
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0;
|
||||||
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0;
|
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0;
|
||||||
|
879
src/Protocol/Protocol110x.cpp
Normal file
879
src/Protocol/Protocol110x.cpp
Normal file
@ -0,0 +1,879 @@
|
|||||||
|
|
||||||
|
// Protocol110x.cpp
|
||||||
|
|
||||||
|
/*
|
||||||
|
Implements the 1.10.x protocol classes:
|
||||||
|
- cProtocol1100
|
||||||
|
- release 1.10.0 protocol (#210)
|
||||||
|
(others may be added later in the future for the 1.10 release series)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Globals.h"
|
||||||
|
#include "Protocol110x.h"
|
||||||
|
#include "Packetizer.h"
|
||||||
|
|
||||||
|
#include "../Root.h"
|
||||||
|
#include "../Server.h"
|
||||||
|
|
||||||
|
#include "../Entities/Boat.h"
|
||||||
|
#include "../Entities/ExpOrb.h"
|
||||||
|
#include "../Entities/Minecart.h"
|
||||||
|
#include "../Entities/FallingBlock.h"
|
||||||
|
#include "../Entities/Painting.h"
|
||||||
|
#include "../Entities/Pickup.h"
|
||||||
|
#include "../Entities/Player.h"
|
||||||
|
#include "../Entities/ItemFrame.h"
|
||||||
|
#include "../Entities/ArrowEntity.h"
|
||||||
|
#include "../Entities/FireworkEntity.h"
|
||||||
|
#include "../Entities/SplashPotionEntity.h"
|
||||||
|
|
||||||
|
#include "../Mobs/IncludeAllMonsters.h"
|
||||||
|
|
||||||
|
#include "Bindings/PluginManager.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// The disabled error is intended, since the Metadata have overlapping indexes
|
||||||
|
// based on the type of the Entity.
|
||||||
|
//
|
||||||
|
// IMPORTANT: The enum is used to automate the sequential counting of the
|
||||||
|
// Metadata indexes. Adding a new enum value causes the following values to
|
||||||
|
// increase their index. Therefore the ordering of the enum values is VERY important!
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wduplicate-enum"
|
||||||
|
|
||||||
|
namespace Metadata
|
||||||
|
{
|
||||||
|
enum Metadata_Index
|
||||||
|
{
|
||||||
|
// Entity
|
||||||
|
ENTITY_FLAGS,
|
||||||
|
ENTITY_AIR,
|
||||||
|
ENTITY_CUSTOM_NAME,
|
||||||
|
ENTITY_CUSTOM_NAME_VISIBLE,
|
||||||
|
ENTITY_SILENT,
|
||||||
|
ENTITY_NO_GRAVITY,
|
||||||
|
_ENTITY_NEXT, // Used by descendants
|
||||||
|
|
||||||
|
// Potion
|
||||||
|
POTION_THROWN = _ENTITY_NEXT,
|
||||||
|
|
||||||
|
// FallingBlock
|
||||||
|
FALLING_BLOCK_POSITION = _ENTITY_NEXT,
|
||||||
|
|
||||||
|
// AreaEffectCloud
|
||||||
|
AREA_EFFECT_CLOUD_RADIUS = _ENTITY_NEXT,
|
||||||
|
AREA_EFFECT_CLOUD_COLOR,
|
||||||
|
AREA_EFFECT_CLOUD_SINGLE_POINT_EFFECT,
|
||||||
|
AREA_EFFECT_CLOUD_PARTICLE_ID,
|
||||||
|
AREA_EFFECT_CLOUD_PARTICLE_PARAMETER1,
|
||||||
|
AREA_EFFECT_CLOUD_PARTICLE_PARAMETER2,
|
||||||
|
|
||||||
|
// Arrow
|
||||||
|
ARROW_CRITICAL = _ENTITY_NEXT,
|
||||||
|
_ARROW_NEXT,
|
||||||
|
|
||||||
|
// TippedArrow
|
||||||
|
TIPPED_ARROW_COLOR = _ARROW_NEXT,
|
||||||
|
|
||||||
|
// Boat
|
||||||
|
BOAT_LAST_HIT_TIME = _ENTITY_NEXT,
|
||||||
|
BOAT_FORWARD_DIRECTION,
|
||||||
|
BOAT_DAMAGE_TAKEN,
|
||||||
|
BOAT_TYPE,
|
||||||
|
BOAT_RIGHT_PADDLE_TURNING,
|
||||||
|
BOAT_LEFT_PADDLE_TURNING,
|
||||||
|
|
||||||
|
// EnderCrystal
|
||||||
|
ENDER_CRYSTAL_BEAM_TARGET = _ENTITY_NEXT,
|
||||||
|
ENDER_CRYSTAL_SHOW_BOTTOM,
|
||||||
|
|
||||||
|
// Fireball
|
||||||
|
_FIREBALL_NEXT = _ENTITY_NEXT,
|
||||||
|
|
||||||
|
// WitherSkull
|
||||||
|
WITHER_SKULL_INVULNERABLE = _FIREBALL_NEXT,
|
||||||
|
|
||||||
|
// Fireworks
|
||||||
|
FIREWORK_INFO = _ENTITY_NEXT,
|
||||||
|
|
||||||
|
// Hanging
|
||||||
|
_HANGING_NEXT = _ENTITY_NEXT,
|
||||||
|
|
||||||
|
// ItemFrame
|
||||||
|
ITEM_FRAME_ITEM = _HANGING_NEXT,
|
||||||
|
ITEM_FRAME_ROTATION,
|
||||||
|
|
||||||
|
// Item
|
||||||
|
ITEM_ITEM = _ENTITY_NEXT,
|
||||||
|
|
||||||
|
// Living
|
||||||
|
LIVING_ACTIVE_HAND = _ENTITY_NEXT,
|
||||||
|
LIVING_HEALTH,
|
||||||
|
LIVING_POTION_EFFECT_COLOR,
|
||||||
|
LIVING_POTION_EFFECT_AMBIENT,
|
||||||
|
LIVING_NUMBER_OF_ARROWS,
|
||||||
|
_LIVING_NEXT,
|
||||||
|
|
||||||
|
// Player
|
||||||
|
PLAYER_ADDITIONAL_HEARTHS = _LIVING_NEXT,
|
||||||
|
PLAYER_SCORE,
|
||||||
|
PLAYER_DISPLAYED_SKIN_PARTS,
|
||||||
|
PLAYER_MAIN_HAND,
|
||||||
|
|
||||||
|
// ArmorStand
|
||||||
|
ARMOR_STAND_STATUS = _LIVING_NEXT,
|
||||||
|
ARMOR_STAND_HEAD_ROTATION,
|
||||||
|
ARMOR_STAND_BODY_ROTATION,
|
||||||
|
ARMOR_STAND_LEFT_ARM_ROTATION,
|
||||||
|
ARMOR_STAND_RIGHT_ARM_ROTATION,
|
||||||
|
ARMOR_STAND_LEFT_LEG_ROTATION,
|
||||||
|
ARMOR_STAND_RIGHT_LEG_ROTATION,
|
||||||
|
|
||||||
|
// Insentient
|
||||||
|
INSENTIENT_STATUS = _LIVING_NEXT,
|
||||||
|
_INSENTIENT_NEXT,
|
||||||
|
|
||||||
|
// Ambient
|
||||||
|
_AMBIENT_NEXT = _INSENTIENT_NEXT,
|
||||||
|
|
||||||
|
// Bat
|
||||||
|
BAT_HANGING = _AMBIENT_NEXT,
|
||||||
|
|
||||||
|
// Creature
|
||||||
|
_CREATURE_NEXT = _INSENTIENT_NEXT,
|
||||||
|
|
||||||
|
// Ageable
|
||||||
|
AGEABLE_BABY = _CREATURE_NEXT,
|
||||||
|
_AGEABLE_NEXT,
|
||||||
|
|
||||||
|
// PolarBear
|
||||||
|
POLAR_BEAR_STANDING = _AGEABLE_NEXT,
|
||||||
|
|
||||||
|
// Animal
|
||||||
|
_ANIMAL_NEXT = _AGEABLE_NEXT,
|
||||||
|
|
||||||
|
// Horse
|
||||||
|
HORSE_STATUS = _ANIMAL_NEXT,
|
||||||
|
HORSE_TYPE,
|
||||||
|
HORSE_VARIANT,
|
||||||
|
HORSE_OWNER,
|
||||||
|
HORSE_ARMOR,
|
||||||
|
|
||||||
|
// Pig
|
||||||
|
PIG_HAS_SADDLE = _ANIMAL_NEXT,
|
||||||
|
|
||||||
|
// Rabbit
|
||||||
|
RABBIT_TYPE = _ANIMAL_NEXT,
|
||||||
|
|
||||||
|
// Sheep
|
||||||
|
SHEEP_STATUS = _ANIMAL_NEXT,
|
||||||
|
|
||||||
|
// TameableAnimal
|
||||||
|
TAMEABLE_ANIMAL_STATUS = _ANIMAL_NEXT,
|
||||||
|
TAMEABLE_ANIMAL_OWNER,
|
||||||
|
_TAMEABLE_NEXT,
|
||||||
|
|
||||||
|
// Ocelot
|
||||||
|
OCELOT_TYPE = _TAMEABLE_NEXT,
|
||||||
|
|
||||||
|
// Wolf
|
||||||
|
WOLF_DAMAGE_TAKEN = _TAMEABLE_NEXT,
|
||||||
|
WOLF_BEGGING,
|
||||||
|
WOLF_COLLAR_COLOR,
|
||||||
|
|
||||||
|
// Villager
|
||||||
|
VILLAGER_PROFESSION = _AGEABLE_NEXT,
|
||||||
|
|
||||||
|
// Golem
|
||||||
|
_GOLEM_NEXT = _CREATURE_NEXT,
|
||||||
|
|
||||||
|
// IronGolem
|
||||||
|
IRON_GOLEM_PLAYER_CREATED = _GOLEM_NEXT,
|
||||||
|
|
||||||
|
// Shulker
|
||||||
|
SHULKER_FACING_DIRECTION = _GOLEM_NEXT,
|
||||||
|
SHULKER_ATTACHMENT_FALLING_BLOCK_POSITION,
|
||||||
|
SHULKER_SHIELD_HEIGHT,
|
||||||
|
|
||||||
|
// Monster
|
||||||
|
_MONSTER_NEXT = _CREATURE_NEXT,
|
||||||
|
|
||||||
|
// Blaze
|
||||||
|
BLAZE_ON_FIRE = _MONSTER_NEXT,
|
||||||
|
|
||||||
|
// Creeper
|
||||||
|
CREEPER_STATE = _MONSTER_NEXT,
|
||||||
|
CREEPER_POWERED,
|
||||||
|
CREEPER_IGNITED,
|
||||||
|
|
||||||
|
// Guardian
|
||||||
|
GUARDIAN_STATUS = _MONSTER_NEXT,
|
||||||
|
GUARDIAN_TARGET,
|
||||||
|
|
||||||
|
// Skeleton
|
||||||
|
SKELETON_TYPE = _MONSTER_NEXT,
|
||||||
|
SKELETON_ARMS_SWINGING,
|
||||||
|
|
||||||
|
// Spider
|
||||||
|
SPIDER_CLIMBING = _MONSTER_NEXT,
|
||||||
|
|
||||||
|
// Witch
|
||||||
|
WITCH_AGGRESIVE = _MONSTER_NEXT,
|
||||||
|
|
||||||
|
// Wither
|
||||||
|
WITHER_FIRST_HEAD_TARGET = _MONSTER_NEXT,
|
||||||
|
WITHER_SECOND_HEAD_TARGET,
|
||||||
|
WITHER_THIRD_HEAD_TARGET,
|
||||||
|
WITHER_INVULNERABLE_TIMER,
|
||||||
|
|
||||||
|
// Zombie
|
||||||
|
ZOMBIE_IS_BABY = _MONSTER_NEXT,
|
||||||
|
ZOMBIE_TYPE,
|
||||||
|
ZOMBIE_CONVERTING,
|
||||||
|
ZOMBIE_HANDS_RISED_UP,
|
||||||
|
|
||||||
|
// Enderman
|
||||||
|
ENDERMAN_CARRIED_BLOCK = _MONSTER_NEXT,
|
||||||
|
ENDERMAN_SCREAMING,
|
||||||
|
|
||||||
|
// EnderDragon
|
||||||
|
ENDER_DRAGON_DRAGON_PHASE = _INSENTIENT_NEXT,
|
||||||
|
|
||||||
|
// Flying
|
||||||
|
_FLYING_NEXT = _INSENTIENT_NEXT,
|
||||||
|
|
||||||
|
// Ghast
|
||||||
|
GHAST_ATTACKING = _FLYING_NEXT,
|
||||||
|
|
||||||
|
// Slime
|
||||||
|
SLIME_SIZE = _INSENTIENT_NEXT,
|
||||||
|
|
||||||
|
// Minecart
|
||||||
|
MINECART_SHAKING_POWER = _ENTITY_NEXT,
|
||||||
|
MINECART_SHAKING_DIRECTION,
|
||||||
|
MINECART_SHAKING_MULTIPLIER,
|
||||||
|
MINECART_BLOCK_ID_META,
|
||||||
|
MINECART_BLOCK_Y,
|
||||||
|
MINECART_SHOW_BLOCK,
|
||||||
|
_MINECART_NEXT,
|
||||||
|
|
||||||
|
// MinecartCommandBlock
|
||||||
|
MINECART_COMMAND_BLOCK_COMMAND = _MINECART_NEXT,
|
||||||
|
MINECART_COMMAND_BLOCK_LAST_OUTPUT,
|
||||||
|
|
||||||
|
// MinecartFurnace
|
||||||
|
MINECART_FURNACE_POWERED = _MINECART_NEXT,
|
||||||
|
|
||||||
|
// TNTPrimed
|
||||||
|
TNT_PRIMED_FUSE_TIME = _ENTITY_NEXT,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma clang diagnostic pop // Restore ignored clang errors
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
cProtocol1100::cProtocol1100(cClientHandle * a_Client, const AString &a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) :
|
||||||
|
super(a_Client, a_ServerAddress, a_ServerPort, a_State)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cProtocol1100::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch)
|
||||||
|
{
|
||||||
|
ASSERT(m_State == 3); // In game mode?
|
||||||
|
|
||||||
|
cPacketizer Pkt(*this, 0x19); // Named sound effect packet
|
||||||
|
Pkt.WriteString(a_SoundName);
|
||||||
|
Pkt.WriteVarInt32(0); // Master sound category (may want to be changed to a parameter later)
|
||||||
|
Pkt.WriteBEInt32(FloorC(a_X * 8.0));
|
||||||
|
Pkt.WriteBEInt32(FloorC(a_Y * 8.0));
|
||||||
|
Pkt.WriteBEInt32(FloorC(a_Z * 8.0));
|
||||||
|
Pkt.WriteBEFloat(a_Volume);
|
||||||
|
Pkt.WriteBEFloat(a_Pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cProtocol1100::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
|
||||||
|
{
|
||||||
|
cServer * Server = cRoot::Get()->GetServer();
|
||||||
|
AString ServerDescription = Server->GetDescription();
|
||||||
|
int NumPlayers = Server->GetNumPlayers();
|
||||||
|
int MaxPlayers = Server->GetMaxPlayers();
|
||||||
|
AString Favicon = Server->GetFaviconData();
|
||||||
|
cRoot::Get()->GetPluginManager()->CallHookServerPing(*m_Client, ServerDescription, NumPlayers, MaxPlayers, Favicon);
|
||||||
|
|
||||||
|
// Version:
|
||||||
|
Json::Value Version;
|
||||||
|
Version["name"] = "Cuberite 1.10";
|
||||||
|
Version["protocol"] = 210;
|
||||||
|
|
||||||
|
// Players:
|
||||||
|
Json::Value Players;
|
||||||
|
Players["online"] = NumPlayers;
|
||||||
|
Players["max"] = MaxPlayers;
|
||||||
|
// TODO: Add "sample"
|
||||||
|
|
||||||
|
// Description:
|
||||||
|
Json::Value Description;
|
||||||
|
Description["text"] = ServerDescription.c_str();
|
||||||
|
|
||||||
|
// Create the response:
|
||||||
|
Json::Value ResponseValue;
|
||||||
|
ResponseValue["version"] = Version;
|
||||||
|
ResponseValue["players"] = Players;
|
||||||
|
ResponseValue["description"] = Description;
|
||||||
|
if (!Favicon.empty())
|
||||||
|
{
|
||||||
|
ResponseValue["favicon"] = Printf("data:image/png;base64,%s", Favicon.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
Json::StyledWriter Writer;
|
||||||
|
AString Response = Writer.write(ResponseValue);
|
||||||
|
|
||||||
|
cPacketizer Pkt(*this, 0x00); // Response packet
|
||||||
|
Pkt.WriteString(Response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cProtocol1100::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity)
|
||||||
|
{
|
||||||
|
using namespace Metadata;
|
||||||
|
|
||||||
|
// Common metadata:
|
||||||
|
Int8 Flags = 0;
|
||||||
|
if (a_Entity.IsOnFire())
|
||||||
|
{
|
||||||
|
Flags |= 0x01;
|
||||||
|
}
|
||||||
|
if (a_Entity.IsCrouched())
|
||||||
|
{
|
||||||
|
Flags |= 0x02;
|
||||||
|
}
|
||||||
|
if (a_Entity.IsSprinting())
|
||||||
|
{
|
||||||
|
Flags |= 0x08;
|
||||||
|
}
|
||||||
|
if (a_Entity.IsRclking())
|
||||||
|
{
|
||||||
|
Flags |= 0x10;
|
||||||
|
}
|
||||||
|
if (a_Entity.IsInvisible())
|
||||||
|
{
|
||||||
|
Flags |= 0x20;
|
||||||
|
}
|
||||||
|
a_Pkt.WriteBEUInt8(ENTITY_FLAGS); // Index
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE); // Type
|
||||||
|
a_Pkt.WriteBEInt8(Flags);
|
||||||
|
|
||||||
|
switch (a_Entity.GetEntityType())
|
||||||
|
{
|
||||||
|
case cEntity::etPlayer:
|
||||||
|
{
|
||||||
|
auto & Player = reinterpret_cast<const cPlayer &>(a_Entity);
|
||||||
|
|
||||||
|
// TODO Set player custom name to their name.
|
||||||
|
// Then it's possible to move the custom name of mobs to the entities
|
||||||
|
// and to remove the "special" player custom name.
|
||||||
|
a_Pkt.WriteBEUInt8(ENTITY_CUSTOM_NAME);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_STRING);
|
||||||
|
a_Pkt.WriteString(Player.GetName());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(LIVING_HEALTH);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT);
|
||||||
|
a_Pkt.WriteBEFloat(static_cast<float>(Player.GetHealth()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case cEntity::etPickup:
|
||||||
|
{
|
||||||
|
a_Pkt.WriteBEUInt8(ITEM_ITEM);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_ITEM);
|
||||||
|
WriteItem(a_Pkt, reinterpret_cast<const cPickup &>(a_Entity).GetItem());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case cEntity::etMinecart:
|
||||||
|
{
|
||||||
|
a_Pkt.WriteBEUInt8(MINECART_SHAKING_POWER);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
|
||||||
|
// The following expression makes Minecarts shake more with less health or higher damage taken
|
||||||
|
auto & Minecart = reinterpret_cast<const cMinecart &>(a_Entity);
|
||||||
|
auto maxHealth = a_Entity.GetMaxHealth();
|
||||||
|
auto curHealth = a_Entity.GetHealth();
|
||||||
|
a_Pkt.WriteVarInt32(static_cast<UInt32>((maxHealth - curHealth) * Minecart.LastDamage() * 4));
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(MINECART_SHAKING_DIRECTION); // (doesn't seem to effect anything)
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(1);
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(MINECART_SHAKING_MULTIPLIER); // or damage taken
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT);
|
||||||
|
a_Pkt.WriteBEFloat(static_cast<float>(Minecart.LastDamage() + 10));
|
||||||
|
|
||||||
|
if (Minecart.GetPayload() == cMinecart::mpNone)
|
||||||
|
{
|
||||||
|
auto & RideableMinecart = reinterpret_cast<const cRideableMinecart &>(Minecart);
|
||||||
|
const cItem & MinecartContent = RideableMinecart.GetContent();
|
||||||
|
if (!MinecartContent.IsEmpty())
|
||||||
|
{
|
||||||
|
a_Pkt.WriteBEUInt8(MINECART_BLOCK_ID_META);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
int Content = MinecartContent.m_ItemType;
|
||||||
|
Content |= MinecartContent.m_ItemDamage << 8;
|
||||||
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Content));
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(MINECART_BLOCK_Y);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(RideableMinecart.GetBlockHeight()));
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(MINECART_SHOW_BLOCK);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Minecart.GetPayload() == cMinecart::mpFurnace)
|
||||||
|
{
|
||||||
|
a_Pkt.WriteBEUInt8(MINECART_FURNACE_POWERED);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(reinterpret_cast<const cMinecartWithFurnace &>(Minecart).IsFueled());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} // case etMinecart
|
||||||
|
|
||||||
|
case cEntity::etProjectile:
|
||||||
|
{
|
||||||
|
auto & Projectile = reinterpret_cast<const cProjectileEntity &>(a_Entity);
|
||||||
|
switch (Projectile.GetProjectileKind())
|
||||||
|
{
|
||||||
|
case cProjectileEntity::pkArrow:
|
||||||
|
{
|
||||||
|
a_Pkt.WriteBEUInt8(ARROW_CRITICAL);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
||||||
|
a_Pkt.WriteBEInt8(reinterpret_cast<const cArrowEntity &>(Projectile).IsCritical() ? 1 : 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case cProjectileEntity::pkFirework:
|
||||||
|
{
|
||||||
|
a_Pkt.WriteBEUInt8(FIREWORK_INFO); // Firework item used for this firework
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_ITEM);
|
||||||
|
WriteItem(a_Pkt, reinterpret_cast<const cFireworkEntity &>(Projectile).GetItem());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case cProjectileEntity::pkSplashPotion:
|
||||||
|
{
|
||||||
|
a_Pkt.WriteBEUInt8(POTION_THROWN); // Potion item which was thrown
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_ITEM);
|
||||||
|
WriteItem(a_Pkt, reinterpret_cast<const cSplashPotionEntity &>(Projectile).GetItem());
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} // case etProjectile
|
||||||
|
|
||||||
|
case cEntity::etMonster:
|
||||||
|
{
|
||||||
|
WriteMobMetadata(a_Pkt, reinterpret_cast<const cMonster &>(a_Entity));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case cEntity::etBoat:
|
||||||
|
{
|
||||||
|
auto & Boat = reinterpret_cast<const cBoat &>(a_Entity);
|
||||||
|
|
||||||
|
a_Pkt.WriteBEInt8(BOAT_LAST_HIT_TIME);
|
||||||
|
a_Pkt.WriteBEInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteBEInt32(Boat.GetLastDamage());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEInt8(BOAT_FORWARD_DIRECTION);
|
||||||
|
a_Pkt.WriteBEInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteBEInt32(Boat.GetForwardDirection());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEInt8(BOAT_DAMAGE_TAKEN);
|
||||||
|
a_Pkt.WriteBEInt8(METADATA_TYPE_FLOAT);
|
||||||
|
a_Pkt.WriteBEFloat(Boat.GetDamageTaken());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEInt8(BOAT_TYPE);
|
||||||
|
a_Pkt.WriteBEInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteBEInt32(Boat.GetType());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEInt8(BOAT_RIGHT_PADDLE_TURNING);
|
||||||
|
a_Pkt.WriteBEInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Boat.IsRightPaddleUsed());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEInt8(BOAT_LEFT_PADDLE_TURNING);
|
||||||
|
a_Pkt.WriteBEInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Boat.IsLeftPaddleUsed());
|
||||||
|
|
||||||
|
break;
|
||||||
|
} // case etBoat
|
||||||
|
|
||||||
|
case cEntity::etItemFrame:
|
||||||
|
{
|
||||||
|
auto & Frame = reinterpret_cast<const cItemFrame &>(a_Entity);
|
||||||
|
a_Pkt.WriteBEUInt8(ITEM_FRAME_ITEM);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_ITEM);
|
||||||
|
WriteItem(a_Pkt, Frame.GetItem());
|
||||||
|
a_Pkt.WriteBEUInt8(ITEM_FRAME_ROTATION);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(Frame.GetItemRotation());
|
||||||
|
break;
|
||||||
|
} // case etItemFrame
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void cProtocol1100::WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob)
|
||||||
|
{
|
||||||
|
using namespace Metadata;
|
||||||
|
|
||||||
|
// Living Enitiy Metadata
|
||||||
|
if (a_Mob.HasCustomName())
|
||||||
|
{
|
||||||
|
// TODO: As of 1.9 _all_ entities can have custom names; should this be moved up?
|
||||||
|
a_Pkt.WriteBEUInt8(ENTITY_CUSTOM_NAME);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_STRING);
|
||||||
|
a_Pkt.WriteString(a_Mob.GetCustomName());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(ENTITY_CUSTOM_NAME_VISIBLE); // Custom name always visible
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(a_Mob.IsCustomNameAlwaysVisible());
|
||||||
|
}
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(LIVING_HEALTH);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT);
|
||||||
|
a_Pkt.WriteBEFloat(static_cast<float>(a_Mob.GetHealth()));
|
||||||
|
|
||||||
|
switch (a_Mob.GetMobType())
|
||||||
|
{
|
||||||
|
case mtBat:
|
||||||
|
{
|
||||||
|
auto & Bat = reinterpret_cast<const cBat &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(BAT_HANGING);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
||||||
|
a_Pkt.WriteBEInt8(Bat.IsHanging() ? 1 : 0);
|
||||||
|
break;
|
||||||
|
} // case mtBat
|
||||||
|
|
||||||
|
case mtCreeper:
|
||||||
|
{
|
||||||
|
auto & Creeper = reinterpret_cast<const cCreeper &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(CREEPER_STATE); // (idle or "blowing")
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(Creeper.IsBlowing() ? 1 : static_cast<UInt32>(-1));
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(CREEPER_POWERED);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Creeper.IsCharged());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(CREEPER_IGNITED);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Creeper.IsBurnedWithFlintAndSteel());
|
||||||
|
break;
|
||||||
|
} // case mtCreeper
|
||||||
|
|
||||||
|
case mtEnderman:
|
||||||
|
{
|
||||||
|
auto & Enderman = reinterpret_cast<const cEnderman &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(ENDERMAN_CARRIED_BLOCK);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BLOCKID);
|
||||||
|
UInt32 Carried = 0;
|
||||||
|
Carried |= static_cast<UInt32>(Enderman.GetCarriedBlock() << 4);
|
||||||
|
Carried |= Enderman.GetCarriedMeta();
|
||||||
|
a_Pkt.WriteVarInt32(Carried);
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(ENDERMAN_SCREAMING);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Enderman.IsScreaming());
|
||||||
|
break;
|
||||||
|
} // case mtEnderman
|
||||||
|
|
||||||
|
case mtGhast:
|
||||||
|
{
|
||||||
|
auto & Ghast = reinterpret_cast<const cGhast &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(GHAST_ATTACKING);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Ghast.IsCharging());
|
||||||
|
break;
|
||||||
|
} // case mtGhast
|
||||||
|
|
||||||
|
case mtHorse:
|
||||||
|
{
|
||||||
|
auto & Horse = reinterpret_cast<const cHorse &>(a_Mob);
|
||||||
|
Int8 Flags = 0;
|
||||||
|
if (Horse.IsTame())
|
||||||
|
{
|
||||||
|
Flags |= 0x02;
|
||||||
|
}
|
||||||
|
if (Horse.IsSaddled())
|
||||||
|
{
|
||||||
|
Flags |= 0x04;
|
||||||
|
}
|
||||||
|
if (Horse.IsChested())
|
||||||
|
{
|
||||||
|
Flags |= 0x08;
|
||||||
|
}
|
||||||
|
if (Horse.IsEating())
|
||||||
|
{
|
||||||
|
Flags |= 0x20;
|
||||||
|
}
|
||||||
|
if (Horse.IsRearing())
|
||||||
|
{
|
||||||
|
Flags |= 0x40;
|
||||||
|
}
|
||||||
|
if (Horse.IsMthOpen())
|
||||||
|
{
|
||||||
|
Flags |= 0x80;
|
||||||
|
}
|
||||||
|
a_Pkt.WriteBEUInt8(HORSE_STATUS);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
||||||
|
a_Pkt.WriteBEInt8(Flags);
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(HORSE_TYPE);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Horse.GetHorseType()));
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(HORSE_VARIANT); // Color / style
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
int Appearance = 0;
|
||||||
|
Appearance = Horse.GetHorseColor();
|
||||||
|
Appearance |= Horse.GetHorseStyle() << 8;
|
||||||
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Appearance));
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(HORSE_ARMOR);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Horse.GetHorseArmour()));
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Horse.IsBaby());
|
||||||
|
break;
|
||||||
|
} // case mtHorse
|
||||||
|
|
||||||
|
case mtMagmaCube:
|
||||||
|
{
|
||||||
|
auto & MagmaCube = reinterpret_cast<const cMagmaCube &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(SLIME_SIZE);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(MagmaCube.GetSize()));
|
||||||
|
break;
|
||||||
|
} // case mtMagmaCube
|
||||||
|
|
||||||
|
case mtOcelot:
|
||||||
|
{
|
||||||
|
auto & Ocelot = reinterpret_cast<const cOcelot &>(a_Mob);
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Ocelot.IsBaby());
|
||||||
|
break;
|
||||||
|
} // case mtOcelot
|
||||||
|
|
||||||
|
case mtCow:
|
||||||
|
{
|
||||||
|
auto & Cow = reinterpret_cast<const cCow &>(a_Mob);
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Cow.IsBaby());
|
||||||
|
break;
|
||||||
|
} // case mtCow
|
||||||
|
|
||||||
|
case mtChicken:
|
||||||
|
{
|
||||||
|
auto & Chicken = reinterpret_cast<const cChicken &>(a_Mob);
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Chicken.IsBaby());
|
||||||
|
break;
|
||||||
|
} // case mtChicken
|
||||||
|
|
||||||
|
case mtPig:
|
||||||
|
{
|
||||||
|
auto & Pig = reinterpret_cast<const cPig &>(a_Mob);
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Pig.IsBaby());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(PIG_HAS_SADDLE);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Pig.IsSaddled());
|
||||||
|
|
||||||
|
break;
|
||||||
|
} // case mtPig
|
||||||
|
|
||||||
|
case mtSheep:
|
||||||
|
{
|
||||||
|
auto & Sheep = reinterpret_cast<const cSheep &>(a_Mob);
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Sheep.IsBaby());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(SHEEP_STATUS);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
||||||
|
Int8 SheepMetadata = 0;
|
||||||
|
SheepMetadata = static_cast<Int8>(Sheep.GetFurColor());
|
||||||
|
if (Sheep.IsSheared())
|
||||||
|
{
|
||||||
|
SheepMetadata |= 0x10;
|
||||||
|
}
|
||||||
|
a_Pkt.WriteBEInt8(SheepMetadata);
|
||||||
|
break;
|
||||||
|
} // case mtSheep
|
||||||
|
|
||||||
|
case mtRabbit:
|
||||||
|
{
|
||||||
|
auto & Rabbit = reinterpret_cast<const cRabbit &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Rabbit.IsBaby());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(RABBIT_TYPE);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Rabbit.GetRabbitType()));
|
||||||
|
break;
|
||||||
|
} // case mtRabbit
|
||||||
|
|
||||||
|
case mtSkeleton:
|
||||||
|
{
|
||||||
|
auto & Skeleton = reinterpret_cast<const cSkeleton &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(SKELETON_TYPE);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(Skeleton.IsWither() ? 1 : 0);
|
||||||
|
break;
|
||||||
|
} // case mtSkeleton
|
||||||
|
|
||||||
|
case mtSlime:
|
||||||
|
{
|
||||||
|
auto & Slime = reinterpret_cast<const cSlime &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(SLIME_SIZE);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Slime.GetSize()));
|
||||||
|
break;
|
||||||
|
} // case mtSlime
|
||||||
|
|
||||||
|
case mtVillager:
|
||||||
|
{
|
||||||
|
auto & Villager = reinterpret_cast<const cVillager &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Villager.IsBaby());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(VILLAGER_PROFESSION);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Villager.GetVilType()));
|
||||||
|
break;
|
||||||
|
} // case mtVillager
|
||||||
|
|
||||||
|
case mtWitch:
|
||||||
|
{
|
||||||
|
auto & Witch = reinterpret_cast<const cWitch &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(WITCH_AGGRESIVE);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Witch.IsAngry());
|
||||||
|
break;
|
||||||
|
} // case mtWitch
|
||||||
|
|
||||||
|
case mtWither:
|
||||||
|
{
|
||||||
|
auto & Wither = reinterpret_cast<const cWither &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(WITHER_INVULNERABLE_TIMER);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(Wither.GetWitherInvulnerableTicks());
|
||||||
|
|
||||||
|
// TODO: Use boss bar packet for health
|
||||||
|
break;
|
||||||
|
} // case mtWither
|
||||||
|
|
||||||
|
case mtWolf:
|
||||||
|
{
|
||||||
|
auto & Wolf = reinterpret_cast<const cWolf &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Wolf.IsBaby());
|
||||||
|
|
||||||
|
Int8 WolfStatus = 0;
|
||||||
|
if (Wolf.IsSitting())
|
||||||
|
{
|
||||||
|
WolfStatus |= 0x1;
|
||||||
|
}
|
||||||
|
if (Wolf.IsAngry())
|
||||||
|
{
|
||||||
|
WolfStatus |= 0x2;
|
||||||
|
}
|
||||||
|
if (Wolf.IsTame())
|
||||||
|
{
|
||||||
|
WolfStatus |= 0x4;
|
||||||
|
}
|
||||||
|
a_Pkt.WriteBEUInt8(TAMEABLE_ANIMAL_STATUS);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BYTE);
|
||||||
|
a_Pkt.WriteBEInt8(WolfStatus);
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(WOLF_DAMAGE_TAKEN);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT);
|
||||||
|
a_Pkt.WriteBEFloat(static_cast<float>(a_Mob.GetHealth())); // TODO Not use the current health
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(WOLF_BEGGING);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Wolf.IsBegging());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(WOLF_COLLAR_COLOR);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(static_cast<UInt32>(Wolf.GetCollarColor()));
|
||||||
|
break;
|
||||||
|
} // case mtWolf
|
||||||
|
|
||||||
|
case mtZombie:
|
||||||
|
{
|
||||||
|
auto & Zombie = reinterpret_cast<const cZombie &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Zombie.IsBaby());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(ZOMBIE_TYPE);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_VARINT);
|
||||||
|
a_Pkt.WriteVarInt32(Zombie.IsVillagerZombie() ? 1 : 0); // TODO: This actually encodes the zombie villager profession, but that isn't implemented yet.
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(ZOMBIE_CONVERTING);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(Zombie.IsConverting());
|
||||||
|
break;
|
||||||
|
} // case mtZombie
|
||||||
|
|
||||||
|
case mtZombiePigman:
|
||||||
|
{
|
||||||
|
auto & ZombiePigman = reinterpret_cast<const cZombiePigman &>(a_Mob);
|
||||||
|
a_Pkt.WriteBEUInt8(AGEABLE_BABY);
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL);
|
||||||
|
a_Pkt.WriteBool(ZombiePigman.IsBaby());
|
||||||
|
break;
|
||||||
|
} // case mtZombiePigman
|
||||||
|
} // switch (a_Mob.GetType())
|
||||||
|
}
|
34
src/Protocol/Protocol110x.h
Normal file
34
src/Protocol/Protocol110x.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
|
||||||
|
// Protocol110x.h
|
||||||
|
|
||||||
|
/*
|
||||||
|
Declares the 1.10.x protocol classes:
|
||||||
|
- cProtocol1100
|
||||||
|
- release 1.10.0 protocol (#210)
|
||||||
|
(others may be added later in the future for the 1.10 release series)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Protocol19x.h"
|
||||||
|
|
||||||
|
class cProtocol1100 :
|
||||||
|
public cProtocol194
|
||||||
|
{
|
||||||
|
typedef cProtocol194 super;
|
||||||
|
|
||||||
|
public:
|
||||||
|
cProtocol1100(cClientHandle * a_Client, const AString &a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State);
|
||||||
|
|
||||||
|
virtual void SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
|
||||||
|
|
||||||
|
virtual void HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity) override;
|
||||||
|
virtual void WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob) override;
|
||||||
|
};
|
@ -107,8 +107,7 @@ cProtocol180::cProtocol180(cClientHandle * a_Client, const AString & a_ServerAdd
|
|||||||
m_ServerPort(a_ServerPort),
|
m_ServerPort(a_ServerPort),
|
||||||
m_State(a_State),
|
m_State(a_State),
|
||||||
m_ReceivedData(32 KiB),
|
m_ReceivedData(32 KiB),
|
||||||
m_IsEncrypted(false),
|
m_IsEncrypted(false)
|
||||||
m_LastSentDimension(dimNotSet)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// BungeeCord handling:
|
// BungeeCord handling:
|
||||||
@ -626,7 +625,6 @@ void cProtocol180::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
|
|||||||
Pkt.WriteString("default"); // Level type - wtf?
|
Pkt.WriteString("default"); // Level type - wtf?
|
||||||
Pkt.WriteBool(false); // Reduced Debug Info - wtf?
|
Pkt.WriteBool(false); // Reduced Debug Info - wtf?
|
||||||
}
|
}
|
||||||
m_LastSentDimension = a_World.GetDimension();
|
|
||||||
|
|
||||||
// Send the spawn position:
|
// Send the spawn position:
|
||||||
{
|
{
|
||||||
@ -1084,13 +1082,8 @@ void cProtocol180::SendResetTitle(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cProtocol180::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
|
void cProtocol180::SendRespawn(eDimension a_Dimension)
|
||||||
{
|
{
|
||||||
if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks)
|
|
||||||
{
|
|
||||||
// Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cPacketizer Pkt(*this, 0x07); // Respawn packet
|
cPacketizer Pkt(*this, 0x07); // Respawn packet
|
||||||
cPlayer * Player = m_Client->GetPlayer();
|
cPlayer * Player = m_Client->GetPlayer();
|
||||||
@ -1098,7 +1091,6 @@ void cProtocol180::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimens
|
|||||||
Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal)
|
Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal)
|
||||||
Pkt.WriteBEUInt8(static_cast<Byte>(Player->GetEffectiveGameMode()));
|
Pkt.WriteBEUInt8(static_cast<Byte>(Player->GetEffectiveGameMode()));
|
||||||
Pkt.WriteString("default");
|
Pkt.WriteString("default");
|
||||||
m_LastSentDimension = a_Dimension;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ public:
|
|||||||
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
|
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
|
||||||
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
|
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
|
||||||
virtual void SendResetTitle (void) override;
|
virtual void SendResetTitle (void) override;
|
||||||
virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
|
virtual void SendRespawn (eDimension a_Dimension) override;
|
||||||
virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
|
virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
|
||||||
virtual void SendExperience (void) override;
|
virtual void SendExperience (void) override;
|
||||||
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
|
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
|
||||||
@ -177,11 +177,6 @@ protected:
|
|||||||
/** The logfile where the comm is logged, when g_ShouldLogComm is true */
|
/** The logfile where the comm is logged, when g_ShouldLogComm is true */
|
||||||
cFile m_CommLogFile;
|
cFile m_CommLogFile;
|
||||||
|
|
||||||
/** The dimension that was last sent to a player in a Respawn or Login packet.
|
|
||||||
Used to avoid Respawning into the same dimension, which confuses the client. */
|
|
||||||
eDimension m_LastSentDimension;
|
|
||||||
|
|
||||||
|
|
||||||
/** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */
|
/** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */
|
||||||
void AddReceivedData(const char * a_Data, size_t a_Size);
|
void AddReceivedData(const char * a_Data, size_t a_Size);
|
||||||
|
|
||||||
|
@ -117,8 +117,7 @@ cProtocol190::cProtocol190(cClientHandle * a_Client, const AString & a_ServerAdd
|
|||||||
m_ServerPort(a_ServerPort),
|
m_ServerPort(a_ServerPort),
|
||||||
m_State(a_State),
|
m_State(a_State),
|
||||||
m_ReceivedData(32 KiB),
|
m_ReceivedData(32 KiB),
|
||||||
m_IsEncrypted(false),
|
m_IsEncrypted(false)
|
||||||
m_LastSentDimension(dimNotSet)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
// BungeeCord handling:
|
// BungeeCord handling:
|
||||||
@ -640,7 +639,6 @@ void cProtocol190::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
|
|||||||
Pkt.WriteString("default"); // Level type - wtf?
|
Pkt.WriteString("default"); // Level type - wtf?
|
||||||
Pkt.WriteBool(false); // Reduced Debug Info - wtf?
|
Pkt.WriteBool(false); // Reduced Debug Info - wtf?
|
||||||
}
|
}
|
||||||
m_LastSentDimension = a_World.GetDimension();
|
|
||||||
|
|
||||||
// Send the spawn position:
|
// Send the spawn position:
|
||||||
{
|
{
|
||||||
@ -741,7 +739,7 @@ void cProtocol190::SendPickupSpawn(const cPickup & a_Pickup)
|
|||||||
{
|
{
|
||||||
ASSERT(m_State == 3); // In game mode?
|
ASSERT(m_State == 3); // In game mode?
|
||||||
|
|
||||||
{
|
{ // TODO Use SendSpawnObject
|
||||||
cPacketizer Pkt(*this, 0x00); // Spawn Object packet
|
cPacketizer Pkt(*this, 0x00); // Spawn Object packet
|
||||||
Pkt.WriteVarInt32(a_Pickup.GetUniqueID());
|
Pkt.WriteVarInt32(a_Pickup.GetUniqueID());
|
||||||
// TODO: Bad way to write a UUID, and it's not a true UUID, but this is functional for now.
|
// TODO: Bad way to write a UUID, and it's not a true UUID, but this is functional for now.
|
||||||
@ -759,14 +757,7 @@ void cProtocol190::SendPickupSpawn(const cPickup & a_Pickup)
|
|||||||
Pkt.WriteBEInt16(0);
|
Pkt.WriteBEInt16(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
SendEntityMetadata(a_Pickup);
|
||||||
cPacketizer Pkt(*this, 0x39); // Entity Metadata packet
|
|
||||||
Pkt.WriteVarInt32(a_Pickup.GetUniqueID());
|
|
||||||
Pkt.WriteBEUInt8(5); // Index 5: Item
|
|
||||||
Pkt.WriteBEUInt8(METADATA_TYPE_ITEM);
|
|
||||||
WriteItem(Pkt, a_Pickup.GetItem());
|
|
||||||
Pkt.WriteBEUInt8(0xff); // End of metadata
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1059,12 +1050,7 @@ void cProtocol190::SendPlayerSpawn(const cPlayer & a_Player)
|
|||||||
Pkt.WriteBEDouble(a_Player.GetPosZ());
|
Pkt.WriteBEDouble(a_Player.GetPosZ());
|
||||||
Pkt.WriteByteAngle(a_Player.GetYaw());
|
Pkt.WriteByteAngle(a_Player.GetYaw());
|
||||||
Pkt.WriteByteAngle(a_Player.GetPitch());
|
Pkt.WriteByteAngle(a_Player.GetPitch());
|
||||||
Pkt.WriteBEUInt8(6); // Start metadata - Index 6: Health
|
WriteEntityMetadata(Pkt, a_Player);
|
||||||
Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT);
|
|
||||||
Pkt.WriteBEFloat(static_cast<float>(a_Player.GetHealth()));
|
|
||||||
Pkt.WriteBEUInt8(2); // Index 2: Custom name
|
|
||||||
Pkt.WriteBEUInt8(METADATA_TYPE_STRING);
|
|
||||||
Pkt.WriteString(a_Player.GetName());
|
|
||||||
Pkt.WriteBEUInt8(0xff); // Metadata: end
|
Pkt.WriteBEUInt8(0xff); // Metadata: end
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1110,21 +1096,14 @@ void cProtocol190::SendResetTitle(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cProtocol190::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
|
void cProtocol190::SendRespawn(eDimension a_Dimension)
|
||||||
{
|
{
|
||||||
if ((m_LastSentDimension == a_Dimension) && !a_ShouldIgnoreDimensionChecks)
|
|
||||||
{
|
|
||||||
// Must not send a respawn for the world with the same dimension, the client goes cuckoo if we do (unless we are respawning from death)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cPacketizer Pkt(*this, 0x33); // Respawn packet
|
cPacketizer Pkt(*this, 0x33); // Respawn packet
|
||||||
cPlayer * Player = m_Client->GetPlayer();
|
cPlayer * Player = m_Client->GetPlayer();
|
||||||
Pkt.WriteBEInt32(static_cast<Int32>(a_Dimension));
|
Pkt.WriteBEInt32(static_cast<Int32>(a_Dimension));
|
||||||
Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal)
|
Pkt.WriteBEUInt8(2); // TODO: Difficulty (set to Normal)
|
||||||
Pkt.WriteBEUInt8(static_cast<Byte>(Player->GetEffectiveGameMode()));
|
Pkt.WriteBEUInt8(static_cast<Byte>(Player->GetEffectiveGameMode()));
|
||||||
Pkt.WriteString("default");
|
Pkt.WriteString("default");
|
||||||
m_LastSentDimension = a_Dimension;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3529,7 +3508,22 @@ void cProtocol190::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_En
|
|||||||
|
|
||||||
switch (a_Entity.GetEntityType())
|
switch (a_Entity.GetEntityType())
|
||||||
{
|
{
|
||||||
case cEntity::etPlayer: break; // TODO?
|
case cEntity::etPlayer:
|
||||||
|
{
|
||||||
|
auto & Player = reinterpret_cast<const cPlayer &>(a_Entity);
|
||||||
|
|
||||||
|
// TODO Set player custom name to their name.
|
||||||
|
// Then it's possible to move the custom name of mobs to the entities
|
||||||
|
// and to remove the "special" player custom name.
|
||||||
|
a_Pkt.WriteBEUInt8(2); // Index 2: Custom name
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_STRING);
|
||||||
|
a_Pkt.WriteString(Player.GetName());
|
||||||
|
|
||||||
|
a_Pkt.WriteBEUInt8(6); // Start metadata - Index 6: Health
|
||||||
|
a_Pkt.WriteBEUInt8(METADATA_TYPE_FLOAT);
|
||||||
|
a_Pkt.WriteBEFloat(static_cast<float>(Player.GetHealth()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
case cEntity::etPickup:
|
case cEntity::etPickup:
|
||||||
{
|
{
|
||||||
a_Pkt.WriteBEUInt8(5); // Index 5: Item
|
a_Pkt.WriteBEUInt8(5); // Index 5: Item
|
||||||
@ -4058,7 +4052,6 @@ void cProtocol191::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
|
|||||||
Pkt.WriteString("default"); // Level type - wtf?
|
Pkt.WriteString("default"); // Level type - wtf?
|
||||||
Pkt.WriteBool(false); // Reduced Debug Info - wtf?
|
Pkt.WriteBool(false); // Reduced Debug Info - wtf?
|
||||||
}
|
}
|
||||||
m_LastSentDimension = a_World.GetDimension();
|
|
||||||
|
|
||||||
// Send the spawn position:
|
// Send the spawn position:
|
||||||
{
|
{
|
||||||
@ -4377,8 +4370,3 @@ void cProtocol194::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, cons
|
|||||||
Writer.Finish();
|
Writer.Finish();
|
||||||
Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size());
|
Pkt.WriteBuf(Writer.GetResult().data(), Writer.GetResult().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,7 +116,7 @@ public:
|
|||||||
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
|
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
|
||||||
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
|
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
|
||||||
virtual void SendResetTitle (void) override;
|
virtual void SendResetTitle (void) override;
|
||||||
virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
|
virtual void SendRespawn (eDimension a_Dimension) override;
|
||||||
virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
|
virtual void SendSoundEffect (const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch) override;
|
||||||
virtual void SendExperience (void) override;
|
virtual void SendExperience (void) override;
|
||||||
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
|
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
|
||||||
@ -183,11 +183,6 @@ protected:
|
|||||||
/** The logfile where the comm is logged, when g_ShouldLogComm is true */
|
/** The logfile where the comm is logged, when g_ShouldLogComm is true */
|
||||||
cFile m_CommLogFile;
|
cFile m_CommLogFile;
|
||||||
|
|
||||||
/** The dimension that was last sent to a player in a Respawn or Login packet.
|
|
||||||
Used to avoid Respawning into the same dimension, which confuses the client. */
|
|
||||||
eDimension m_LastSentDimension;
|
|
||||||
|
|
||||||
|
|
||||||
/** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */
|
/** Adds the received (unencrypted) data to m_ReceivedData, parses complete packets */
|
||||||
void AddReceivedData(const char * a_Data, size_t a_Size);
|
void AddReceivedData(const char * a_Data, size_t a_Size);
|
||||||
|
|
||||||
@ -264,10 +259,10 @@ protected:
|
|||||||
void WriteItem(cPacketizer & a_Pkt, const cItem & a_Item);
|
void WriteItem(cPacketizer & a_Pkt, const cItem & a_Item);
|
||||||
|
|
||||||
/** Writes the metadata for the specified entity, not including the terminating 0xff. */
|
/** Writes the metadata for the specified entity, not including the terminating 0xff. */
|
||||||
void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity);
|
virtual void WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_Entity);
|
||||||
|
|
||||||
/** Writes the mob-specific metadata for the specified mob */
|
/** Writes the mob-specific metadata for the specified mob */
|
||||||
void WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob);
|
virtual void WriteMobMetadata(cPacketizer & a_Pkt, const cMonster & a_Mob);
|
||||||
|
|
||||||
/** Writes the entity properties for the specified entity, including the Count field. */
|
/** Writes the entity properties for the specified entity, including the Count field. */
|
||||||
void WriteEntityProperties(cPacketizer & a_Pkt, const cEntity & a_Entity);
|
void WriteEntityProperties(cPacketizer & a_Pkt, const cEntity & a_Entity);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "ProtocolRecognizer.h"
|
#include "ProtocolRecognizer.h"
|
||||||
#include "Protocol18x.h"
|
#include "Protocol18x.h"
|
||||||
#include "Protocol19x.h"
|
#include "Protocol19x.h"
|
||||||
|
#include "Protocol110x.h"
|
||||||
#include "Packetizer.h"
|
#include "Packetizer.h"
|
||||||
#include "../ClientHandle.h"
|
#include "../ClientHandle.h"
|
||||||
#include "../Root.h"
|
#include "../Root.h"
|
||||||
@ -52,6 +53,7 @@ AString cProtocolRecognizer::GetVersionTextFromInt(int a_ProtocolVersion)
|
|||||||
case PROTO_VERSION_1_9_1: return "1.9.1";
|
case PROTO_VERSION_1_9_1: return "1.9.1";
|
||||||
case PROTO_VERSION_1_9_2: return "1.9.2";
|
case PROTO_VERSION_1_9_2: return "1.9.2";
|
||||||
case PROTO_VERSION_1_9_4: return "1.9.4";
|
case PROTO_VERSION_1_9_4: return "1.9.4";
|
||||||
|
case PROTO_VERSION_1_10_0: return "1.10";
|
||||||
}
|
}
|
||||||
ASSERT(!"Unknown protocol version");
|
ASSERT(!"Unknown protocol version");
|
||||||
return Printf("Unknown protocol (%d)", a_ProtocolVersion);
|
return Printf("Unknown protocol (%d)", a_ProtocolVersion);
|
||||||
@ -635,10 +637,10 @@ void cProtocolRecognizer::SendResetTitle(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cProtocolRecognizer::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
|
void cProtocolRecognizer::SendRespawn(eDimension a_Dimension)
|
||||||
{
|
{
|
||||||
ASSERT(m_Protocol != nullptr);
|
ASSERT(m_Protocol != nullptr);
|
||||||
m_Protocol->SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks);
|
m_Protocol->SendRespawn(a_Dimension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1047,6 +1049,11 @@ bool cProtocolRecognizer::TryRecognizeLengthedProtocol(UInt32 a_PacketLengthRema
|
|||||||
m_Protocol = new cProtocol194(m_Client, ServerAddress, ServerPort, NextState);
|
m_Protocol = new cProtocol194(m_Client, ServerAddress, ServerPort, NextState);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case PROTO_VERSION_1_10_0:
|
||||||
|
{
|
||||||
|
m_Protocol = new cProtocol1100(m_Client, ServerAddress, ServerPort, NextState);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u (0x%x))",
|
LOGINFO("Client \"%s\" uses an unsupported protocol (lengthed, version %u (0x%x))",
|
||||||
|
@ -18,8 +18,8 @@
|
|||||||
|
|
||||||
|
|
||||||
// Adjust these if a new protocol is added or an old one is removed:
|
// Adjust these if a new protocol is added or an old one is removed:
|
||||||
#define MCS_CLIENT_VERSIONS "1.8.x, 1.9.x"
|
#define MCS_CLIENT_VERSIONS "1.8.x, 1.9.x, 1.10.x"
|
||||||
#define MCS_PROTOCOL_VERSIONS "47, 107, 108, 109, 110"
|
#define MCS_PROTOCOL_VERSIONS "47, 107, 108, 109, 110, 210"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -38,6 +38,7 @@ public:
|
|||||||
PROTO_VERSION_1_9_1 = 108,
|
PROTO_VERSION_1_9_1 = 108,
|
||||||
PROTO_VERSION_1_9_2 = 109,
|
PROTO_VERSION_1_9_2 = 109,
|
||||||
PROTO_VERSION_1_9_4 = 110,
|
PROTO_VERSION_1_9_4 = 110,
|
||||||
|
PROTO_VERSION_1_10_0 = 210,
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
cProtocolRecognizer(cClientHandle * a_Client);
|
cProtocolRecognizer(cClientHandle * a_Client);
|
||||||
@ -100,7 +101,7 @@ public:
|
|||||||
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
|
virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
|
||||||
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
|
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
|
||||||
virtual void SendResetTitle (void) override;
|
virtual void SendResetTitle (void) override;
|
||||||
virtual void SendRespawn (eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks) override;
|
virtual void SendRespawn (eDimension a_Dimension) override;
|
||||||
virtual void SendExperience (void) override;
|
virtual void SendExperience (void) override;
|
||||||
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
|
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
|
||||||
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
|
virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
|
||||||
|
49
src/Root.cpp
49
src/Root.cpp
@ -361,55 +361,6 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn
|
|||||||
m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld;
|
m_WorldsByName[ DefaultWorldName ] = m_pDefaultWorld;
|
||||||
auto Worlds = a_Settings.GetValues("Worlds");
|
auto Worlds = a_Settings.GetValues("Worlds");
|
||||||
|
|
||||||
// Fix servers that have default world configs created prior to #2815. See #2810.
|
|
||||||
// This can probably be removed several years after 2016
|
|
||||||
// We start by inspecting the world linkage and determining if it's the default one
|
|
||||||
if ((DefaultWorldName == "world") && (Worlds.size() == 1))
|
|
||||||
{
|
|
||||||
auto DefaultWorldIniFile= cpp14::make_unique<cIniFile>();
|
|
||||||
if (DefaultWorldIniFile->ReadFile("world/world.ini"))
|
|
||||||
{
|
|
||||||
AString NetherName = DefaultWorldIniFile->GetValue("LinkedWorlds", "NetherWorldName", "");
|
|
||||||
AString EndName = DefaultWorldIniFile->GetValue("LinkedWorlds", "EndWorldName", "");
|
|
||||||
if ((NetherName.compare("world_nether") == 0) && (EndName.compare("world_end") == 0))
|
|
||||||
{
|
|
||||||
// This is a default world linkage config, see if the nether and end are in settings.ini
|
|
||||||
// If both of them are not in settings.ini, then this is a pre-#2815 default config
|
|
||||||
// so we add them to settings.ini
|
|
||||||
// Note that if only one of them is not in settings.ini, it's nondefault and we don't touch it
|
|
||||||
|
|
||||||
bool NetherInSettings = false;
|
|
||||||
bool EndInSettings = false;
|
|
||||||
|
|
||||||
for (auto WorldNameValue : Worlds)
|
|
||||||
{
|
|
||||||
AString ValueName = WorldNameValue.first;
|
|
||||||
if (ValueName.compare("World") != 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
AString WorldName = WorldNameValue.second;
|
|
||||||
if (WorldName.compare("world_nether") == 0)
|
|
||||||
{
|
|
||||||
NetherInSettings = true;
|
|
||||||
}
|
|
||||||
else if (WorldName.compare("world_end") == 0)
|
|
||||||
{
|
|
||||||
EndInSettings = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!NetherInSettings) && (!EndInSettings))
|
|
||||||
{
|
|
||||||
a_Settings.AddValue("Worlds", "World", "world_nether");
|
|
||||||
a_Settings.AddValue("Worlds", "World", "world_end");
|
|
||||||
Worlds = a_Settings.GetValues("Worlds"); // Refresh the Worlds list so that the rest of the function works as usual
|
|
||||||
LOG("The server detected an old default config with bad world linkages. This has been autofixed by adding \"world_nether\" and \"world_end\" to settings.ini. If you do not want this autofix to trigger, please remove the nether and / or end from settings.ini and from world/world.ini");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then load the other worlds
|
// Then load the other worlds
|
||||||
if (Worlds.size() <= 0)
|
if (Worlds.size() <= 0)
|
||||||
{
|
{
|
||||||
|
@ -149,7 +149,7 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin
|
|||||||
m_WorldAge(0),
|
m_WorldAge(0),
|
||||||
m_TimeOfDay(0),
|
m_TimeOfDay(0),
|
||||||
m_LastTimeUpdate(0),
|
m_LastTimeUpdate(0),
|
||||||
m_LastUnload(0),
|
m_LastChunkCheck(0),
|
||||||
m_LastSave(0),
|
m_LastSave(0),
|
||||||
m_SkyDarkness(0),
|
m_SkyDarkness(0),
|
||||||
m_GameMode(gmNotSet),
|
m_GameMode(gmNotSet),
|
||||||
@ -453,6 +453,13 @@ void cWorld::Start(void)
|
|||||||
// The presence of a configuration value overrides everything
|
// The presence of a configuration value overrides everything
|
||||||
// If no configuration value is found, GetDimension() is written to file and the variable is written to again to ensure that cosmic rays haven't sneakily changed its value
|
// If no configuration value is found, GetDimension() is written to file and the variable is written to again to ensure that cosmic rays haven't sneakily changed its value
|
||||||
m_Dimension = StringToDimension(IniFile.GetValueSet("General", "Dimension", DimensionToString(GetDimension())));
|
m_Dimension = StringToDimension(IniFile.GetValueSet("General", "Dimension", DimensionToString(GetDimension())));
|
||||||
|
int UnusedDirtyChunksCap = IniFile.GetValueSetI("General", "UnusedChunkCap", 1000);
|
||||||
|
if (UnusedDirtyChunksCap < 0)
|
||||||
|
{
|
||||||
|
UnusedDirtyChunksCap *= -1;
|
||||||
|
IniFile.SetValueI("General", "UnusedChunkCap", UnusedDirtyChunksCap);
|
||||||
|
}
|
||||||
|
m_UnusedDirtyChunksCap = static_cast<size_t>(UnusedDirtyChunksCap);
|
||||||
|
|
||||||
m_BroadcastDeathMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastDeathMessages", true);
|
m_BroadcastDeathMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastDeathMessages", true);
|
||||||
m_BroadcastAchievementMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastAchievementMessages", true);
|
m_BroadcastAchievementMessages = IniFile.GetValueSetB("Broadcasting", "BroadcastAchievementMessages", true);
|
||||||
@ -1057,16 +1064,22 @@ void cWorld::Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_La
|
|||||||
|
|
||||||
TickWeather(static_cast<float>(a_Dt.count()));
|
TickWeather(static_cast<float>(a_Dt.count()));
|
||||||
|
|
||||||
if (m_WorldAge - m_LastSave > std::chrono::minutes(5)) // Save each 5 minutes
|
if (m_WorldAge - m_LastChunkCheck > std::chrono::seconds(10))
|
||||||
{
|
{
|
||||||
|
// Unload every 10 seconds
|
||||||
|
UnloadUnusedChunks();
|
||||||
|
|
||||||
|
if (m_WorldAge - m_LastSave > std::chrono::minutes(5))
|
||||||
|
{
|
||||||
|
// Save every 5 minutes
|
||||||
SaveAllChunks();
|
SaveAllChunks();
|
||||||
}
|
}
|
||||||
|
else if (GetNumUnusedDirtyChunks() > m_UnusedDirtyChunksCap)
|
||||||
if (m_WorldAge - m_LastUnload > std::chrono::seconds(10)) // Unload every 10 seconds
|
|
||||||
{
|
{
|
||||||
UnloadUnusedChunks();
|
// Save if we have too many dirty unused chunks
|
||||||
|
SaveAllChunks();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -2964,7 +2977,7 @@ bool cWorld::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const
|
|||||||
|
|
||||||
void cWorld::UnloadUnusedChunks(void)
|
void cWorld::UnloadUnusedChunks(void)
|
||||||
{
|
{
|
||||||
m_LastUnload = std::chrono::duration_cast<cTickTimeLong>(m_WorldAge);
|
m_LastChunkCheck = std::chrono::duration_cast<cTickTimeLong>(m_WorldAge);
|
||||||
m_ChunkMap->UnloadUnusedChunks();
|
m_ChunkMap->UnloadUnusedChunks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3578,7 +3591,7 @@ unsigned int cWorld::GetNumPlayers(void)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
int cWorld::GetNumChunks(void) const
|
size_t cWorld::GetNumChunks(void) const
|
||||||
{
|
{
|
||||||
return m_ChunkMap->GetNumChunks();
|
return m_ChunkMap->GetNumChunks();
|
||||||
}
|
}
|
||||||
@ -3587,6 +3600,15 @@ int cWorld::GetNumChunks(void) const
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
size_t cWorld::GetNumUnusedDirtyChunks(void) const
|
||||||
|
{
|
||||||
|
return m_ChunkMap->GetNumUnusedDirtyChunks();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void cWorld::GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue)
|
void cWorld::GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue)
|
||||||
{
|
{
|
||||||
m_ChunkMap->GetChunkStats(a_NumValid, a_NumDirty);
|
m_ChunkMap->GetChunkStats(a_NumValid, a_NumDirty);
|
||||||
|
12
src/World.h
12
src/World.h
@ -683,7 +683,10 @@ public:
|
|||||||
void ScheduleTask(int a_DelayTicks, std::function<void(cWorld &)> a_Task);
|
void ScheduleTask(int a_DelayTicks, std::function<void(cWorld &)> a_Task);
|
||||||
|
|
||||||
/** Returns the number of chunks loaded */
|
/** Returns the number of chunks loaded */
|
||||||
int GetNumChunks() const; // tolua_export
|
size_t GetNumChunks() const; // tolua_export
|
||||||
|
|
||||||
|
/** Returns the number of unused dirty chunks. That's the number of chunks that we can save and then unload. */
|
||||||
|
size_t GetNumUnusedDirtyChunks(void) const; // tolua_export
|
||||||
|
|
||||||
/** Returns the number of chunks loaded and dirty, and in the lighting queue */
|
/** Returns the number of chunks loaded and dirty, and in the lighting queue */
|
||||||
void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue);
|
void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue);
|
||||||
@ -851,6 +854,11 @@ private:
|
|||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
|
||||||
|
/** The maximum number of allowed unused dirty chunks for this world.
|
||||||
|
Loaded from config, enforced every 10 seconds by freeing some unused dirty chunks
|
||||||
|
if this was exceeded. */
|
||||||
|
size_t m_UnusedDirtyChunksCap;
|
||||||
|
|
||||||
AString m_WorldName;
|
AString m_WorldName;
|
||||||
|
|
||||||
/** The name of the overworld that portals in this world should link to.
|
/** The name of the overworld that portals in this world should link to.
|
||||||
@ -889,7 +897,7 @@ private:
|
|||||||
std::chrono::milliseconds m_WorldAge;
|
std::chrono::milliseconds m_WorldAge;
|
||||||
std::chrono::milliseconds m_TimeOfDay;
|
std::chrono::milliseconds m_TimeOfDay;
|
||||||
cTickTimeLong m_LastTimeUpdate; // The tick in which the last time update has been sent.
|
cTickTimeLong m_LastTimeUpdate; // The tick in which the last time update has been sent.
|
||||||
cTickTimeLong m_LastUnload; // The last WorldAge (in ticks) in which unloading was triggerred
|
cTickTimeLong m_LastChunkCheck; // The last WorldAge (in ticks) in which unloading and possibly saving was triggered
|
||||||
cTickTimeLong m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred
|
cTickTimeLong m_LastSave; // The last WorldAge (in ticks) in which save-all was triggerred
|
||||||
std::map<cMonster::eFamily, cTickTimeLong> m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed)
|
std::map<cMonster::eFamily, cTickTimeLong> m_LastSpawnMonster; // The last WorldAge (in ticks) in which a monster was spawned (for each megatype of monster) // MG TODO : find a way to optimize without creating unmaintenability (if mob IDs are becoming unrowed)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user