summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/LuaState.cpp26
-rw-r--r--src/Bindings/LuaState.h3
-rw-r--r--src/Bindings/Plugin.h2
-rw-r--r--src/Bindings/PluginLua.cpp50
-rw-r--r--src/Bindings/PluginLua.h2
-rw-r--r--src/Bindings/PluginManager.cpp36
-rw-r--r--src/Bindings/PluginManager.h4
-rw-r--r--src/Blocks/BlockBed.cpp4
-rw-r--r--src/Blocks/BlockLeaves.h28
-rw-r--r--src/Entities/Entity.cpp11
-rw-r--r--src/Entities/Player.cpp10
-rw-r--r--src/FastRandom.cpp26
-rw-r--r--src/Mobs/AggressiveMonster.cpp1
-rw-r--r--src/Mobs/Monster.cpp121
-rw-r--r--src/Mobs/Monster.h7
-rw-r--r--src/Mobs/Path.cpp103
-rw-r--r--src/Mobs/Path.h23
-rw-r--r--src/Protocol/Protocol18x.cpp4
-rw-r--r--src/World.cpp2
19 files changed, 384 insertions, 79 deletions
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index ccf812417..08c7e19d7 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -927,6 +927,9 @@ bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam)
VERIFY(lua_getstack(m_LuaState, 0, &entry));
VERIFY(lua_getinfo (m_LuaState, "n", &entry));
AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
+
+ BreakIntoDebugger(m_LuaState);
+
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
return false;
} // for i - Param
@@ -1366,6 +1369,7 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
{
LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
LogStackTrace(a_LuaState, 1);
+ BreakIntoDebugger(a_LuaState);
return 1; // We left the error message on the stack as the return value
}
@@ -1373,6 +1377,28 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
+int cLuaState::BreakIntoDebugger(lua_State * a_LuaState)
+{
+ // Call the BreakIntoDebugger function, if available:
+ lua_getglobal(a_LuaState, "BreakIntoDebugger");
+ if (!lua_isfunction(a_LuaState, -1))
+ {
+ LOGD("LUA: BreakIntoDebugger() not found / not a function");
+ lua_pop(a_LuaState, 1);
+ return 1;
+ }
+ lua_insert(a_LuaState, -2); // Copy the string that has been passed to us
+ LOGD("Calling BreakIntoDebugger()...");
+ lua_call(a_LuaState, 1, 0);
+ LOGD("Returned from BreakIntoDebugger().");
+
+ return 0;
+}
+
+
+
+
+
////////////////////////////////////////////////////////////////////////////////
// cLuaState::cRef:
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index b38401fd8..5b4ec3ae4 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -389,6 +389,9 @@ protected:
/** Used as the error reporting function for function calls */
static int ReportFnCallErrors(lua_State * a_LuaState);
+
+ /** Tries to break into the MobDebug debugger, if it is installed. */
+ static int BreakIntoDebugger(lua_State * a_LuaState);
} ;
diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h
index d0c2bcefa..b4bbfd802 100644
--- a/src/Bindings/Plugin.h
+++ b/src/Bindings/Plugin.h
@@ -56,6 +56,8 @@ public:
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0;
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0;
virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0;
+ virtual bool OnEntityChangeWorld (cEntity & a_Entity, cWorld & a_World) = 0;
+ virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) = 0;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) = 0;
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 76d3557a4..a8be26f71 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -534,6 +534,54 @@ bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_E
+bool cPluginLua::OnEntityChangeWorld(cEntity & a_Entity, cWorld & a_World)
+{
+ cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGE_WORLD];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Entity, &a_World, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World)
+{
+ cCSLock Lock(m_CriticalSection);
+ if (!m_LuaState.IsValid())
+ {
+ return false;
+ }
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGED_WORLD];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Entity, &a_World, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result)
{
cCSLock Lock(m_CriticalSection);
@@ -1884,6 +1932,8 @@ const char * cPluginLua::GetHookFnName(int a_HookType)
case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect";
case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation";
case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect";
+ case cPluginManager::HOOK_ENTITY_CHANGE_WORLD: return "OnEntityChangeWorld";
+ case cPluginManager::HOOK_ENTITY_CHANGED_WORLD: return "OnEntityChangedWorld";
case cPluginManager::HOOK_ENTITY_TELEPORT: return "OnEntityTeleport";
case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand";
case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake";
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index 524c249b0..533dc0331 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -115,6 +115,8 @@ public:
virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) override;
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override;
virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) override;
+ virtual bool OnEntityChangeWorld (cEntity & a_Entity, cWorld & a_World) override;
+ virtual bool OnEntityChangedWorld (cEntity & a_Entity, cWorld & a_World) override;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) override;
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 86be30938..6fb6ef4fa 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -525,6 +525,42 @@ bool cPluginManager::CallHookEntityTeleport(cEntity & a_Entity, const Vector3d &
+bool cPluginManager::CallHookEntityChangeWorld(cEntity & a_Entity, cWorld & a_World)
+{
+ FIND_HOOK(HOOK_ENTITY_CHANGE_WORLD);
+ VERIFY_HOOK;
+
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnEntityChangeWorld(a_Entity, a_World))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+bool cPluginManager::CallHookEntityChangedWorld(cEntity & a_Entity, cWorld & a_World)
+{
+ FIND_HOOK(HOOK_ENTITY_CHANGED_WORLD);
+ VERIFY_HOOK;
+
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnEntityChangedWorld(a_Entity, a_World))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result)
{
FIND_HOOK(HOOK_EXECUTE_COMMAND);
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index faabf3aec..e528c049f 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -86,6 +86,8 @@ public:
HOOK_DISCONNECT,
HOOK_PLAYER_ANIMATION,
HOOK_ENTITY_ADD_EFFECT,
+ HOOK_ENTITY_CHANGE_WORLD,
+ HOOK_ENTITY_CHANGED_WORLD,
HOOK_EXECUTE_COMMAND,
HOOK_EXPLODED,
HOOK_EXPLODING,
@@ -201,6 +203,8 @@ public:
bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason);
bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier);
bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition);
+ bool CallHookEntityChangeWorld (cEntity & a_Entity, cWorld & a_World);
+ bool CallHookEntityChangedWorld (cEntity & a_Entity, cWorld & a_World);
bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result); // If a_Player == nullptr, it is a console cmd
bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp
index e56f4bfe0..dfa392d9b 100644
--- a/src/Blocks/BlockBed.cpp
+++ b/src/Blocks/BlockBed.cpp
@@ -14,7 +14,7 @@ void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInt
NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
Vector3i ThisPos( a_BlockX, a_BlockY, a_BlockZ);
- Vector3i Direction = MetaDataToDirection( OldMeta & 0x7);
+ Vector3i Direction = MetaDataToDirection( OldMeta & 0x3);
if (OldMeta & 0x8)
{
// Was pillow
@@ -111,7 +111,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
// Is foot end
VERIFY((Meta & 0x4) != 0x4); // Occupied flag should never be set, else our compilator (intended) is broken
- PillowDirection = MetaDataToDirection(Meta & 0x7);
+ PillowDirection = MetaDataToDirection(Meta & 0x3);
if (a_ChunkInterface.GetBlock(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z) == E_BLOCK_BED) // Must always use pillow location for sleeping
{
a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z);
diff --git a/src/Blocks/BlockLeaves.h b/src/Blocks/BlockLeaves.h
index bd9a7414e..4d4610fd8 100644
--- a/src/Blocks/BlockLeaves.h
+++ b/src/Blocks/BlockLeaves.h
@@ -40,29 +40,41 @@ public:
{
cFastRandom rand;
- // Old leaves - 3 bits contain display; new leaves - 1st bit, shifted left two for saplings to understand
- if (rand.NextInt(6) == 0)
+ // There is a chance to drop a sapling that varies depending on the type of leaf broken.
+ // TODO: Take into account fortune for sapling drops.
+ int chance;
+ if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_JUNGLE))
+ {
+ // Jungle leaves have a 2.5% chance of dropping a sapling.
+ chance = rand.NextInt(40);
+ }
+ else
+ {
+ // Other leaves have a 5% chance of dropping a sapling.
+ chance = rand.NextInt(20);
+ }
+ if (chance == 0)
{
a_Pickups.push_back(
cItem(
E_BLOCK_SAPLING,
1,
- (m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (2 << (a_BlockMeta & 0x01))
+ (m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : (4 + (a_BlockMeta & 0x01))
)
);
}
-
- // 1 % chance of dropping an apple, if the leaves' type is Apple Leaves
+
+ // 0.5 % chance of dropping an apple, if the leaves' type is Apple Leaves
if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_APPLE))
{
- if (rand.NextInt(101) == 0)
+ if (rand.NextInt(200) == 0)
{
a_Pickups.push_back(cItem(E_ITEM_RED_APPLE, 1, 0));
}
}
}
-
-
+
+
virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
{
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 4a909a1fd..bc2b3e93e 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -1403,14 +1403,25 @@ bool cEntity::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
return false;
}
+ // Ask the plugins if the entity is allowed to change the world
+ if (cRoot::Get()->GetPluginManager()->CallHookEntityChangeWorld(*this, *a_World))
+ {
+ // A Plugin doesn't allow the entity to change the world
+ return false;
+ }
+
// Remove all links to the old world
SetWorldTravellingFrom(GetWorld()); // cChunk::Tick() handles entity removal
GetWorld()->BroadcastDestroyEntity(*this);
// Queue add to new world
a_World->AddEntity(this);
+ cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
SetWorld(a_World);
+ // Entity changed the world, call the hook
+ cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld);
+
return true;
}
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 607a663de..01ad26297 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -1606,6 +1606,12 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
return false;
}
+ if (cRoot::Get()->GetPluginManager()->CallHookEntityChangeWorld(*this, *a_World))
+ {
+ // A Plugin doesn't allow the player to change the world
+ return false;
+ }
+
// Send the respawn packet:
if (a_ShouldSendRespawn && (m_ClientHandle != nullptr))
{
@@ -1621,6 +1627,7 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
// Queue adding player to the new world, including all the necessary adjustments to the object
a_World->AddPlayer(this);
+ cWorld * OldWorld = cRoot::Get()->GetWorld(GetWorld()->GetName()); // Required for the hook HOOK_ENTITY_CHANGED_WORLD
SetWorld(a_World); // Chunks may be streamed before cWorld::AddPlayer() sets the world to the new value
// Update the view distance.
@@ -1635,6 +1642,9 @@ bool cPlayer::DoMoveToWorld(cWorld * a_World, bool a_ShouldSendRespawn)
// Broadcast the player into the new world.
a_World->BroadcastSpawnEntity(*this);
+ // Player changed the world, call the hook
+ cRoot::Get()->GetPluginManager()->CallHookEntityChangedWorld(*this, *OldWorld);
+
return true;
}
diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp
index 737b13535..c1716f026 100644
--- a/src/FastRandom.cpp
+++ b/src/FastRandom.cpp
@@ -6,12 +6,28 @@
#include "Globals.h"
#include "FastRandom.h"
+#include <random>
+
#ifdef _WIN32
- #define thread_local __declspec(thread)
+ #define thread_local static __declspec(thread)
+#elif defined __APPLE__
+ #define thread_local static __thread
#endif
-thread_local unsigned int m_Counter = 0;
-
+static unsigned int GetRandomSeed()
+{
+ thread_local bool SeedCounterInitialized = 0;
+ thread_local unsigned int SeedCounter = 0;
+
+ if (!SeedCounterInitialized)
+ {
+ std::random_device rd;
+ std::uniform_int_distribution<unsigned int> dist;
+ SeedCounter = dist(rd);
+ SeedCounterInitialized = true;
+ }
+ return ++SeedCounter;
+}
@@ -92,7 +108,7 @@ public:
cFastRandom::cFastRandom(void) :
- m_LinearRand(m_Counter++)
+ m_LinearRand(GetRandomSeed())
{
}
@@ -136,7 +152,7 @@ int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End)
// MTRand:
MTRand::MTRand() :
- m_MersenneRand(m_Counter++)
+ m_MersenneRand(GetRandomSeed())
{
}
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
index d0fb79f6d..055ff47d2 100644
--- a/src/Mobs/AggressiveMonster.cpp
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -36,7 +36,6 @@ void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt)
return;
}
}
-
MoveToPosition(m_Target->GetPosition());
}
}
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index f3f8c6b24..2b00f6959 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -89,7 +89,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_SoundDeath(a_SoundDeath)
, m_AttackRate(3)
, m_AttackDamage(1)
- , m_AttackRange(2)
+ , m_AttackRange(1)
, m_AttackInterval(0)
, m_SightDistance(25)
, m_DropChanceWeapon(0.085f)
@@ -147,10 +147,15 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
(Recalculate lots when close, calculate rarely when far) */
if (
((GetPosition() - m_PathFinderDestination).Length() < 0.25) ||
- ((m_TicksSinceLastPathReset > 10) && (m_TicksSinceLastPathReset > (0.15 * (m_FinalDestination - GetPosition()).SqrLength())))
+ ((m_TicksSinceLastPathReset > 10) && (m_TicksSinceLastPathReset > (0.4 * (m_FinalDestination - GetPosition()).SqrLength())))
)
{
- ResetPathFinding();
+ /* Re-calculating is expensive when there's no path to target, and it results in mobs freezing very often as a result of always recalculating.
+ This is a workaround till we get better path recalculation. */
+ if (!m_NoPathToTarget)
+ {
+ ResetPathFinding();
+ }
}
}
@@ -161,12 +166,21 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
StopMovingToPosition(); // Invalid chunks, probably world is loading or something, cancel movement.
return false;
}
+ m_NoPathToTarget = false;
+ m_NoMoreWayPoints = false;
m_PathFinderDestination = m_FinalDestination;
m_Path = new cPath(a_Chunk, GetPosition().Floor(), m_PathFinderDestination.Floor(), 20);
}
switch (m_Path->Step(a_Chunk))
{
+ case ePathFinderStatus::NEARBY_FOUND:
+ {
+ m_NoPathToTarget = true;
+ m_Path->AcceptNearbyPath();
+ break;
+ }
+
case ePathFinderStatus::PATH_NOT_FOUND:
{
StopMovingToPosition(); // Give up pathfinding to that destination.
@@ -179,15 +193,22 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
}
case ePathFinderStatus::PATH_FOUND:
{
- if (--m_GiveUpCounter == 0)
+ if (m_NoMoreWayPoints || (--m_GiveUpCounter == 0))
{
ResetPathFinding(); // Try to calculate a path again.
return false;
}
- else if (!m_Path->IsLastPoint() && (m_Path->IsFirstPoint() || ReachedNextWaypoint())) // Have we arrived at the next cell, as denoted by m_NextWayPointPosition?
+ else if (!m_Path->IsLastPoint()) // Have we arrived at the next cell, as denoted by m_NextWayPointPosition?
+ {
+ if ((m_Path->IsFirstPoint() || ReachedNextWaypoint()))
+ {
+ m_NextWayPointPosition = Vector3d(0.5, 0, 0.5) + m_Path->GetNextPoint();
+ m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition.
+ }
+ }
+ else
{
- m_NextWayPointPosition = Vector3d(0.5, 0, 0.5) + m_Path->GetNextPoint();
- m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition.
+ m_NoMoreWayPoints = true;
}
return true;
}
@@ -269,21 +290,59 @@ bool cMonster::EnsureProperDestination(cChunk & a_Chunk)
{
return false;
}
-
+
int RelX = FloorC(m_FinalDestination.x) - Chunk->GetPosX() * cChunkDef::Width;
int RelZ = FloorC(m_FinalDestination.z) - Chunk->GetPosZ() * cChunkDef::Width;
- // If destination in the air, go down to the lowest air block.
- while (m_FinalDestination.y > 0)
+ // If destination in the air, first try to go 1 block north, or east, or west.
+ // This fixes the player leaning issue.
+ // If that failed, we instead go down to the lowest air block.
+ Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
+ if (!cBlockInfo::IsSolid(BlockType))
{
- Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
- if (cBlockInfo::IsSolid(BlockType))
+ bool InTheAir = true;
+ int x, z;
+ for (z = -1; z <= 1; ++z)
{
- break;
+ for (x = -1; x <= 1; ++x)
+ {
+ if ((x==0) && (z==0))
+ {
+ continue;
+ }
+ Chunk = a_Chunk.GetNeighborChunk(FloorC(m_FinalDestination.x+x), FloorC(m_FinalDestination.z+z));
+ if ((Chunk == nullptr) || !Chunk->IsValid())
+ {
+ return false;
+ }
+ RelX = FloorC(m_FinalDestination.x+x) - Chunk->GetPosX() * cChunkDef::Width;
+ RelZ = FloorC(m_FinalDestination.z+z) - Chunk->GetPosZ() * cChunkDef::Width;
+ Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
+ if (cBlockInfo::IsSolid(BlockType))
+ {
+ m_FinalDestination.x += x;
+ m_FinalDestination.z += z;
+ InTheAir = false;
+ goto breakBothLoops;
+ }
+ }
}
- m_FinalDestination.y -= 1;
- }
+ breakBothLoops:
+ // Go down to the lowest air block.
+ if (InTheAir)
+ {
+ while (m_FinalDestination.y > 0)
+ {
+ Chunk->GetBlockTypeMeta(RelX, FloorC(m_FinalDestination.y) - 1, RelZ, BlockType, BlockMeta);
+ if (cBlockInfo::IsSolid(BlockType))
+ {
+ break;
+ }
+ m_FinalDestination.y -= 1;
+ }
+ }
+ }
// If destination in water, go up to the highest water block.
// If destination in solid, go up to first air block.
@@ -447,21 +506,29 @@ void cMonster::SetPitchAndYawFromDestination()
}
}
+
+
+ Vector3d BodyDistance = m_NextWayPointPosition - GetPosition();
+ double BodyRotation, BodyPitch;
+ BodyDistance.Normalize();
+ VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, BodyRotation, BodyPitch);
+ SetYaw(BodyRotation);
+
Vector3d Distance = FinalDestination - GetPosition();
{
- double Rotation, Pitch;
+ double HeadRotation, HeadPitch;
Distance.Normalize();
- VectorToEuler(Distance.x, Distance.y, Distance.z, Rotation, Pitch);
- SetHeadYaw(Rotation);
- SetPitch(-Pitch);
- }
-
- {
- Vector3d BodyDistance = m_NextWayPointPosition - GetPosition();
- double Rotation, Pitch;
- BodyDistance.Normalize();
- VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, Rotation, Pitch);
- SetYaw(Rotation);
+ VectorToEuler(Distance.x, Distance.y, Distance.z, HeadRotation, HeadPitch);
+ if (std::abs(BodyRotation - HeadRotation) < 120)
+ {
+ SetHeadYaw(HeadRotation);
+ SetPitch(-HeadPitch);
+ }
+ else // We're not an owl. If it's more than 120, don't look behind and instead look at where you're walking.
+ {
+ SetHeadYaw(BodyRotation);
+ SetPitch(-BodyPitch);
+ }
}
}
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index 5d20ba810..c4043b0e5 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -180,6 +180,13 @@ protected:
/** Coordinates for the ultimate, final destination last given to the pathfinder. */
Vector3d m_PathFinderDestination;
+ /** True if there's no path to target and we're walking to an approximated location. */
+ bool m_NoPathToTarget;
+
+ /** Whether The mob has finished their path, note that this does not imply reaching the destination,
+ the destination may sometimes differ from the current path. */
+ bool m_NoMoreWayPoints;
+
/** Finds the lowest non-air block position (not the highest, as cWorld::GetHeight does)
If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
If current Y is solid, goes up to find first nonsolid block, and returns that.
diff --git a/src/Mobs/Path.cpp b/src/Mobs/Path.cpp
index ba7d615ae..ba8046a2b 100644
--- a/src/Mobs/Path.cpp
+++ b/src/Mobs/Path.cpp
@@ -8,7 +8,7 @@
#define DISTANCE_MANHATTAN 0 // 1: More speed, a bit less accuracy 0: Max accuracy, less speed.
#define HEURISTICS_ONLY 0 // 1: Much more speed, much less accurate.
-#define CALCULATIONS_PER_STEP 5 // Higher means more CPU load but faster path calculations.
+#define CALCULATIONS_PER_STEP 10 // Higher means more CPU load but faster path calculations.
// The only version which guarantees the shortest path is 0, 0.
enum class eCellStatus {OPENLIST, CLOSEDLIST, NOLIST};
@@ -44,7 +44,8 @@ cPath::cPath(
m_Destination(a_EndingPoint.Floor()),
m_Source(a_StartingPoint.Floor()),
m_CurrentPoint(0), // GetNextPoint increments this to 1, but that's fine, since the first cell is always a_StartingPoint
- m_Chunk(&a_Chunk)
+ m_Chunk(&a_Chunk),
+ m_BadChunkFound(false)
{
// TODO: if src not walkable OR dest not walkable, then abort.
// Borrow a new "isWalkable" from ProcessIfWalkable, make ProcessIfWalkable also call isWalkable
@@ -55,6 +56,7 @@ cPath::cPath(
return;
}
+ m_NearestPointToTarget = GetCell(m_Source);
m_Status = ePathFinderStatus::CALCULATING;
m_StepsLeft = a_MaxSteps;
@@ -81,15 +83,20 @@ cPath::~cPath()
ePathFinderStatus cPath::Step(cChunk & a_Chunk)
{
m_Chunk = &a_Chunk;
-
if (m_Status != ePathFinderStatus::CALCULATING)
{
return m_Status;
}
- if (m_StepsLeft == 0)
+ if (m_BadChunkFound)
{
FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND);
+ return m_Status;
+ }
+
+ if (m_StepsLeft == 0)
+ {
+ AttemptToFindAlternative();
}
else
{
@@ -102,9 +109,9 @@ ePathFinderStatus cPath::Step(cChunk & a_Chunk)
break; // if we're here, m_Status must have changed either to PATH_FOUND or PATH_NOT_FOUND.
}
}
- }
- m_Chunk = nullptr;
+ m_Chunk = nullptr;
+ }
return m_Status;
}
@@ -112,6 +119,17 @@ ePathFinderStatus cPath::Step(cChunk & a_Chunk)
+Vector3i cPath::AcceptNearbyPath()
+{
+ ASSERT(m_Status == ePathFinderStatus::NEARBY_FOUND);
+ m_Status = ePathFinderStatus::PATH_FOUND;
+ return m_Destination;
+}
+
+
+
+
+
bool cPath::IsSolid(const Vector3i & a_Location)
{
ASSERT(m_Chunk != nullptr);
@@ -119,6 +137,7 @@ bool cPath::IsSolid(const Vector3i & a_Location)
auto Chunk = m_Chunk->GetNeighborChunk(a_Location.x, a_Location.z);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
+ m_BadChunkFound = true;
return true;
}
m_Chunk = Chunk;
@@ -149,34 +168,29 @@ bool cPath::Step_Internal()
{
cPathCell * CurrentCell = OpenListPop();
- // Path not reachable, open list exauhsted.
+ // Path not reachable.
if (CurrentCell == nullptr)
{
- FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND);
- ASSERT(m_Status == ePathFinderStatus::PATH_NOT_FOUND);
+ AttemptToFindAlternative();
return true;
}
// Path found.
- if (
- (CurrentCell->m_Location == m_Destination + Vector3i(0, 0, 1)) ||
- (CurrentCell->m_Location == m_Destination + Vector3i(1, 0, 0)) ||
- (CurrentCell->m_Location == m_Destination + Vector3i(-1, 0, 0)) ||
- (CurrentCell->m_Location == m_Destination + Vector3i(0, 0, -1)) ||
- (CurrentCell->m_Location == m_Destination + Vector3i(0, -1, 0))
- )
+ if (CurrentCell->m_Location == m_Destination)
{
- do
- {
- m_PathPoints.push_back(CurrentCell->m_Location); // Populate the cPath with points.
- CurrentCell = CurrentCell->m_Parent;
- } while (CurrentCell != nullptr);
-
+ BuildPath();
FinishCalculation(ePathFinderStatus::PATH_FOUND);
return true;
}
- // Calculation not finished yet, process a currentCell by inspecting all neighbors.
+ // Calculation not finished yet.
+ // Check if we have a new NearestPoint.
+ if (CurrentCell->m_H < m_NearestPointToTarget->m_H)
+ {
+ m_NearestPointToTarget = CurrentCell;
+ }
+
+ // process a currentCell by inspecting all neighbors.
// Check North, South, East, West on all 3 different heights.
int i;
@@ -213,13 +227,40 @@ bool cPath::Step_Internal()
-void cPath::FinishCalculation()
+void cPath::AttemptToFindAlternative()
{
- for (auto && pair : m_Map)
+ if (m_NearestPointToTarget == GetCell(m_Source))
+ {
+ FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND);
+ }
+ else
{
- delete pair.second;
+ m_Destination = m_NearestPointToTarget->m_Location;
+ BuildPath();
+ FinishCalculation(ePathFinderStatus::NEARBY_FOUND);
}
+}
+
+
+
+
+
+void cPath::BuildPath()
+{
+ cPathCell * CurrentCell = GetCell(m_Destination);
+ do
+ {
+ m_PathPoints.push_back(CurrentCell->m_Location); // Populate the cPath with points.
+ CurrentCell = CurrentCell->m_Parent;
+ } while (CurrentCell != nullptr);
+}
+
+
+
+
+void cPath::FinishCalculation()
+{
m_Map.clear();
m_OpenList = std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics>{};
}
@@ -230,6 +271,10 @@ void cPath::FinishCalculation()
void cPath::FinishCalculation(ePathFinderStatus a_NewStatus)
{
+ if (m_BadChunkFound)
+ {
+ a_NewStatus = ePathFinderStatus::PATH_NOT_FOUND;
+ }
m_Status = a_NewStatus;
FinishCalculation();
}
@@ -255,7 +300,7 @@ cPathCell * cPath::OpenListPop() // Popping from the open list also means addin
{
if (m_OpenList.size() == 0)
{
- return nullptr; // We've exhausted the search space and nothing was found, this will trigger a PATH_NOT_FOUND status.
+ return nullptr; // We've exhausted the search space and nothing was found, this will trigger a PATH_NOT_FOUND or NEARBY_FOUND status.
}
cPathCell * Ret = m_OpenList.top();
@@ -348,7 +393,7 @@ cPathCell * cPath::GetCell(const Vector3i & a_Location)
{
Cell = new cPathCell();
Cell->m_Location = a_Location;
- m_Map[a_Location] = Cell;
+ m_Map[a_Location] = UniquePtr<cPathCell>(Cell);
Cell->m_IsSolid = IsSolid(a_Location);
Cell->m_Status = eCellStatus::NOLIST;
#ifdef COMPILING_PATHFIND_DEBUGGER
@@ -360,6 +405,6 @@ cPathCell * cPath::GetCell(const Vector3i & a_Location)
}
else
{
- return m_Map[a_Location];
+ return m_Map[a_Location].get();
}
}
diff --git a/src/Mobs/Path.h b/src/Mobs/Path.h
index adae77984..7a4182f17 100644
--- a/src/Mobs/Path.h
+++ b/src/Mobs/Path.h
@@ -23,7 +23,7 @@ Put this in your .cpp:
class cChunk;
/* Various little structs and classes */
-enum class ePathFinderStatus {CALCULATING, PATH_FOUND, PATH_NOT_FOUND};
+enum class ePathFinderStatus {CALCULATING, PATH_FOUND, PATH_NOT_FOUND, NEARBY_FOUND};
struct cPathCell; // Defined inside Path.cpp
class compareHeuristics
{
@@ -62,9 +62,17 @@ public:
/** Destroys the path and frees its memory. */
~cPath();
- /** Performs part of the path calculation and returns true if the path computation has finished. */
+ /** Performs part of the path calculation and returns the appropriate status.
+ If NEARBY_FOUND is returned, it means that the destination is not reachable, but a nearby destination
+ is reachable. If the user likes the alternative destination, they can call AcceptNearbyPath to treat the path as found,
+ and to make consequent calls to step return PATH_FOUND*/
ePathFinderStatus Step(cChunk & a_Chunk);
+ /** Called after the PathFinder's step returns NEARBY_FOUND.
+ Changes the PathFinder status from NEARBY_FOUND to PATH_FOUND, returns the nearby destination that
+ the PathFinder found a path to. */
+ Vector3i AcceptNearbyPath();
+
/* Point retrieval functions, inlined for performance. */
/** Returns the next point in the path. */
inline Vector3i GetNextPoint()
@@ -93,7 +101,10 @@ public:
/** Returns the total number of points this path has. */
inline int GetPointCount()
{
- ASSERT(m_Status == ePathFinderStatus::PATH_FOUND);
+ if (m_Status != ePathFinderStatus::PATH_FOUND)
+ {
+ return 0;
+ }
return m_PathPoints.size();
}
@@ -119,6 +130,8 @@ private:
bool Step_Internal(); // The public version just calls this version * CALCULATIONS_PER_CALL times.
void FinishCalculation(); // Clears the memory used for calculating the path.
void FinishCalculation(ePathFinderStatus a_NewStatus); // Clears the memory used for calculating the path and changes the status.
+ void AttemptToFindAlternative();
+ void BuildPath();
/* Openlist and closedlist management */
void OpenListAdd(cPathCell * a_Cell);
@@ -131,10 +144,11 @@ private:
/* Pathfinding fields */
std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics> m_OpenList;
- std::unordered_map<Vector3i, cPathCell *, VectorHasher> m_Map;
+ std::unordered_map<Vector3i, UniquePtr<cPathCell>, VectorHasher> m_Map;
Vector3i m_Destination;
Vector3i m_Source;
int m_StepsLeft;
+ cPathCell * m_NearestPointToTarget;
/* Control fields */
ePathFinderStatus m_Status;
@@ -145,6 +159,7 @@ private:
/* Interfacing with the world */
cChunk * m_Chunk; // Only valid inside Step()!
+ bool m_BadChunkFound;
#ifdef COMPILING_PATHFIND_DEBUGGER
#include "../path_irrlicht.cpp"
#endif
diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp
index 17faca27e..4612af4a5 100644
--- a/src/Protocol/Protocol18x.cpp
+++ b/src/Protocol/Protocol18x.cpp
@@ -51,7 +51,7 @@ Implements the 1.8.x protocol classes:
/** The slot number that the client uses to indicate "outside the window". */
-static const Int16 SLOT_NUM_OUTSIDE = -1;
+static const Int16 SLOT_NUM_OUTSIDE = -999;
@@ -2265,7 +2265,7 @@ void cProtocol180::HandlePacketCreativeInventoryAction(cByteBuffer & a_ByteBuffe
{
return;
}
- m_Client->HandleCreativeInventory(SlotNum, Item, (SlotNum == SLOT_NUM_OUTSIDE) ? caLeftClickOutside : caLeftClick);
+ m_Client->HandleCreativeInventory(SlotNum, Item, (SlotNum == -1) ? caLeftClickOutside : caLeftClick);
}
diff --git a/src/World.cpp b/src/World.cpp
index eb8835467..e93d6bb26 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -800,7 +800,7 @@ void cWorld::InitialiseGeneratorDefaults(cIniFile & a_IniFile)
a_IniFile.GetValueSet("Generator", "BiomeGen", "Grown");
a_IniFile.GetValueSet("Generator", "ShapeGen", "BiomalNoise3D");
a_IniFile.GetValueSet("Generator", "CompositionGen", "Biomal");
- a_IniFile.GetValueSet("Generator", "Finishers", "Ravines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals");
+ a_IniFile.GetValueSet("Generator", "Finishers", "RoughRavines, WormNestCaves, WaterLakes, WaterSprings, LavaLakes, LavaSprings, OreNests, Mineshafts, Trees, Villages, TallGrass, SprinkleFoliage, Ice, Snow, Lilypads, BottomLava, DeadBushes, NaturalPatches, PreSimulator, Animals");
break;
}
case dimNether: