Merge branch 'master' into GeneratingBenchmark2

Conflicts:
	src/World.h
master
Tycho 2014-01-31 15:31:05 -08:00
commit fec17409d2
46 changed files with 931 additions and 223 deletions

View File

@ -1,25 +1,30 @@
Many people have contributed to MCServer, and this list attempts to broadcast at least some of them.
faketruth (founder)
xoft
keyboard
STR_Warrior
mgueydan
tigerw
bearbin (Alexander Harkness)
Lapayo
rs2k
derouinw
Diusrex
Duralex
mtilden
FakeTruth (founder)
keyboard
Lapayo
Luksor
marmot21
Sofapriester
mborland
mgueydan
MikeHunsinger
mtilden
nesco
rs2k
SamJBarney
worktycho
Sxw1212
tonibm19
Diusrex
Sofapriester
STR_Warrior
structinf (xdot)
Sxw1212
Taugeshtu
tigerw (Tiger Wang)
tonibm19
worktycho
xoft
Please add yourself to this list if you contribute to MCServer.

View File

@ -813,6 +813,20 @@ cFile:Delete("/usr/bin/virus.exe");
},
}, -- cFile
cFloater =
{
Desc = [[
When a player uses his/her fishing rod it creates a floater entity. This class manages it.
]],
Functions =
{
CanPickup = { Params = "", Return = "bool", Notes = "Returns true if the floater gives an item when the player right clicks." },
GetAttachedMobID = { Params = "", Return = "EntityID", Notes = "A floater can get attached to an mob. When it is and this functions gets called it returns the mob ID. If it isn't attached to a mob it returns -1" },
GetOwnerID = { Params = "", Return = "EntityID", Notes = "Returns the EntityID of the player who owns the floater." },
},
Inherits = "cEntity",
},
cGroup =
{
Desc = [[

View File

@ -25,7 +25,7 @@
</p>
<p>
Next, we must obtain a copy of CoreMessaging.lua. This can be found
<a href="https://raw.github.com/mc-server/MCServer/master/MCServer/Plugins/MagicCarpet/coremessaging.lua">here.</a>
<a href="https://gist.github.com/bearbin/8715888">here.</a>
This is used to provide messaging support that is compliant with MCServer standards.
</p>
<h2>Creating the basic template</h2>
@ -35,19 +35,14 @@
Format it like so:
</p>
<pre class="prettyprint lang-lua">
local PLUGIN
PLUGIN = nil
function Initialize(Plugin)
Plugin:SetName("DerpyPlugin")
Plugin:SetName("NewPlugin")
Plugin:SetVersion(1)
PLUGIN = Plugin
-- Hooks
local PluginManager = cPluginManager:Get()
-- Command bindings
LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
return true
end
@ -84,7 +79,7 @@ end
To register a hook, insert the following code template into the "-- Hooks" area in the previous code example.
</p>
<pre class="prettyprint lang-lua">
cPluginManager:AddHook(cPluginManager.HOOK_NAME_HERE, FunctionNameToBeCalled)
cPluginManager.AddHook(cPluginManager.HOOK_NAME_HERE, FunctionNameToBeCalled)
</pre>
<p>
What does this code do?
@ -102,10 +97,7 @@ function Initialize(Plugin)
Plugin:SetName("DerpyPlugin")
Plugin:SetVersion(1)
cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
local PluginManager = cPluginManager:Get()
-- Command bindings
cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
return true
@ -127,10 +119,10 @@ end
</p>
<pre class="prettyprint lang-lua">
-- ADD THIS IF COMMAND DOES NOT REQUIRE A PARAMETER (/explode)
PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " - Description of command")
cPluginManager.BindCommand("/commandname", "permissionnode", FunctionToCall, " - Description of command")
-- ADD THIS IF COMMAND DOES REQUIRE A PARAMETER (/explode Notch)
PluginManager:BindCommand("/commandname", "permissionnode", FunctionToCall, " ~ Description of command and parameter(s)")
cPluginManager.BindCommand("/commandname", "permissionnode", FunctionToCall, " ~ Description of command and parameter(s)")
</pre>
<p>
What does it do, and why are there two?
@ -197,8 +189,7 @@ function Initialize(Plugin)
Plugin:SetName("DerpyPluginThatBlowsPeopleUp")
Plugin:SetVersion(9001)
local PluginManager = cPluginManager:Get()
PluginManager:BindCommand("/explode", "derpyplugin.explode", Explode, " ~ Explode a player");
cPluginManager.BindCommand("/explode", "derpyplugin.explode", Explode, " ~ Explode a player");
cPluginManager:AddHook(cPluginManager.HOOK_COLLECTING_PICKUP, OnCollectingPickup)

View File

@ -12,7 +12,9 @@ Installation
To install MCServer, you can either download the repository and compile it, or download a pre-compiled version.
After you've cloned the repository, you probably want to pull down the submodules (core plugins, some dependencies). This can be achieved with `git submodule init` and then on a regular basis (to keep up to date) `git submodule update`.
If you've cloned the repository using Git, you need to pull down the submodules (core plugins, some dependencies). This can be achieved with `git submodule init` and then on a regular basis (to keep up to date) `git submodule update`.
If you downloaded a ZIP file of the sources instead, you will need to download PolarSSL, too, from https://github.com/polarssl/polarssl , and unpack it into the `lib/polarssl` folder. You will also need to manually download all the plugins that you want included.
Compilation instructions are available in the COMPILING file.

View File

@ -1302,6 +1302,7 @@ bool cConnection::HandleServerLoginEncryptionKeyRequest(void)
}
Log("Got PACKET_ENCRYPTION_KEY_REQUEST from the SERVER:");
Log(" ServerID = %s", ServerID.c_str());
DataLog(PublicKey.data(), PublicKey.size(), " Public key (%u bytes)", (unsigned)PublicKey.size());
// Reply to the server:
SendEncryptionKeyResponse(PublicKey, Nonce);
@ -2863,14 +2864,25 @@ void cConnection::SendEncryptionKeyResponse(const AString & a_ServerPublicKey, c
Byte SharedSecret[16];
Byte EncryptedSecret[128];
memset(SharedSecret, 0, sizeof(SharedSecret)); // Use all zeroes for the initial secret
m_Server.GetPrivateKey().Encrypt(SharedSecret, sizeof(SharedSecret), EncryptedSecret, sizeof(EncryptedSecret));
cPublicKey PubKey(a_ServerPublicKey);
int res = PubKey.Encrypt(SharedSecret, sizeof(SharedSecret), EncryptedSecret, sizeof(EncryptedSecret));
if (res < 0)
{
Log("Shared secret encryption failed: %d (0x%x)", res, res);
return;
}
m_ServerEncryptor.Init(SharedSecret, SharedSecret);
m_ServerDecryptor.Init(SharedSecret, SharedSecret);
// Encrypt the nonce:
Byte EncryptedNonce[128];
m_Server.GetPrivateKey().Encrypt((const Byte *)a_Nonce.data(), a_Nonce.size(), EncryptedNonce, sizeof(EncryptedNonce));
res = PubKey.Encrypt((const Byte *)a_Nonce.data(), a_Nonce.size(), EncryptedNonce, sizeof(EncryptedNonce));
if (res < 0)
{
Log("Nonce encryption failed: %d (0x%x)", res, res);
return;
}
// Send the packet to the server:
Log("Sending PACKET_ENCRYPTION_KEY_RESPONSE to the SERVER");
@ -2880,6 +2892,11 @@ void cConnection::SendEncryptionKeyResponse(const AString & a_ServerPublicKey, c
ToServer.WriteBuf(EncryptedSecret, sizeof(EncryptedSecret));
ToServer.WriteBEShort((short)sizeof(EncryptedNonce));
ToServer.WriteBuf(EncryptedNonce, sizeof(EncryptedNonce));
DataLog(EncryptedSecret, sizeof(EncryptedSecret), "Encrypted secret (%u bytes)", (unsigned)sizeof(EncryptedSecret));
DataLog(EncryptedNonce, sizeof(EncryptedNonce), "Encrypted nonce (%u bytes)", (unsigned)sizeof(EncryptedNonce));
cByteBuffer Len(5);
Len.WriteVarInt(ToServer.GetReadableSpace());
SERVERSEND(Len);
SERVERSEND(ToServer);
m_ServerState = csEncryptedUnderstood;
m_IsServerEncrypted = true;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}
AString Path = PluginPath + *itr;
if (!m_LuaState.LoadFile(Path))
if (*itr == "Info.lua")
{
Close();
return false;
HasInfoLua = true;
}
else
{
numFiles++;
LuaFiles.push_back(*itr);
}
} // for itr - Files[]
}
}
std::sort(LuaFiles.begin(), LuaFiles.end());
if (numFiles == 0)
// 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;
}
// Call intialize function
// 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;
}
} // for itr - Files[]
if (HasInfoLua)
{
AString Path = PluginPath + "Info.lua";
if (!m_LuaState.LoadFile(Path))
{
Close();
return false;
}
}
// 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());

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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
{

View File

@ -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();

View File

@ -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();

View File

@ -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

View File

@ -9,7 +9,6 @@
cChicken::cChicken(void) :
super("Chicken", mtChicken, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4),
m_EggDropTimer(0)

View File

@ -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;
} ;

View File

@ -41,5 +41,3 @@ void cCow::OnRightClicked(cPlayer & a_Player)
}
}

View File

@ -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); }
} ;

View File

@ -6,7 +6,6 @@
// TODO: Milk Cow
@ -30,4 +29,3 @@ void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer)

View File

@ -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); }
} ;

View File

@ -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);
}
}
}

View File

@ -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(); }
} ;

View File

@ -73,5 +73,3 @@ void cPig::OnRightClicked(cPlayer & a_Player)

View File

@ -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:

View File

@ -97,3 +97,4 @@ void cSheep::Tick(float a_Dt, cChunk & a_Chunk)
}
}
}

View File

@ -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; }

View File

@ -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;
}

View File

@ -29,12 +29,36 @@ public:
CLASS_PROTODEF(cVillager);
// Override functions
virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
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;
} ;

View File

@ -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();
}
}

View File

@ -24,6 +24,12 @@ public:
{
IPv4 = AF_INET,
IPv6 = AF_INET6,
#ifdef _WIN32
ErrWouldBlock = WSAEWOULDBLOCK,
#else
ErrWouldBlock = EWOULDBLOCK,
#endif
} ;
#ifdef _WIN32
@ -111,6 +117,9 @@ public:
const AString & GetIPString(void) const { return m_IPString; }
/** Sets the socket into non-blocking mode */
void SetNonBlocking(void);
private:
xSocket m_Socket;
AString m_IPString;

View File

@ -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:
fd_set fdRead;
cSocket::xSocket Highest = m_ControlSocket1.GetSocket();
// Read outgoing data from the clients:
QueueOutgoingData();
PrepareSet(&fdRead, Highest, false);
// Put sockets into the sets
fd_set fdRead;
fd_set fdWrite;
cSocket::xSocket Highest = m_ControlSocket1.GetSocket();
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[]
}
@ -479,6 +478,8 @@ void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read)
char Buffer[1024];
int Received = m_Slots[i].m_Socket.Receive(Buffer, ARRAYCOUNT(Buffer), 0);
if (Received <= 0)
{
if (cSocket::GetLastError() != cSocket::ErrWouldBlock)
{
// The socket has been closed by the remote party
switch (m_Slots[i].m_State)
@ -509,6 +510,7 @@ void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read)
}
} // switch (m_Slots[i].m_State)
}
}
else
{
if (m_Slots[i].m_Client != NULL)
@ -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;
}
}
}

View File

@ -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;

View File

@ -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
}

View File

@ -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);

View File

@ -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);
} ;

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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:

View File

@ -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];
}

View File

@ -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);

View File

@ -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

View File

@ -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:

View File

@ -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());
}

View File

@ -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;
}
}