diff --git a/Server/Plugins/APIDump/APIDesc.lua b/Server/Plugins/APIDump/APIDesc.lua index f1346ffee..fb74d8032 100644 --- a/Server/Plugins/APIDump/APIDesc.lua +++ b/Server/Plugins/APIDump/APIDesc.lua @@ -4500,7 +4500,7 @@ local Hash = cCryptoHash.sha1HexString("DataToHash") }, etEnderCrystal = { - Notes = "", + Notes = "The entity is a {{cEnderCrystal}}", }, etEntity = { diff --git a/Server/Plugins/APIDump/Classes/World.lua b/Server/Plugins/APIDump/Classes/World.lua index 7f137cf72..0ddd66e60 100644 --- a/Server/Plugins/APIDump/Classes/World.lua +++ b/Server/Plugins/APIDump/Classes/World.lua @@ -3306,6 +3306,28 @@ function OnAllChunksAvailable() All return values from the callbacks are i Notes = "Spawns a {{cBoat|boat}} at the specific coordinates. Returns the EntityID of the new boat, or {{cEntity#INVALID_ID|cEntity#INVALID_ID}} if no boat was created. (DEPRECATED, use vector-parametered version)", }, }, + SpawnEnderCrystal = + { + Params = + { + { + Name = "Pos", + Type = "Vector3i", + }, + { + Name = "ShowBottom", + Type = "boolean", + } + }, + Returns = + { + { + Name = "EntityID", + Type = "number", + }, + }, + Notes = "Spawns an {{cEnderCrystal|ender crystal}} at the specified coords. Returns the EntityID of the new ender crystal, or {{cEntity#INVALID_ID|cEntity#INVALID_ID}} if no ender crystal was created.", + }, SpawnExperienceOrb = { Params = diff --git a/src/Entities/EnderCrystal.cpp b/src/Entities/EnderCrystal.cpp index 4f2bd857f..269714b58 100644 --- a/src/Entities/EnderCrystal.cpp +++ b/src/Entities/EnderCrystal.cpp @@ -10,8 +10,9 @@ -cEnderCrystal::cEnderCrystal(Vector3d a_Pos): - Super(etEnderCrystal, a_Pos, 1.0, 1.0) +cEnderCrystal::cEnderCrystal(Vector3d a_Pos, bool a_ShowBottom): + Super(etEnderCrystal, a_Pos, 1.0, 1.0), + m_ShowBottom(a_ShowBottom) { SetMaxHealth(5); } @@ -33,6 +34,10 @@ void cEnderCrystal::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { UNUSED(a_Dt); // No further processing (physics e.t.c.) is needed + if (m_World->GetDimension() == dimEnd) + { + m_World->SetBlock(POS_TOINT.addedY(1), E_BLOCK_FIRE, 0); + } } @@ -47,8 +52,7 @@ void cEnderCrystal::KilledBy(TakeDamageInfo & a_TDI) Destroy(); - m_World->SetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT, E_BLOCK_BEDROCK, 0); - m_World->SetBlock(POSX_TOINT, POSY_TOINT + 1, POSZ_TOINT, E_BLOCK_FIRE, 0); + m_World->SetBlock(POSX_TOINT, POSY_TOINT, POSZ_TOINT, E_BLOCK_FIRE, 0); } diff --git a/src/Entities/EnderCrystal.h b/src/Entities/EnderCrystal.h index f29927549..b2a28c517 100644 --- a/src/Entities/EnderCrystal.h +++ b/src/Entities/EnderCrystal.h @@ -18,10 +18,27 @@ public: CLASS_PROTODEF(cEnderCrystal) - cEnderCrystal(Vector3d a_Pos); + cEnderCrystal(Vector3d a_Pos, bool a_ShowBottom); + + // Getters and Setters + bool ShowsBottom() const { return m_ShowBottom; } + void SetShowBottom(bool a_ShowBottom) { m_ShowBottom = a_ShowBottom; } + + Vector3i GetBeamTarget() const { return m_BeamTarget; } + void SetBeamTarget(Vector3i a_BeamTarget) { m_BeamTarget = a_BeamTarget; } + + /** If the EnderCrystal should send it's beam to the client and store to disk. */ + bool DisplaysBeam() const { return m_DisplayBeam; } + void SetDisplayBeam(bool a_DisplayBeam) { m_DisplayBeam = a_DisplayBeam; } private: + // If the bedrock base should be displayed + bool m_ShowBottom; + + Vector3i m_BeamTarget; + bool m_DisplayBeam; + // cEntity overrides: virtual void SpawnOn(cClientHandle & a_ClientHandle) override; virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; diff --git a/src/Protocol/Packetizer.h b/src/Protocol/Packetizer.h index 22ef01ed9..b2f2b11f4 100644 --- a/src/Protocol/Packetizer.h +++ b/src/Protocol/Packetizer.h @@ -132,6 +132,13 @@ public: VERIFY(m_Out.WriteXYZPosition64(a_BlockX, a_BlockY, a_BlockZ)); } + /** Writes the specified block position as a single encoded 64-bit BigEndian integer. + The three coordinates are written in XYZ order. */ + inline void WriteXYZPosition64(const Vector3i & a_Pos) + { + VERIFY(m_Out.WriteXYZPosition64(a_Pos.x, a_Pos.y, a_Pos.z)); + } + /** Writes the specified block position as a single encoded 64-bit BigEndian integer. The three coordinates are written in XZY order, in 1.14+. */ inline void WriteXZYPosition64(int a_BlockX, int a_BlockY, int a_BlockZ) diff --git a/src/Protocol/Protocol_1_10.cpp b/src/Protocol/Protocol_1_10.cpp index f40bf7b73..d1b48c11a 100644 --- a/src/Protocol/Protocol_1_10.cpp +++ b/src/Protocol/Protocol_1_10.cpp @@ -21,6 +21,7 @@ Implements the 1.10 protocol classes: #include "../WorldStorage/FastNBT.h" #include "../Entities/Boat.h" +#include "../Entities/EnderCrystal.h" #include "../Entities/ExpOrb.h" #include "../Entities/Minecart.h" #include "../Entities/FallingBlock.h" @@ -539,6 +540,22 @@ void cProtocol_1_10_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & break; } // case etItemFrame + case cEntity::etEnderCrystal: + { + const auto & EnderCrystal = static_cast(a_Entity); + a_Pkt.WriteBEUInt8(7); + a_Pkt.WriteBEUInt8(METADATA_TYPE_OPTIONAL_POSITION); + a_Pkt.WriteBool(EnderCrystal.DisplaysBeam()); + if (EnderCrystal.DisplaysBeam()) + { + a_Pkt.WriteXYZPosition64(EnderCrystal.GetBeamTarget()); + } + a_Pkt.WriteBEUInt8(8); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); + a_Pkt.WriteBool(EnderCrystal.ShowsBottom()); + break; + } // case etEnderCrystal + default: { break; diff --git a/src/Protocol/Protocol_1_11.cpp b/src/Protocol/Protocol_1_11.cpp index 1e07419ec..9ea6332b8 100644 --- a/src/Protocol/Protocol_1_11.cpp +++ b/src/Protocol/Protocol_1_11.cpp @@ -16,6 +16,7 @@ Implements the 1.11 protocol classes: #include "../WorldStorage/FastNBT.h" #include "../Entities/Boat.h" +#include "../Entities/EnderCrystal.h" #include "../Entities/ExpOrb.h" #include "../Entities/Minecart.h" #include "../Entities/FallingBlock.h" @@ -805,6 +806,22 @@ void cProtocol_1_11_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & break; } // case etItemFrame + case cEntity::etEnderCrystal: + { + const auto & EnderCrystal = static_cast(a_Entity); + a_Pkt.WriteBEUInt8(7); + a_Pkt.WriteBEUInt8(METADATA_TYPE_OPTIONAL_POSITION); + a_Pkt.WriteBool(EnderCrystal.DisplaysBeam()); + if (EnderCrystal.DisplaysBeam()) + { + a_Pkt.WriteXYZPosition64(EnderCrystal.GetBeamTarget()); + } + a_Pkt.WriteBEUInt8(8); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); + a_Pkt.WriteBool(EnderCrystal.ShowsBottom()); + break; + } // case etEnderCrystal + default: { break; diff --git a/src/Protocol/Protocol_1_12.cpp b/src/Protocol/Protocol_1_12.cpp index 16651bdf6..ee2016ae2 100644 --- a/src/Protocol/Protocol_1_12.cpp +++ b/src/Protocol/Protocol_1_12.cpp @@ -13,6 +13,7 @@ Implements the 1.12 protocol classes: #include "Packetizer.h" #include "../Entities/Boat.h" +#include "../Entities/EnderCrystal.h" #include "../Entities/Minecart.h" #include "../Entities/Pickup.h" #include "../Entities/Player.h" @@ -518,6 +519,22 @@ void cProtocol_1_12::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_ break; } // case etItemFrame + case cEntity::etEnderCrystal: + { + const auto & EnderCrystal = static_cast(a_Entity); + a_Pkt.WriteBEUInt8(7); + a_Pkt.WriteBEUInt8(METADATA_TYPE_OPTIONAL_POSITION); + a_Pkt.WriteBool(EnderCrystal.DisplaysBeam()); + if (EnderCrystal.DisplaysBeam()) + { + a_Pkt.WriteXYZPosition64(EnderCrystal.GetBeamTarget()); + } + a_Pkt.WriteBEUInt8(8); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); + a_Pkt.WriteBool(EnderCrystal.ShowsBottom()); + break; + } // case etEnderCrystal + default: { break; diff --git a/src/Protocol/Protocol_1_13.cpp b/src/Protocol/Protocol_1_13.cpp index 1b505d58d..efc1e9fc1 100644 --- a/src/Protocol/Protocol_1_13.cpp +++ b/src/Protocol/Protocol_1_13.cpp @@ -12,6 +12,7 @@ Implements the 1.13 protocol classes: #include "Protocol_1_13.h" #include "../Entities/Boat.h" +#include "../Entities/EnderCrystal.h" #include "../Entities/Minecart.h" #include "../Entities/Pickup.h" #include "../Entities/Player.h" @@ -887,6 +888,20 @@ void cProtocol_1_13::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a_ break; } // case etItemFrame + case cEntity::etEnderCrystal: + { + const auto & EnderCrystal = static_cast(a_Entity); + WriteEntityMetadata(a_Pkt, EntityMetadata::EnderCrystalBeamTarget, EntityMetadataType::OptPosition); + a_Pkt.WriteBool(EnderCrystal.DisplaysBeam()); + if (EnderCrystal.DisplaysBeam()) + { + a_Pkt.WriteXYZPosition64(EnderCrystal.GetBeamTarget()); + } + WriteEntityMetadata(a_Pkt, EntityMetadata::EnderCrystalShowBottom, EntityMetadataType::Boolean); + a_Pkt.WriteBool(EnderCrystal.ShowsBottom()); + break; + } // case etEnderCrystal + default: { break; diff --git a/src/Protocol/Protocol_1_9.cpp b/src/Protocol/Protocol_1_9.cpp index a152a46cb..778d4ca30 100644 --- a/src/Protocol/Protocol_1_9.cpp +++ b/src/Protocol/Protocol_1_9.cpp @@ -28,6 +28,7 @@ Implements the 1.9 protocol classes: #include "../WorldStorage/FastNBT.h" +#include "../Entities/EnderCrystal.h" #include "../Entities/ExpOrb.h" #include "../Entities/Minecart.h" #include "../Entities/FallingBlock.h" @@ -1727,6 +1728,21 @@ void cProtocol_1_9_0::WriteEntityMetadata(cPacketizer & a_Pkt, const cEntity & a break; } // case etItemFrame + case cEntity::etEnderCrystal: + { + const auto & EnderCrystal = static_cast(a_Entity); + a_Pkt.WriteBEUInt8(7); + a_Pkt.WriteBEUInt8(METADATA_TYPE_OPTIONAL_POSITION); + a_Pkt.WriteBool(EnderCrystal.DisplaysBeam()); + if (EnderCrystal.DisplaysBeam()) + { + a_Pkt.WriteXYZPosition64(EnderCrystal.GetBeamTarget()); + } + a_Pkt.WriteBEUInt8(8); + a_Pkt.WriteBEUInt8(METADATA_TYPE_BOOL); + a_Pkt.WriteBool(EnderCrystal.ShowsBottom()); + break; + } // case etEnderCrystal default: { break; diff --git a/src/World.cpp b/src/World.cpp index c3996c8ba..3f9150527 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -20,6 +20,7 @@ #include "WorldStorage/ScoreboardSerializer.h" // Entities (except mobs): +#include "Entities/EnderCrystal.h" #include "Entities/ExpOrb.h" #include "Entities/FallingBlock.h" #include "Entities/Minecart.h" @@ -2164,6 +2165,21 @@ UInt32 cWorld::SpawnPrimedTNT(Vector3d a_Pos, int a_FuseTicks, double a_InitialV +UInt32 cWorld::SpawnEnderCrystal(Vector3d a_Pos, bool a_ShowBottom) +{ + auto EnderCrystal = std::make_unique(a_Pos, a_ShowBottom); + auto EnderCrystalPtr = EnderCrystal.get(); + if (!EnderCrystalPtr->Initialize(std::move(EnderCrystal), *this)) + { + return cEntity::INVALID_ID; + } + return EnderCrystalPtr->GetUniqueID(); +} + + + + + void cWorld::PlaceBlock(const Vector3i a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta) { SetBlock(a_Position, a_BlockType, a_BlockMeta); diff --git a/src/World.h b/src/World.h index 2ff8fbe29..7460dc624 100644 --- a/src/World.h +++ b/src/World.h @@ -652,6 +652,10 @@ public: Returns the UniqueID of the created entity, or cEntity::INVALID_ID on failure. */ UInt32 SpawnPrimedTNT(Vector3d a_Pos, int a_FuseTimeInSec = 80, double a_InitialVelocityCoeff = 1, bool a_ShouldPlayFuseSound = true); + /** Spawns a new ender crystal at the specified block coords. + Returns the UniqueID of the created entity, or cEntity::INVALID_ID on failure. */ + UInt32 SpawnEnderCrystal(Vector3d a_Pos, bool a_ShowBottom = false); + // tolua_end /** Replaces the specified block with another, and calls the OnPlaced block handler. diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index f78f7029f..d159f6e49 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -683,6 +683,16 @@ public: { mWriter.BeginCompound(""); AddBasicEntity(a_EnderCrystal, "EnderCrystal"); + mWriter.AddByte("ShowBottom", a_EnderCrystal->ShowsBottom() ? 1 : 0); + if (a_EnderCrystal->DisplaysBeam()) + { + mWriter.BeginCompound("BeamTarget"); + const auto & BeamTarget = a_EnderCrystal->GetBeamTarget(); + mWriter.AddInt("X", BeamTarget.x); + mWriter.AddInt("Y", BeamTarget.y); + mWriter.AddInt("Z", BeamTarget.z); + mWriter.EndCompound(); + } mWriter.EndCompound(); } diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index 03e60bb26..c7b4c7e1f 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1718,11 +1718,42 @@ void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N void cWSSAnvil::LoadEnderCrystalFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { - auto EnderCrystal = std::make_unique(Vector3d()); + auto EnderCrystal = std::make_unique(Vector3d(), false); if (!LoadEntityBaseFromNBT(*EnderCrystal.get(), a_NBT, a_TagIdx)) { return; } + + int CurrentLine = a_NBT.FindChildByName(a_TagIdx, "BeamTarget"); + if (CurrentLine > 0) + { + EnderCrystal->SetDisplayBeam(true); + if (a_NBT.GetType(CurrentLine) == TAG_Compound) + { + Vector3d BeamTarget = {0, 0, 0}; + int CoordinateLine = a_NBT.FindChildByName(CurrentLine, "X"); + if (CoordinateLine > 0) + { + BeamTarget.x = a_NBT.GetInt(CoordinateLine); + } + CoordinateLine = a_NBT.FindChildByName(CurrentLine, "Y"); + if (CoordinateLine > 0) + { + BeamTarget.y = a_NBT.GetInt(CoordinateLine); + } + CoordinateLine = a_NBT.FindChildByName(CurrentLine, "Z"); + if (CoordinateLine > 0) + { + BeamTarget.z = a_NBT.GetInt(CoordinateLine); + } + } + } + CurrentLine = a_NBT.FindChildByName(a_TagIdx, "ShowBottom"); + if (CurrentLine > 0) + { + EnderCrystal->SetShowBottom(a_NBT.GetByte(CurrentLine) == 1); + } + a_Entities.emplace_back(std::move(EnderCrystal)); }