From c0711407e96353e14bfa41f17db4e45c43f8ebb9 Mon Sep 17 00:00:00 2001 From: 12xx12 <44411062+12xx12@users.noreply.github.com> Date: Sat, 10 Oct 2020 21:31:44 +0200 Subject: Adding more customize options to mob spawners and improving the way to look for surrounding entities (#4955) * added nearly any customize option * fixed unnecessary diff added comments * removed unnecessary const qualifier * fixed build * changed to ForEachEntityInBox * added docs * updated lua api description * checkstyle * added changes suggested by @peterbell10 And now the player may break the server by setting ridiculous ranges * updated docs changed cast to static cast * fixed clang * fixed clang on WSSAnvil.cpp Co-authored-by: 12xx12 <12xx12100@gmail.com> --- Server/Plugins/APIDump/Classes/BlockEntities.lua | 120 ++++++++++++++++++++++- src/BlockEntities/MobSpawnerEntity.cpp | 92 ++++++----------- src/BlockEntities/MobSpawnerEntity.h | 60 ++++++++---- src/WorldStorage/NBTChunkSerializer.cpp | 6 ++ src/WorldStorage/WSSAnvil.cpp | 48 ++++++++- 5 files changed, 240 insertions(+), 86 deletions(-) diff --git a/Server/Plugins/APIDump/Classes/BlockEntities.lua b/Server/Plugins/APIDump/Classes/BlockEntities.lua index b5e4cdf3b..98de99bf4 100644 --- a/Server/Plugins/APIDump/Classes/BlockEntities.lua +++ b/Server/Plugins/APIDump/Classes/BlockEntities.lua @@ -1353,7 +1353,7 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(), Type = "number", }, }, - Notes = "Returns the amount of this monster type in a 8-block radius (Y: 4-block radius).", + Notes = "Returns the amount of this monster type in a radius defined by SetSpawnRange (Y: 4-block radius).", }, GetNearbyPlayersNum = { @@ -1365,6 +1365,22 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(), }, Notes = "Returns the amount of the nearby players in a 16-block radius.", }, + GetSpawnCount = + { + Returns = + { + Type = "number", + }, + Notes = "Returns the number of entities the spawner will try to spawn on each activation.", + }, + GetSpawnRange = + { + Returns = + { + Type = "number", + }, + Notes = "Returns half the length of the square the spawner tries to spawn entities in.", + }, GetSpawnDelay = { Returns = @@ -1375,6 +1391,38 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(), }, Notes = "Returns the spawn delay. This is the tick delay that is needed to spawn new monsters.", }, + GetMinSpawnDelay = + { + Returns = + { + Type = "number", + }, + Notes = "Returns the minimum number of ticks the spawner waits until spawning new entities automatically.", + }, + GetMaxSpawnDelay = + { + Returns = + { + Type = "number", + }, + Notes = "Returns the maximum number of ticks the spawner waits until spawning new entities automatically.", + }, + GetMaxNearbyEntities = + { + Returns = + { + Type = "number", + }, + Notes = "Returns the maximum number of entities of the same type that can be present before the spawner cannot spawn more entities.", + }, + GetRequiredPlayerRange = + { + Returns = + { + Type = "number" + }, + Notes = "Returns the maximum euclidean distance from a player where the spawner can be activated.", + }, ResetTimer = { Notes = "Sets the spawn delay to a new random value.", @@ -1401,13 +1449,79 @@ World:ForEachChestInChunk(Player:GetChunkX(), Player:GetChunkZ(), }, Notes = "Sets the spawn delay.", }, + SetSpawnCount = + { + Params = + { + { + Name = "SpawnCount", + Type = "number", + }, + }, + Notes = "Sets the number of entities the spawner will try to spawn in each activation. Might not spawn all of them due to spawn limitations of the entity.", + }, + SetSpawnRange = + { + Params = + { + { + Name = "SpawnRange", + Type = "number", + }, + }, + Notes = "Sets half the length of the square the spawner will try to spawn entities in.", + }, + SetMinSpawnDelay = + { + Params = + { + { + Name = "MinSpawnDelay", + Type = "number", + }, + }, + Notes = "Sets the minimum amount of ticks the spawner will wait before spawning new entities.", + }, + SetMaxSpawnDelay = + { + Params = + { + { + Name = "MaxSpawnDelay", + Type = "number", + }, + }, + Notes = "Sets the maximum amount of ticks the spawner will wait before spawning new entities.", + }, + SetMaxNearbyEntities = + { + Params = + { + { + Name = "MaxNearbyEntities", + Type = "number", + }, + }, + Notes = "Sets the maximum amount of nearby entities until the spawner will stop spawning this entity type.", + }, + SetRequiredPlayerRange = + { + Params = + { + { + Name = "RequiredPlayerRange", + Type = "number", + }, + }, + Notes = "Sets the maximum euclidean distance from a player where the spawner can be activated.", + }, SpawnEntity = { - Notes = "Spawns the entity. This function automaticly change the spawn delay!", + Notes = "Spawns the entity. NOTE: This function resets the delay before the next automatic activation of the spawner.", }, UpdateActiveState = { - Notes = "Upate the active flag from the mob spawner. This function is called every 5 seconds from the Tick() function.", + Notes = "Update the active flag from the mob spawner. This function is called every 5 seconds from the Tick() function.", }, }, Inherits = "cBlockEntity", diff --git a/src/BlockEntities/MobSpawnerEntity.cpp b/src/BlockEntities/MobSpawnerEntity.cpp index f5fb5c3ff..8095fc12b 100644 --- a/src/BlockEntities/MobSpawnerEntity.cpp +++ b/src/BlockEntities/MobSpawnerEntity.cpp @@ -33,6 +33,12 @@ void cMobSpawnerEntity::CopyFrom(const cBlockEntity & a_Src) m_Entity = src.m_Entity; m_IsActive = src.m_IsActive; m_SpawnDelay = src.m_SpawnDelay; + m_SpawnCount = src.m_SpawnCount; + m_SpawnRange = src.m_SpawnRange; + m_MinSpawnDelay = src.m_MinSpawnDelay; + m_MaxSpawnDelay = src.m_MaxSpawnDelay; + m_MaxNearbyEntities = src.m_MaxNearbyEntities; + m_RequiredPlayerRange = src.m_RequiredPlayerRange; } @@ -114,7 +120,7 @@ bool cMobSpawnerEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cMobSpawnerEntity::ResetTimer(void) { - m_SpawnDelay = GetRandomProvider().RandInt(200, 800); + m_SpawnDelay = GetRandomProvider().RandInt(m_MinSpawnDelay, m_MaxSpawnDelay); m_World->BroadcastBlockEntity(GetPos()); } @@ -125,7 +131,7 @@ void cMobSpawnerEntity::ResetTimer(void) void cMobSpawnerEntity::SpawnEntity(void) { auto NearbyEntities = GetNearbyMonsterNum(m_Entity); - if (NearbyEntities >= 6) + if (NearbyEntities >= m_MaxNearbyEntities) { ResetTimer(); return; @@ -136,18 +142,18 @@ void cMobSpawnerEntity::SpawnEntity(void) auto & Random = GetRandomProvider(); bool HaveSpawnedEntity = false; - for (size_t i = 0; i < 4; i++) + for (short I = 0; I < m_SpawnCount; I++) { - if (NearbyEntities >= 6) + if (NearbyEntities >= m_MaxNearbyEntities) { break; } auto SpawnRelPos(GetRelPos()); SpawnRelPos += Vector3i( - static_cast((Random.RandReal() - Random.RandReal()) * 4.0), + static_cast((Random.RandReal() - Random.RandReal()) * static_cast(m_SpawnRange)), Random.RandInt(-1, 1), - static_cast((Random.RandReal() - Random.RandReal()) * 4.0) + static_cast((Random.RandReal() - Random.RandReal()) * static_cast(m_SpawnRange)) ); auto Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(SpawnRelPos); @@ -191,39 +197,24 @@ void cMobSpawnerEntity::SpawnEntity(void) int cMobSpawnerEntity::GetNearbyPlayersNum(void) { - auto SpawnerPos = Vector3d(0.5, 0.5, 0.5) + m_Pos; int NumPlayers = 0; - class cCallback : public cChunkDataCallback + auto Callback = [&] (cEntity & a_Entity) { - public: - cCallback(Vector3d a_SpawnerPos, int & a_NumPlayers) : - m_SpawnerPos(a_SpawnerPos), - m_NumPlayers(a_NumPlayers) + if (!a_Entity.IsPlayer()) { + return false; } - - virtual void Entity(cEntity * a_Entity) override + if ((m_Pos - a_Entity.GetPosition()).Length() <= m_RequiredPlayerRange) { - if (!a_Entity->IsPlayer()) - { - return; - } - - if ((m_SpawnerPos - a_Entity->GetPosition()).Length() <= 16) - { - m_NumPlayers++; - } + NumPlayers++; } + return false; + }; - protected: - Vector3d m_SpawnerPos; - int & m_NumPlayers; - } Callback(SpawnerPos, NumPlayers); + auto PlayerBoundingBox = cBoundingBox(Vector3d(m_Pos.x, m_Pos.y - m_RequiredPlayerRange, m_Pos.z), m_RequiredPlayerRange, m_RequiredPlayerRange * 2); - int ChunkX = GetChunkX(); - int ChunkZ = GetChunkZ(); - m_World->ForEachChunkInRect(ChunkX - 1, ChunkX + 1, ChunkZ - 1, ChunkZ + 1, Callback); + m_World->ForEachEntityInBox(PlayerBoundingBox, Callback); return NumPlayers; } @@ -234,47 +225,26 @@ int cMobSpawnerEntity::GetNearbyPlayersNum(void) int cMobSpawnerEntity::GetNearbyMonsterNum(eMonsterType a_EntityType) { - auto SpawnerPos = Vector3d(0.5, 0.5, 0.5) + m_Pos; int NumEntities = 0; - class cCallback : public cChunkDataCallback + auto Callback = [&] (cEntity & a_Entity) { - public: - cCallback(Vector3d a_SpawnerPos, eMonsterType a_CallbackEntityType, int & a_NumEntities) : - m_SpawnerPos(a_SpawnerPos), - m_EntityType(a_CallbackEntityType), - m_NumEntities(a_NumEntities) + if (!a_Entity.IsMob()) { + return false; } - virtual void Entity(cEntity * a_Entity) override + auto & Mob = static_cast(a_Entity); + if (Mob.GetMobType() == m_Entity) { - if (!a_Entity->IsMob()) - { - return; - } - - cMonster * Mob = static_cast(a_Entity); - if (Mob->GetMobType() != m_EntityType) - { - return; - } - - if ((Diff(m_SpawnerPos.x, a_Entity->GetPosX()) <= 8.0) && (Diff(m_SpawnerPos.y, a_Entity->GetPosY()) <= 4.0) && (Diff(m_SpawnerPos.z, a_Entity->GetPosZ()) <= 8.0)) - { - m_NumEntities++; - } + NumEntities++; } + return false; + }; - protected: - Vector3d m_SpawnerPos; - eMonsterType m_EntityType; - int & m_NumEntities; - } Callback(SpawnerPos, a_EntityType, NumEntities); + auto EntityBoundingBox = cBoundingBox(Vector3d(m_Pos.x, m_Pos.y - 4, m_Pos.z), m_SpawnRange, 8); - int ChunkX = GetChunkX(); - int ChunkZ = GetChunkZ(); - m_World->ForEachChunkInRect(ChunkX - 1, ChunkX + 1, ChunkZ - 1, ChunkZ + 1, Callback); + m_World->ForEachEntityInBox(EntityBoundingBox, Callback); return NumEntities; } diff --git a/src/BlockEntities/MobSpawnerEntity.h b/src/BlockEntities/MobSpawnerEntity.h index 0d1991e87..a037343f4 100644 --- a/src/BlockEntities/MobSpawnerEntity.h +++ b/src/BlockEntities/MobSpawnerEntity.h @@ -33,46 +33,68 @@ public: // tolua_export virtual bool UsedBy(cPlayer * a_Player) override; virtual bool Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + static const char * GetClassStatic(void) { return "cMobSpawnerEntity"; } // tolua_begin - /** Upate the active flag from the mob spawner. This function will called every 5 seconds from the Tick() function. */ + /** Update the active flag from the mob spawner. This function will called every 5 seconds from the Tick() function. */ void UpdateActiveState(void); /** Sets the spawn delay to a new random value. */ void ResetTimer(void); - /** Spawns the entity. This function automaticly change the spawn delay! */ + /** Spawns the entity. This function automatically change the spawn delay! */ void SpawnEntity(void); - /** Returns the entity type that will be spawn by this mob spawner. */ - eMonsterType GetEntity(void) const { return m_Entity; } - - /** Sets the entity type who will be spawn by this mob spawner. */ - void SetEntity(eMonsterType a_EntityType) { m_Entity = a_EntityType; } - - /** Returns the spawn delay. This is the tick delay that is needed to spawn new monsters. */ - short GetSpawnDelay(void) const { return m_SpawnDelay; } - - /** Sets the spawn delay. */ - void SetSpawnDelay(short a_Delay) { m_SpawnDelay = a_Delay; } - - /** Returns the amount of the nearby players in a 16-block radius. */ + // Getters + int GetNearbyMonsterNum(eMonsterType a_EntityType); int GetNearbyPlayersNum(void); - /** Returns the amount of this monster type in a 8-block radius (Y: 4-block radius). */ - int GetNearbyMonsterNum(eMonsterType a_EntityType); + eMonsterType GetEntity(void) const { return m_Entity; } + short GetSpawnCount(void) const { return m_SpawnCount; } + short GetSpawnRange(void) const { return m_SpawnRange; } + short GetSpawnDelay(void) const { return m_SpawnDelay; } + short GetMinSpawnDelay(void) const { return m_MinSpawnDelay; } + short GetMaxSpawnDelay(void) const { return m_MaxSpawnDelay; } + short GetMaxNearbyEntities(void) const { return m_MaxNearbyEntities; } + short GetRequiredPlayerRange(void) const { return m_RequiredPlayerRange; } + + // Setters + void SetEntity(eMonsterType a_EntityType) { m_Entity = a_EntityType; } + void SetSpawnDelay(short a_Delay) { m_SpawnDelay = a_Delay; } + void SetSpawnCount(short a_SpawnCount) { m_SpawnCount = a_SpawnCount; } + void SetSpawnRange(short a_SpawnRange) { m_SpawnRange = a_SpawnRange; } + void SetMinSpawnDelay(short a_Min) { m_MinSpawnDelay = a_Min; } + void SetMaxSpawnDelay(short a_Max) { m_MaxSpawnDelay = a_Max; } + void SetMaxNearbyEntities(short a_MaxNearbyEntities) { m_MaxNearbyEntities = a_MaxNearbyEntities; } + void SetRequiredPlayerRange(short a_RequiredPlayerRange) { m_RequiredPlayerRange = a_RequiredPlayerRange; } // tolua_end - static const char * GetClassStatic(void) { return "cMobSpawnerEntity"; } - private: /** The entity to spawn. */ eMonsterType m_Entity; + /** Time in ticks until the next entity spawns */ short m_SpawnDelay; bool m_IsActive; + + /** Number of entities the spawner tries to spawn each activation. */ + short m_SpawnCount = 4; + + /** Diameter of the square the new monsters are spawned in */ + short m_SpawnRange = 8; + + short m_MinSpawnDelay = 200; + + short m_MaxSpawnDelay = 800; + + /** Maximum amount of the same entity type in proximity. */ + short m_MaxNearbyEntities = 6; + + /** Maximum distance to player for activation */ + short m_RequiredPlayerRange = 16; + } ; // tolua_end diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index 70f07557f..f78f7029f 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -547,7 +547,13 @@ public: mWriter.BeginCompound(""); AddBasicTileEntity(a_MobSpawner, "MobSpawner"); mWriter.AddString("EntityId", cMonster::MobTypeToVanillaName(a_MobSpawner->GetEntity())); + mWriter.AddShort("SpawnCount", a_MobSpawner->GetSpawnCount()); + mWriter.AddShort("SpawnRange", a_MobSpawner->GetSpawnRange()); mWriter.AddShort("Delay", a_MobSpawner->GetSpawnDelay()); + mWriter.AddShort("MinSpawnDelay", a_MobSpawner->GetMinSpawnDelay()); + mWriter.AddShort("MaxSpawnDelay", a_MobSpawner->GetMaxSpawnDelay()); + mWriter.AddShort("MaxNearbyEntities", a_MobSpawner->GetMaxNearbyEntities()); + mWriter.AddShort("RequiredPlayerRange", a_MobSpawner->GetRequiredPlayerRange()); mWriter.EndCompound(); } diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index f822f9375..03e60bb26 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1349,13 +1349,55 @@ OwnedBlockEntity cWSSAnvil::LoadMobSpawnerFromNBT(const cParsedNBT & a_NBT, int } } + // Load spawn count: + int CurrentLine = a_NBT.FindChildByName(a_TagIdx, "SpawnCount"); + if ((CurrentLine >= 0) && (a_NBT.GetType(CurrentLine) == TAG_Short)) + { + MobSpawner->SetSpawnCount(a_NBT.GetShort(CurrentLine)); + } + + // Load spawn range: + CurrentLine = a_NBT.FindChildByName(a_TagIdx, "SpawnRange"); + if ((CurrentLine >= 0) && (a_NBT.GetType(CurrentLine) == TAG_Short)) + { + MobSpawner->SetSpawnRange(a_NBT.GetShort(CurrentLine)); + } + // Load delay: - int Delay = a_NBT.FindChildByName(a_TagIdx, "Delay"); - if ((Delay >= 0) && (a_NBT.GetType(Delay) == TAG_Short)) + CurrentLine = a_NBT.FindChildByName(a_TagIdx, "Delay"); + if ((CurrentLine >= 0) && (a_NBT.GetType(CurrentLine) == TAG_Short)) { - MobSpawner->SetSpawnDelay(a_NBT.GetShort(Delay)); + MobSpawner->SetSpawnDelay(a_NBT.GetShort(CurrentLine)); } + // Load delay range: + CurrentLine = a_NBT.FindChildByName(a_TagIdx, "MinSpawnDelay"); + if ((CurrentLine >= 0) && (a_NBT.GetType(CurrentLine) == TAG_Short)) + { + MobSpawner->SetMinSpawnDelay(a_NBT.GetShort(CurrentLine)); + } + + CurrentLine = a_NBT.FindChildByName(a_TagIdx, "MaxSpawnDelay"); + if ((CurrentLine >= 0) && (a_NBT.GetType(CurrentLine) == TAG_Short)) + { + MobSpawner->SetMaxSpawnDelay(a_NBT.GetShort(CurrentLine)); + } + + // Load MaxNearbyEntities: + CurrentLine = a_NBT.FindChildByName(a_TagIdx, "MaxNearbyEntities"); + if ((CurrentLine >= 0) && (a_NBT.GetType(CurrentLine) == TAG_Short)) + { + MobSpawner->SetMaxNearbyEntities(a_NBT.GetShort(CurrentLine)); + } + + // Load RequiredPlayerRange: + CurrentLine = a_NBT.FindChildByName(a_TagIdx, "RequiredPlayerRange"); + if ((CurrentLine >= 0) && (a_NBT.GetType(CurrentLine) == TAG_Short)) + { + MobSpawner->SetRequiredPlayerRange(a_NBT.GetShort(CurrentLine)); + } + + return MobSpawner; } -- cgit v1.2.3