1
0
This commit is contained in:
STRWarrior 2015-05-18 09:30:43 +02:00
commit 007bac638b
15 changed files with 319 additions and 178 deletions

View File

@ -2,7 +2,7 @@ language: cpp
compiler: clang compiler: clang
before_install: before_install:
- if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi # - if [ "$TRAVIS_MCSERVER_BUILD_TYPE" == "COVERAGE" ]; then sudo pip install cpp_coveralls; fi
# g++4.8 # g++4.8
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
@ -21,17 +21,17 @@ install:
# Build MCServer # Build MCServer
script: ./CIbuild.sh script: ./CIbuild.sh
after_success: #after_success:
- ./uploadCoverage.sh # - ./uploadCoverage.sh
env: env:
- TRAVIS_MCSERVER_BUILD_TYPE=RELEASE MCSERVER_PATH=./MCServer - TRAVIS_MCSERVER_BUILD_TYPE=RELEASE MCSERVER_PATH=./MCServer
- TRAVIS_MCSERVER_BUILD_TYPE=DEBUG MCSERVER_PATH=./MCServer_debug - TRAVIS_MCSERVER_BUILD_TYPE=DEBUG MCSERVER_PATH=./MCServer_debug
matrix: #matrix:
include: # include:
- compiler: gcc # - compiler: gcc
env: TRAVIS_MCSERVER_BUILD_TYPE=COVERAGE MCSERVER_PATH=./MCServer # env: TRAVIS_MCSERVER_BUILD_TYPE=COVERAGE MCSERVER_PATH=./MCServer
# Notification Settings # Notification Settings
notifications: notifications:

View File

@ -971,7 +971,7 @@ end
--- Writes a list of undocumented objects into a file --- Writes a list of undocumented objects into a file
local function ListUndocumentedObjects(API, UndocumentedHooks) local function ListUndocumentedObjects(API, UndocumentedHooks)
f = io.open("API/_undocumented.lua", "w"); local f = io.open("API/_undocumented.lua", "w");
if (f ~= nil) then if (f ~= nil) then
f:write("\n-- This is the list of undocumented API objects, automatically generated by APIDump\n\n"); f:write("\n-- This is the list of undocumented API objects, automatically generated by APIDump\n\n");
f:write("g_APIDesc =\n{\n\tClasses =\n\t{\n"); f:write("g_APIDesc =\n{\n\tClasses =\n\t{\n");

View File

@ -1,12 +1,14 @@
MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer/master.svg?style=flat)](https://travis-ci.org/mc-server/MCServer) [![Coverity Scan Build Status](https://scan.coverity.com/projects/1930/badge.svg)](https://scan.coverity.com/projects/1930) [![weekly tips](http://img.shields.io/gratipay/cuberite_team.svg?style=flat)](http://gratipay.com/cuberite_team) [![tip for next commit](http://tip4commit.com/projects/74.svg)](http://tip4commit.com/projects/74) MCServer [![Build Status](http://img.shields.io/travis/mc-server/MCServer/master.svg?style=flat)](https://travis-ci.org/mc-server/MCServer) [![Coverity Scan Build Status](https://img.shields.io/coverity/scan/1930.svg)](https://scan.coverity.com/projects/1930) [![weekly tips](http://img.shields.io/gratipay/cuberite_team.svg?style=flat)](http://gratipay.com/cuberite_team)
======== ========
MCServer is a Minecraft server that is written in C++ and designed to be efficient with memory and CPU, as well as having a flexible Lua Plugin API. MCServer is a Minecraft-compatible multiplayer game server that is written in C++ and designed to be efficient with memory and CPU, as well as having a flexible Lua Plugin API. MCServer is compatible with the vanilla Minecraft client.
MCServer can run on Windows, *nix and Android operating systems. This includes Android phones and tablets as well as Raspberry Pis. MCServer can run on Windows, *nix and Android operating systems. This includes Android phones and tablets as well as Raspberry Pis.
We currently support Release 1.7 and 1.8 (not beta) Minecraft protocol versions. We currently support Release 1.7 and 1.8 (not beta) Minecraft protocol versions.
Subscribe to [the newsletter](http://newsletter.cuberite.org/subscribe.htm) for important updates and project news.
Installation Installation
------------ ------------
Hosted MCServer is available DIY on DigitalOcean: [![Install on DigitalOcean](http://doinstall.bearbin.net/button.svg)](http://doinstall.bearbin.net/install?url=https://github.com/mc-server/MCServer) and [Gamososm](https://gamocosm.com) also offers MCServer support. Hosted MCServer is available DIY on DigitalOcean: [![Install on DigitalOcean](http://doinstall.bearbin.net/button.svg)](http://doinstall.bearbin.net/install?url=https://github.com/mc-server/MCServer) and [Gamososm](https://gamocosm.com) also offers MCServer support.

View File

@ -927,6 +927,9 @@ bool cLuaState::CheckParamTable(int a_StartParam, int a_EndParam)
VERIFY(lua_getstack(m_LuaState, 0, &entry)); VERIFY(lua_getstack(m_LuaState, 0, &entry));
VERIFY(lua_getinfo (m_LuaState, "n", &entry)); VERIFY(lua_getinfo (m_LuaState, "n", &entry));
AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?"); AString ErrMsg = Printf("#ferror in function '%s'.", (entry.name != nullptr) ? entry.name : "?");
BreakIntoDebugger(m_LuaState);
tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err); tolua_error(m_LuaState, ErrMsg.c_str(), &tolua_err);
return false; return false;
} // for i - Param } // for i - Param
@ -1366,6 +1369,7 @@ int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
{ {
LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1)); LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
LogStackTrace(a_LuaState, 1); LogStackTrace(a_LuaState, 1);
BreakIntoDebugger(a_LuaState);
return 1; // We left the error message on the stack as the return value 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: // cLuaState::cRef:

View File

@ -389,6 +389,9 @@ protected:
/** Used as the error reporting function for function calls */ /** Used as the error reporting function for function calls */
static int ReportFnCallErrors(lua_State * a_LuaState); 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);
} ; } ;

View File

@ -747,39 +747,38 @@ static int tolua_cPluginManager_AddHook(lua_State * tolua_S)
static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S)
{ {
int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ /*
if (NumArgs != 1) Function signature:
cPluginManager:Get():ForEachCommand(a_CallbackFn) -> bool
*/
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cPluginManager") ||
!L.CheckParamFunction(2) ||
!L.CheckParamEnd(3)
)
{ {
LOGWARN("Error in function call 'ForEachCommand': Requires 1 argument, got %i", NumArgs);
return 0; return 0;
} }
cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, nullptr); // Get the params:
if (self == nullptr) cLuaState::cRef FnRef;
{ L.GetStackValues(2, FnRef);
LOGWARN("Error in function call 'ForEachCommand': Not called on an object instance"); if (!FnRef.IsValid())
return 0;
}
if (!lua_isfunction(tolua_S, 2))
{
LOGWARN("Error in function call 'ForEachCommand': Expected a function for parameter #1");
return 0;
}
int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
if (FuncRef == LUA_REFNIL)
{ {
LOGWARN("Error in function call 'ForEachCommand': Could not get function reference of parameter #1"); LOGWARN("Error in function call 'ForEachCommand': Could not get function reference of parameter #1");
return 0; return 0;
} }
// Callback for enumerating all commands:
class cLuaCallback : public cPluginManager::cCommandEnumCallback class cLuaCallback : public cPluginManager::cCommandEnumCallback
{ {
public: public:
cLuaCallback(lua_State * a_LuaState, int a_FuncRef): cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
LuaState(a_LuaState), m_LuaState(a_LuaState),
FuncRef(a_FuncRef) m_FnRef(a_FnRef)
{ {
} }
@ -787,35 +786,16 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S)
virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override
{ {
UNUSED(a_Plugin); UNUSED(a_Plugin);
bool ret = false;
lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ m_LuaState.Call(m_FnRef, a_Command, a_Permission, a_HelpString, cLuaState::Return, ret);
tolua_pushcppstring(LuaState, a_Command); return ret;
tolua_pushcppstring(LuaState, a_Permission);
tolua_pushcppstring(LuaState, a_HelpString);
int s = lua_pcall(LuaState, 3, 1, 0);
if (cLuaState::ReportErrors(LuaState, s))
{
return true; /* Abort enumeration */
}
if (lua_isboolean(LuaState, -1))
{
return (tolua_toboolean(LuaState, -1, 0) > 0);
}
return false; /* Continue enumeration */
} }
lua_State * LuaState; cLuaState & m_LuaState;
int FuncRef; cLuaState::cRef & m_FnRef;
} Callback(tolua_S, FuncRef); } Callback(L, FnRef);
bool bRetVal = self->ForEachCommand(Callback); // Execute and push the returned value:
L.Push(cPluginManager::Get()->ForEachCommand(Callback));
/* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
/* Push return value on stack */
tolua_pushboolean(tolua_S, bRetVal);
return 1; return 1;
} }
@ -825,39 +805,38 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S)
static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S)
{ {
int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ /*
if (NumArgs != 1) Function signature:
cPluginManager:Get():ForEachConsoleCommand(a_CallbackFn) -> bool
*/
// Check params:
cLuaState L(tolua_S);
if (
!L.CheckParamUserType(1, "cPluginManager") ||
!L.CheckParamFunction(2) ||
!L.CheckParamEnd(3)
)
{ {
LOGWARN("Error in function call 'ForEachConsoleCommand': Requires 1 argument, got %i", NumArgs);
return 0; return 0;
} }
cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, nullptr); // Get the params:
if (self == nullptr) cLuaState::cRef FnRef;
{ L.GetStackValues(2, FnRef);
LOGWARN("Error in function call 'ForEachConsoleCommand': Not called on an object instance"); if (!FnRef.IsValid())
return 0;
}
if (!lua_isfunction(tolua_S, 2))
{
LOGWARN("Error in function call 'ForEachConsoleCommand': Expected a function for parameter #1");
return 0;
}
int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
if (FuncRef == LUA_REFNIL)
{ {
LOGWARN("Error in function call 'ForEachConsoleCommand': Could not get function reference of parameter #1"); LOGWARN("Error in function call 'ForEachConsoleCommand': Could not get function reference of parameter #1");
return 0; return 0;
} }
// Callback for enumerating all commands:
class cLuaCallback : public cPluginManager::cCommandEnumCallback class cLuaCallback : public cPluginManager::cCommandEnumCallback
{ {
public: public:
cLuaCallback(lua_State * a_LuaState, int a_FuncRef): cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef):
LuaState(a_LuaState), m_LuaState(a_LuaState),
FuncRef(a_FuncRef) m_FnRef(a_FnRef)
{ {
} }
@ -866,34 +845,16 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S)
{ {
UNUSED(a_Plugin); UNUSED(a_Plugin);
UNUSED(a_Permission); UNUSED(a_Permission);
bool ret = false;
lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ m_LuaState.Call(m_FnRef, a_Command, a_HelpString, cLuaState::Return, ret);
tolua_pushcppstring(LuaState, a_Command); return ret;
tolua_pushcppstring(LuaState, a_HelpString);
int s = lua_pcall(LuaState, 2, 1, 0);
if (cLuaState::ReportErrors(LuaState, s))
{
return true; /* Abort enumeration */
}
if (lua_isboolean(LuaState, -1))
{
return (tolua_toboolean(LuaState, -1, 0) > 0);
}
return false; /* Continue enumeration */
} }
lua_State * LuaState; cLuaState & m_LuaState;
int FuncRef; cLuaState::cRef & m_FnRef;
} Callback(tolua_S, FuncRef); } Callback(L, FnRef);
bool bRetVal = self->ForEachConsoleCommand(Callback); // Execute and push the returned value:
L.Push(cPluginManager::Get()->ForEachConsoleCommand(Callback));
/* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */
luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef);
/* Push return value on stack */
tolua_pushboolean(tolua_S, bRetVal);
return 1; return 1;
} }

View File

@ -14,7 +14,7 @@ void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInt
NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
Vector3i ThisPos( 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) if (OldMeta & 0x8)
{ {
// Was pillow // Was pillow
@ -111,7 +111,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
// Is foot end // Is foot end
VERIFY((Meta & 0x4) != 0x4); // Occupied flag should never be set, else our compilator (intended) is broken 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 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); a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z);

View File

@ -41,7 +41,7 @@ public:
cFastRandom rand; cFastRandom rand;
// Old leaves - 3 bits contain display; new leaves - 1st bit, shifted left two for saplings to understand // Old leaves - 3 bits contain display; new leaves - 1st bit, shifted left two for saplings to understand
if (rand.NextInt(6) == 0) if (rand.NextInt(20) == 0)
{ {
a_Pickups.push_back( a_Pickups.push_back(
cItem( cItem(

View File

@ -6,12 +6,28 @@
#include "Globals.h" #include "Globals.h"
#include "FastRandom.h" #include "FastRandom.h"
#include <random>
#ifdef _WIN32 #ifdef _WIN32
#define thread_local __declspec(thread) #define thread_local static __declspec(thread)
#elif defined __APPLE__
#define thread_local static __thread
#endif #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) : 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::MTRand() : MTRand::MTRand() :
m_MersenneRand(m_Counter++) m_MersenneRand(GetRandomSeed())
{ {
} }

View File

@ -36,7 +36,6 @@ void cAggressiveMonster::InStateChasing(std::chrono::milliseconds a_Dt)
return; return;
} }
} }
MoveToPosition(m_Target->GetPosition()); MoveToPosition(m_Target->GetPosition());
} }
} }

View File

@ -89,7 +89,7 @@ cMonster::cMonster(const AString & a_ConfigName, eMonsterType a_MobType, const A
, m_SoundDeath(a_SoundDeath) , m_SoundDeath(a_SoundDeath)
, m_AttackRate(3) , m_AttackRate(3)
, m_AttackDamage(1) , m_AttackDamage(1)
, m_AttackRange(2) , m_AttackRange(1)
, m_AttackInterval(0) , m_AttackInterval(0)
, m_SightDistance(25) , m_SightDistance(25)
, m_DropChanceWeapon(0.085f) , m_DropChanceWeapon(0.085f)
@ -147,10 +147,15 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
(Recalculate lots when close, calculate rarely when far) */ (Recalculate lots when close, calculate rarely when far) */
if ( if (
((GetPosition() - m_PathFinderDestination).Length() < 0.25) || ((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. StopMovingToPosition(); // Invalid chunks, probably world is loading or something, cancel movement.
return false; return false;
} }
m_NoPathToTarget = false;
m_NoMoreWayPoints = false;
m_PathFinderDestination = m_FinalDestination; m_PathFinderDestination = m_FinalDestination;
m_Path = new cPath(a_Chunk, GetPosition().Floor(), m_PathFinderDestination.Floor(), 20); m_Path = new cPath(a_Chunk, GetPosition().Floor(), m_PathFinderDestination.Floor(), 20);
} }
switch (m_Path->Step(a_Chunk)) switch (m_Path->Step(a_Chunk))
{ {
case ePathFinderStatus::NEARBY_FOUND:
{
m_NoPathToTarget = true;
m_Path->AcceptNearbyPath();
break;
}
case ePathFinderStatus::PATH_NOT_FOUND: case ePathFinderStatus::PATH_NOT_FOUND:
{ {
StopMovingToPosition(); // Give up pathfinding to that destination. StopMovingToPosition(); // Give up pathfinding to that destination.
@ -179,15 +193,22 @@ bool cMonster::TickPathFinding(cChunk & a_Chunk)
} }
case ePathFinderStatus::PATH_FOUND: case ePathFinderStatus::PATH_FOUND:
{ {
if (--m_GiveUpCounter == 0) if (m_NoMoreWayPoints || (--m_GiveUpCounter == 0))
{ {
ResetPathFinding(); // Try to calculate a path again. ResetPathFinding(); // Try to calculate a path again.
return false; 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?
{ {
m_NextWayPointPosition = Vector3d(0.5, 0, 0.5) + m_Path->GetNextPoint(); if ((m_Path->IsFirstPoint() || ReachedNextWaypoint()))
m_GiveUpCounter = 40; // Give up after 40 ticks (2 seconds) if failed to reach m_NextWayPointPosition. {
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_NoMoreWayPoints = true;
} }
return true; return true;
} }
@ -269,21 +290,59 @@ bool cMonster::EnsureProperDestination(cChunk & a_Chunk)
{ {
return false; return false;
} }
int RelX = FloorC(m_FinalDestination.x) - Chunk->GetPosX() * cChunkDef::Width; int RelX = FloorC(m_FinalDestination.x) - Chunk->GetPosX() * cChunkDef::Width;
int RelZ = FloorC(m_FinalDestination.z) - Chunk->GetPosZ() * cChunkDef::Width; int RelZ = FloorC(m_FinalDestination.z) - Chunk->GetPosZ() * cChunkDef::Width;
// If destination in the air, go down to the lowest air block. // If destination in the air, first try to go 1 block north, or east, or west.
while (m_FinalDestination.y > 0) // 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); bool InTheAir = true;
if (cBlockInfo::IsSolid(BlockType)) 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 water, go up to the highest water block.
// If destination in solid, go up to first air 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(); Vector3d Distance = FinalDestination - GetPosition();
{ {
double Rotation, Pitch; double HeadRotation, HeadPitch;
Distance.Normalize(); Distance.Normalize();
VectorToEuler(Distance.x, Distance.y, Distance.z, Rotation, Pitch); VectorToEuler(Distance.x, Distance.y, Distance.z, HeadRotation, HeadPitch);
SetHeadYaw(Rotation); if (abs(BodyRotation - HeadRotation) < 120)
SetPitch(-Pitch); {
} SetHeadYaw(HeadRotation);
SetPitch(-HeadPitch);
{ }
Vector3d BodyDistance = m_NextWayPointPosition - GetPosition(); else // We're not an owl. If it's more than 120, don't look behind and instead look at where you're walking.
double Rotation, Pitch; {
BodyDistance.Normalize(); SetHeadYaw(BodyRotation);
VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, Rotation, Pitch); SetPitch(-BodyPitch);
SetYaw(Rotation); }
} }
} }

View File

@ -180,6 +180,13 @@ protected:
/** Coordinates for the ultimate, final destination last given to the pathfinder. */ /** Coordinates for the ultimate, final destination last given to the pathfinder. */
Vector3d m_PathFinderDestination; 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) /** 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 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. If current Y is solid, goes up to find first nonsolid block, and returns that.

View File

@ -8,7 +8,7 @@
#define DISTANCE_MANHATTAN 0 // 1: More speed, a bit less accuracy 0: Max accuracy, less speed. #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 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. // The only version which guarantees the shortest path is 0, 0.
enum class eCellStatus {OPENLIST, CLOSEDLIST, NOLIST}; enum class eCellStatus {OPENLIST, CLOSEDLIST, NOLIST};
@ -44,7 +44,8 @@ cPath::cPath(
m_Destination(a_EndingPoint.Floor()), m_Destination(a_EndingPoint.Floor()),
m_Source(a_StartingPoint.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_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. // TODO: if src not walkable OR dest not walkable, then abort.
// Borrow a new "isWalkable" from ProcessIfWalkable, make ProcessIfWalkable also call isWalkable // Borrow a new "isWalkable" from ProcessIfWalkable, make ProcessIfWalkable also call isWalkable
@ -55,6 +56,7 @@ cPath::cPath(
return; return;
} }
m_NearestPointToTarget = GetCell(m_Source);
m_Status = ePathFinderStatus::CALCULATING; m_Status = ePathFinderStatus::CALCULATING;
m_StepsLeft = a_MaxSteps; m_StepsLeft = a_MaxSteps;
@ -81,15 +83,20 @@ cPath::~cPath()
ePathFinderStatus cPath::Step(cChunk & a_Chunk) ePathFinderStatus cPath::Step(cChunk & a_Chunk)
{ {
m_Chunk = &a_Chunk; m_Chunk = &a_Chunk;
if (m_Status != ePathFinderStatus::CALCULATING) if (m_Status != ePathFinderStatus::CALCULATING)
{ {
return m_Status; return m_Status;
} }
if (m_StepsLeft == 0) if (m_BadChunkFound)
{ {
FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND); FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND);
return m_Status;
}
if (m_StepsLeft == 0)
{
AttemptToFindAlternative();
} }
else 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. 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; 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) bool cPath::IsSolid(const Vector3i & a_Location)
{ {
ASSERT(m_Chunk != nullptr); 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); auto Chunk = m_Chunk->GetNeighborChunk(a_Location.x, a_Location.z);
if ((Chunk == nullptr) || !Chunk->IsValid()) if ((Chunk == nullptr) || !Chunk->IsValid())
{ {
m_BadChunkFound = true;
return true; return true;
} }
m_Chunk = Chunk; m_Chunk = Chunk;
@ -149,34 +168,29 @@ bool cPath::Step_Internal()
{ {
cPathCell * CurrentCell = OpenListPop(); cPathCell * CurrentCell = OpenListPop();
// Path not reachable, open list exauhsted. // Path not reachable.
if (CurrentCell == nullptr) if (CurrentCell == nullptr)
{ {
FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND); AttemptToFindAlternative();
ASSERT(m_Status == ePathFinderStatus::PATH_NOT_FOUND);
return true; return true;
} }
// Path found. // Path found.
if ( if (CurrentCell->m_Location == m_Destination)
(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))
)
{ {
do BuildPath();
{
m_PathPoints.push_back(CurrentCell->m_Location); // Populate the cPath with points.
CurrentCell = CurrentCell->m_Parent;
} while (CurrentCell != nullptr);
FinishCalculation(ePathFinderStatus::PATH_FOUND); FinishCalculation(ePathFinderStatus::PATH_FOUND);
return true; 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. // Check North, South, East, West on all 3 different heights.
int i; int i;
@ -213,13 +227,40 @@ bool cPath::Step_Internal()
void cPath::AttemptToFindAlternative()
{
if (m_NearestPointToTarget == GetCell(m_Source))
{
FinishCalculation(ePathFinderStatus::PATH_NOT_FOUND);
}
else
{
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() void cPath::FinishCalculation()
{ {
for (auto && pair : m_Map)
{
delete pair.second;
}
m_Map.clear(); m_Map.clear();
m_OpenList = std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics>{}; m_OpenList = std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics>{};
} }
@ -230,6 +271,10 @@ void cPath::FinishCalculation()
void cPath::FinishCalculation(ePathFinderStatus a_NewStatus) void cPath::FinishCalculation(ePathFinderStatus a_NewStatus)
{ {
if (m_BadChunkFound)
{
a_NewStatus = ePathFinderStatus::PATH_NOT_FOUND;
}
m_Status = a_NewStatus; m_Status = a_NewStatus;
FinishCalculation(); FinishCalculation();
} }
@ -255,7 +300,7 @@ cPathCell * cPath::OpenListPop() // Popping from the open list also means addin
{ {
if (m_OpenList.size() == 0) 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(); cPathCell * Ret = m_OpenList.top();
@ -348,7 +393,7 @@ cPathCell * cPath::GetCell(const Vector3i & a_Location)
{ {
Cell = new cPathCell(); Cell = new cPathCell();
Cell->m_Location = a_Location; 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_IsSolid = IsSolid(a_Location);
Cell->m_Status = eCellStatus::NOLIST; Cell->m_Status = eCellStatus::NOLIST;
#ifdef COMPILING_PATHFIND_DEBUGGER #ifdef COMPILING_PATHFIND_DEBUGGER
@ -360,6 +405,6 @@ cPathCell * cPath::GetCell(const Vector3i & a_Location)
} }
else else
{ {
return m_Map[a_Location]; return m_Map[a_Location].get();
} }
} }

View File

@ -23,7 +23,7 @@ Put this in your .cpp:
class cChunk; class cChunk;
/* Various little structs and classes */ /* 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 struct cPathCell; // Defined inside Path.cpp
class compareHeuristics class compareHeuristics
{ {
@ -62,9 +62,17 @@ public:
/** Destroys the path and frees its memory. */ /** Destroys the path and frees its memory. */
~cPath(); ~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); 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. */ /* Point retrieval functions, inlined for performance. */
/** Returns the next point in the path. */ /** Returns the next point in the path. */
inline Vector3i GetNextPoint() inline Vector3i GetNextPoint()
@ -93,7 +101,10 @@ public:
/** Returns the total number of points this path has. */ /** Returns the total number of points this path has. */
inline int GetPointCount() inline int GetPointCount()
{ {
ASSERT(m_Status == ePathFinderStatus::PATH_FOUND); if (m_Status != ePathFinderStatus::PATH_FOUND)
{
return 0;
}
return m_PathPoints.size(); return m_PathPoints.size();
} }
@ -119,6 +130,8 @@ private:
bool Step_Internal(); // The public version just calls this version * CALCULATIONS_PER_CALL times. 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(); // 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 FinishCalculation(ePathFinderStatus a_NewStatus); // Clears the memory used for calculating the path and changes the status.
void AttemptToFindAlternative();
void BuildPath();
/* Openlist and closedlist management */ /* Openlist and closedlist management */
void OpenListAdd(cPathCell * a_Cell); void OpenListAdd(cPathCell * a_Cell);
@ -131,10 +144,11 @@ private:
/* Pathfinding fields */ /* Pathfinding fields */
std::priority_queue<cPathCell *, std::vector<cPathCell *>, compareHeuristics> m_OpenList; 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_Destination;
Vector3i m_Source; Vector3i m_Source;
int m_StepsLeft; int m_StepsLeft;
cPathCell * m_NearestPointToTarget;
/* Control fields */ /* Control fields */
ePathFinderStatus m_Status; ePathFinderStatus m_Status;
@ -145,6 +159,7 @@ private:
/* Interfacing with the world */ /* Interfacing with the world */
cChunk * m_Chunk; // Only valid inside Step()! cChunk * m_Chunk; // Only valid inside Step()!
bool m_BadChunkFound;
#ifdef COMPILING_PATHFIND_DEBUGGER #ifdef COMPILING_PATHFIND_DEBUGGER
#include "../path_irrlicht.cpp" #include "../path_irrlicht.cpp"
#endif #endif

View File

@ -51,7 +51,7 @@ Implements the 1.8.x protocol classes:
/** The slot number that the client uses to indicate "outside the window". */ /** 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; return;
} }
m_Client->HandleCreativeInventory(SlotNum, Item, (SlotNum == SLOT_NUM_OUTSIDE) ? caLeftClickOutside : caLeftClick); m_Client->HandleCreativeInventory(SlotNum, Item, (SlotNum == -1) ? caLeftClickOutside : caLeftClick);
} }