summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/LuaState.cpp6
-rw-r--r--src/Bindings/ManualBindings.cpp45
-rw-r--r--src/Bindings/PluginLua.cpp52
-rw-r--r--src/BlockID.cpp1
-rw-r--r--src/ByteBuffer.cpp35
-rw-r--r--src/Crypto.cpp82
-rw-r--r--src/Crypto.h31
-rw-r--r--src/Entities/ProjectileEntity.cpp5
-rw-r--r--src/Generating/ChunkDesc.cpp25
-rw-r--r--src/Generating/ChunkDesc.h40
-rw-r--r--src/Mobs/Chicken.cpp1
-rw-r--r--src/Mobs/Chicken.h3
-rw-r--r--src/Mobs/Cow.cpp2
-rw-r--r--src/Mobs/Cow.h3
-rw-r--r--src/Mobs/Mooshroom.cpp2
-rw-r--r--src/Mobs/Mooshroom.h2
-rw-r--r--src/Mobs/PassiveMonster.cpp16
-rw-r--r--src/Mobs/PassiveMonster.h3
-rw-r--r--src/Mobs/Pig.cpp2
-rw-r--r--src/Mobs/Pig.h3
-rw-r--r--src/Mobs/Sheep.cpp1
-rw-r--r--src/Mobs/Sheep.h2
-rw-r--r--src/Mobs/Villager.cpp156
-rw-r--r--src/Mobs/Villager.h26
-rw-r--r--src/OSSupport/Socket.cpp27
-rw-r--r--src/OSSupport/Socket.h9
-rw-r--r--src/OSSupport/SocketThreads.cpp172
-rw-r--r--src/OSSupport/SocketThreads.h26
-rw-r--r--src/OSSupport/Timer.cpp2
-rw-r--r--src/Protocol/Protocol17x.cpp156
-rw-r--r--src/Protocol/Protocol17x.h2
-rw-r--r--src/Protocol/ProtocolRecognizer.h2
-rw-r--r--src/Root.cpp2
-rw-r--r--src/Server.cpp3
-rw-r--r--src/Server.h28
-rw-r--r--src/StringUtils.cpp6
-rw-r--r--src/World.cpp4
-rw-r--r--src/World.h3
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp4
-rw-r--r--src/WorldStorage/WSSAnvil.cpp23
-rw-r--r--src/main.cpp34
41 files changed, 863 insertions, 184 deletions
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index 2fca7142c..d49cd8ef3 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -289,9 +289,13 @@ bool cLuaState::PushFunction(const cTableRef & a_TableRef)
if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1))
{
// Not a valid function, bail out
- lua_pop(m_LuaState, 2);
+ lua_pop(m_LuaState, 3);
return false;
}
+
+ // Pop the table off the stack:
+ lua_remove(m_LuaState, -2);
+
Printf(m_CurrentFunctionName, "<table-callback %s>", a_TableRef.GetFnName());
m_NumCurrentFunctionArgs = 0;
return true;
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index e7c66c6fb..dbaf32756 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -1429,7 +1429,10 @@ static int tolua_cPluginManager_BindCommand(lua_State * L)
// Read the arguments to this API call:
tolua_Error tolua_err;
int idx = 1;
- if (tolua_isusertype(L, 1, "cPluginManager", 0, &tolua_err))
+ if (
+ tolua_isusertype (L, 1, "cPluginManager", 0, &tolua_err) ||
+ tolua_isusertable(L, 1, "cPluginManager", 0, &tolua_err)
+ )
{
idx++;
}
@@ -2128,26 +2131,40 @@ protected:
static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S)
{
- // cLineBlockTracer.Trace(World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ)
+ /* Supported function signatures:
+ cLineBlockTracer:Trace(World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ) // Canonical
+ cLineBlockTracer.Trace(World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ)
+ */
+
+ // If the first param is the cLineBlockTracer class, shift param index by one:
+ int idx = 1;
+ tolua_Error err;
+ if (tolua_isusertable(tolua_S, 1, "cLineBlockTracer", 0, &err))
+ {
+ idx = 2;
+ }
+
+ // Check params:
cLuaState L(tolua_S);
if (
- !L.CheckParamUserType(1, "cWorld") ||
- !L.CheckParamTable (2) ||
- !L.CheckParamNumber (3, 8) ||
- !L.CheckParamEnd (9)
+ !L.CheckParamUserType(idx, "cWorld") ||
+ !L.CheckParamTable (idx + 1) ||
+ !L.CheckParamNumber (idx + 2, idx + 7) ||
+ !L.CheckParamEnd (idx + 8)
)
{
return 0;
}
- cWorld * World = (cWorld *)tolua_tousertype(L, 1, NULL);
- cLuaBlockTracerCallbacks Callbacks(L, 2);
- double StartX = tolua_tonumber(L, 3, 0);
- double StartY = tolua_tonumber(L, 4, 0);
- double StartZ = tolua_tonumber(L, 5, 0);
- double EndX = tolua_tonumber(L, 6, 0);
- double EndY = tolua_tonumber(L, 7, 0);
- double EndZ = tolua_tonumber(L, 8, 0);
+ // Trace:
+ cWorld * World = (cWorld *)tolua_tousertype(L, idx, NULL);
+ cLuaBlockTracerCallbacks Callbacks(L, idx + 1);
+ double StartX = tolua_tonumber(L, idx + 2, 0);
+ double StartY = tolua_tonumber(L, idx + 3, 0);
+ double StartZ = tolua_tonumber(L, idx + 4, 0);
+ double EndX = tolua_tonumber(L, idx + 5, 0);
+ double EndY = tolua_tonumber(L, idx + 6, 0);
+ double EndZ = tolua_tonumber(L, idx + 7, 0);
bool res = cLineBlockTracer::Trace(*World, Callbacks, StartX, StartY, StartZ, EndX, EndY, EndZ);
tolua_pushboolean(L, res ? 1 : 0);
return 1;
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 1d8c4c6ed..7b2362887 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -88,36 +88,55 @@ bool cPluginLua::Initialize(void)
std::string PluginPath = FILE_IO_PREFIX + GetLocalFolder() + "/";
- // Load all files for this plugin, and execute them
+ // List all Lua files for this plugin. Info.lua has a special handling - make it the last to load:
AStringVector Files = cFile::GetFolderContents(PluginPath.c_str());
-
- int numFiles = 0;
- for (AStringVector::const_iterator itr = Files.begin(); itr != Files.end(); ++itr)
+ AStringVector LuaFiles;
+ bool HasInfoLua = false;
+ for (AStringVector::const_iterator itr = Files.begin(), end = Files.end(); itr != end; ++itr)
{
- if (itr->rfind(".lua") == AString::npos)
+ if (itr->rfind(".lua") != AString::npos)
{
- continue;
+ if (*itr == "Info.lua")
+ {
+ HasInfoLua = true;
+ }
+ else
+ {
+ LuaFiles.push_back(*itr);
+ }
}
+ }
+ std::sort(LuaFiles.begin(), LuaFiles.end());
+
+ // Warn if there are no Lua files in the plugin folder:
+ if (LuaFiles.empty())
+ {
+ LOGWARNING("No lua files found: plugin %s is missing.", GetName().c_str());
+ Close();
+ return false;
+ }
+
+ // Load all files in the list, including the Info.lua as last, if it exists:
+ for (AStringVector::const_iterator itr = LuaFiles.begin(), end = LuaFiles.end(); itr != end; ++itr)
+ {
AString Path = PluginPath + *itr;
if (!m_LuaState.LoadFile(Path))
{
Close();
return false;
- }
- else
- {
- numFiles++;
}
} // for itr - Files[]
-
- if (numFiles == 0)
+ if (HasInfoLua)
{
- LOGWARNING("No lua files found: plugin %s is missing.", GetName().c_str());
- Close();
- return false;
+ AString Path = PluginPath + "Info.lua";
+ if (!m_LuaState.LoadFile(Path))
+ {
+ Close();
+ return false;
+ }
}
- // Call intialize function
+ // Call the Initialize function:
bool res = false;
if (!m_LuaState.Call("Initialize", this, cLuaState::Return, res))
{
@@ -125,7 +144,6 @@ bool cPluginLua::Initialize(void)
Close();
return false;
}
-
if (!res)
{
LOGINFO("Plugin %s: Initialize() call failed, plugin is temporarily disabled.", GetName().c_str());
diff --git a/src/BlockID.cpp b/src/BlockID.cpp
index 095865d07..fbb5a0720 100644
--- a/src/BlockID.cpp
+++ b/src/BlockID.cpp
@@ -767,6 +767,7 @@ public:
g_BlockIsSolid[E_BLOCK_MELON_STEM] = false;
g_BlockIsSolid[E_BLOCK_NETHER_PORTAL] = false;
g_BlockIsSolid[E_BLOCK_PISTON_EXTENSION] = false;
+ g_BlockIsSolid[E_BLOCK_POTATOES] = false;
g_BlockIsSolid[E_BLOCK_POWERED_RAIL] = false;
g_BlockIsSolid[E_BLOCK_RAIL] = false;
g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_OFF] = false;
diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp
index e06f63a0e..d2d3beb97 100644
--- a/src/ByteBuffer.cpp
+++ b/src/ByteBuffer.cpp
@@ -54,6 +54,7 @@ public:
{
TestRead();
TestWrite();
+ TestWrap();
}
void TestRead(void)
@@ -61,11 +62,11 @@ public:
cByteBuffer buf(50);
buf.Write("\x05\xac\x02\x00", 4);
UInt32 v1;
- ASSERT(buf.ReadVarInt(v1) && (v1 == 5));
+ assert(buf.ReadVarInt(v1) && (v1 == 5));
UInt32 v2;
- ASSERT(buf.ReadVarInt(v2) && (v2 == 300));
+ assert(buf.ReadVarInt(v2) && (v2 == 300));
UInt32 v3;
- ASSERT(buf.ReadVarInt(v3) && (v3 == 0));
+ assert(buf.ReadVarInt(v3) && (v3 == 0));
}
void TestWrite(void)
@@ -76,9 +77,30 @@ public:
buf.WriteVarInt(0);
AString All;
buf.ReadAll(All);
- ASSERT(All.size() == 4);
- ASSERT(memcmp(All.data(), "\x05\xac\x02\x00", All.size()) == 0);
+ assert(All.size() == 4);
+ assert(memcmp(All.data(), "\x05\xac\x02\x00", All.size()) == 0);
}
+
+ void TestWrap(void)
+ {
+ cByteBuffer buf(3);
+ for (int i = 0; i < 1000; i++)
+ {
+ int FreeSpace = buf.GetFreeSpace();
+ assert(buf.GetReadableSpace() == 0);
+ assert(FreeSpace > 0);
+ assert(buf.Write("a", 1));
+ assert(buf.CanReadBytes(1));
+ assert(buf.GetReadableSpace() == 1);
+ unsigned char v = 0;
+ assert(buf.ReadByte(v));
+ assert(v == 'a');
+ assert(buf.GetReadableSpace() == 0);
+ buf.CommitRead();
+ assert(buf.GetFreeSpace() == FreeSpace); // We're back to normal
+ }
+ }
+
} g_ByteBufferTest;
#endif
@@ -159,7 +181,7 @@ bool cByteBuffer::Write(const char * a_Bytes, int a_Count)
int CurReadableSpace = GetReadableSpace();
int WrittenBytes = 0;
- if (GetFreeSpace() < a_Count)
+ if (CurFreeSpace < a_Count)
{
return false;
}
@@ -864,3 +886,4 @@ void cByteBuffer::CheckValid(void) const
+
diff --git a/src/Crypto.cpp b/src/Crypto.cpp
index 2045d0385..7a06d7fa3 100644
--- a/src/Crypto.cpp
+++ b/src/Crypto.cpp
@@ -206,7 +206,7 @@ int cRSAPrivateKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte
ASSERT(!"Invalid a_DecryptedMaxLength!");
return -1;
}
- if (a_PlainLength < m_Rsa.len)
+ if (a_EncryptedMaxLength < m_Rsa.len)
{
LOGD("%s: Invalid a_PlainLength: got %u, exp at least %u",
__FUNCTION__, (unsigned)a_PlainLength, (unsigned)(m_Rsa.len)
@@ -214,16 +214,90 @@ int cRSAPrivateKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte
ASSERT(!"Invalid a_PlainLength!");
return -1;
}
- size_t DecryptedLength;
int res = rsa_pkcs1_encrypt(
- &m_Rsa, ctr_drbg_random, &m_Ctr_drbg, RSA_PUBLIC,
+ &m_Rsa, ctr_drbg_random, &m_Ctr_drbg, RSA_PRIVATE,
a_PlainLength, a_PlainData, a_EncryptedData
);
if (res != 0)
{
return -1;
}
- return (int)DecryptedLength;
+ return (int)m_Rsa.len;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cPublicKey:
+
+cPublicKey::cPublicKey(const AString & a_PublicKeyDER)
+{
+ pk_init(&m_Pk);
+ if (pk_parse_public_key(&m_Pk, (const Byte *)a_PublicKeyDER.data(), a_PublicKeyDER.size()) != 0)
+ {
+ ASSERT(!"Cannot parse PubKey");
+ return;
+ }
+ InitRnd();
+}
+
+
+
+
+
+cPublicKey::~cPublicKey()
+{
+ pk_free(&m_Pk);
+}
+
+
+
+
+
+int cPublicKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength)
+{
+ size_t DecryptedLen = a_DecryptedMaxLength;
+ int res = pk_decrypt(&m_Pk,
+ a_EncryptedData, a_EncryptedLength,
+ a_DecryptedData, &DecryptedLen, a_DecryptedMaxLength,
+ ctr_drbg_random, &m_Ctr_drbg
+ );
+ if (res != 0)
+ {
+ return res;
+ }
+ return (int)DecryptedLen;
+}
+
+
+
+
+
+int cPublicKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength)
+{
+ size_t EncryptedLength = a_EncryptedMaxLength;
+ int res = pk_encrypt(&m_Pk,
+ a_PlainData, a_PlainLength, a_EncryptedData, &EncryptedLength, a_EncryptedMaxLength,
+ ctr_drbg_random, &m_Ctr_drbg
+ );
+ if (res != 0)
+ {
+ return res;
+ }
+ return (int)EncryptedLength;
+}
+
+
+
+
+
+void cPublicKey::InitRnd(void)
+{
+ entropy_init(&m_Entropy);
+ const unsigned char pers[] = "rsa_genkey";
+ ctr_drbg_init(&m_Ctr_drbg, entropy_func, &m_Entropy, pers, sizeof(pers) - 1);
}
diff --git a/src/Crypto.h b/src/Crypto.h
index a97f34fbf..d68f7ec24 100644
--- a/src/Crypto.h
+++ b/src/Crypto.h
@@ -14,6 +14,7 @@
#include "polarssl/entropy.h"
#include "polarssl/ctr_drbg.h"
#include "polarssl/sha1.h"
+#include "polarssl/pk.h"
@@ -62,6 +63,36 @@ protected:
+class cPublicKey
+{
+public:
+ cPublicKey(const AString & a_PublicKeyDER);
+ ~cPublicKey();
+
+ /** Decrypts the data using the stored public key
+ Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
+ Returns the number of bytes decrypted, or negative number for error. */
+ int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength);
+
+ /** Encrypts the data using the stored public key
+ Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
+ Returns the number of bytes decrypted, or negative number for error. */
+ int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength);
+
+protected:
+ pk_context m_Pk;
+ entropy_context m_Entropy;
+ ctr_drbg_context m_Ctr_drbg;
+
+ /** Initializes the m_Entropy and m_Ctr_drbg contexts
+ Common part of this object's construction, called from all constructors. */
+ void InitRnd(void);
+} ;
+
+
+
+
+
/** Decrypts data using the AES / CFB (128) algorithm */
class cAESCFBDecryptor
{
diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index 12ce9a303..bffa790a3 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -679,7 +679,8 @@ super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
{
- // TODO: Spawn experience orbs
+ // Spawn an experience orb with a reward between 3 and 11.
+ m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8));
Destroy();
}
@@ -710,8 +711,6 @@ void cFireworkEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
SetSpeed(0, 0, 0);
SetPosition(GetPosX(), GetPosY() - 0.5, GetPosZ());
- std::cout << a_HitPos.x << " " << a_HitPos.y << " " << a_HitPos.z << std::endl;
-
m_IsInGround = true;
BroadcastMovementUpdate();
diff --git a/src/Generating/ChunkDesc.cpp b/src/Generating/ChunkDesc.cpp
index af1a8a6c7..87566aa78 100644
--- a/src/Generating/ChunkDesc.cpp
+++ b/src/Generating/ChunkDesc.cpp
@@ -562,6 +562,31 @@ cBlockEntity * cChunkDesc::GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ)
+void cChunkDesc::UpdateHeightmap(void)
+{
+ for (int x = 0; x < cChunkDef::Width; x++)
+ {
+ for (int z = 0; z < cChunkDef::Width; z++)
+ {
+ int Height = 0;
+ for (int y = cChunkDef::Height - 1; y > 0; y--)
+ {
+ BLOCKTYPE BlockType = GetBlockType(x, y, z);
+ if (BlockType != E_BLOCK_AIR)
+ {
+ Height = y;
+ break;
+ }
+ } // for y
+ SetHeight(x, z, Height);
+ } // for z
+ } // for x
+}
+
+
+
+
+
void cChunkDesc::CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas)
{
const NIBBLETYPE * AreaMetas = m_BlockArea.GetBlockMetas();
diff --git a/src/Generating/ChunkDesc.h b/src/Generating/ChunkDesc.h
index e130c463f..e258383d5 100644
--- a/src/Generating/ChunkDesc.h
+++ b/src/Generating/ChunkDesc.h
@@ -30,7 +30,7 @@ class cChunkDesc
public:
// tolua_end
- /// Uncompressed block metas, 1 meta per byte
+ /** Uncompressed block metas, 1 meta per byte */
typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks];
cChunkDesc(int a_ChunkX, int a_ChunkZ);
@@ -56,6 +56,8 @@ public:
void SetBiome(int a_RelX, int a_RelZ, int a_BiomeID);
EMCSBiome GetBiome(int a_RelX, int a_RelZ);
+ // These operate on the heightmap, so they could get out of sync with the data
+ // Use UpdateHeightmap() to re-sync
void SetHeight(int a_RelX, int a_RelZ, int a_Height);
int GetHeight(int a_RelX, int a_RelZ);
@@ -71,16 +73,16 @@ public:
void SetUseDefaultFinish(bool a_bUseDefaultFinish);
bool IsUsingDefaultFinish(void) const;
- /// Writes the block area into the chunk, with its origin set at the specified relative coords. Area's data overwrite everything in the chunk.
+ /** Writes the block area into the chunk, with its origin set at the specified relative coords. Area's data overwrite everything in the chunk. */
void WriteBlockArea(const cBlockArea & a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy = cBlockArea::msOverwrite);
- /// Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas
+ /** Reads an area from the chunk into a cBlockArea, blocktypes and blockmetas */
void ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX, int a_MinRelY, int a_MaxRelY, int a_MinRelZ, int a_MaxRelZ);
- /// Returns the maximum height value in the heightmap
+ /** Returns the maximum height value in the heightmap */
HEIGHTTYPE GetMaxHeight(void) const;
- /// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk
+ /** Fills the relative cuboid with specified block; allows cuboid out of range of this chunk */
void FillRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
@@ -88,7 +90,7 @@ public:
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
);
- /// Fills the relative cuboid with specified block; allows cuboid out of range of this chunk
+ /** Fills the relative cuboid with specified block; allows cuboid out of range of this chunk */
void FillRelCuboid(const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
FillRelCuboid(
@@ -99,7 +101,7 @@ public:
);
}
- /// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk
+ /** Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk */
void ReplaceRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
@@ -108,7 +110,7 @@ public:
BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
);
- /// Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk
+ /** Replaces the specified src blocks in the cuboid by the dst blocks; allows cuboid out of range of this chunk */
void ReplaceRelCuboid(
const cCuboid & a_RelCuboid,
BLOCKTYPE a_SrcType, NIBBLETYPE a_SrcMeta,
@@ -124,7 +126,7 @@ public:
);
}
- /// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk
+ /** Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk */
void FloorRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
@@ -132,7 +134,7 @@ public:
BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
);
- /// Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk
+ /** Replaces the blocks in the cuboid by the dst blocks if they are considered non-floor (air, water); allows cuboid out of range of this chunk */
void FloorRelCuboid(
const cCuboid & a_RelCuboid,
BLOCKTYPE a_DstType, NIBBLETYPE a_DstMeta
@@ -146,7 +148,7 @@ public:
);
}
- /// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk
+ /** Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk */
void RandomFillRelCuboid(
int a_MinX, int a_MaxX,
int a_MinY, int a_MaxY,
@@ -155,7 +157,7 @@ public:
int a_RandomSeed, int a_ChanceOutOf10k
);
- /// Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk
+ /** Fills the relative cuboid with specified block with a random chance; allows cuboid out of range of this chunk */
void RandomFillRelCuboid(
const cCuboid & a_RelCuboid, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
int a_RandomSeed, int a_ChanceOutOf10k
@@ -170,11 +172,15 @@ public:
);
}
- /// Returns the block entity at the specified coords.
- /// If there is no block entity at those coords, tries to create one, based on the block type
- /// If the blocktype doesn't support a block entity, returns NULL.
+ /** Returns the block entity at the specified coords.
+ If there is no block entity at those coords, tries to create one, based on the block type
+ If the blocktype doesn't support a block entity, returns NULL. */
cBlockEntity * GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ);
+ /** Updates the heightmap to match the current contents.
+ Useful for plugins when writing custom block areas into the chunk */
+ void UpdateHeightmap(void);
+
// tolua_end
// Accessors used by cChunkGenerator::Generator descendants:
@@ -187,11 +193,11 @@ public:
inline cEntityList & GetEntities (void) { return m_Entities; }
inline cBlockEntityList & GetBlockEntities (void) { return m_BlockEntities; }
- /// Compresses the metas from the BlockArea format (1 meta per byte) into regular format (2 metas per byte)
+ /** Compresses the metas from the BlockArea format (1 meta per byte) into regular format (2 metas per byte) */
void CompressBlockMetas(cChunkDef::BlockNibbles & a_DestMetas);
#ifdef _DEBUG
- /// Verifies that the heightmap corresponds to blocktype contents; if not, asserts on that column
+ /** Verifies that the heightmap corresponds to blocktype contents; if not, asserts on that column */
void VerifyHeightmap(void);
#endif // _DEBUG
diff --git a/src/Mobs/Chicken.cpp b/src/Mobs/Chicken.cpp
index 087fd088a..fab92ce49 100644
--- a/src/Mobs/Chicken.cpp
+++ b/src/Mobs/Chicken.cpp
@@ -9,7 +9,6 @@
-
cChicken::cChicken(void) :
super("Chicken", mtChicken, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4),
m_EggDropTimer(0)
diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h
index 979c4d8a0..a4c1d6b9e 100644
--- a/src/Mobs/Chicken.h
+++ b/src/Mobs/Chicken.h
@@ -19,8 +19,9 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
-private:
+ virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_SEEDS); }
+private:
int m_EggDropTimer;
} ;
diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp
index 9eb74dac2..d8e905217 100644
--- a/src/Mobs/Cow.cpp
+++ b/src/Mobs/Cow.cpp
@@ -41,5 +41,3 @@ void cCow::OnRightClicked(cPlayer & a_Player)
}
}
-
-
diff --git a/src/Mobs/Cow.h b/src/Mobs/Cow.h
index 0391d4a31..973171ab5 100644
--- a/src/Mobs/Cow.h
+++ b/src/Mobs/Cow.h
@@ -19,6 +19,9 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
+
+ virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); }
+
} ;
diff --git a/src/Mobs/Mooshroom.cpp b/src/Mobs/Mooshroom.cpp
index 940e2db44..88101cd83 100644
--- a/src/Mobs/Mooshroom.cpp
+++ b/src/Mobs/Mooshroom.cpp
@@ -6,7 +6,6 @@
-
// TODO: Milk Cow
@@ -30,4 +29,3 @@ void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-
diff --git a/src/Mobs/Mooshroom.h b/src/Mobs/Mooshroom.h
index 73f6348b6..c94301098 100644
--- a/src/Mobs/Mooshroom.h
+++ b/src/Mobs/Mooshroom.h
@@ -18,6 +18,8 @@ public:
CLASS_PROTODEF(cMooshroom);
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+
+ virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); }
} ;
diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp
index d774d3170..904cd63cc 100644
--- a/src/Mobs/PassiveMonster.cpp
+++ b/src/Mobs/PassiveMonster.cpp
@@ -3,7 +3,7 @@
#include "PassiveMonster.h"
#include "../World.h"
-
+#include "../Entities/Player.h"
@@ -39,6 +39,20 @@ void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
{
CheckEventLostPlayer();
}
+ cItem FollowedItem = GetFollowedItem();
+ if (FollowedItem.IsEmpty())
+ {
+ return;
+ }
+ cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), (float)m_SightDistance);
+ if (a_Closest_Player != NULL)
+ {
+ if (a_Closest_Player->GetEquippedItem().IsEqual(FollowedItem))
+ {
+ Vector3d PlayerPos = a_Closest_Player->GetPosition();
+ MoveToPosition(PlayerPos);
+ }
+ }
}
diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h
index 14a6be6b1..0b3c155da 100644
--- a/src/Mobs/PassiveMonster.h
+++ b/src/Mobs/PassiveMonster.h
@@ -19,6 +19,9 @@ public:
/// When hit by someone, run away
virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+ /** Returns the item that the animal of this class follows when a player holds it in hand
+ Return an empty item not to follow (default). */
+ virtual const cItem GetFollowedItem(void) const { return cItem(); }
} ;
diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp
index 0871a38a9..d8f3dda37 100644
--- a/src/Mobs/Pig.cpp
+++ b/src/Mobs/Pig.cpp
@@ -73,5 +73,3 @@ void cPig::OnRightClicked(cPlayer & a_Player)
-
-
diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h
index 4fd0d8db8..d434324c1 100644
--- a/src/Mobs/Pig.h
+++ b/src/Mobs/Pig.h
@@ -19,6 +19,9 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
+
+ virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); }
+
bool IsSaddled(void) const { return m_bIsSaddled; }
private:
diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp
index 702108ae4..4761103e5 100644
--- a/src/Mobs/Sheep.cpp
+++ b/src/Mobs/Sheep.cpp
@@ -97,3 +97,4 @@ void cSheep::Tick(float a_Dt, cChunk & a_Chunk)
}
}
}
+
diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h
index 4eee3db1c..402e8e61c 100644
--- a/src/Mobs/Sheep.h
+++ b/src/Mobs/Sheep.h
@@ -21,6 +21,8 @@ public:
virtual void OnRightClicked(cPlayer & a_Player) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); }
+
bool IsSheared(void) const { return m_IsSheared; }
int GetFurColor(void) const { return m_WoolColor; }
diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp
index c8ff4f101..44ec14295 100644
--- a/src/Mobs/Villager.cpp
+++ b/src/Mobs/Villager.cpp
@@ -3,6 +3,8 @@
#include "Villager.h"
#include "../World.h"
+#include "../BlockArea.h"
+#include "../Blocks/BlockHandler.h"
@@ -10,7 +12,9 @@
cVillager::cVillager(eVillagerType VillagerType) :
super("Villager", mtVillager, "", "", 0.6, 1.8),
- m_Type(VillagerType)
+ m_Type(VillagerType),
+ m_VillagerAction(false),
+ m_ActionCountDown(-1)
{
}
@@ -33,3 +37,153 @@ void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
+
+void cVillager::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ if (m_ActionCountDown > -1)
+ {
+ m_ActionCountDown--;
+ if (m_ActionCountDown == 0)
+ {
+ switch (m_Type)
+ {
+ case vtFarmer:
+ {
+ HandleFarmerPlaceCrops();
+ }
+ }
+ }
+ return;
+ }
+
+ if (m_VillagerAction)
+ {
+ switch (m_Type)
+ {
+ case vtFarmer:
+ {
+ HandleFarmerTryHarvestCrops();
+ }
+ }
+ m_VillagerAction = false;
+ return;
+ }
+
+ // Don't always try to do a special action. Each tick has 1% to do a special action.
+ if (m_World->GetTickRandomNumber(99) != 0)
+ {
+ return;
+ }
+
+ switch (m_Type)
+ {
+ case vtFarmer:
+ {
+ HandleFarmerPrepareFarmCrops();
+ }
+ }
+}
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Farmer functions.
+void cVillager::HandleFarmerPrepareFarmCrops()
+{
+ if (!m_World->VillagersShouldHarvestCrops())
+ {
+ return;
+ }
+
+ cBlockArea Surrounding;
+ /// Read a 11x7x11 area.
+ Surrounding.Read(
+ m_World,
+ (int) GetPosX() - 5,
+ (int) GetPosX() + 5,
+ (int) GetPosY() - 3,
+ (int) GetPosY() + 3,
+ (int) GetPosZ() - 5,
+ (int) GetPosZ() + 5
+ );
+
+ for (int I = 0; I < 5; I++)
+ {
+ for (int Y = 0; Y < 6; Y++)
+ {
+ // Pick random coordinates and check for crops.
+ int X = m_World->GetTickRandomNumber(11);
+ int Z = m_World->GetTickRandomNumber(11);
+
+ // A villager can't farm this.
+ if (!IsBlockFarmable(Surrounding.GetRelBlockType(X, Y, Z)))
+ {
+ continue;
+ }
+ if (Surrounding.GetRelBlockMeta(X, Y, Z) != 0x7)
+ {
+ continue;
+ }
+
+ m_VillagerAction = true;
+ m_CropsPos = Vector3i((int) GetPosX() + X - 5, (int) GetPosY() + Y - 3, (int) GetPosZ() + Z - 5);
+ MoveToPosition(Vector3f((float) (m_CropsPos.x + 0.5), (float) m_CropsPos.y, (float) (m_CropsPos.z + 0.5)));
+ return;
+ } // for Y loop.
+ } // Repeat the procces 5 times.
+}
+
+
+
+
+
+void cVillager::HandleFarmerTryHarvestCrops()
+{
+ // Harvest the crops if the villager isn't moving and if the crops are closer then 2 blocks.
+ if (!m_bMovingToDestination && (GetPosition() - m_CropsPos).Length() < 2)
+ {
+ // Check if the blocks didn't change while the villager was walking to the coordinates.
+ BLOCKTYPE CropBlock = m_World->GetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z);
+ if (IsBlockFarmable(CropBlock) && m_World->GetBlockMeta(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z) == 0x7)
+ {
+ cBlockHandler * Handler = cBlockHandler::GetBlockHandler(CropBlock);
+ Handler->DropBlock(m_World, this, m_CropsPos.x, m_CropsPos.y, m_CropsPos.z);
+ m_World->SetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z, E_BLOCK_AIR, 0);
+ m_ActionCountDown = 20;
+ }
+ }
+}
+
+
+
+
+void cVillager::HandleFarmerPlaceCrops()
+{
+ // Check if there is still farmland at the spot where the crops were.
+ if (m_World->GetBlock(m_CropsPos.x, m_CropsPos.y - 1, m_CropsPos.z) == E_BLOCK_FARMLAND)
+ {
+ m_World->SetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z, E_BLOCK_CROPS, 0);
+ }
+}
+
+
+
+
+
+bool cVillager::IsBlockFarmable(BLOCKTYPE a_BlockType)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_CROPS:
+ case E_BLOCK_POTATOES:
+ case E_BLOCK_CARROTS:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h
index 4cd9aaa8e..b99ae876f 100644
--- a/src/Mobs/Villager.h
+++ b/src/Mobs/Villager.h
@@ -29,12 +29,36 @@ public:
CLASS_PROTODEF(cVillager);
+ // Override functions
virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
- int GetVilType(void) const { return m_Type; }
+ virtual void Tick (float a_Dt, cChunk & a_Chunk) override;
+
+ // cVillager functions
+ /** return true if the given blocktype are: crops, potatoes or carrots.*/
+ bool IsBlockFarmable(BLOCKTYPE a_BlockType);
+
+ //////////////////////////////////////////////////////////////////
+ // Farmer functions
+ /** It searches in a 11x7x11 area for crops. If it found some it will navigate to them.*/
+ void HandleFarmerPrepareFarmCrops();
+
+ /** Looks if the farmer has reached it's destination, and if it's still crops and the destination is closer then 2 blocks it will harvest them.*/
+ void HandleFarmerTryHarvestCrops();
+
+ /** Replaces the crops he harvested.*/
+ void HandleFarmerPlaceCrops();
+
+ // Get and set functions.
+ int GetVilType(void) const { return m_Type; }
+ Vector3i GetCropsPos(void) const { return m_CropsPos; }
+ bool DoesHaveActionActivated(void) const { return m_VillagerAction; }
private:
+ int m_ActionCountDown;
int m_Type;
+ bool m_VillagerAction;
+ Vector3i m_CropsPos;
} ;
diff --git a/src/OSSupport/Socket.cpp b/src/OSSupport/Socket.cpp
index 4226a7535..6afaceedf 100644
--- a/src/OSSupport/Socket.cpp
+++ b/src/OSSupport/Socket.cpp
@@ -6,7 +6,8 @@
#ifndef _WIN32
#include <netdb.h>
#include <unistd.h>
- #include <arpa/inet.h> //inet_ntoa()
+ #include <arpa/inet.h> // inet_ntoa()
+ #include <sys/ioctl.h> // ioctl()
#else
#define socklen_t int
#endif
@@ -320,7 +321,7 @@ bool cSocket::ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Por
-int cSocket::Receive(char* a_Buffer, unsigned int a_Length, unsigned int a_Flags)
+int cSocket::Receive(char * a_Buffer, unsigned int a_Length, unsigned int a_Flags)
{
return recv(m_Socket, a_Buffer, a_Length, a_Flags);
}
@@ -354,3 +355,25 @@ unsigned short cSocket::GetPort(void) const
+
+void cSocket::SetNonBlocking(void)
+{
+ #ifdef _WIN32
+ u_long NonBlocking = 1;
+ int res = ioctlsocket(m_Socket, FIONBIO, &NonBlocking);
+ #else
+ int NonBlocking = 1;
+ int res = ioctl(m_Socket, FIONBIO, (char *)&NonBlocking);
+ #endif
+ if (res != 0)
+ {
+ LOGERROR("Cannot set socket to non-blocking. This would make the server deadlock later on, aborting.\nErr: %d, %d, %s",
+ res, GetLastError(), GetLastErrorString().c_str()
+ );
+ abort();
+ }
+}
+
+
+
+
diff --git a/src/OSSupport/Socket.h b/src/OSSupport/Socket.h
index 4ca3d61f4..bdc2babf5 100644
--- a/src/OSSupport/Socket.h
+++ b/src/OSSupport/Socket.h
@@ -24,6 +24,12 @@ public:
{
IPv4 = AF_INET,
IPv6 = AF_INET6,
+
+ #ifdef _WIN32
+ ErrWouldBlock = WSAEWOULDBLOCK,
+ #else
+ ErrWouldBlock = EWOULDBLOCK,
+ #endif
} ;
#ifdef _WIN32
@@ -110,6 +116,9 @@ public:
unsigned short GetPort(void) const; // Returns 0 on failure
const AString & GetIPString(void) const { return m_IPString; }
+
+ /** Sets the socket into non-blocking mode */
+ void SetNonBlocking(void);
private:
xSocket m_Socket;
diff --git a/src/OSSupport/SocketThreads.cpp b/src/OSSupport/SocketThreads.cpp
index 74932daf8..3e2631733 100644
--- a/src/OSSupport/SocketThreads.cpp
+++ b/src/OSSupport/SocketThreads.cpp
@@ -175,6 +175,7 @@ void cSocketThreads::cSocketThread::AddClient(const cSocket & a_Socket, cCallbac
m_Slots[m_NumSlots].m_Client = a_Client;
m_Slots[m_NumSlots].m_Socket = a_Socket;
+ m_Slots[m_NumSlots].m_Socket.SetNonBlocking();
m_Slots[m_NumSlots].m_Outgoing.clear();
m_Slots[m_NumSlots].m_State = sSlot::ssNormal;
m_NumSlots++;
@@ -213,7 +214,9 @@ bool cSocketThreads::cSocketThread::RemoveClient(const cCallback * a_Client)
else
{
// Query and queue the last batch of outgoing data:
- m_Slots[i].m_Client->GetOutgoingData(m_Slots[i].m_Outgoing);
+ AString Data;
+ m_Slots[i].m_Client->GetOutgoingData(Data);
+ m_Slots[i].m_Outgoing.append(Data);
if (m_Slots[i].m_Outgoing.empty())
{
// No more outgoing data, shut the socket down immediately:
@@ -386,38 +389,28 @@ void cSocketThreads::cSocketThread::Execute(void)
// The main thread loop:
while (!m_ShouldTerminate)
{
- // Put all sockets into the Read set:
+ // Read outgoing data from the clients:
+ QueueOutgoingData();
+
+ // Put sockets into the sets
fd_set fdRead;
+ fd_set fdWrite;
cSocket::xSocket Highest = m_ControlSocket1.GetSocket();
-
- PrepareSet(&fdRead, Highest, false);
+ PrepareSets(&fdRead, &fdWrite, Highest);
// Wait for the sockets:
timeval Timeout;
Timeout.tv_sec = 5;
Timeout.tv_usec = 0;
- if (select(Highest + 1, &fdRead, NULL, NULL, &Timeout) == -1)
+ if (select(Highest + 1, &fdRead, &fdWrite, NULL, &Timeout) == -1)
{
- LOG("select(R) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str());
+ LOG("select() call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str());
continue;
}
+ // Perform the IO:
ReadFromSockets(&fdRead);
-
- // Test sockets for writing:
- fd_set fdWrite;
- Highest = m_ControlSocket1.GetSocket();
- PrepareSet(&fdWrite, Highest, true);
- Timeout.tv_sec = 0;
- Timeout.tv_usec = 0;
- if (select(Highest + 1, NULL, &fdWrite, NULL, &Timeout) == -1)
- {
- LOG("select(W) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str());
- continue;
- }
-
WriteToSockets(&fdWrite);
-
CleanUpShutSockets();
} // while (!mShouldTerminate)
}
@@ -426,10 +419,11 @@ void cSocketThreads::cSocketThread::Execute(void)
-void cSocketThreads::cSocketThread::PrepareSet(fd_set * a_Set, cSocket::xSocket & a_Highest, bool a_IsForWriting)
+void cSocketThreads::cSocketThread::PrepareSets(fd_set * a_Read, fd_set * a_Write, cSocket::xSocket & a_Highest)
{
- FD_ZERO(a_Set);
- FD_SET(m_ControlSocket1.GetSocket(), a_Set);
+ FD_ZERO(a_Read);
+ FD_ZERO(a_Write);
+ FD_SET(m_ControlSocket1.GetSocket(), a_Read);
cCSLock Lock(m_Parent->m_CS);
for (int i = m_NumSlots - 1; i >= 0; --i)
@@ -444,11 +438,16 @@ void cSocketThreads::cSocketThread::PrepareSet(fd_set * a_Set, cSocket::xSocket
continue;
}
cSocket::xSocket s = m_Slots[i].m_Socket.GetSocket();
- FD_SET(s, a_Set);
+ FD_SET(s, a_Read);
if (s > a_Highest)
{
a_Highest = s;
}
+ if (!m_Slots[i].m_Outgoing.empty())
+ {
+ // There's outgoing data for the socket, put it in the Write set
+ FD_SET(s, a_Write);
+ }
} // for i - m_Slots[]
}
@@ -480,34 +479,37 @@ void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read)
int Received = m_Slots[i].m_Socket.Receive(Buffer, ARRAYCOUNT(Buffer), 0);
if (Received <= 0)
{
- // The socket has been closed by the remote party
- switch (m_Slots[i].m_State)
+ if (cSocket::GetLastError() != cSocket::ErrWouldBlock)
{
- case sSlot::ssNormal:
- {
- // Notify the callback that the remote has closed the socket; keep the slot
- m_Slots[i].m_Client->SocketClosed();
- m_Slots[i].m_State = sSlot::ssRemoteClosed;
- break;
- }
- case sSlot::ssWritingRestOut:
- case sSlot::ssShuttingDown:
- case sSlot::ssShuttingDown2:
- {
- // Force-close the socket and remove the slot:
- m_Slots[i].m_Socket.CloseSocket();
- m_Slots[i] = m_Slots[--m_NumSlots];
- break;
- }
- default:
+ // The socket has been closed by the remote party
+ switch (m_Slots[i].m_State)
{
- LOG("%s: Unexpected socket state: %d (%s)",
- __FUNCTION__, m_Slots[i].m_Socket.GetSocket(), m_Slots[i].m_Socket.GetIPString().c_str()
- );
- ASSERT(!"Unexpected socket state");
- break;
- }
- } // switch (m_Slots[i].m_State)
+ case sSlot::ssNormal:
+ {
+ // Notify the callback that the remote has closed the socket; keep the slot
+ m_Slots[i].m_Client->SocketClosed();
+ m_Slots[i].m_State = sSlot::ssRemoteClosed;
+ break;
+ }
+ case sSlot::ssWritingRestOut:
+ case sSlot::ssShuttingDown:
+ case sSlot::ssShuttingDown2:
+ {
+ // Force-close the socket and remove the slot:
+ m_Slots[i].m_Socket.CloseSocket();
+ m_Slots[i] = m_Slots[--m_NumSlots];
+ break;
+ }
+ default:
+ {
+ LOG("%s: Unexpected socket state: %d (%s)",
+ __FUNCTION__, m_Slots[i].m_Socket.GetSocket(), m_Slots[i].m_Socket.GetIPString().c_str()
+ );
+ ASSERT(!"Unexpected socket state");
+ break;
+ }
+ } // switch (m_Slots[i].m_State)
+ }
}
else
{
@@ -539,7 +541,9 @@ void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write)
// Request another chunk of outgoing data:
if (m_Slots[i].m_Client != NULL)
{
- m_Slots[i].m_Client->GetOutgoingData(m_Slots[i].m_Outgoing);
+ AString Data;
+ m_Slots[i].m_Client->GetOutgoingData(Data);
+ m_Slots[i].m_Outgoing.append(Data);
}
if (m_Slots[i].m_Outgoing.empty())
{
@@ -553,8 +557,7 @@ void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write)
}
} // if (outgoing data is empty)
- int Sent = m_Slots[i].m_Socket.Send(m_Slots[i].m_Outgoing.data(), m_Slots[i].m_Outgoing.size());
- if (Sent < 0)
+ if (!SendDataThroughSocket(m_Slots[i].m_Socket, m_Slots[i].m_Outgoing))
{
int Err = cSocket::GetLastError();
LOGWARNING("Error %d while writing to client \"%s\", disconnecting. \"%s\"", Err, m_Slots[i].m_Socket.GetIPString().c_str(), GetOSErrorString(Err).c_str());
@@ -565,7 +568,6 @@ void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write)
}
return;
}
- m_Slots[i].m_Outgoing.erase(0, Sent);
if (m_Slots[i].m_Outgoing.empty() && (m_Slots[i].m_State == sSlot::ssWritingRestOut))
{
@@ -590,8 +592,41 @@ void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write)
+bool cSocketThreads::cSocketThread::SendDataThroughSocket(cSocket & a_Socket, AString & a_Data)
+{
+ // Send data in smaller chunks, so that the OS send buffers aren't overflown easily
+ while (!a_Data.empty())
+ {
+ size_t NumToSend = std::min(a_Data.size(), (size_t)1024);
+ int Sent = a_Socket.Send(a_Data.data(), NumToSend);
+ if (Sent < 0)
+ {
+ int Err = cSocket::GetLastError();
+ if (Err == cSocket::ErrWouldBlock)
+ {
+ // The OS send buffer is full, leave the outgoing data for the next time
+ return true;
+ }
+ // An error has occured
+ return false;
+ }
+ if (Sent == 0)
+ {
+ a_Socket.CloseSocket();
+ return true;
+ }
+ a_Data.erase(0, Sent);
+ }
+ return true;
+}
+
+
+
+
+
void cSocketThreads::cSocketThread::CleanUpShutSockets(void)
{
+ cCSLock Lock(m_Parent->m_CS);
for (int i = m_NumSlots - 1; i >= 0; i--)
{
switch (m_Slots[i].m_State)
@@ -617,3 +652,32 @@ void cSocketThreads::cSocketThread::CleanUpShutSockets(void)
+void cSocketThreads::cSocketThread::QueueOutgoingData(void)
+{
+ cCSLock Lock(m_Parent->m_CS);
+ for (int i = 0; i < m_NumSlots; i++)
+ {
+ if (m_Slots[i].m_Client != NULL)
+ {
+ AString Data;
+ m_Slots[i].m_Client->GetOutgoingData(Data);
+ m_Slots[i].m_Outgoing.append(Data);
+ }
+ if (m_Slots[i].m_Outgoing.empty())
+ {
+ // No outgoing data is ready
+ if (m_Slots[i].m_State == sSlot::ssWritingRestOut)
+ {
+ // The socket doesn't want to be kept alive anymore, and doesn't have any remaining data to send.
+ // Shut it down and then close it after a timeout, or when the other side agrees
+ m_Slots[i].m_State = sSlot::ssShuttingDown;
+ m_Slots[i].m_Socket.ShutdownReadWrite();
+ }
+ continue;
+ }
+ }
+}
+
+
+
+
diff --git a/src/OSSupport/SocketThreads.h b/src/OSSupport/SocketThreads.h
index 9e1947ab6..fcd2ce11f 100644
--- a/src/OSSupport/SocketThreads.h
+++ b/src/OSSupport/SocketThreads.h
@@ -66,7 +66,8 @@ public:
/** Called when data is received from the remote party */
virtual void DataReceived(const char * a_Data, int a_Size) = 0;
- /** Called when data can be sent to remote party; the function is supposed to *append* outgoing data to a_Data */
+ /** Called when data can be sent to remote party
+ The function is supposed to *set* outgoing data to a_Data (overwrite) */
virtual void GetOutgoingData(AString & a_Data) = 0;
/** Called when the socket has been closed for any reason */
@@ -156,16 +157,27 @@ private:
virtual void Execute(void) override;
- /** Puts all sockets into the set, along with m_ControlSocket1.
- Only sockets that are able to send and receive data are put in the Set.
- Is a_IsForWriting is true, the ssWritingRestOut sockets are added as well. */
- void PrepareSet(fd_set * a_Set, cSocket::xSocket & a_Highest, bool a_IsForWriting);
+ /** Prepares the Read and Write socket sets for select()
+ Puts all sockets into the read set, along with m_ControlSocket1.
+ Only sockets that have outgoing data queued on them are put in the write set.*/
+ void PrepareSets(fd_set * a_ReadSet, fd_set * a_WriteSet, cSocket::xSocket & a_Highest);
- void ReadFromSockets(fd_set * a_Read); // Reads from sockets indicated in a_Read
- void WriteToSockets (fd_set * a_Write); // Writes to sockets indicated in a_Write
+ /** Reads from sockets indicated in a_Read */
+ void ReadFromSockets(fd_set * a_Read);
+ /** Writes to sockets indicated in a_Write */
+ void WriteToSockets (fd_set * a_Write);
+
+ /** Sends data through the specified socket, trying to fill the OS send buffer in chunks.
+ Returns true if there was no error while sending, false if an error has occured.
+ Modifies a_Data to contain only the unsent data. */
+ bool SendDataThroughSocket(cSocket & a_Socket, AString & a_Data);
+
/** Removes those slots in ssShuttingDown2 state, sets those with ssShuttingDown state to ssShuttingDown2 */
void CleanUpShutSockets(void);
+
+ /** Calls each client's callback to retrieve outgoing data for that client. */
+ void QueueOutgoingData(void);
} ;
typedef std::list<cSocketThread *> cSocketThreadList;
diff --git a/src/OSSupport/Timer.cpp b/src/OSSupport/Timer.cpp
index ed16f9e3a..fd838dd0d 100644
--- a/src/OSSupport/Timer.cpp
+++ b/src/OSSupport/Timer.cpp
@@ -28,7 +28,7 @@ long long cTimer::GetNowTime(void)
#else
struct timeval now;
gettimeofday(&now, NULL);
- return (long long)(now.tv_sec * 1000 + now.tv_usec / 1000);
+ return (long long)now.tv_sec * 1000 + (long long)now.tv_usec / 1000;
#endif
}
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 9bb2cfbf0..04bade867 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -53,8 +53,14 @@ Implements the 1.7.x protocol classes:
+const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
+
+
+
+
+
// fwd: main.cpp:
-extern bool g_ShouldLogComm;
+extern bool g_ShouldLogCommIn, g_ShouldLogCommOut;
@@ -74,7 +80,7 @@ cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAdd
m_IsEncrypted(false)
{
// Create the comm log file, if so requested:
- if (g_ShouldLogComm)
+ if (g_ShouldLogCommIn || g_ShouldLogCommOut)
{
cFile::CreateFolder("CommLogs");
AString FileName = Printf("CommLogs/%x__%s.log", (unsigned)time(NULL), a_Client->GetIPString().c_str());
@@ -979,10 +985,11 @@ void cProtocol172::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, cons
Pkt.WriteInt(a_BlockX);
Pkt.WriteShort((short)a_BlockY);
Pkt.WriteInt(a_BlockZ);
- Pkt.WriteString(a_Line1);
- Pkt.WriteString(a_Line2);
- Pkt.WriteString(a_Line3);
- Pkt.WriteString(a_Line4);
+ // Need to send only up to 15 chars, otherwise the client crashes (#598)
+ Pkt.WriteString(a_Line1.substr(0, 15));
+ Pkt.WriteString(a_Line2.substr(0, 15));
+ Pkt.WriteString(a_Line3.substr(0, 15));
+ Pkt.WriteString(a_Line4.substr(0, 15));
}
@@ -1083,7 +1090,7 @@ void cProtocol172::SendWindowProperty(const cWindow & a_Window, short a_Property
void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
{
// Write the incoming data into the comm log file:
- if (g_ShouldLogComm)
+ if (g_ShouldLogCommIn)
{
if (m_ReceivedData.GetReadableSpace() > 0)
{
@@ -1092,9 +1099,10 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
m_ReceivedData.ReadAll(AllData);
m_ReceivedData.ResetRead();
m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace);
+ ASSERT(m_ReceivedData.GetReadableSpace() == OldReadableSpace);
AString Hex;
CreateHexDump(Hex, AllData.data(), AllData.size(), 16);
- m_CommLogFile.Printf("Incoming data, %d (0x%x) bytes unparsed already present in buffer:\n%s\n",
+ m_CommLogFile.Printf("Incoming data, %d (0x%x) unparsed bytes already present in buffer:\n%s\n",
AllData.size(), AllData.size(), Hex.c_str()
);
}
@@ -1103,6 +1111,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
m_CommLogFile.Printf("Incoming data: %d (0x%x) bytes: \n%s\n",
a_Size, a_Size, Hex.c_str()
);
+ m_CommLogFile.Flush();
}
if (!m_ReceivedData.Write(a_Data, a_Size))
@@ -1119,12 +1128,14 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
if (!m_ReceivedData.ReadVarInt(PacketLen))
{
// Not enough data
- return;
+ m_ReceivedData.ResetRead();
+ break;
}
if (!m_ReceivedData.CanReadBytes(PacketLen))
{
// The full packet hasn't been received yet
- return;
+ m_ReceivedData.ResetRead();
+ break;
}
cByteBuffer bb(PacketLen + 1);
VERIFY(m_ReceivedData.ReadToByteBuffer(bb, (int)PacketLen));
@@ -1137,11 +1148,11 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
if (!bb.ReadVarInt(PacketType))
{
// Not enough data
- return;
+ break;
}
// Log the packet info into the comm log file:
- if (g_ShouldLogComm)
+ if (g_ShouldLogCommIn)
{
AString PacketData;
bb.ReadAll(PacketData);
@@ -1173,7 +1184,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
#endif // _DEBUG
// Put a message in the comm log:
- if (g_ShouldLogComm)
+ if (g_ShouldLogCommIn)
{
m_CommLogFile.Printf("^^^^^^ Unhandled packet ^^^^^^\n\n\n");
}
@@ -1189,7 +1200,7 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
);
// Put a message in the comm log:
- if (g_ShouldLogComm)
+ if (g_ShouldLogCommIn)
{
m_CommLogFile.Printf("^^^^^^ Wrong number of bytes read for this packet (exp %d left, got %d left) ^^^^^^\n\n\n",
1, bb.GetReadableSpace()
@@ -1200,7 +1211,24 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
ASSERT(!"Read wrong number of bytes!");
m_Client->PacketError(PacketType);
}
- } // while (true)
+ } // for(ever)
+
+ // Log any leftover bytes into the logfile:
+ if (g_ShouldLogCommIn && (m_ReceivedData.GetReadableSpace() > 0))
+ {
+ AString AllData;
+ int OldReadableSpace = m_ReceivedData.GetReadableSpace();
+ m_ReceivedData.ReadAll(AllData);
+ m_ReceivedData.ResetRead();
+ m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace);
+ ASSERT(m_ReceivedData.GetReadableSpace() == OldReadableSpace);
+ AString Hex;
+ CreateHexDump(Hex, AllData.data(), AllData.size(), 16);
+ m_CommLogFile.Printf("There are %d (0x%x) bytes of non-parse-able data left in the buffer:\n%s",
+ m_ReceivedData.GetReadableSpace(), m_ReceivedData.GetReadableSpace(), Hex.c_str()
+ );
+ m_CommLogFile.Flush();
+ }
}
@@ -1330,7 +1358,64 @@ void cProtocol172::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffer)
{
- // TODO: Add protocol encryption
+ short EncKeyLength, EncNonceLength;
+ a_ByteBuffer.ReadBEShort(EncKeyLength);
+ AString EncKey;
+ if (!a_ByteBuffer.ReadString(EncKey, EncKeyLength))
+ {
+ return;
+ }
+ a_ByteBuffer.ReadBEShort(EncNonceLength);
+ AString EncNonce;
+ if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength))
+ {
+ return;
+ }
+ if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
+ {
+ LOGD("Too long encryption");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Decrypt EncNonce using privkey
+ cRSAPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey();
+ Int32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)];
+ int res = rsaDecryptor.Decrypt((const Byte *)EncNonce.data(), EncNonce.size(), (Byte *)DecryptedNonce, sizeof(DecryptedNonce));
+ if (res != 4)
+ {
+ LOGD("Bad nonce length: got %d, exp %d", res, 4);
+ m_Client->Kick("Hacked client");
+ return;
+ }
+ if (ntohl(DecryptedNonce[0]) != (unsigned)(uintptr_t)this)
+ {
+ LOGD("Bad nonce value");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Decrypt the symmetric encryption key using privkey:
+ Byte DecryptedKey[MAX_ENC_LEN];
+ res = rsaDecryptor.Decrypt((const Byte *)EncKey.data(), EncKey.size(), DecryptedKey, sizeof(DecryptedKey));
+ if (res != 16)
+ {
+ LOGD("Bad key length");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ StartEncryption(DecryptedKey);
+
+ // Send login success:
+ {
+ cPacketizer Pkt(*this, 0x02); // Login success packet
+ Pkt.WriteString(Printf("%d", m_Client->GetUniqueID())); // TODO: proper UUID
+ Pkt.WriteString(m_Client->GetUsername());
+ }
+
+ m_State = 3; // State = Game
+ m_Client->HandleLogin(4, m_Client->GetUsername());
}
@@ -1342,14 +1427,26 @@ void cProtocol172::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer)
AString Username;
a_ByteBuffer.ReadVarUTF8String(Username);
- // TODO: Protocol encryption should be set up here if not localhost / auth
-
if (!m_Client->HandleHandshake(Username))
{
// The client is not welcome here, they have been sent a Kick packet already
return;
}
+ // If auth is required, then send the encryption request:
+ if (cRoot::Get()->GetServer()->ShouldAuthenticate())
+ {
+ cPacketizer Pkt(*this, 0x01);
+ Pkt.WriteString(cRoot::Get()->GetServer()->GetServerID());
+ const AString & PubKeyDer = cRoot::Get()->GetServer()->GetPublicKeyDER();
+ Pkt.WriteShort(PubKeyDer.size());
+ Pkt.WriteBuf(PubKeyDer.data(), PubKeyDer.size());
+ Pkt.WriteShort(4);
+ Pkt.WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
+ m_Client->SetUsername(Username);
+ return;
+ }
+
// Send login success:
{
cPacketizer Pkt(*this, 0x02); // Login success packet
@@ -1861,6 +1958,27 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
+void cProtocol172::StartEncryption(const Byte * a_Key)
+{
+ m_Encryptor.Init(a_Key, a_Key);
+ m_Decryptor.Init(a_Key, a_Key);
+ m_IsEncrypted = true;
+
+ // Prepare the m_AuthServerID:
+ cSHA1Checksum Checksum;
+ const AString & ServerID = cRoot::Get()->GetServer()->GetServerID();
+ Checksum.Update((const Byte *)ServerID.c_str(), ServerID.length());
+ Checksum.Update(a_Key, 16);
+ Checksum.Update((const Byte *)cRoot::Get()->GetServer()->GetPublicKeyDER().data(), cRoot::Get()->GetServer()->GetPublicKeyDER().size());
+ Byte Digest[20];
+ Checksum.Finalize(Digest);
+ cSHA1Checksum::DigestToJava(Digest, m_AuthServerID);
+}
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol172::cPacketizer:
@@ -1881,7 +1999,7 @@ cProtocol172::cPacketizer::~cPacketizer()
m_Out.CommitRead();
// Log the comm into logfile:
- if (g_ShouldLogComm)
+ if (g_ShouldLogCommOut)
{
AString Hex;
ASSERT(DataToSend.size() > 0);
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index 72544b575..6a75e41c8 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -281,6 +281,8 @@ protected:
/// Parses item metadata as read by ReadItem(), into the item enchantments.
void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
+
+ void StartEncryption(const Byte * a_Key);
} ;
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index daeed1845..f58c66d10 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -18,7 +18,7 @@
// Adjust these if a new protocol is added or an old one is removed:
-#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2"
+#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2, 1.7.4"
#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78, 4"
diff --git a/src/Root.cpp b/src/Root.cpp
index fa1fdb37a..883bfe76e 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -200,7 +200,7 @@ void cRoot::Start(void)
long long finishmseconds = Time.GetNowTime();
finishmseconds -= mseconds;
- LOG("Startup complete, took %i ms!", finishmseconds);
+ LOG("Startup complete, took %lld ms!", finishmseconds);
#ifdef _WIN32
EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button
#endif
diff --git a/src/Server.cpp b/src/Server.cpp
index 3f9f8a4ac..ba2b46d55 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -237,7 +237,8 @@ bool cServer::InitServer(cIniFile & a_SettingsIni)
m_bIsConnected = true;
m_ServerID = "-";
- if (a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true))
+ m_ShouldAuthenticate = a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true);
+ if (m_ShouldAuthenticate)
{
MTRand mtrand1;
unsigned int r1 = (mtrand1.randInt() % 1147483647) + 1000000000;
diff --git a/src/Server.h b/src/Server.h
index 15eafc00a..b5280c59d 100644
--- a/src/Server.h
+++ b/src/Server.h
@@ -71,13 +71,13 @@ public: // tolua_export
bool Command(cClientHandle & a_Client, AString & a_Cmd);
- /// Executes the console command, sends output through the specified callback
+ /** Executes the console command, sends output through the specified callback */
void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
- /// Lists all available console commands and their helpstrings
+ /** Lists all available console commands and their helpstrings */
void PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
- /// Binds the built-in console commands with the plugin manager
+ /** Binds the built-in console commands with the plugin manager */
static void BindBuiltInConsoleCommands(void);
void Shutdown(void);
@@ -97,13 +97,13 @@ public: // tolua_export
void RemoveClient(const cClientHandle * a_Client); // Removes the clienthandle from m_SocketThreads
- /// Don't tick a_Client anymore, it will be ticked from its cPlayer instead
+ /** Don't tick a_Client anymore, it will be ticked from its cPlayer instead */
void ClientMovedToWorld(const cClientHandle * a_Client);
- /// Notifies the server that a player was created; the server uses this to adjust the number of players
+ /** Notifies the server that a player was created; the server uses this to adjust the number of players */
void PlayerCreated(const cPlayer * a_Player);
- /// Notifies the server that a player is being destroyed; the server uses this to adjust the number of players
+ /** Notifies the server that a player is being destroyed; the server uses this to adjust the number of players */
void PlayerDestroying(const cPlayer * a_Player);
/** Returns base64 encoded favicon data (obtained from favicon.png) */
@@ -112,11 +112,13 @@ public: // tolua_export
cRSAPrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; }
+ bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; }
+
private:
friend class cRoot; // so cRoot can create and destroy cServer
- /// When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap)
+ /** When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap) */
class cNotifyWriteThread :
public cIsThread
{
@@ -140,7 +142,7 @@ private:
void NotifyClientWrite(const cClientHandle * a_Client);
} ;
- /// The server tick thread takes care of the players who aren't yet spawned in a world
+ /** The server tick thread takes care of the players who aren't yet spawned in a world */
class cTickThread :
public cIsThread
{
@@ -195,18 +197,22 @@ private:
cTickThread m_TickThread;
cEvent m_RestartEvent;
- /// The server ID used for client authentication
+ /** The server ID used for client authentication */
AString m_ServerID;
+ /** If true, players will be online-authenticated agains Mojang servers.
+ This setting is the same as the "online-mode" setting in Vanilla. */
+ bool m_ShouldAuthenticate;
+
cServer(void);
- /// Loads, or generates, if missing, RSA keys for protocol encryption
+ /** Loads, or generates, if missing, RSA keys for protocol encryption */
void PrepareKeys(void);
bool Tick(float a_Dt);
- /// Ticks the clients in m_Clients, manages the list in respect to removing clients
+ /** Ticks the clients in m_Clients, manages the list in respect to removing clients */
void TickClients(float a_Dt);
// cListenThread::cCallback overrides:
diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp
index 0dbd41c12..3fe75d611 100644
--- a/src/StringUtils.cpp
+++ b/src/StringUtils.cpp
@@ -833,7 +833,8 @@ AString Base64Encode(const AString & a_Input)
short GetBEShort(const char * a_Mem)
{
- return (((short)a_Mem[0]) << 8) | a_Mem[1];
+ const Byte * Bytes = (const Byte *)a_Mem;
+ return (Bytes[0] << 8) | Bytes[1];
}
@@ -842,7 +843,8 @@ short GetBEShort(const char * a_Mem)
int GetBEInt(const char * a_Mem)
{
- return (((int)a_Mem[0]) << 24) | (((int)a_Mem[1]) << 16) | (((int)a_Mem[2]) << 8) | a_Mem[3];
+ const Byte * Bytes = (const Byte *)a_Mem;
+ return (Bytes[0] << 24) | (Bytes[1] << 16) | (Bytes[2] << 8) | Bytes[3];
}
diff --git a/src/World.cpp b/src/World.cpp
index f5e207520..7b4cb553a 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -1,4 +1,3 @@
-
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "BlockID.h"
@@ -234,7 +233,7 @@ cWorld::cWorld(const AString & a_WorldName) :
m_WorldName(a_WorldName),
m_IniFileName(m_WorldName + "/world.ini"),
m_StorageSchema("Default"),
-#ifdef _arm_
+#ifdef __arm__
m_StorageCompressionFactor(0),
#else
m_StorageCompressionFactor(6),
@@ -547,6 +546,7 @@ void cWorld::Start(void)
m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", false);
m_ShouldLavaSpawnFire = IniFile.GetValueSetB("Physics", "ShouldLavaSpawnFire", true);
m_bCommandBlocksEnabled = IniFile.GetValueSetB("Mechanics", "CommandBlocksEnabled", false);
+ m_VillagersShouldHarvestCrops = IniFile.GetValueSetB("Monsters", "VillagersShouldHarvestCrops", true);
m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode);
diff --git a/src/World.h b/src/World.h
index 6d5df7b8f..cdfd1510a 100644
--- a/src/World.h
+++ b/src/World.h
@@ -139,6 +139,8 @@ public:
bool ShouldLavaSpawnFire(void) const { return m_ShouldLavaSpawnFire; }
+ bool VillagersShouldHarvestCrops(void) const { return m_VillagersShouldHarvestCrops; }
+
virtual eDimension GetDimension(void) const { return m_Dimension; }
/** Returns the world height at the specified coords; waits for the chunk to get loaded / generated */
@@ -747,6 +749,7 @@ private:
bool m_bEnabledPVP;
bool m_IsDeepSnowEnabled;
bool m_ShouldLavaSpawnFire;
+ bool m_VillagersShouldHarvestCrops;
std::vector<BlockTickQueueItem *> m_BlockTickQueue;
std::vector<BlockTickQueueItem *> m_BlockTickQueueCopy; // Second is for safely removing the objects from the queue
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index e46a28caa..9c454c028 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -454,8 +454,8 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
}
case cMonster::mtWolf:
{
- // TODO:
- // _X: CopyPasta error: m_Writer.AddInt("Profession", ((const cVillager *)a_Monster)->GetVilType());
+ m_Writer.AddString("Owner", ((const cWolf *)a_Monster)->GetOwner());
+ m_Writer.AddByte("Sitting", ((const cWolf *)a_Monster)->IsSitting());
break;
}
case cMonster::mtZombie:
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index e2a882f65..02396bb16 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -611,12 +611,18 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx)
{
- int ID = a_NBT.FindChildByName(a_TagIdx, "id");
- if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short))
+ int Type = a_NBT.FindChildByName(a_TagIdx, "id");
+ if ((Type < 0) || (a_NBT.GetType(Type) != TAG_Short))
{
return false;
}
- a_Item.m_ItemType = (ENUM_ITEM_ID)(a_NBT.GetShort(ID));
+ a_Item.m_ItemType = a_NBT.GetShort(Type);
+ if (a_Item.m_ItemType < 0)
+ {
+ LOGD("Encountered an item with negative type (%d). Replacing with an empty item.", a_NBT.GetShort(Type));
+ a_Item.Empty();
+ return true;
+ }
int Damage = a_NBT.FindChildByName(a_TagIdx, "Damage");
if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short))
@@ -1870,7 +1876,16 @@ void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N
{
return;
}
-
+ int OwnerIdx = a_NBT.FindChildByName(a_TagIdx, "Owner");
+ if (OwnerIdx > 0)
+ {
+ AString OwnerName = a_NBT.GetString(OwnerIdx);
+ if (OwnerName != "")
+ {
+ Monster->SetOwner(OwnerName);
+ Monster->SetIsTame(true);
+ }
+ }
a_Entities.push_back(Monster.release());
}
diff --git a/src/main.cpp b/src/main.cpp
index 06b344c25..c8cd2d4fe 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -19,8 +19,11 @@ bool g_SERVER_TERMINATED = false; // Set to true when the server terminates, so
-/** If set to true, the protocols will log each player's communication to a separate logfile */
-bool g_ShouldLogComm;
+/** If set to true, the protocols will log each player's incoming (C->S) communication to a per-connection logfile */
+bool g_ShouldLogCommIn;
+
+/** If set to true, the protocols will log each player's outgoing (S->C) communication to a per-connection logfile */
+bool g_ShouldLogCommOut;
@@ -66,11 +69,13 @@ void NonCtrlHandler(int a_Signal)
std::signal(a_Signal, SIG_DFL);
LOGERROR(" D: | MCServer has encountered an error and needs to close");
LOGERROR("Details | SIGABRT: Server self-terminated due to an internal fault");
+ exit(EXIT_FAILURE);
break;
}
+ case SIGINT:
case SIGTERM:
{
- std::signal(SIGTERM, SIG_IGN); // Server is shutting down, wait for it...
+ std::signal(a_Signal, SIG_IGN); // Server is shutting down, wait for it...
break;
}
default: break;
@@ -224,6 +229,10 @@ int main( int argc, char **argv )
std::signal(SIGSEGV, NonCtrlHandler);
std::signal(SIGTERM, NonCtrlHandler);
std::signal(SIGINT, NonCtrlHandler);
+ std::signal(SIGABRT, NonCtrlHandler);
+ #ifdef SIGABRT_COMPAT
+ std::signal(SIGABRT_COMPAT, NonCtrlHandler);
+ #endif // SIGABRT_COMPAT
#endif
// DEBUG: test the dumpfile creation:
@@ -237,7 +246,24 @@ int main( int argc, char **argv )
(NoCaseCompare(argv[i], "/logcomm") == 0)
)
{
- g_ShouldLogComm = true;
+ g_ShouldLogCommIn = true;
+ g_ShouldLogCommOut = true;
+ }
+ if (
+ (NoCaseCompare(argv[i], "/commlogin") == 0) ||
+ (NoCaseCompare(argv[i], "/comminlog") == 0) ||
+ (NoCaseCompare(argv[i], "/logcommin") == 0)
+ )
+ {
+ g_ShouldLogCommIn = true;
+ }
+ if (
+ (NoCaseCompare(argv[i], "/commlogout") == 0) ||
+ (NoCaseCompare(argv[i], "/commoutlog") == 0) ||
+ (NoCaseCompare(argv[i], "/logcommout") == 0)
+ )
+ {
+ g_ShouldLogCommOut = true;
}
}