1
0

Some change to Entity.cpp

* Added comments to BroadcastMovementUpdate() and the collision tracer
This commit is contained in:
Tiger Wang 2014-04-23 21:06:46 +01:00
parent f763242e5c
commit 7f5cf417de
7 changed files with 105 additions and 123 deletions

View File

@ -10,7 +10,6 @@
#include "../Simulator/FluidSimulator.h" #include "../Simulator/FluidSimulator.h"
#include "../Bindings/PluginManager.h" #include "../Bindings/PluginManager.h"
#include "../Tracer.h" #include "../Tracer.h"
#include "Minecart.h"
#include "Player.h" #include "Player.h"
@ -32,16 +31,10 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_Attachee(NULL) , m_Attachee(NULL)
, m_bDirtyHead(true) , m_bDirtyHead(true)
, m_bDirtyOrientation(true) , m_bDirtyOrientation(true)
, m_bDirtyPosition(true) , m_bHasSentNoSpeed(true)
, m_bDirtySpeed(true) , m_bOnGround(false)
, m_bOnGround( false ) , m_Gravity(-9.81f)
, m_Gravity( -9.81f ) , m_LastPos(a_X, a_Y, a_Z)
, m_LastPosX( 0.0 )
, m_LastPosY( 0.0 )
, m_LastPosZ( 0.0 )
, m_TimeLastTeleportPacket(0)
, m_TimeLastMoveReltPacket(0)
, m_TimeLastSpeedPacket(0)
, m_IsInitialized(false) , m_IsInitialized(false)
, m_EntityType(a_EntityType) , m_EntityType(a_EntityType)
, m_World(NULL) , m_World(NULL)
@ -52,7 +45,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_TicksSinceLastVoidDamage(0) , m_TicksSinceLastVoidDamage(0)
, m_IsSwimming(false) , m_IsSwimming(false)
, m_IsSubmerged(false) , m_IsSubmerged(false)
, m_HeadYaw( 0.0 ) , m_HeadYaw(0.0)
, m_Rot(0.0, 0.0, 0.0) , m_Rot(0.0, 0.0, 0.0)
, m_Pos(a_X, a_Y, a_Z) , m_Pos(a_X, a_Y, a_Z)
, m_WaterSpeed(0, 0, 0) , m_WaterSpeed(0, 0, 0)
@ -725,30 +718,45 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
NextSpeed += m_WaterSpeed; NextSpeed += m_WaterSpeed;
if( NextSpeed.SqrLength() > 0.f ) if (NextSpeed.SqrLength() > 0.f)
{ {
cTracer Tracer( GetWorld() ); cTracer Tracer(GetWorld());
bool HasHit = Tracer.Trace( NextPos, NextSpeed, 2 );
if (HasHit) // Oh noez! we hit something bool HasHit = Tracer.Trace(NextPos, NextSpeed,
// Distance traced is an integer, so we round up from the distance we should go (Speed * Delta), else we will encounter collision detection failures
(int)(ceil((NextSpeed * a_Dt).SqrLength()) * 2)
);
if (HasHit)
{ {
// Set to hit position // Oh noez! We hit something: verify that the (hit position - current) was smaller or equal to the (position that we should travel without obstacles - current)
// This is because previously, we traced with a length that was rounded up (due to integer limitations), and in the case that something was hit, we don't want to overshoot our projected movement
if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * a_Dt).SqrLength()) if ((Tracer.RealHit - NextPos).SqrLength() <= (NextSpeed * a_Dt).SqrLength())
{ {
// Block hit was within our projected path
// Begin by stopping movement in the direction that we hit something. The Normal is the line perpendicular to a 2D face and in this case, stores what block face was hit through either -1 or 1.
// Por ejemplo: HitNormal.y = -1 : BLOCK_FACE_YM; HitNormal.y = 1 : BLOCK_FACE_YP
if (Tracer.HitNormal.x != 0.f) NextSpeed.x = 0.f; if (Tracer.HitNormal.x != 0.f) NextSpeed.x = 0.f;
if (Tracer.HitNormal.y != 0.f) NextSpeed.y = 0.f; if (Tracer.HitNormal.y != 0.f) NextSpeed.y = 0.f;
if (Tracer.HitNormal.z != 0.f) NextSpeed.z = 0.f; if (Tracer.HitNormal.z != 0.f) NextSpeed.z = 0.f;
if (Tracer.HitNormal.y > 0) // means on ground if (Tracer.HitNormal.y == 1) // Hit BLOCK_FACE_YP, we are on the ground
{ {
m_bOnGround = true; m_bOnGround = true;
} }
NextPos.Set(Tracer.RealHit.x,Tracer.RealHit.y,Tracer.RealHit.z);
NextPos.x += Tracer.HitNormal.x * 0.3f; // Now, set our position to the hit block (i.e. move part way along our intended trajectory)
NextPos.y += Tracer.HitNormal.y * 0.05f; // Any larger produces entity vibration-upon-the-spot NextPos.Set(Tracer.RealHit.x, Tracer.RealHit.y, Tracer.RealHit.z);
NextPos.z += Tracer.HitNormal.z * 0.3f; NextPos.x += Tracer.HitNormal.x * 0.1;
NextPos.y += Tracer.HitNormal.y * 0.05;
NextPos.z += Tracer.HitNormal.z * 0.1;
} }
else else
{ {
// We have hit a block but overshot our intended trajectory, move normally, safe in the warm cocoon of knowledge that we won't appear to teleport forwards on clients,
// and that this piece of software will come to be hailed as the epitome of performance and functionality in C++, never before seen, and of such a like that will never
// be henceforth seen again in the time of programmers and man alike
// </&sensationalist>
NextPos += (NextSpeed * a_Dt); NextPos += (NextSpeed * a_Dt);
} }
} }
@ -934,8 +942,8 @@ void cEntity::SetSwimState(cChunk & a_Chunk)
{ {
// This sometimes happens on Linux machines // This sometimes happens on Linux machines
// Ref.: http://forum.mc-server.org/showthread.php?tid=1244 // Ref.: http://forum.mc-server.org/showthread.php?tid=1244
LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}", LOGD("SetSwimState failure: RelX = %d, RelZ = %d, Pos = %.02f, %.02f}",
RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ() RelX, RelY, GetPosX(), GetPosZ()
); );
m_IsSwimming = false; m_IsSwimming = false;
m_IsSubmerged = false; m_IsSubmerged = false;
@ -1092,72 +1100,70 @@ void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ)
void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude) void cEntity::BroadcastMovementUpdate(const cClientHandle * a_Exclude)
{ {
// Send velocity packet every two ticks if: speed is not negligible or speed was set (as indicated by the DirtySpeed flag) // Process packet sending every two ticks
if (((m_Speed.SqrLength() > 0.0004f) || m_bDirtySpeed) && ((m_World->GetWorldAge() - m_TimeLastSpeedPacket) >= 2)) if (GetWorld()->GetWorldAge() % 2 == 0)
{ {
m_World->BroadcastEntityVelocity(*this,a_Exclude); double SpeedSqr = GetSpeed().SqrLength();
m_bDirtySpeed = false; if (SpeedSqr == 0.0)
m_TimeLastSpeedPacket = m_World->GetWorldAge();
}
// Have to process position related packets this every two ticks
if (m_World->GetWorldAge() % 2 == 0)
{
int DiffX = (int) (floor(GetPosX() * 32.0) - floor(m_LastPosX * 32.0));
int DiffY = (int) (floor(GetPosY() * 32.0) - floor(m_LastPosY * 32.0));
int DiffZ = (int) (floor(GetPosZ() * 32.0) - floor(m_LastPosZ * 32.0));
Int64 DiffTeleportPacket = m_World->GetWorldAge() - m_TimeLastTeleportPacket;
// 4 blocks is max Relative So if the Diff is greater than 127 or. Send an absolute position every 20 seconds
if (DiffTeleportPacket >= 400 ||
((DiffX > 127) || (DiffX < -128) ||
(DiffY > 127) || (DiffY < -128) ||
(DiffZ > 127) || (DiffZ < -128)))
{ {
// // Speed is zero, send this to clients once only as well as an absolute position
m_World->BroadcastTeleportEntity(*this,a_Exclude); if (!m_bHasSentNoSpeed)
m_TimeLastTeleportPacket = m_World->GetWorldAge(); {
m_TimeLastMoveReltPacket = m_TimeLastTeleportPacket; //Must synchronize. m_World->BroadcastEntityVelocity(*this, a_Exclude);
m_LastPosX = GetPosX(); m_World->BroadcastTeleportEntity(*this, a_Exclude);
m_LastPosY = GetPosY(); m_bHasSentNoSpeed = true;
m_LastPosZ = GetPosZ(); }
m_bDirtyPosition = false;
m_bDirtyOrientation = false;
} }
else else
{ {
Int64 DiffMoveRelPacket = m_World->GetWorldAge() - m_TimeLastMoveReltPacket; // Movin'
//if the change is big enough. m_World->BroadcastEntityVelocity(*this, a_Exclude);
if ((abs(DiffX) >= 4 || abs(DiffY) >= 4 || abs(DiffZ) >= 4 || DiffMoveRelPacket >= 60) && m_bDirtyPosition) m_bHasSentNoSpeed = false;
}
// TODO: Pickups move disgracefully if relative move packets are sent as opposed to just velocity. Have a system to send relmove only when SetPosXXX() is called with a large difference in position
int DiffX = (int)(floor(GetPosX() * 32.0) - floor(m_LastPos.x * 32.0));
int DiffY = (int)(floor(GetPosY() * 32.0) - floor(m_LastPos.y * 32.0));
int DiffZ = (int)(floor(GetPosZ() * 32.0) - floor(m_LastPos.z * 32.0));
if ((abs(DiffX) >= 4) || (abs(DiffY) >= 4) || (abs(DiffZ) >= 4))
{
if ((abs(DiffX) <= 127) && (abs(DiffY) <= 127) && (abs(DiffZ) <= 127)) // Limitations of a Byte
{ {
// Difference within Byte limitations, use a relative move packet
if (m_bDirtyOrientation) if (m_bDirtyOrientation)
{ {
m_World->BroadcastEntityRelMoveLook(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); m_World->BroadcastEntityRelMoveLook(*this, (char)DiffX, (char)DiffY, (char)DiffZ, a_Exclude);
m_bDirtyOrientation = false; m_bDirtyOrientation = false;
} }
else else
{ {
m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ,a_Exclude); m_World->BroadcastEntityRelMove(*this, (char)DiffX, (char)DiffY, (char)DiffZ, a_Exclude);
} }
m_LastPosX = GetPosX(); // Clients seem to store two positions, one for the velocity packet and one for the teleport/relmove packet
m_LastPosY = GetPosY(); // The latter is only changed with a relmove/teleport, and m_LastPos stores this position
m_LastPosZ = GetPosZ(); m_LastPos = GetPosition();
m_bDirtyPosition = false;
m_TimeLastMoveReltPacket = m_World->GetWorldAge();
} }
else else
{ {
if (m_bDirtyOrientation) // Too big a movement, do a teleport
{ m_World->BroadcastTeleportEntity(*this, a_Exclude);
m_World->BroadcastEntityLook(*this,a_Exclude); m_LastPos = GetPosition(); // See above
m_bDirtyOrientation = false; m_bDirtyOrientation = false;
} }
}
} }
if (m_bDirtyHead) if (m_bDirtyHead)
{ {
m_World->BroadcastEntityHeadLook(*this,a_Exclude); m_World->BroadcastEntityHeadLook(*this, a_Exclude);
m_bDirtyHead = false; m_bDirtyHead = false;
} }
if (m_bDirtyOrientation)
{
// Send individual update in case above (sending with rel-move packet) wasn't done
GetWorld()->BroadcastEntityLook(*this, a_Exclude);
m_bDirtyOrientation = false;
}
} }
} }
@ -1297,7 +1303,7 @@ void cEntity::SetRoll(double a_Roll)
void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ) void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
{ {
m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ); m_Speed.Set(a_SpeedX, a_SpeedY, a_SpeedZ);
m_bDirtySpeed = true;
WrapSpeed(); WrapSpeed();
} }
@ -1307,7 +1313,7 @@ void cEntity::SetSpeed(double a_SpeedX, double a_SpeedY, double a_SpeedZ)
void cEntity::SetSpeedX(double a_SpeedX) void cEntity::SetSpeedX(double a_SpeedX)
{ {
m_Speed.x = a_SpeedX; m_Speed.x = a_SpeedX;
m_bDirtySpeed = true;
WrapSpeed(); WrapSpeed();
} }
@ -1317,7 +1323,7 @@ void cEntity::SetSpeedX(double a_SpeedX)
void cEntity::SetSpeedY(double a_SpeedY) void cEntity::SetSpeedY(double a_SpeedY)
{ {
m_Speed.y = a_SpeedY; m_Speed.y = a_SpeedY;
m_bDirtySpeed = true;
WrapSpeed(); WrapSpeed();
} }
@ -1327,7 +1333,7 @@ void cEntity::SetSpeedY(double a_SpeedY)
void cEntity::SetSpeedZ(double a_SpeedZ) void cEntity::SetSpeedZ(double a_SpeedZ)
{ {
m_Speed.z = a_SpeedZ; m_Speed.z = a_SpeedZ;
m_bDirtySpeed = true;
WrapSpeed(); WrapSpeed();
} }
@ -1347,7 +1353,7 @@ void cEntity::SetWidth(double a_Width)
void cEntity::AddPosX(double a_AddPosX) void cEntity::AddPosX(double a_AddPosX)
{ {
m_Pos.x += a_AddPosX; m_Pos.x += a_AddPosX;
m_bDirtyPosition = true;
} }
@ -1356,7 +1362,7 @@ void cEntity::AddPosX(double a_AddPosX)
void cEntity::AddPosY(double a_AddPosY) void cEntity::AddPosY(double a_AddPosY)
{ {
m_Pos.y += a_AddPosY; m_Pos.y += a_AddPosY;
m_bDirtyPosition = true;
} }
@ -1365,7 +1371,7 @@ void cEntity::AddPosY(double a_AddPosY)
void cEntity::AddPosZ(double a_AddPosZ) void cEntity::AddPosZ(double a_AddPosZ)
{ {
m_Pos.z += a_AddPosZ; m_Pos.z += a_AddPosZ;
m_bDirtyPosition = true;
} }
@ -1376,7 +1382,7 @@ void cEntity::AddPosition(double a_AddPosX, double a_AddPosY, double a_AddPosZ)
m_Pos.x += a_AddPosX; m_Pos.x += a_AddPosX;
m_Pos.y += a_AddPosY; m_Pos.y += a_AddPosY;
m_Pos.z += a_AddPosZ; m_Pos.z += a_AddPosZ;
m_bDirtyPosition = true;
} }
@ -1386,8 +1392,7 @@ void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeed
{ {
m_Speed.x += a_AddSpeedX; m_Speed.x += a_AddSpeedX;
m_Speed.y += a_AddSpeedY; m_Speed.y += a_AddSpeedY;
m_Speed.z += a_AddSpeedZ; m_Speed.z += a_AddSpeedZ;
m_bDirtySpeed = true;
WrapSpeed(); WrapSpeed();
} }
@ -1397,8 +1402,7 @@ void cEntity::AddSpeed(double a_AddSpeedX, double a_AddSpeedY, double a_AddSpeed
void cEntity::AddSpeedX(double a_AddSpeedX) void cEntity::AddSpeedX(double a_AddSpeedX)
{ {
m_Speed.x += a_AddSpeedX; m_Speed.x += a_AddSpeedX;
m_bDirtySpeed = true;
WrapSpeed(); WrapSpeed();
} }
@ -1408,8 +1412,7 @@ void cEntity::AddSpeedX(double a_AddSpeedX)
void cEntity::AddSpeedY(double a_AddSpeedY) void cEntity::AddSpeedY(double a_AddSpeedY)
{ {
m_Speed.y += a_AddSpeedY; m_Speed.y += a_AddSpeedY;
m_bDirtySpeed = true;
WrapSpeed(); WrapSpeed();
} }
@ -1419,8 +1422,7 @@ void cEntity::AddSpeedY(double a_AddSpeedY)
void cEntity::AddSpeedZ(double a_AddSpeedZ) void cEntity::AddSpeedZ(double a_AddSpeedZ)
{ {
m_Speed.z += a_AddSpeedZ; m_Speed.z += a_AddSpeedZ;
m_bDirtySpeed = true;
WrapSpeed(); WrapSpeed();
} }
@ -1475,8 +1477,7 @@ Vector3d cEntity::GetLookVector(void) const
// Set position // Set position
void cEntity::SetPosition(double a_PosX, double a_PosY, double a_PosZ) void cEntity::SetPosition(double a_PosX, double a_PosY, double a_PosZ)
{ {
m_Pos.Set(a_PosX, a_PosY, a_PosZ); m_Pos.Set(a_PosX, a_PosY, a_PosZ);
m_bDirtyPosition = true;
} }
@ -1485,8 +1486,7 @@ void cEntity::SetPosition(double a_PosX, double a_PosY, double a_PosZ)
void cEntity::SetPosX(double a_PosX) void cEntity::SetPosX(double a_PosX)
{ {
m_Pos.x = a_PosX; m_Pos.x = a_PosX;
m_bDirtyPosition = true;
} }
@ -1495,8 +1495,7 @@ void cEntity::SetPosX(double a_PosX)
void cEntity::SetPosY(double a_PosY) void cEntity::SetPosY(double a_PosY)
{ {
m_Pos.y = a_PosY; m_Pos.y = a_PosY;
m_bDirtyPosition = true;
} }
@ -1506,7 +1505,6 @@ void cEntity::SetPosY(double a_PosY)
void cEntity::SetPosZ(double a_PosZ) void cEntity::SetPosZ(double a_PosZ)
{ {
m_Pos.z = a_PosZ; m_Pos.z = a_PosZ;
m_bDirtyPosition = true;
} }

View File

@ -417,17 +417,14 @@ protected:
// Flags that signal that we haven't updated the clients with the latest. // Flags that signal that we haven't updated the clients with the latest.
bool m_bDirtyHead; bool m_bDirtyHead;
bool m_bDirtyOrientation; bool m_bDirtyOrientation;
bool m_bDirtyPosition; bool m_bHasSentNoSpeed;
bool m_bDirtySpeed;
bool m_bOnGround; bool m_bOnGround;
float m_Gravity; float m_Gravity;
// Last Position. /** Last position sent to client via the Relative Move or Teleport packets (not Velocity)
double m_LastPosX, m_LastPosY, m_LastPosZ; Only updated if cEntity::BroadcastMovementUpdate() is called! */
Vector3d m_LastPos;
// This variables keep track of the last time a packet was sent
Int64 m_TimeLastTeleportPacket, m_TimeLastMoveReltPacket, m_TimeLastSpeedPacket; // In ticks
bool m_IsInitialized; // Is set to true when it's initialized, until it's destroyed (Initialize() till Destroy() ) bool m_IsInitialized; // Is set to true when it's initialized, until it's destroyed (Initialize() till Destroy() )

View File

@ -34,8 +34,6 @@ cExpOrb::cExpOrb(const Vector3d & a_Pos, int a_Reward)
void cExpOrb::SpawnOn(cClientHandle & a_Client) void cExpOrb::SpawnOn(cClientHandle & a_Client)
{ {
a_Client.SendExperienceOrb(*this); a_Client.SendExperienceOrb(*this);
m_bDirtyPosition = false;
m_bDirtySpeed = false;
m_bDirtyOrientation = false; m_bDirtyOrientation = false;
m_bDirtyHead = false; m_bDirtyHead = false;
} }

View File

@ -76,11 +76,8 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
cTimer t1; cTimer t1;
m_LastPlayerListTime = t1.GetNowTime(); m_LastPlayerListTime = t1.GetNowTime();
m_TimeLastTeleportPacket = 0;
m_PlayerName = a_PlayerName; m_PlayerName = a_PlayerName;
m_bDirtyPosition = true; // So chunks are streamed to player at spawn
if (!LoadFromDisk()) if (!LoadFromDisk())
{ {
@ -208,25 +205,22 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
m_BowCharge += 1; m_BowCharge += 1;
} }
//handle updating experience // Handle updating experience
if (m_bDirtyExperience) if (m_bDirtyExperience)
{ {
SendExperience(); SendExperience();
} }
if (m_bDirtyPosition) if ((GetPosition() - m_LastPos).SqrLength() != 0.0) // Change in position from last tick?
{ {
// Apply food exhaustion from movement: // Apply food exhaustion from movement:
ApplyFoodExhaustionFromMovement(); ApplyFoodExhaustionFromMovement();
cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this); cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*this);
BroadcastMovementUpdate(m_ClientHandle);
m_ClientHandle->StreamChunks(); m_ClientHandle->StreamChunks();
} }
else
{ BroadcastMovementUpdate(m_ClientHandle);
BroadcastMovementUpdate(m_ClientHandle);
}
if (m_Health > 0) // make sure player is alive if (m_Health > 0) // make sure player is alive
{ {
@ -1592,10 +1586,7 @@ bool cPlayer::LoadFromDisk()
SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble()); SetPosX(JSON_PlayerPosition[(unsigned int)0].asDouble());
SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble()); SetPosY(JSON_PlayerPosition[(unsigned int)1].asDouble());
SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble()); SetPosZ(JSON_PlayerPosition[(unsigned int)2].asDouble());
m_LastPosX = GetPosX(); m_LastPos = GetPosition();
m_LastPosY = GetPosY();
m_LastPosZ = GetPosZ();
m_LastFoodPos = GetPosition();
} }
Json::Value & JSON_PlayerRotation = root["rotation"]; Json::Value & JSON_PlayerRotation = root["rotation"];
@ -1858,9 +1849,8 @@ void cPlayer::ApplyFoodExhaustionFromMovement()
} }
// Calculate the distance travelled, update the last pos: // Calculate the distance travelled, update the last pos:
Vector3d Movement(GetPosition() - m_LastFoodPos); Vector3d Movement(GetPosition() - m_LastPos);
Movement.y = 0; // Only take XZ movement into account Movement.y = 0; // Only take XZ movement into account
m_LastFoodPos = GetPosition();
// If riding anything, apply no food exhaustion // If riding anything, apply no food exhaustion
if (m_AttachedTo != NULL) if (m_AttachedTo != NULL)

View File

@ -423,9 +423,6 @@ protected:
/** Number of ticks remaining for the foodpoisoning effect; zero if not foodpoisoned */ /** Number of ticks remaining for the foodpoisoning effect; zero if not foodpoisoned */
int m_FoodPoisonedTicksRemaining; int m_FoodPoisonedTicksRemaining;
/** Last position that has been recorded for food-related processing: */
Vector3d m_LastFoodPos;
float m_LastJumpHeight; float m_LastJumpHeight;
float m_LastGroundHeight; float m_LastGroundHeight;
bool m_bTouchGround; bool m_bTouchGround;

View File

@ -30,8 +30,6 @@ cTNTEntity::cTNTEntity(const Vector3d & a_Pos, int a_FuseTicks) :
void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle) void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle)
{ {
a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT a_ClientHandle.SendSpawnObject(*this, 50, 1, 0, 0); // 50 means TNT
m_bDirtyPosition = false;
m_bDirtySpeed = false;
m_bDirtyOrientation = false; m_bDirtyOrientation = false;
m_bDirtyHead = false; m_bDirtyHead = false;
} }

View File

@ -219,6 +219,10 @@ bool cTracer::Trace( const Vector3f & a_Start, const Vector3f & a_Direction, int
return false; return false;
} }
if ((pos.y < 0) || (pos.y >= cChunkDef::Height))
{
return false;
}
BLOCKTYPE BlockID = m_World->GetBlock(pos.x, pos.y, pos.z); BLOCKTYPE BlockID = m_World->GetBlock(pos.x, pos.y, pos.z);
// Block is counted as a collision if we are not doing a line of sight and it is solid, // Block is counted as a collision if we are not doing a line of sight and it is solid,
// or if the block is not air and not water. That way mobs can still see underwater. // or if the block is not air and not water. That way mobs can still see underwater.
@ -226,7 +230,7 @@ bool cTracer::Trace( const Vector3f & a_Start, const Vector3f & a_Direction, int
{ {
BlockHitPosition = pos; BlockHitPosition = pos;
int Normal = GetHitNormal(a_Start, End, pos ); int Normal = GetHitNormal(a_Start, End, pos );
if(Normal > 0) if (Normal > 0)
{ {
HitNormal = m_NormalTable[Normal-1]; HitNormal = m_NormalTable[Normal-1];
} }