1
0

Merge branch 'master' into fixes.

This commit is contained in:
madmaxoft 2014-05-02 19:34:28 +02:00
commit 839447f0bb
181 changed files with 4644 additions and 2527 deletions

2
.gitmodules vendored
View File

@ -9,4 +9,4 @@
url = https://github.com/bearbin/transapi.git
[submodule "lib/polarssl"]
path = lib/polarssl
url = https://github.com/polarssl/polarssl
url = https://github.com/mc-server/polarssl

17
CoverityModel.cpp Normal file
View File

@ -0,0 +1,17 @@
extern "C" {
struct lua_State;
struct tolua_Error
{
int index;
int array;
const char* type;
};
void tolua_error (lua_State* L, const char* msg, tolua_Error* err)
{
__coverity_panic__();
}
}

View File

@ -699,7 +699,7 @@ end</pre>
GetLevel = { Params = "EnchantmentNumID", Return = "number", Notes = "Returns the level of the specified enchantment stored in this object; 0 if not stored" },
IsEmpty = { Params = "", Return = "bool", Notes = "Returns true if the object stores no enchantments" },
SetLevel = { Params = "EnchantmentNumID, Level", Return = "", Notes = "Sets the level for the specified enchantment, adding it if not stored before or removing it if level < = 0" },
StringToEnchantmentID = { Params = "EnchantmentTextID", Return = "number", Notes = "(static) Returns the enchantment numerical ID, -1 if not understood. Case insensitive" },
StringToEnchantmentID = { Params = "EnchantmentTextID", Return = "number", Notes = "(static) Returns the enchantment numerical ID, -1 if not understood. Case insensitive. Also understands plain numbers." },
ToString = { Params = "", Return = "string", Notes = "Returns the string description of all the enchantments stored in this object, in numerical-ID form" },
},
Constants =

View File

@ -2,23 +2,33 @@ return
{
HOOK_DISCONNECT =
{
CalledWhen = "A player has explicitly disconnected.",
CalledWhen = [[
A client has disconnected, either by explicitly sending the disconnect packet (in older protocols) or
their connection was terminated
]],
DefaultFnName = "OnDisconnect", -- also used as pagename
Desc = [[
This hook is called when a client is about to be disconnected from the server, for whatever reason.
<p><b>Note that this hook will be removed after <1.7 protocol support is removed, as it was originally a hook for
the client sending the server a disconnect packet, which no longer happens.</b></p>
This hook is called when a client has disconnected from the server, for whatever reason. It is also
called when the client sends the Disconnect packet (only in pre-1.7 protocols). This hook is not called
for server ping connections.</p>
<p>
Note that the hook is called even for connections to players who failed to auth. In such a case there's
no {{cPlayer}} object associated with the client.</p>
<p>
See also the {{OnHandshake|HOOK_HANDSHAKE}} hook which is called when the client connects (and presents
a handshake message, so that they are not just status-pinging). If you need to store a per-player
object, use the {{OnPlayerJoined|HOOK_PLAYER_JOINED}} and {{OnPlayerDestroyed|HOOK_PLAYER_DESTROYED}}
hooks instead, those are guaranteed to have the {{cPlayer}} object associated.
]],
Params =
{
{ Name = "Player", Type = "{{cPlayer}}", Notes = "The player who has disconnected" },
{ Name = "Client", Type = "{{cClientHandle}}", Notes = "The client who has disconnected" },
{ Name = "Reason", Type = "string", Notes = "The reason that the client has sent in the disconnect packet" },
},
Returns = [[
If the function returns false or no value, MCServer calls other plugins' callbacks for this event.
If the function returns true, no other plugins are called for this event. In either case,
the player is disconnected.
the client is disconnected.
]],
}, -- HOOK_DISCONNECT
}

View File

@ -27,10 +27,14 @@ local function LoadAPIFiles(a_Folder, a_DstTable)
-- We only want .lua files from the folder:
if (cFile:IsFile(FileName) and fnam:match(".*%.lua$")) then
local TablesFn, Err = loadfile(FileName);
if (TablesFn == nil) then
if (type(TablesFn) ~= "function") then
LOGWARNING("Cannot load API descriptions from " .. FileName .. ", Lua error '" .. Err .. "'.");
else
local Tables = TablesFn();
if (type(Tables) ~= "table") then
LOGWARNING("Cannot load API descriptions from " .. FileName .. ", returned object is not a table (" .. type(Tables) .. ").");
break
end
for k, cls in pairs(Tables) do
a_DstTable[k] = cls;
end

View File

@ -55,7 +55,7 @@ SightDistance=25.0
MaxHealth=20
IsFireproof=1
[Cavespider]
[CaveSpider]
AttackRange=2.0
AttackRate=1
AttackDamage=2.0

View File

@ -45,6 +45,8 @@ macro(set_flags)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -std=c++11")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -std=c++11")
add_flags_cxx("-stdlib=libc++")
add_flags_lnk("-stdlib=libc++")
else()
add_flags_cxx("-pthread")
endif()

View File

@ -36,14 +36,24 @@ set(SHARED_SRC
../../src/StringUtils.cpp
../../src/Log.cpp
../../src/MCLogger.cpp
../../src/Crypto.cpp
../../src/PolarSSL++/AesCfb128Decryptor.cpp
../../src/PolarSSL++/AesCfb128Encryptor.cpp
../../src/PolarSSL++/CtrDrbgContext.cpp
../../src/PolarSSL++/EntropyContext.cpp
../../src/PolarSSL++/PublicKey.cpp
../../src/PolarSSL++/RsaPrivateKey.cpp
)
set(SHARED_HDR
../../src/ByteBuffer.h
../../src/StringUtils.h
../../src/Log.h
../../src/MCLogger.h
../../src/Crypto.h
../../src/PolarSSL++/AesCfb128Decryptor.h
../../src/PolarSSL++/AesCfb128Encryptor.h
../../src/PolarSSL++/CtrDrbgContext.h
../../src/PolarSSL++/EntropyContext.h
../../src/PolarSSL++/PublicKey.h
../../src/PolarSSL++/RsaPrivateKey.h
)
set(SHARED_OSS_SRC
../../src/OSSupport/CriticalSection.cpp

View File

@ -7,6 +7,7 @@
#include "Connection.h"
#include "Server.h"
#include <iostream>
#include "PolarSSL++/PublicKey.h"
#ifdef _WIN32
#include <direct.h> // For _mkdir()
@ -471,7 +472,7 @@ bool cConnection::SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer)
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer)
{
DataLog(a_Data, a_Size, "Encrypting %d bytes to %s", a_Size, a_Peer);
const Byte * Data = (const Byte *)a_Data;
@ -495,7 +496,7 @@ bool cConnection::SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryp
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer)
bool cConnection::SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer)
{
AString All;
a_Data.ReadAll(All);

View File

@ -11,6 +11,8 @@
#include "ByteBuffer.h"
#include "OSSupport/Timer.h"
#include "PolarSSL++/AesCfb128Decryptor.h"
#include "PolarSSL++/AesCfb128Encryptor.h"
@ -66,8 +68,8 @@ protected:
cByteBuffer m_ClientBuffer;
cByteBuffer m_ServerBuffer;
cAESCFBDecryptor m_ServerDecryptor;
cAESCFBEncryptor m_ServerEncryptor;
cAesCfb128Decryptor m_ServerDecryptor;
cAesCfb128Encryptor m_ServerEncryptor;
AString m_ServerEncryptionBuffer; // Buffer for the data to be sent to the server once encryption is established
@ -109,10 +111,10 @@ protected:
bool SendData(SOCKET a_Socket, cByteBuffer & a_Data, const char * a_Peer);
/// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false
bool SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer);
bool SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, const char * a_Data, size_t a_Size, const char * a_Peer);
/// Sends data to the specfied socket, after encrypting it using a_Encryptor. If sending fails, prints a fail message using a_Peer and returns false
bool SendEncryptedData(SOCKET a_Socket, cAESCFBEncryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer);
bool SendEncryptedData(SOCKET a_Socket, cAesCfb128Encryptor & a_Encryptor, cByteBuffer & a_Data, const char * a_Peer);
/// Decodes packets coming from the client, sends appropriate counterparts to the server; returns false if the connection is to be dropped
bool DecodeClientsPackets(const char * a_Data, int a_Size);

View File

@ -216,6 +216,20 @@ typedef unsigned char Byte;
// Pretty much the same as ASSERT() but stays in Release builds
#define VERIFY( x ) ( !!(x) || ( LOGERROR("Verification failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), exit(1), 0 ) )
// Allow both Older versions of MSVC and newer versions of everything use a shared_ptr:
// Note that we cannot typedef, because C++ doesn't allow (partial) templates to be typedeffed.
#if (defined(_MSC_VER) && (_MSC_VER < 1600))
// MSVC before 2010 doesn't have std::shared_ptr, but has std::tr1::shared_ptr, defined in <memory> included earlier
#define SharedPtr std::tr1::shared_ptr
#elif (__cplusplus >= 201103L)
// C++11 has std::shared_ptr in <memory>, included earlier
#define SharedPtr std::shared_ptr
#else
// C++03 has std::tr1::shared_ptr in <tr1/memory>
#include <tr1/memory>
#define SharedPtr std::tr1::shared_ptr
#endif
@ -232,12 +246,6 @@ public:
#include "../../src/Crypto.h"
#define LOGERROR printf
#define LOGINFO printf
#define LOGWARNING printf

View File

@ -20,7 +20,7 @@ You need to set the server *not* to verify usernames ("online-mode=false" in ser
ProtoProxy is not much dependent on the protocol - it will work with unknown packets, it just won't parse them into human-readable format.
The latest protocol which has been tested is 1.6.1 (#73).
The latest protocol which has been tested is 1.7.9 (#5).
*/

View File

@ -9,6 +9,8 @@
#pragma once
#include "PolarSSL++/RsaPrivateKey.h"
@ -17,7 +19,7 @@
class cServer
{
SOCKET m_ListenSocket;
cRSAPrivateKey m_PrivateKey;
cRsaPrivateKey m_PrivateKey;
AString m_PublicKeyDER;
short m_ConnectPort;
@ -27,7 +29,7 @@ public:
int Init(short a_ListenPort, short a_ConnectPort);
void Run(void);
cRSAPrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
cRsaPrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
const AString & GetPublicKeyDER (void) { return m_PublicKeyDER; }
short GetConnectPort(void) const { return m_ConnectPort; }

View File

@ -4,12 +4,11 @@ project (expat)
file(GLOB SOURCE
"*.c"
"*.h"
)
# add headers to MSVC project files:
if (WIN32)
file(GLOB HEADERS "*.h")
set(SOURCE ${SOURCE} ${HEADERS})
# Set files to go to a "Sources" folder in MSVC project files:
if (MSVC)
source_group("Sources" FILES ${SOURCE})
endif()

View File

@ -1,7 +1,11 @@
cmake_minimum_required (VERSION 2.6)
project (iniFile)
include_directories ("${PROJECT_SOURCE_DIR}/../../src/")
add_library(iniFile iniFile)
file(GLOB SOURCE
"*.h"
"*.cpp"
)
add_library(iniFile ${SOURCE})

View File

@ -7,6 +7,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.c"
"*.h"
)
add_library(luaexpat ${SOURCE})

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../../src/")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(md5 ${SOURCE})

@ -1 +1 @@
Subproject commit 2cb1a0c4009ecf368ecc74eb428394e10f9e6d00
Subproject commit 1ed82759c68f92c4acc7e3f33b850cf9f01c8aba

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../../src/")
file(GLOB SOURCE
"*.c"
"*.h"
)
if(NOT TARGET zlib)

View File

@ -42,7 +42,7 @@ bool cLuaChunkStay::AddChunks(int a_ChunkCoordTableStackPos)
// Add each set of coords:
int NumChunks = luaL_getn(L, a_ChunkCoordTableStackPos);
m_Chunks.reserve(NumChunks);
m_Chunks.reserve((size_t)NumChunks);
for (int idx = 1; idx <= NumChunks; idx++)
{
// Push the idx-th element of the array onto stack top, check that it's a table:

View File

@ -4,12 +4,12 @@
#include <time.h>
// tolua_begin
unsigned int GetTime()
inline unsigned int GetTime()
{
return (unsigned int)time(0);
}
std::string GetChar( std::string & a_Str, unsigned int a_Idx )
inline std::string GetChar( std::string & a_Str, unsigned int a_Idx )
{
return std::string(1, a_Str[ a_Idx ]);
}

View File

@ -37,7 +37,7 @@
/****************************
* Better error reporting for Lua
**/
int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError)
static int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaError)
{
// Retrieve current function name
lua_Debug entry;
@ -57,7 +57,7 @@ int tolua_do_error(lua_State* L, const char * a_pMsg, tolua_Error * a_pToLuaErro
int lua_do_error(lua_State* L, const char * a_pFormat, ...)
static int lua_do_error(lua_State* L, const char * a_pFormat, ...)
{
// Retrieve current function name
lua_Debug entry;
@ -235,7 +235,7 @@ static int tolua_Base64Decode(lua_State * tolua_S)
cPluginLua * GetLuaPlugin(lua_State * L)
static cPluginLua * GetLuaPlugin(lua_State * L)
{
// Get the plugin identification out of LuaState:
lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME);
@ -1750,7 +1750,6 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
{
return 0;
}
cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin);
// Read the params:
cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, NULL);
@ -1760,8 +1759,12 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
L.LogStackTrace();
return 0;
}
cLuaChunkStay * ChunkStay = new cLuaChunkStay(*Plugin);
if (!ChunkStay->AddChunks(2))
{
delete ChunkStay;
return 0;
}
@ -1773,20 +1776,20 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S)
static int tolua_cPlayer_GetGroups(lua_State* tolua_S)
static int tolua_cPlayer_GetGroups(lua_State * tolua_S)
{
cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL);
cPlayer * self = (cPlayer *)tolua_tousertype(tolua_S, 1, NULL);
const cPlayer::GroupList & AllGroups = self->GetGroups();
lua_createtable(tolua_S, AllGroups.size(), 0);
lua_createtable(tolua_S, (int)AllGroups.size(), 0);
int newTable = lua_gettop(tolua_S);
int index = 1;
cPlayer::GroupList::const_iterator iter = AllGroups.begin();
while(iter != AllGroups.end())
while (iter != AllGroups.end())
{
const cGroup* Group = *iter;
tolua_pushusertype( tolua_S, (void*)Group, "const cGroup" );
const cGroup * Group = *iter;
tolua_pushusertype(tolua_S, (void *)Group, "const cGroup");
lua_rawseti(tolua_S, newTable, index);
++iter;
++index;
@ -1798,20 +1801,20 @@ static int tolua_cPlayer_GetGroups(lua_State* tolua_S)
static int tolua_cPlayer_GetResolvedPermissions(lua_State* tolua_S)
static int tolua_cPlayer_GetResolvedPermissions(lua_State * tolua_S)
{
cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL);
cPlayer * self = (cPlayer*) tolua_tousertype(tolua_S, 1, NULL);
cPlayer::StringList AllPermissions = self->GetResolvedPermissions();
lua_createtable(tolua_S, AllPermissions.size(), 0);
lua_createtable(tolua_S, (int)AllPermissions.size(), 0);
int newTable = lua_gettop(tolua_S);
int index = 1;
cPlayer::StringList::iterator iter = AllPermissions.begin();
while(iter != AllPermissions.end())
while (iter != AllPermissions.end())
{
std::string& Permission = *iter;
tolua_pushstring( tolua_S, Permission.c_str() );
std::string & Permission = *iter;
lua_pushlstring(tolua_S, Permission.c_str(), Permission.length());
lua_rawseti(tolua_S, newTable, index);
++iter;
++index;
@ -2073,18 +2076,18 @@ static int tolua_get_HTTPRequest_FormData(lua_State* tolua_S)
static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S)
{
cWebAdmin* self = (cWebAdmin*) tolua_tousertype(tolua_S, 1, NULL);
cWebAdmin * self = (cWebAdmin *)tolua_tousertype(tolua_S, 1, NULL);
const cWebAdmin::PluginList & AllPlugins = self->GetPlugins();
lua_createtable(tolua_S, AllPlugins.size(), 0);
lua_createtable(tolua_S, (int)AllPlugins.size(), 0);
int newTable = lua_gettop(tolua_S);
int index = 1;
cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin();
while(iter != AllPlugins.end())
while (iter != AllPlugins.end())
{
const cWebPlugin* Plugin = *iter;
tolua_pushusertype( tolua_S, (void*)Plugin, "const cWebPlugin" );
const cWebPlugin * Plugin = *iter;
tolua_pushusertype(tolua_S, (void *)Plugin, "const cWebPlugin");
lua_rawseti(tolua_S, newTable, index);
++iter;
++index;

View File

@ -56,7 +56,7 @@ public:
virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) = 0;
virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) = 0;
virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) = 0;
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0;
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0;

View File

@ -400,14 +400,14 @@ bool cPluginLua::OnCraftingNoRecipe(const cPlayer * a_Player, const cCraftingGri
bool cPluginLua::OnDisconnect(cPlayer * a_Player, const AString & a_Reason)
bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason)
{
cCSLock Lock(m_CriticalSection);
bool res = false;
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
m_LuaState.Call((int)(**itr), a_Player, a_Reason, cLuaState::Return, res);
m_LuaState.Call((int)(**itr), &a_Client, a_Reason, cLuaState::Return, res);
if (res)
{
return true;
@ -1042,7 +1042,7 @@ bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Cha
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGIN_MESSAGE];
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
{
m_LuaState.Call((int)(**itr), &a_Client, a_Channel, a_Message);
m_LuaState.Call((int)(**itr), &a_Client, a_Channel, a_Message, cLuaState::Return, res);
if (res)
{
return true;

View File

@ -79,7 +79,7 @@ public:
virtual bool OnChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ) override;
virtual bool OnCollectingPickup (cPlayer * a_Player, cPickup * a_Pickup) override;
virtual bool OnCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
virtual bool OnDisconnect (cPlayer * a_Player, const AString & a_Reason) override;
virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) override;
virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) override;
virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;
virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) override;

View File

@ -442,7 +442,7 @@ bool cPluginManager::CallHookCraftingNoRecipe(const cPlayer * a_Player, const cC
bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Reason)
bool cPluginManager::CallHookDisconnect(cClientHandle & a_Client, const AString & a_Reason)
{
HookMap::iterator Plugins = m_Hooks.find(HOOK_DISCONNECT);
if (Plugins == m_Hooks.end())
@ -451,7 +451,7 @@ bool cPluginManager::CallHookDisconnect(cPlayer * a_Player, const AString & a_Re
}
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
{
if ((*itr)->OnDisconnect(a_Player, a_Reason))
if ((*itr)->OnDisconnect(a_Client, a_Reason))
{
return true;
}

View File

@ -172,7 +172,7 @@ public: // tolua_export
bool CallHookChunkUnloading (cWorld * a_World, int a_ChunkX, int a_ChunkZ);
bool CallHookCollectingPickup (cPlayer * a_Player, cPickup & a_Pickup);
bool CallHookCraftingNoRecipe (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
bool CallHookDisconnect (cPlayer * a_Player, const AString & a_Reason);
bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason);
bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == NULL, it is a console cmd
bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);

View File

@ -7,6 +7,88 @@
#include "BiomeDef.h"
// The "map" used for biome <-> string conversions:
static struct {
EMCSBiome m_Biome;
const char * m_String;
} g_BiomeMap[] =
{
{biOcean, "Ocean"} ,
{biPlains, "Plains"},
{biDesert, "Desert"},
{biExtremeHills, "ExtremeHills"},
{biForest, "Forest"},
{biTaiga, "Taiga"},
{biSwampland, "Swampland"},
{biRiver, "River"},
{biNether, "Hell"},
{biNether, "Nether"},
{biEnd, "Sky"},
{biEnd, "End"},
{biFrozenOcean, "FrozenOcean"},
{biFrozenRiver, "FrozenRiver"},
{biIcePlains, "IcePlains"},
{biIcePlains, "Tundra"},
{biIceMountains, "IceMountains"},
{biMushroomIsland, "MushroomIsland"},
{biMushroomShore, "MushroomShore"},
{biBeach, "Beach"},
{biDesertHills, "DesertHills"},
{biForestHills, "ForestHills"},
{biTaigaHills, "TaigaHills"},
{biExtremeHillsEdge, "ExtremeHillsEdge"},
{biJungle, "Jungle"},
{biJungleHills, "JungleHills"},
// Release 1.7 biomes:
{biJungleEdge, "JungleEdge"},
{biDeepOcean, "DeepOcean"},
{biStoneBeach, "StoneBeach"},
{biColdBeach, "ColdBeach"},
{biBirchForest, "BirchForest"},
{biBirchForestHills, "BirchForestHills"},
{biRoofedForest, "RoofedForest"},
{biColdTaiga, "ColdTaiga"},
{biColdTaigaHills, "ColdTaigaHills"},
{biMegaTaiga, "MegaTaiga"},
{biMegaTaigaHills, "MegaTaigaHills"},
{biExtremeHillsPlus, "ExtremeHillsPlus"},
{biSavanna, "Savanna"},
{biSavannaPlateau, "SavannaPlateau"},
{biMesa, "Mesa"},
{biMesaPlateauF, "MesaPlateauF"},
{biMesaPlateau, "MesaPlateau"},
// Release 1.7 variants:
{biSunflowerPlains, "SunflowerPlains"},
{biDesertM, "DesertM"},
{biExtremeHillsM, "ExtremeHillsM"},
{biFlowerForest, "FlowerForest"},
{biTaigaM, "TaigaM"},
{biSwamplandM, "SwamplandM"},
{biIcePlainsSpikes, "IcePlainsSpikes"},
{biJungleM, "JungleM"},
{biJungleEdgeM, "JungleEdgeM"},
{biBirchForestM, "BirchForestM"},
{biBirchForestHillsM, "BirchForestHillsM"},
{biRoofedForestM, "RoofedForestM"},
{biColdTaigaM, "ColdTaigaM"},
{biMegaSpruceTaiga, "MegaSpruceTaiga"},
{biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"},
{biExtremeHillsPlusM, "ExtremeHillsPlusM"},
{biSavannaM, "SavannaM"},
{biSavannaPlateauM, "SavannaPlateauM"},
{biMesaBryce, "MesaBryce"},
{biMesaPlateauFM, "MesaPlateauFM"},
{biMesaPlateauM, "MesaPlateauM"},
} ;
EMCSBiome StringToBiome(const AString & a_BiomeString)
{
// If it is a number, return it:
@ -25,87 +107,11 @@ EMCSBiome StringToBiome(const AString & a_BiomeString)
return biInvalidBiome;
}
// Convert using the built-in map:
static struct {
EMCSBiome m_Biome;
const char * m_String;
} BiomeMap[] =
for (size_t i = 0; i < ARRAYCOUNT(g_BiomeMap); i++)
{
{biOcean, "Ocean"} ,
{biPlains, "Plains"},
{biDesert, "Desert"},
{biExtremeHills, "ExtremeHills"},
{biForest, "Forest"},
{biTaiga, "Taiga"},
{biSwampland, "Swampland"},
{biRiver, "River"},
{biNether, "Hell"},
{biNether, "Nether"},
{biEnd, "Sky"},
{biEnd, "End"},
{biFrozenOcean, "FrozenOcean"},
{biFrozenRiver, "FrozenRiver"},
{biIcePlains, "IcePlains"},
{biIcePlains, "Tundra"},
{biIceMountains, "IceMountains"},
{biMushroomIsland, "MushroomIsland"},
{biMushroomShore, "MushroomShore"},
{biBeach, "Beach"},
{biDesertHills, "DesertHills"},
{biForestHills, "ForestHills"},
{biTaigaHills, "TaigaHills"},
{biExtremeHillsEdge, "ExtremeHillsEdge"},
{biJungle, "Jungle"},
{biJungleHills, "JungleHills"},
// Release 1.7 biomes:
{biJungleEdge, "JungleEdge"},
{biDeepOcean, "DeepOcean"},
{biStoneBeach, "StoneBeach"},
{biColdBeach, "ColdBeach"},
{biBirchForest, "BirchForest"},
{biBirchForestHills, "BirchForestHills"},
{biRoofedForest, "RoofedForest"},
{biColdTaiga, "ColdTaiga"},
{biColdTaigaHills, "ColdTaigaHills"},
{biMegaTaiga, "MegaTaiga"},
{biMegaTaigaHills, "MegaTaigaHills"},
{biExtremeHillsPlus, "ExtremeHillsPlus"},
{biSavanna, "Savanna"},
{biSavannaPlateau, "SavannaPlateau"},
{biMesa, "Mesa"},
{biMesaPlateauF, "MesaPlateauF"},
{biMesaPlateau, "MesaPlateau"},
// Release 1.7 variants:
{biSunflowerPlains, "SunflowerPlains"},
{biDesertM, "DesertM"},
{biExtremeHillsM, "ExtremeHillsM"},
{biFlowerForest, "FlowerForest"},
{biTaigaM, "TaigaM"},
{biSwamplandM, "SwamplandM"},
{biIcePlainsSpikes, "IcePlainsSpikes"},
{biJungleM, "JungleM"},
{biJungleEdgeM, "JungleEdgeM"},
{biBirchForestM, "BirchForestM"},
{biBirchForestHillsM, "BirchForestHillsM"},
{biRoofedForestM, "RoofedForestM"},
{biColdTaigaM, "ColdTaigaM"},
{biMegaSpruceTaiga, "MegaSpruceTaiga"},
{biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"},
{biExtremeHillsPlusM, "ExtremeHillsPlusM"},
{biSavannaM, "SavannaM"},
{biSavannaPlateauM, "SavannaPlateauM"},
{biMesaBryce, "MesaBryce"},
{biMesaPlateauFM, "MesaPlateauFM"},
{biMesaPlateauM, "MesaPlateauM"},
} ;
for (size_t i = 0; i < ARRAYCOUNT(BiomeMap); i++)
{
if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0)
if (NoCaseCompare(g_BiomeMap[i].m_String, a_BiomeString) == 0)
{
return BiomeMap[i].m_Biome;
return g_BiomeMap[i].m_Biome;
}
} // for i - BiomeMap[]
return biInvalidBiome;
@ -115,6 +121,22 @@ EMCSBiome StringToBiome(const AString & a_BiomeString)
AString BiomeToString(int a_Biome)
{
for (size_t i = 0; i < ARRAYCOUNT(g_BiomeMap); i++)
{
if (g_BiomeMap[i].m_Biome == a_Biome)
{
return g_BiomeMap[i].m_String;
}
}
return AString();
}
bool IsBiomeNoDownfall(EMCSBiome a_Biome)
{
switch (a_Biome)

View File

@ -104,10 +104,13 @@ enum EMCSBiome
biMaxVariantBiome = biNumVariantBiomes - 1, // The maximum biome value
} ;
/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns biInvalidBiome on failure.
/** Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns biInvalidBiome on failure. */
extern EMCSBiome StringToBiome(const AString & a_BiomeString);
/// Returns true if the biome has no downfall - deserts and savannas
/** Translates biome enum into biome string. Returns empty string on failure (unknown biome). */
extern AString BiomeToString(int a_Biome);
/** Returns true if the biome has no downfall - deserts and savannas */
extern bool IsBiomeNoDownfall(EMCSBiome a_Biome);

View File

@ -14,17 +14,29 @@
// Disable MSVC warnings: "conditional expression is constant"
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4127)
#endif
typedef void (CombinatorFunc)(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta);
// This wild construct allows us to pass a function argument and still have it inlined by the compiler :)
/// Merges two blocktypes and blockmetas of the specified sizes and offsets using the specified combinator function
template<typename Combinator> void InternalMergeBlocks(
template<bool MetasValid, CombinatorFunc Combinator>
void InternalMergeBlocks(
BLOCKTYPE * a_DstTypes, const BLOCKTYPE * a_SrcTypes,
NIBBLETYPE * a_DstMetas, const NIBBLETYPE * a_SrcMetas,
int a_SizeX, int a_SizeY, int a_SizeZ,
int a_SrcOffX, int a_SrcOffY, int a_SrcOffZ,
int a_DstOffX, int a_DstOffY, int a_DstOffZ,
int a_SrcSizeX, int a_SrcSizeY, int a_SrcSizeZ,
int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ,
Combinator a_Combinator
int a_DstSizeX, int a_DstSizeY, int a_DstSizeZ
)
{
UNUSED(a_SrcSizeY);
@ -41,7 +53,15 @@ template<typename Combinator> void InternalMergeBlocks(
int DstIdx = DstBaseZ + a_DstOffX;
for (int x = 0; x < a_SizeX; x++)
{
a_Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]);
if (MetasValid)
{
Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], a_DstMetas[DstIdx], a_SrcMetas[SrcIdx]);
}
else
{
BLOCKTYPE FakeDestMeta = 0;
Combinator(a_DstTypes[DstIdx], a_SrcTypes[SrcIdx], FakeDestMeta, (NIBBLETYPE)0);
}
++DstIdx;
++SrcIdx;
} // for x
@ -54,10 +74,14 @@ template<typename Combinator> void InternalMergeBlocks(
/// Combinator used for cBlockArea::msOverwrite merging
static inline void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
template<bool MetaValid>
void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
a_DstType = a_SrcType;
a_DstMeta = a_SrcMeta;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
}
@ -65,12 +89,16 @@ static inline void MergeCombinatorOverwrite(BLOCKTYPE & a_DstType, BLOCKTYPE a_S
/// Combinator used for cBlockArea::msFillAir merging
static inline void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
template<bool MetaValid>
void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if (a_DstType == E_BLOCK_AIR)
{
a_DstType = a_SrcType;
a_DstMeta = a_SrcMeta;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
}
// "else" is the default, already in place
}
@ -80,12 +108,16 @@ static inline void MergeCombinatorFillAir(BLOCKTYPE & a_DstType, BLOCKTYPE a_Src
/// Combinator used for cBlockArea::msImprint merging
static inline void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
template<bool MetaValid>
void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if (a_SrcType != E_BLOCK_AIR)
{
a_DstType = a_SrcType;
a_DstMeta = a_SrcMeta;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
}
// "else" is the default, already in place
}
@ -95,7 +127,8 @@ static inline void MergeCombinatorImprint(BLOCKTYPE & a_DstType, BLOCKTYPE a_Src
/// Combinator used for cBlockArea::msLake merging
static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
template<bool MetaValid>
void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// Sponge is the NOP block
if (a_SrcType == E_BLOCK_SPONGE)
@ -107,7 +140,10 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp
if (a_SrcType == E_BLOCK_AIR)
{
a_DstType = E_BLOCK_AIR;
a_DstMeta = 0;
if (MetaValid)
{
a_DstMeta = 0;
}
return;
}
@ -132,7 +168,10 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp
case E_BLOCK_STATIONARY_LAVA:
{
a_DstType = a_SrcType;
a_DstMeta = a_SrcMeta;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
return;
}
}
@ -146,7 +185,10 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp
case E_BLOCK_MYCELIUM:
{
a_DstType = E_BLOCK_STONE;
a_DstMeta = 0;
if (MetaValid)
{
a_DstMeta = 0;
}
return;
}
}
@ -159,13 +201,17 @@ static inline void MergeCombinatorLake(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcTyp
/** Combinator used for cBlockArea::msSpongePrint merging */
static inline void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
template<bool MetaValid>
void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// Sponge overwrites nothing, everything else overwrites anything
if (a_SrcType != E_BLOCK_SPONGE)
{
a_DstType = a_SrcType;
a_DstMeta = a_SrcMeta;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
}
}
@ -174,17 +220,24 @@ static inline void MergeCombinatorSpongePrint(BLOCKTYPE & a_DstType, BLOCKTYPE a
/** Combinator used for cBlockArea::msDifference merging */
static inline void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
template<bool MetaValid>
void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
if ((a_DstType == a_SrcType) && (a_DstMeta == a_SrcMeta))
if ((a_DstType == a_SrcType) && (!MetaValid || (a_DstMeta == a_SrcMeta)))
{
a_DstType = E_BLOCK_AIR;
a_DstMeta = 0;
if (MetaValid)
{
a_DstMeta = 0;
}
}
else
{
a_DstType = a_SrcType;
a_DstMeta = a_SrcMeta;
if (MetaValid)
{
a_DstMeta = a_SrcMeta;
}
}
}
@ -193,16 +246,25 @@ static inline void MergeCombinatorDifference(BLOCKTYPE & a_DstType, BLOCKTYPE a_
/** Combinator used for cBlockArea::msMask merging */
static inline void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
template<bool MetaValid>
void MergeCombinatorMask(BLOCKTYPE & a_DstType, BLOCKTYPE a_SrcType, NIBBLETYPE & a_DstMeta, NIBBLETYPE a_SrcMeta)
{
// If the blocks are the same, keep the dest; otherwise replace with air
if ((a_SrcType != a_DstType) || (a_SrcMeta != a_DstMeta))
if ((a_SrcType != a_DstType) || !MetaValid || (a_SrcMeta != a_DstMeta))
{
a_DstType = E_BLOCK_AIR;
a_DstMeta = 0;
if (MetaValid)
{
a_DstMeta = 0;
}
}
}
// Re-enable previously disabled MSVC warnings
#ifdef _MSC_VER
#pragma warning(pop)
#endif
@ -484,7 +546,7 @@ void cBlockArea::CopyTo(cBlockArea & a_Into) const
a_Into.Clear();
a_Into.SetSize(m_Size.x, m_Size.y, m_Size.z, GetDataTypes());
a_Into.m_Origin = m_Origin;
int BlockCount = GetBlockCount();
size_t BlockCount = GetBlockCount();
if (HasBlockTypes())
{
memcpy(a_Into.m_BlockTypes, m_BlockTypes, BlockCount * sizeof(BLOCKTYPE));
@ -532,7 +594,7 @@ void cBlockArea::DumpToRawFile(const AString & a_FileName)
f.Write(&SizeZ, 4);
unsigned char DataTypes = (unsigned char)GetDataTypes();
f.Write(&DataTypes, 1);
int NumBlocks = GetBlockCount();
size_t NumBlocks = GetBlockCount();
if (HasBlockTypes())
{
f.Write(m_BlockTypes, NumBlocks * sizeof(BLOCKTYPE));
@ -637,155 +699,19 @@ void cBlockArea::Expand(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMa
void cBlockArea::Merge(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy)
{
// Block types are compulsory, block metas are voluntary
if (!HasBlockTypes() || !a_Src.HasBlockTypes())
{
LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__);
return;
}
// Dst is *this, Src is a_Src
int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading
int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing
int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy
int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading
int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing
int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy
int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading
int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing
int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy
const NIBBLETYPE * SrcMetas = a_Src.GetBlockMetas();
NIBBLETYPE * DstMetas = m_BlockMetas;
bool IsDummyMetas = ((SrcMetas == NULL) || (DstMetas == NULL));
if (IsDummyMetas)
{
SrcMetas = new NIBBLETYPE[a_Src.GetBlockCount()];
DstMetas = new NIBBLETYPE[GetBlockCount()];
MergeByStrategy<true>(a_Src, a_RelX, a_RelY, a_RelZ, a_Strategy, SrcMetas, DstMetas);
}
switch (a_Strategy)
else
{
case msOverwrite:
{
InternalMergeBlocks(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z,
MergeCombinatorOverwrite
);
break;
} // case msOverwrite
case msFillAir:
{
InternalMergeBlocks(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z,
MergeCombinatorFillAir
);
break;
} // case msFillAir
case msImprint:
{
InternalMergeBlocks(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z,
MergeCombinatorImprint
);
break;
} // case msImprint
case msLake:
{
InternalMergeBlocks(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z,
MergeCombinatorLake
);
break;
} // case msLake
case msSpongePrint:
{
InternalMergeBlocks(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z,
MergeCombinatorSpongePrint
);
break;
} // case msSpongePrint
case msDifference:
{
InternalMergeBlocks(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z,
MergeCombinatorDifference
);
break;
} // case msDifference
case msMask:
{
InternalMergeBlocks(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z,
MergeCombinatorMask
);
break;
} // case msMask
default:
{
LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
ASSERT(!"Unknown block area merge strategy");
break;
}
} // switch (a_Strategy)
if (IsDummyMetas)
{
delete[] SrcMetas;
delete[] DstMetas;
MergeByStrategy<false>(a_Src, a_RelX, a_RelY, a_RelZ, a_Strategy, SrcMetas, DstMetas);
}
}
@ -2079,7 +2005,7 @@ void cBlockArea::ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, i
int NewSizeX = m_Size.x + a_SubMinX + a_AddMaxX;
int NewSizeY = m_Size.y + a_SubMinY + a_AddMaxY;
int NewSizeZ = m_Size.z + a_SubMinZ + a_AddMaxZ;
int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
size_t BlockCount = (size_t)(NewSizeX * NewSizeY * NewSizeZ);
BLOCKTYPE * NewBlockTypes = new BLOCKTYPE[BlockCount];
memset(NewBlockTypes, 0, BlockCount * sizeof(BLOCKTYPE));
int OldIndex = 0;
@ -2109,7 +2035,7 @@ void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMa
int NewSizeX = m_Size.x + a_SubMinX + a_AddMaxX;
int NewSizeY = m_Size.y + a_SubMinY + a_AddMaxY;
int NewSizeZ = m_Size.z + a_SubMinZ + a_AddMaxZ;
int BlockCount = NewSizeX * NewSizeY * NewSizeZ;
size_t BlockCount = (size_t)(NewSizeX * NewSizeY * NewSizeZ);
NIBBLETYPE * NewNibbles = new NIBBLETYPE[BlockCount];
memset(NewNibbles, 0, BlockCount * sizeof(NIBBLETYPE));
int OldIndex = 0;
@ -2161,4 +2087,137 @@ void cBlockArea::RelSetData(
template<bool MetasValid>
void cBlockArea::MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas)
{
// Block types are compulsory, block metas are voluntary
if (!HasBlockTypes() || !a_Src.HasBlockTypes())
{
LOGWARNING("%s: cannot merge because one of the areas doesn't have blocktypes.", __FUNCTION__);
return;
}
// Dst is *this, Src is a_Src
int SrcOffX = std::max(0, -a_RelX); // Offset in Src where to start reading
int DstOffX = std::max(0, a_RelX); // Offset in Dst where to start writing
int SizeX = std::min(a_Src.GetSizeX() - SrcOffX, GetSizeX() - DstOffX); // How many blocks to copy
int SrcOffY = std::max(0, -a_RelY); // Offset in Src where to start reading
int DstOffY = std::max(0, a_RelY); // Offset in Dst where to start writing
int SizeY = std::min(a_Src.GetSizeY() - SrcOffY, GetSizeY() - DstOffY); // How many blocks to copy
int SrcOffZ = std::max(0, -a_RelZ); // Offset in Src where to start reading
int DstOffZ = std::max(0, a_RelZ); // Offset in Dst where to start writing
int SizeZ = std::min(a_Src.GetSizeZ() - SrcOffZ, GetSizeZ() - DstOffZ); // How many blocks to copy
switch (a_Strategy)
{
case cBlockArea::msOverwrite:
{
InternalMergeBlocks<MetasValid, MergeCombinatorOverwrite<MetasValid> >(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
} // case msOverwrite
case cBlockArea::msFillAir:
{
InternalMergeBlocks<MetasValid, MergeCombinatorFillAir<MetasValid> >(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
} // case msFillAir
case cBlockArea::msImprint:
{
InternalMergeBlocks<MetasValid, MergeCombinatorImprint<MetasValid> >(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
} // case msImprint
case cBlockArea::msLake:
{
InternalMergeBlocks<MetasValid, MergeCombinatorLake<MetasValid> >(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
} // case msLake
case cBlockArea::msSpongePrint:
{
InternalMergeBlocks<MetasValid, MergeCombinatorSpongePrint<MetasValid> >(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
} // case msSpongePrint
case cBlockArea::msDifference:
{
InternalMergeBlocks<MetasValid, MergeCombinatorDifference<MetasValid> >(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
} // case msDifference
case cBlockArea::msMask:
{
InternalMergeBlocks<MetasValid, MergeCombinatorMask<MetasValid> >(
m_BlockTypes, a_Src.GetBlockTypes(),
DstMetas, SrcMetas,
SizeX, SizeY, SizeZ,
SrcOffX, SrcOffY, SrcOffZ,
DstOffX, DstOffY, DstOffZ,
a_Src.GetSizeX(), a_Src.GetSizeY(), a_Src.GetSizeZ(),
m_Size.x, m_Size.y, m_Size.z
);
break;
} // case msMask
default:
{
LOGWARNING("Unknown block area merge strategy: %d", a_Strategy);
ASSERT(!"Unknown block area merge strategy");
break;
}
} // switch (a_Strategy)
}

View File

@ -294,7 +294,7 @@ public:
NIBBLETYPE * GetBlockMetas (void) const { return m_BlockMetas; } // NOTE: one byte per block!
NIBBLETYPE * GetBlockLight (void) const { return m_BlockLight; } // NOTE: one byte per block!
NIBBLETYPE * GetBlockSkyLight(void) const { return m_BlockSkyLight; } // NOTE: one byte per block!
int GetBlockCount(void) const { return m_Size.x * m_Size.y * m_Size.z; }
size_t GetBlockCount(void) const { return (size_t)(m_Size.x * m_Size.y * m_Size.z); }
int MakeIndex(int a_RelX, int a_RelY, int a_RelZ) const;
protected:
@ -363,6 +363,9 @@ protected:
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
NIBBLETYPE a_BlockLight, NIBBLETYPE a_BlockSkyLight
);
template<bool MetasValid>
void MergeByStrategy(const cBlockArea & a_Src, int a_RelX, int a_RelY, int a_RelZ, eMergeStrategy a_Strategy, const NIBBLETYPE * SrcMetas, NIBBLETYPE * DstMetas);
// tolua_begin
} ;
// tolua_end

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(BlockEntities ${SOURCE})

View File

@ -21,7 +21,8 @@
cCommandBlockEntity::cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) :
super(E_BLOCK_COMMAND_BLOCK, a_X, a_Y, a_Z, a_World),
m_ShouldExecute(false),
m_IsPowered(false)
m_IsPowered(false),
m_Result(0)
{}

View File

@ -14,6 +14,8 @@
cMobHeadEntity::cMobHeadEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
super(E_BLOCK_HEAD, a_BlockX, a_BlockY, a_BlockZ, a_World),
m_Type(SKULL_TYPE_SKELETON),
m_Rotation(SKULL_ROTATION_NORTH),
m_Owner("")
{
}

View File

@ -102,7 +102,7 @@ public:
return true;
}
a_Item.m_ItemDamage = atoi(Split[1].c_str());
a_Item.m_ItemDamage = (short)atoi(Split[1].c_str());
if ((a_Item.m_ItemDamage == 0) && (Split[1] != "0"))
{
// Parsing the number failed
@ -324,7 +324,7 @@ eDimension StringToDimension(const AString & a_DimensionString)
{ dimOverworld, "Normal"},
{ dimOverworld, "World"},
{ dimNether, "Nether"},
{ dimNether, "Hell"}, // Alternate name for End
{ dimNether, "Hell"}, // Alternate name for Nether
{ dimEnd, "End"},
{ dimEnd, "Sky"}, // Old name for End
} ;
@ -337,7 +337,8 @@ eDimension StringToDimension(const AString & a_DimensionString)
} // for i - DimensionMap[]
// Not found
return (eDimension)-1000;
LOGWARNING("Unknown dimension: \"%s\". Setting to Overworld", a_DimensionString.c_str());
return dimOverworld;
}

View File

@ -129,6 +129,7 @@ void cBlockInfo::Initialize(void)
ms_Info[E_BLOCK_HEAVY_WEIGHTED_PRESSURE_PLATE].m_Transparent = true;
ms_Info[E_BLOCK_ICE ].m_Transparent = true;
ms_Info[E_BLOCK_IRON_DOOR ].m_Transparent = true;
ms_Info[E_BLOCK_LADDER ].m_Transparent = true;
ms_Info[E_BLOCK_LAVA ].m_Transparent = true;
ms_Info[E_BLOCK_LEAVES ].m_Transparent = true;
ms_Info[E_BLOCK_LEVER ].m_Transparent = true;

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(Blocks ${SOURCE})

View File

@ -143,7 +143,7 @@ protected:
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cByteBuffer:
cByteBuffer::cByteBuffer(int a_BufferSize) :
cByteBuffer::cByteBuffer(size_t a_BufferSize) :
m_Buffer(new char[a_BufferSize + 1]),
m_BufferSize(a_BufferSize + 1),
#ifdef _DEBUG
@ -171,7 +171,7 @@ cByteBuffer::~cByteBuffer()
bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count)
bool cByteBuffer::Write(const void * a_Bytes, size_t a_Count)
{
CHECK_THREAD;
CheckValid();
@ -187,13 +187,14 @@ bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count)
}
ASSERT(m_BufferSize >= m_WritePos);
size_t TillEnd = m_BufferSize - m_WritePos;
const char * Bytes = (const char *)a_Bytes;
if (TillEnd <= a_Count)
{
// Need to wrap around the ringbuffer end
if (TillEnd > 0)
{
memcpy(m_Buffer + m_WritePos, a_Bytes, TillEnd);
a_Bytes += TillEnd;
memcpy(m_Buffer + m_WritePos, Bytes, TillEnd);
Bytes += TillEnd;
a_Count -= TillEnd;
WrittenBytes = TillEnd;
}
@ -203,7 +204,7 @@ bool cByteBuffer::Write(const char * a_Bytes, size_t a_Count)
// We're guaranteed that we'll fit in a single write op
if (a_Count > 0)
{
memcpy(m_Buffer + m_WritePos, a_Bytes, a_Count);
memcpy(m_Buffer + m_WritePos, Bytes, a_Count);
m_WritePos += a_Count;
WrittenBytes += a_Count;
}
@ -326,7 +327,7 @@ bool cByteBuffer::ReadBEShort(short & a_Value)
CheckValid();
NEEDBYTES(2);
ReadBuf(&a_Value, 2);
a_Value = ntohs(a_Value);
a_Value = (short)ntohs((u_short)a_Value);
return true;
}
@ -340,7 +341,7 @@ bool cByteBuffer::ReadBEInt(int & a_Value)
CheckValid();
NEEDBYTES(4);
ReadBuf(&a_Value, 4);
a_Value = ntohl(a_Value);
a_Value = (int)ntohl((u_long)a_Value);
return true;
}
@ -419,7 +420,7 @@ bool cByteBuffer::ReadBEUTF16String16(AString & a_Value)
ASSERT(!"Negative string length? Are you sure?");
return true;
}
return ReadUTF16String(a_Value, Length);
return ReadUTF16String(a_Value, (size_t)Length);
}
@ -437,7 +438,7 @@ bool cByteBuffer::ReadVarInt(UInt32 & a_Value)
{
NEEDBYTES(1);
ReadBuf(&b, 1);
Value = Value | (((Int64)(b & 0x7f)) << Shift);
Value = Value | (((UInt32)(b & 0x7f)) << Shift);
Shift += 7;
} while ((b & 0x80) != 0);
a_Value = Value;
@ -461,7 +462,7 @@ bool cByteBuffer::ReadVarUTF8String(AString & a_Value)
{
LOGWARNING("%s: String too large: %u (%u KiB)", __FUNCTION__, Size, Size / 1024);
}
return ReadString(a_Value, (int)Size);
return ReadString(a_Value, (size_t)Size);
}
@ -516,7 +517,7 @@ bool cByteBuffer::WriteBEShort(short a_Value)
CHECK_THREAD;
CheckValid();
PUTBYTES(2);
short Converted = htons(a_Value);
u_short Converted = htons((u_short)a_Value);
return WriteBuf(&Converted, 2);
}
@ -529,7 +530,7 @@ bool cByteBuffer::WriteBEInt(int a_Value)
CHECK_THREAD;
CheckValid();
PUTBYTES(4);
int Converted = HostToNetwork4(&a_Value);
UInt32 Converted = HostToNetwork4(&a_Value);
return WriteBuf(&Converted, 4);
}
@ -542,7 +543,7 @@ bool cByteBuffer::WriteBEInt64(Int64 a_Value)
CHECK_THREAD;
CheckValid();
PUTBYTES(8);
Int64 Converted = HostToNetwork8(&a_Value);
UInt64 Converted = HostToNetwork8(&a_Value);
return WriteBuf(&Converted, 8);
}
@ -555,7 +556,7 @@ bool cByteBuffer::WriteBEFloat(float a_Value)
CHECK_THREAD;
CheckValid();
PUTBYTES(4);
int Converted = HostToNetwork4(&a_Value);
UInt32 Converted = HostToNetwork4(&a_Value);
return WriteBuf(&Converted, 4);
}
@ -568,7 +569,7 @@ bool cByteBuffer::WriteBEDouble(double a_Value)
CHECK_THREAD;
CheckValid();
PUTBYTES(8);
Int64 Converted = HostToNetwork8(&a_Value);
UInt64 Converted = HostToNetwork8(&a_Value);
return WriteBuf(&Converted, 8);
}
@ -612,7 +613,7 @@ bool cByteBuffer::WriteVarInt(UInt32 a_Value)
// A 32-bit integer can be encoded by at most 5 bytes:
unsigned char b[5];
int idx = 0;
size_t idx = 0;
do
{
b[idx] = (a_Value & 0x7f) | ((a_Value > 0x7f) ? 0x80 : 0x00);
@ -631,7 +632,7 @@ bool cByteBuffer::WriteVarUTF8String(const AString & a_Value)
CHECK_THREAD;
CheckValid();
PUTBYTES(a_Value.size() + 1); // This is a lower-bound on the bytes that will be actually written. Fail early.
bool res = WriteVarInt(a_Value.size());
bool res = WriteVarInt((UInt32)(a_Value.size()));
if (!res)
{
return false;
@ -756,7 +757,7 @@ bool cByteBuffer::ReadString(AString & a_String, size_t a_Count)
bool cByteBuffer::ReadUTF16String(AString & a_String, int a_NumChars)
bool cByteBuffer::ReadUTF16String(AString & a_String, size_t a_NumChars)
{
// Reads 2 * a_NumChars bytes and interprets it as a UTF16 string, converting it into UTF8 string a_String
CHECK_THREAD;
@ -887,9 +888,7 @@ void cByteBuffer::AdvanceReadPos(size_t a_Count)
void cByteBuffer::CheckValid(void) const
{
ASSERT(m_ReadPos >= 0);
ASSERT(m_ReadPos < m_BufferSize);
ASSERT(m_WritePos >= 0);
ASSERT(m_WritePos < m_BufferSize);
}

View File

@ -27,11 +27,11 @@ their own synchronization.
class cByteBuffer
{
public:
cByteBuffer(int a_BufferSize);
cByteBuffer(size_t a_BufferSize);
~cByteBuffer();
/// Writes the bytes specified to the ringbuffer. Returns true if successful, false if not
bool Write(const char * a_Bytes, size_t a_Count);
bool Write(const void * a_Bytes, size_t a_Count);
/// Returns the number of bytes that can be successfully written to the ringbuffer
size_t GetFreeSpace(void) const;
@ -101,7 +101,7 @@ public:
bool ReadString(AString & a_String, size_t a_Count);
/// Reads 2 * a_NumChars bytes and interprets it as a UTF16-BE string, converting it into UTF8 string a_String
bool ReadUTF16String(AString & a_String, int a_NumChars);
bool ReadUTF16String(AString & a_String, size_t a_NumChars);
/// Skips reading by a_Count bytes; returns false if not enough bytes in the ringbuffer
bool SkipRead(size_t a_Count);

View File

@ -5,7 +5,7 @@ include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/")
include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/jsoncpp/include")
include_directories (SYSTEM "${PROJECT_SOURCE_DIR}/../lib/polarssl/include")
set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating)
set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++)
set(FOLDERS ${FOLDERS} WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs)
@ -57,6 +57,14 @@ if (NOT MSVC)
Entities/Pickup.h
Entities/Player.h
Entities/ProjectileEntity.h
Entities/ArrowEntity.h
Entities/ThrownEggEntity.h
Entities/ThrownEnderPearlEntity.h
Entities/ExpBottleEntity.h
Entities/ThrownSnowballEntity.h
Entities/FireChargeEntity.h
Entities/FireworkEntity.h
Entities/GhastFireballEntity.h
Entities/TNTEntity.h
Entities/ExpOrb.h
Entities/HangingEntity.h
@ -123,6 +131,7 @@ if (NOT MSVC)
file(GLOB SOURCE
"*.cpp"
"*.h"
)
list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/StackWalker.cpp" "${PROJECT_SOURCE_DIR}/LeakFinder.cpp")
@ -233,7 +242,7 @@ endif ()
if (NOT MSVC)
target_link_libraries(${EXECUTABLE} OSSupport HTTPServer Bindings Items Blocks)
target_link_libraries(${EXECUTABLE} Protocol Generating Generating_Prefabs WorldStorage)
target_link_libraries(${EXECUTABLE} Mobs Entities Simulator UI BlockEntities)
target_link_libraries(${EXECUTABLE} Mobs Entities Simulator UI BlockEntities PolarSSL++)
endif ()
if (WIN32)
target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib)

View File

@ -240,11 +240,24 @@ void cChunk::GetAllData(cChunkDataCallback & a_Callback)
{
a_Callback.HeightMap (&m_HeightMap);
a_Callback.BiomeData (&m_BiomeMap);
a_Callback.BlockTypes (m_BlockTypes);
a_Callback.BlockMeta (m_BlockMeta);
COMPRESSED_BLOCKTYPE Blocks = m_BlockTypes;
Blocks.resize(NumBlocks);
a_Callback.BlockTypes (&Blocks[0]);
COMPRESSED_NIBBLETYPE Metas = m_BlockMeta;
Metas.resize(NumBlocks / 2);
a_Callback.BlockMeta (&Metas[0]);
a_Callback.LightIsValid (m_IsLightValid);
a_Callback.BlockLight (m_BlockLight);
a_Callback.BlockSkyLight(m_BlockSkyLight);
COMPRESSED_NIBBLETYPE BlockLights = m_BlockLight;
BlockLights.resize(NumBlocks / 2);
a_Callback.BlockLight (&BlockLights[0]);
COMPRESSED_NIBBLETYPE BlockSkyLights = m_BlockSkyLight;
BlockSkyLights.resize(NumBlocks / 2, 0xff);
a_Callback.BlockSkyLight(&BlockSkyLights[0]);
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr)
{
@ -262,7 +275,7 @@ void cChunk::GetAllData(cChunkDataCallback & a_Callback)
void cChunk::SetAllData(
const BLOCKTYPE * a_BlockTypes,
const BLOCKTYPE * a_BlockTypes,
const NIBBLETYPE * a_BlockMeta,
const NIBBLETYPE * a_BlockLight,
const NIBBLETYPE * a_BlockSkyLight,
@ -272,29 +285,61 @@ void cChunk::SetAllData(
)
{
memcpy(m_BiomeMap, a_BiomeMap, sizeof(m_BiomeMap));
if (a_HeightMap != NULL)
{
memcpy(m_HeightMap, a_HeightMap, sizeof(m_HeightMap));
}
if (a_HeightMap == NULL)
{
CalculateHeightmap(a_BlockTypes);
}
int IdxWhereNonEmptyStarts = 0;
{ // Blocktype compression
unsigned char Highest = 0;
int X = 0, Z = 0;
m_BlockTypes.clear();
for (int x = 0; x < Width; x++)
{
for (int z = 0; z < Width; z++)
{
unsigned char Height = m_HeightMap[x + z * Width];
if (Height > Highest)
{
Highest = Height;
X = x; Z = z;
}
}
}
IdxWhereNonEmptyStarts = MakeIndexNoCheck(X, Highest + 1, Z);
m_BlockTypes.insert(m_BlockTypes.end(), &a_BlockTypes[0], &a_BlockTypes[IdxWhereNonEmptyStarts]);
}
{ // Blockmeta compression
m_BlockMeta.clear();
m_BlockMeta.insert(m_BlockMeta.end(), &a_BlockMeta[0], &a_BlockMeta[IdxWhereNonEmptyStarts / 2]);
}
memcpy(m_BlockTypes, a_BlockTypes, sizeof(m_BlockTypes));
memcpy(m_BlockMeta, a_BlockMeta, sizeof(m_BlockMeta));
if (a_BlockLight != NULL)
{
memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
// Compress blocklight
m_BlockLight.clear();
m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[IdxWhereNonEmptyStarts / 2]);
}
if (a_BlockSkyLight != NULL)
{
memcpy(m_BlockSkyLight, a_BlockSkyLight, sizeof(m_BlockSkyLight));
// Compress skylight
m_BlockSkyLight.clear();
m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_BlockSkyLight[0], &a_BlockSkyLight[IdxWhereNonEmptyStarts / 2]);
}
m_IsLightValid = (a_BlockLight != NULL) && (a_BlockSkyLight != NULL);
if (a_HeightMap == NULL)
{
CalculateHeightmap();
}
// Clear the block entities present - either the loader / saver has better, or we'll create empty ones:
for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
@ -332,8 +377,17 @@ void cChunk::SetLight(
{
// TODO: We might get cases of wrong lighting when a chunk changes in the middle of a lighting calculation.
// Postponing until we see how bad it is :)
memcpy(m_BlockLight, a_BlockLight, sizeof(m_BlockLight));
memcpy(m_BlockSkyLight, a_SkyLight, sizeof(m_BlockSkyLight));
{ // Compress blocklight
m_BlockLight.clear();
m_BlockLight.insert(m_BlockLight.end(), &a_BlockLight[0], &a_BlockLight[m_BlockTypes.size()]);
}
{ // Compress skylight
m_BlockSkyLight.clear();
m_BlockSkyLight.insert(m_BlockSkyLight.end(), &a_SkyLight[0], &a_SkyLight[m_BlockTypes.size()]);
}
m_IsLightValid = true;
}
@ -343,7 +397,8 @@ void cChunk::SetLight(
void cChunk::GetBlockTypes(BLOCKTYPE * a_BlockTypes)
{
memcpy(a_BlockTypes, m_BlockTypes, NumBlocks);
std::copy(m_BlockTypes.begin(), m_BlockTypes.end(), a_BlockTypes);
std::fill_n(&a_BlockTypes[m_BlockTypes.size()], NumBlocks - m_BlockTypes.size(), E_BLOCK_AIR);
}
@ -630,7 +685,7 @@ void cChunk::Tick(float a_Dt)
void cChunk::TickBlock(int a_RelX, int a_RelY, int a_RelZ)
{
unsigned Index = MakeIndex(a_RelX, a_RelY, a_RelZ);
cBlockHandler * Handler = BlockHandler(m_BlockTypes[Index]);
cBlockHandler * Handler = BlockHandler(GetBlock(Index));
ASSERT(Handler != NULL); // Happenned on server restart, FS #243
cChunkInterface ChunkInterface(this->GetWorld()->GetChunkMap());
cBlockInServerPluginInterface PluginInterface(*this->GetWorld());
@ -751,7 +806,7 @@ void cChunk::BroadcastPendingBlockChanges(void)
void cChunk::CheckBlocks()
{
if (m_ToTickBlocks.size() == 0)
if (m_ToTickBlocks.empty())
{
return;
}
@ -811,7 +866,7 @@ void cChunk::TickBlocks(void)
}
unsigned int Index = MakeIndexNoCheck(m_BlockTickX, m_BlockTickY, m_BlockTickZ);
cBlockHandler * Handler = BlockHandler(m_BlockTypes[Index]);
cBlockHandler * Handler = BlockHandler(GetBlock(Index));
ASSERT(Handler != NULL); // Happenned on server restart, FS #243
Handler->OnUpdate(ChunkInterface, *this->GetWorld(), PluginInterface, *this, m_BlockTickX, m_BlockTickY, m_BlockTickZ);
} // for i - tickblocks
@ -1296,7 +1351,7 @@ void cChunk::CreateBlockEntities(void)
{
for (int y = 0; y < Height; y++)
{
BLOCKTYPE BlockType = cChunkDef::GetBlock(m_BlockTypes, x, y, z);
BLOCKTYPE BlockType = GetBlock(x, y, z);
switch (BlockType)
{
case E_BLOCK_BEACON:
@ -1349,7 +1404,7 @@ void cChunk::WakeUpSimulators(void)
int BlockZ = z + BaseZ;
for (int y = GetHeight(x, z); y >= 0; y--)
{
BLOCKTYPE Block = cChunkDef::GetBlock(m_BlockTypes, x, y, z);
BLOCKTYPE Block = GetBlock(x, y, z);
// The redstone sim takes multiple blocks, use the inbuilt checker
if (RedstoneSimulator->IsAllowedBlock(Block))
@ -1384,7 +1439,7 @@ void cChunk::WakeUpSimulators(void)
void cChunk::CalculateHeightmap()
void cChunk::CalculateHeightmap(const BLOCKTYPE * a_BlockTypes)
{
for (int x = 0; x < Width; x++)
{
@ -1393,7 +1448,7 @@ void cChunk::CalculateHeightmap()
for (int y = Height - 1; y > -1; y--)
{
int index = MakeIndex( x, y, z );
if (m_BlockTypes[index] != E_BLOCK_AIR)
if (a_BlockTypes[index] != E_BLOCK_AIR)
{
m_HeightMap[x + z * Width] = (unsigned char)y;
break;
@ -1517,7 +1572,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
ASSERT(IsValid());
const int index = MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
const BLOCKTYPE OldBlockType = cChunkDef::GetBlock(m_BlockTypes, index);
const BLOCKTYPE OldBlockType = GetBlock(index);
const BLOCKTYPE OldBlockMeta = GetNibble(m_BlockMeta, index);
if ((OldBlockType == a_BlockType) && (OldBlockMeta == a_BlockMeta))
{
@ -1525,7 +1580,11 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
}
MarkDirty();
if ((size_t)index >= m_BlockTypes.size())
{
m_BlockTypes.resize(index + 1);
}
m_BlockTypes[index] = a_BlockType;
// The client doesn't need to distinguish between stationary and nonstationary fluids:
@ -1565,7 +1624,7 @@ void cChunk::FastSetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockT
{
for (int y = a_RelY - 1; y > 0; --y)
{
if (m_BlockTypes[MakeIndexNoCheck(a_RelX, y, a_RelZ)] != E_BLOCK_AIR)
if (GetBlock(MakeIndexNoCheck(a_RelX, y, a_RelZ)) != E_BLOCK_AIR)
{
m_HeightMap[a_RelX + a_RelZ * Width] = (unsigned char)y;
break;
@ -2452,7 +2511,7 @@ BLOCKTYPE cChunk::GetBlock(int a_RelX, int a_RelY, int a_RelZ) const
return 0; // Clip
}
return m_BlockTypes[MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ)];
return GetBlock(MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ));
}
@ -2466,8 +2525,13 @@ BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const
ASSERT(!"GetBlock(idx) out of bounds!");
return 0;
}
return m_BlockTypes[ a_BlockIdx ];
if ((size_t)a_BlockIdx >= m_BlockTypes.size())
{
return E_BLOCK_AIR;
}
return m_BlockTypes[a_BlockIdx];
}
@ -2477,7 +2541,7 @@ BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const
void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
{
int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
a_BlockType = cChunkDef::GetBlock (m_BlockTypes, Idx);
a_BlockType = GetBlock(Idx);
a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, Idx);
}
@ -2488,7 +2552,7 @@ void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_
void cChunk::GetBlockInfo(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_Meta, NIBBLETYPE & a_SkyLight, NIBBLETYPE & a_BlockLight)
{
int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
a_BlockType = cChunkDef::GetBlock (m_BlockTypes, Idx);
a_BlockType = GetBlock(Idx);
a_Meta = cChunkDef::GetNibble(m_BlockMeta, Idx);
a_SkyLight = cChunkDef::GetNibble(m_BlockSkyLight, Idx);
a_BlockLight = cChunkDef::GetNibble(m_BlockLight, Idx);

View File

@ -267,7 +267,7 @@ public:
void UseBlockEntity(cPlayer * a_Player, int a_X, int a_Y, int a_Z); // [x, y, z] in world block coords
void CalculateLighting(); // Recalculate right now
void CalculateHeightmap();
void CalculateHeightmap(const BLOCKTYPE * a_BlockTypes);
// Broadcast various packets to all clients of this chunk:
// (Please keep these alpha-sorted)
@ -326,9 +326,9 @@ public:
inline void SetMeta(int a_BlockIdx, NIBBLETYPE a_Meta) { cChunkDef::SetNibble(m_BlockMeta, a_BlockIdx, a_Meta); }
inline NIBBLETYPE GetBlockLight(int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockLight, a_RelX, a_RelY, a_RelZ); }
inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ); }
inline NIBBLETYPE GetSkyLight (int a_RelX, int a_RelY, int a_RelZ) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_RelX, a_RelY, a_RelZ, true); }
inline NIBBLETYPE GetBlockLight(int a_Idx) const {return cChunkDef::GetNibble(m_BlockLight, a_Idx); }
inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx); }
inline NIBBLETYPE GetSkyLight (int a_Idx) const {return cChunkDef::GetNibble(m_BlockSkyLight, a_Idx, true); }
/** Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in such a case); returns true on success */
bool UnboundedRelGetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta) const;
@ -421,10 +421,10 @@ private:
cChunkMap * m_ChunkMap;
// TODO: Make these pointers and don't allocate what isn't needed
BLOCKTYPE m_BlockTypes [cChunkDef::NumBlocks];
NIBBLETYPE m_BlockMeta [cChunkDef::NumBlocks / 2];
NIBBLETYPE m_BlockLight [cChunkDef::NumBlocks / 2];
NIBBLETYPE m_BlockSkyLight[cChunkDef::NumBlocks / 2];
COMPRESSED_BLOCKTYPE m_BlockTypes;
COMPRESSED_NIBBLETYPE m_BlockMeta;
COMPRESSED_NIBBLETYPE m_BlockLight;
COMPRESSED_NIBBLETYPE m_BlockSkyLight;
cChunkDef::HeightMap m_HeightMap;
cChunkDef::BiomeMap m_BiomeMap;

View File

@ -77,13 +77,19 @@ public:
idx = x + Width * z // Need to verify this with the protocol spec, currently unknown!
*/
typedef EMCSBiome BiomeMap[Width * Width];
/// The type used for block type operations and storage, AXIS_ORDER ordering
typedef BLOCKTYPE BlockTypes[NumBlocks];
/// The type used for block data in nibble format, AXIS_ORDER ordering
typedef NIBBLETYPE BlockNibbles[NumBlocks / 2];
/** The storage wrapper used for compressed blockdata residing in RAMz */
typedef std::vector<BLOCKTYPE> COMPRESSED_BLOCKTYPE;
/** The storage wrapper used for compressed nibbledata residing in RAMz */
typedef std::vector<NIBBLETYPE> COMPRESSED_NIBBLETYPE;
/// Converts absolute block coords into relative (chunk + block) coords:
inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ )
@ -219,46 +225,67 @@ public:
ASSERT((a_Z >= 0) && (a_Z <= Width));
a_BiomeMap[a_X + Width * a_Z] = a_Biome;
}
static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int a_BlockIdx)
static NIBBLETYPE GetNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int a_BlockIdx, bool a_IsSkyLightNibble = false)
{
if ((a_BlockIdx > -1) && (a_BlockIdx < NumBlocks))
{
return (a_Buffer[a_BlockIdx / 2] >> ((a_BlockIdx & 1) * 4)) & 0x0f;
if ((size_t)(a_BlockIdx / 2) >= a_Buffer.size())
{
return (a_IsSkyLightNibble ? 0xff : 0);
}
return (a_Buffer[(size_t)(a_BlockIdx / 2)] >> ((a_BlockIdx & 1) * 4)) & 0x0f;
}
ASSERT(!"cChunkDef::GetNibble(): index out of chunk range!");
return 0;
}
static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int x, int y, int z)
static NIBBLETYPE GetNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, int x, int y, int z, bool a_IsSkyLightNibble = false)
{
if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1))
{
int Index = MakeIndexNoCheck(x, y, z);
return (a_Buffer[Index / 2] >> ((Index & 1) * 4)) & 0x0f;
size_t Index = (size_t)MakeIndexNoCheck(x, y, z);
if ((Index / 2) >= a_Buffer.size())
{
return (a_IsSkyLightNibble ? 0xff : 0);
}
return ExpandNibble(a_Buffer, Index);
}
ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!");
return 0;
}
static void SetNibble(NIBBLETYPE * a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble)
static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, int x, int y, int z)
{
if ((x < Width) && (x > -1) && (y < Height) && (y > -1) && (z < Width) && (z > -1))
{
int Index = MakeIndexNoCheck(x, y, z);
return (a_Buffer[(size_t)(Index / 2)] >> ((Index & 1) * 4)) & 0x0f;
}
ASSERT(!"cChunkDef::GetNibble(): coords out of chunk range!");
return 0;
}
static void SetNibble(COMPRESSED_NIBBLETYPE & a_Buffer, int a_BlockIdx, NIBBLETYPE a_Nibble)
{
if ((a_BlockIdx < 0) || (a_BlockIdx >= NumBlocks))
{
ASSERT(!"cChunkDef::SetNibble(): index out of range!");
return;
}
a_Buffer[a_BlockIdx / 2] = static_cast<NIBBLETYPE>(
(a_Buffer[a_BlockIdx / 2] & (0xf0 >> ((a_BlockIdx & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((a_BlockIdx & 1) * 4)) // The nibble being set
);
if ((size_t)(a_BlockIdx / 2) >= a_Buffer.size())
{
a_Buffer.resize((size_t)((a_BlockIdx / 2) + 1));
}
a_Buffer[(size_t)(a_BlockIdx / 2)] = PackNibble(a_Buffer, (size_t)a_BlockIdx, a_Nibble);
}
static void SetNibble(NIBBLETYPE * a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble)
static void SetNibble(COMPRESSED_NIBBLETYPE & a_Buffer, int x, int y, int z, NIBBLETYPE a_Nibble)
{
if (
(x >= Width) || (x < 0) ||
@ -270,25 +297,33 @@ public:
return;
}
int Index = MakeIndexNoCheck(x, y, z);
a_Buffer[Index / 2] = static_cast<NIBBLETYPE>(
(a_Buffer[Index / 2] & (0xf0 >> ((Index & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((Index & 1) * 4)) // The nibble being set
size_t Index = (size_t)MakeIndexNoCheck(x, y, z);
if ((Index / 2) >= a_Buffer.size())
{
a_Buffer.resize(((Index / 2) + 1));
}
a_Buffer[(Index / 2)] = PackNibble(a_Buffer, Index, a_Nibble);
}
private:
inline static NIBBLETYPE PackNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, size_t a_Index, NIBBLETYPE a_Nibble)
{
return static_cast<NIBBLETYPE>(
(a_Buffer[a_Index / 2] & (0xf0 >> ((a_Index & 1) * 4))) | // The untouched nibble
((a_Nibble & 0x0f) << ((a_Index & 1) * 4)) // The nibble being set
);
}
inline static NIBBLETYPE GetNibble(const NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos )
inline static NIBBLETYPE ExpandNibble(const COMPRESSED_NIBBLETYPE & a_Buffer, size_t a_Index)
{
return GetNibble(a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z );
}
inline static void SetNibble(NIBBLETYPE * a_Buffer, const Vector3i & a_BlockPos, NIBBLETYPE a_Value )
{
SetNibble( a_Buffer, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Value );
return (a_Buffer[a_Index / 2] >> ((a_Index & 1) * 4)) & 0x0f;
}
} ;

View File

@ -346,9 +346,8 @@ void cChunkMap::BroadcastAttachEntity(const cEntity & a_Entity, const cEntity *
void cChunkMap::BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
int x, y, z, ChunkX, ChunkZ;
int x, z, ChunkX, ChunkZ;
x = a_BlockX;
y = a_BlockY;
z = a_BlockZ;
cChunkDef::BlockToChunk(x, z, ChunkX, ChunkZ);
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
@ -1146,9 +1145,8 @@ BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
// First check if it isn't queued in the m_FastSetBlockQueue:
{
int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
int ChunkX, ChunkY, ChunkZ;
int ChunkX, ChunkZ;
cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
ChunkY = 0;
cCSLock Lock(m_CSFastSetBlock);
for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr)
{
@ -1656,7 +1654,10 @@ void cChunkMap::AddEntity(cEntity * a_Entity)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_Entity->GetChunkX(), ZERO_CHUNK_Y, a_Entity->GetChunkZ());
if ((Chunk == NULL) || !Chunk->IsValid())
if (
(Chunk == NULL) || // Chunk not present at all
(!Chunk->IsValid() && !a_Entity->IsPlayer()) // Chunk present, but no valid data; players need to spawn in such chunks (#953)
)
{
LOGWARNING("Entity at %p (%s, ID %d) spawning in a non-existent chunk, the entity is lost.",
a_Entity, a_Entity->GetClass(), a_Entity->GetUniqueID()

View File

@ -186,6 +186,51 @@ void cClientHandle::GenerateOfflineUUID(void)
AString cClientHandle::FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2)
{
if (ShouldAppendChatPrefixes)
return Printf("%s[%s] %s", m_Color1.c_str(), a_ChatPrefixS.c_str(), m_Color2.c_str());
else
return Printf("%s", m_Color1.c_str());
}
AString cClientHandle::FormatMessageType(bool ShouldAppendChatPrefixes, eMessageType a_ChatPrefix, const AString &a_AdditionalData)
{
switch (a_ChatPrefix)
{
case mtCustom: return AString();
case mtFailure: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Rose, cChatColor::White);
case mtInformation: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Yellow, cChatColor::White);
case mtSuccess: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Green, cChatColor::White);
case mtWarning: return FormatChatPrefix(ShouldAppendChatPrefixes, "WARN", cChatColor::Rose, cChatColor::White);
case mtFatal: return FormatChatPrefix(ShouldAppendChatPrefixes, "FATAL", cChatColor::Red, cChatColor::White);
case mtDeath: return FormatChatPrefix(ShouldAppendChatPrefixes, "DEATH", cChatColor::Gray, cChatColor::White);
case mtJoin: return FormatChatPrefix(ShouldAppendChatPrefixes, "JOIN", cChatColor::Yellow, cChatColor::White);
case mtLeave: return FormatChatPrefix(ShouldAppendChatPrefixes, "LEAVE", cChatColor::Yellow, cChatColor::White);
case mtPrivateMessage:
{
if (ShouldAppendChatPrefixes)
{
return Printf("%s[MSG: %s] %s%s", cChatColor::LightBlue.c_str(), a_AdditionalData.c_str(), cChatColor::White.c_str(), cChatColor::Italic.c_str());
}
else
{
return Printf("%s: %s", a_AdditionalData.c_str(), cChatColor::LightBlue.c_str());
}
}
}
ASSERT(!"Unhandled chat prefix type!");
return AString();
}
AString cClientHandle::GenerateOfflineUUID(const AString & a_Username)
{
// Proper format for a version 3 UUID is:
@ -291,6 +336,11 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID)
// Send scoreboard data
World->GetScoreBoard().SendTo(*this);
// Delay the first ping until the client "settles down"
// This should fix #889, "BadCast exception, cannot convert bit to fm" error in client
cTimer t1;
m_LastPingTime = t1.GetNowTime() + 3000; // Send the first KeepAlive packet in 3 seconds
cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player);
}
@ -666,7 +716,7 @@ void cClientHandle::UnregisterPluginChannels(const AStringVector & a_ChannelList
void cClientHandle::HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length)
void cClientHandle::HandleCommandBlockMessage(const char * a_Data, size_t a_Length)
{
if (a_Length < 14)
{
@ -1037,7 +1087,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
{
HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
}
else if (ItemHandler->IsFood())
else if (ItemHandler->IsFood() && !m_Player->IsGameModeCreative())
{
if (m_Player->IsSatiated())
{
@ -1460,7 +1510,7 @@ void cClientHandle::HandleDisconnect(const AString & a_Reason)
{
LOGD("Received d/c packet from %s with reason \"%s\"", m_Username.c_str(), a_Reason.c_str());
cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, a_Reason);
cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, a_Reason);
m_HasSentDC = true;
Destroy();
@ -1608,7 +1658,7 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size)
{
// There is a queued overflow. Append to it, then send as much from its front as possible
m_OutgoingDataOverflow.append(a_Data, a_Size);
int CanFit = m_OutgoingData.GetFreeSpace();
size_t CanFit = m_OutgoingData.GetFreeSpace();
if (CanFit > 128)
{
// No point in moving the data over if it's not large enough - too much effort for too little an effect
@ -1849,7 +1899,7 @@ void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlock
void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
{
bool ShouldAppendChatPrefixes = true;
if (GetPlayer()->GetWorld() == NULL)
{
cWorld * World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
@ -1868,89 +1918,9 @@ void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefi
ShouldAppendChatPrefixes = false;
}
AString Message;
AString Message = FormatMessageType(ShouldAppendChatPrefixes, a_ChatPrefix, a_AdditionalData);
switch (a_ChatPrefix)
{
case mtCustom: break;
case mtFailure:
{
if (ShouldAppendChatPrefixes)
Message = Printf("%s[INFO] %s", cChatColor::Rose.c_str(), cChatColor::White.c_str());
else
Message = Printf("%s", cChatColor::Rose.c_str());
break;
}
case mtInformation:
{
if (ShouldAppendChatPrefixes)
Message = Printf("%s[INFO] %s", cChatColor::Yellow.c_str(), cChatColor::White.c_str());
else
Message = Printf("%s", cChatColor::Yellow.c_str());
break;
}
case mtSuccess:
{
if (ShouldAppendChatPrefixes)
Message = Printf("%s[INFO] %s", cChatColor::Green.c_str(), cChatColor::White.c_str());
else
Message = Printf("%s", cChatColor::Green.c_str());
break;
}
case mtWarning:
{
if (ShouldAppendChatPrefixes)
Message = Printf("%s[WARN] %s", cChatColor::Rose.c_str(), cChatColor::White.c_str());
else
Message = Printf("%s", cChatColor::Rose.c_str());
break;
}
case mtFatal:
{
if (ShouldAppendChatPrefixes)
Message = Printf("%s[FATAL] %s", cChatColor::Red.c_str(), cChatColor::White.c_str());
else
Message = Printf("%s", cChatColor::Red.c_str());
break;
}
case mtDeath:
{
if (ShouldAppendChatPrefixes)
Message = Printf("%s[DEATH] %s", cChatColor::Gray.c_str(), cChatColor::White.c_str());
else
Message = Printf("%s", cChatColor::Gray.c_str());
break;
}
case mtPrivateMessage:
{
if (ShouldAppendChatPrefixes)
Message = Printf("%s[MSG: %s] %s%s", cChatColor::LightBlue.c_str(), a_AdditionalData.c_str(), cChatColor::White.c_str(), cChatColor::Italic.c_str());
else
Message = Printf("%s: %s", a_AdditionalData.c_str(), cChatColor::LightBlue.c_str());
break;
}
case mtJoin:
{
if (ShouldAppendChatPrefixes)
Message = Printf("%s[JOIN] %s", cChatColor::Yellow.c_str(), cChatColor::White.c_str());
else
Message = Printf("%s", cChatColor::Yellow.c_str());
break;
}
case mtLeave:
{
if (ShouldAppendChatPrefixes)
Message = Printf("%s[LEAVE] %s", cChatColor::Yellow.c_str(), cChatColor::White.c_str());
else
Message = Printf("%s", cChatColor::Yellow.c_str());
break;
}
default: ASSERT(!"Unhandled chat prefix type!"); return;
}
Message.append(a_Message);
m_Protocol->SendChat(Message);
m_Protocol->SendChat(Message.append(a_Message));
}
@ -2719,9 +2689,9 @@ void cClientHandle::SocketClosed(void)
LOGD("Player %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str());
if (m_Username != "") // Ignore client pings
if (!m_Username.empty()) // Ignore client pings
{
cRoot::Get()->GetPluginManager()->CallHookDisconnect(m_Player, "Player disconnected");
cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected");
}
Destroy();

View File

@ -77,6 +77,11 @@ public:
This is used for the offline (non-auth) mode, when there's no UUID source.
Each username generates a unique and constant UUID, so that when the player reconnects with the same name, their UUID is the same. */
static AString GenerateOfflineUUID(const AString & a_Username); // tolua_export
/** Formats the type of message with the proper color and prefix for sending to the client. **/
AString FormatMessageType(bool ShouldAppendChatPrefixes, eMessageType a_ChatPrefix, const AString & a_AdditionalData);
AString FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2);
void Kick(const AString & a_Reason); // tolua_export
void Authenticate(const AString & a_Name, const AString & a_UUID); // Called by cAuthenticator when the user passes authentication
@ -379,7 +384,7 @@ private:
void UnregisterPluginChannels(const AStringVector & a_ChannelList);
/** Handles the "MC|AdvCdm" plugin message */
void HandleCommandBlockMessage(const char * a_Data, unsigned int a_Length);
void HandleCommandBlockMessage(const char * a_Data, size_t a_Length);
// cSocketThreads::cCallback overrides:
virtual void DataReceived (const char * a_Data, size_t a_Size) override; // Data is received from the client

View File

@ -1,509 +0,0 @@
// Crypto.cpp
// Implements classes that wrap the cryptographic code library
#include "Globals.h"
#include "Crypto.h"
#include "polarssl/pk.h"
/*
// Self-test the hash formatting for known values:
// sha1(Notch) : 4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48
// sha1(jeb_) : -7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1
// sha1(simon) : 88e16a1019277b15d58faf0541e11910eb756f6
class Test
{
public:
Test(void)
{
AString DigestNotch, DigestJeb, DigestSimon;
Byte Digest[20];
cSHA1Checksum Checksum;
Checksum.Update((const Byte *)"Notch", 5);
Checksum.Finalize(Digest);
cSHA1Checksum::DigestToJava(Digest, DigestNotch);
Checksum.Restart();
Checksum.Update((const Byte *)"jeb_", 4);
Checksum.Finalize(Digest);
cSHA1Checksum::DigestToJava(Digest, DigestJeb);
Checksum.Restart();
Checksum.Update((const Byte *)"simon", 5);
Checksum.Finalize(Digest);
cSHA1Checksum::DigestToJava(Digest, DigestSimon);
printf("Notch: \"%s\"\n", DigestNotch.c_str());
printf("jeb_: \"%s\"\n", DigestJeb.c_str());
printf("simon: \"%s\"\n", DigestSimon.c_str());
assert(DigestNotch == "4ed1f46bbe04bc756bcb17c0c7ce3e4632f06a48");
assert(DigestJeb == "-7c9d5b0044c130109a5d7b5fb5c317c02b4e28c1");
assert(DigestSimon == "88e16a1019277b15d58faf0541e11910eb756f6");
}
} test;
*/
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cRSAPrivateKey:
cRSAPrivateKey::cRSAPrivateKey(void)
{
rsa_init(&m_Rsa, RSA_PKCS_V15, 0);
InitRnd();
}
cRSAPrivateKey::cRSAPrivateKey(const cRSAPrivateKey & a_Other)
{
rsa_init(&m_Rsa, RSA_PKCS_V15, 0);
rsa_copy(&m_Rsa, &a_Other.m_Rsa);
InitRnd();
}
cRSAPrivateKey::~cRSAPrivateKey()
{
entropy_free(&m_Entropy);
rsa_free(&m_Rsa);
}
void cRSAPrivateKey::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);
}
bool cRSAPrivateKey::Generate(unsigned a_KeySizeBits)
{
if (rsa_gen_key(&m_Rsa, ctr_drbg_random, &m_Ctr_drbg, a_KeySizeBits, 65537) != 0)
{
// Key generation failed
return false;
}
return true;
}
AString cRSAPrivateKey::GetPubKeyDER(void)
{
class cPubKey
{
public:
cPubKey(rsa_context * a_Rsa) :
m_IsValid(false)
{
pk_init(&m_Key);
if (pk_init_ctx(&m_Key, pk_info_from_type(POLARSSL_PK_RSA)) != 0)
{
ASSERT(!"Cannot init PrivKey context");
return;
}
if (rsa_copy(pk_rsa(m_Key), a_Rsa) != 0)
{
ASSERT(!"Cannot copy PrivKey to PK context");
return;
}
m_IsValid = true;
}
~cPubKey()
{
if (m_IsValid)
{
pk_free(&m_Key);
}
}
operator pk_context * (void) { return &m_Key; }
protected:
bool m_IsValid;
pk_context m_Key;
} PkCtx(&m_Rsa);
unsigned char buf[3000];
int res = pk_write_pubkey_der(PkCtx, buf, sizeof(buf));
if (res < 0)
{
return AString();
}
return AString((const char *)(buf + sizeof(buf) - res), (size_t)res);
}
int cRSAPrivateKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength)
{
if (a_EncryptedLength < m_Rsa.len)
{
LOGD("%s: Invalid a_EncryptedLength: got %u, exp at least %u",
__FUNCTION__, (unsigned)a_EncryptedLength, (unsigned)(m_Rsa.len)
);
ASSERT(!"Invalid a_DecryptedMaxLength!");
return -1;
}
if (a_DecryptedMaxLength < m_Rsa.len)
{
LOGD("%s: Invalid a_DecryptedMaxLength: got %u, exp at least %u",
__FUNCTION__, (unsigned)a_EncryptedLength, (unsigned)(m_Rsa.len)
);
ASSERT(!"Invalid a_DecryptedMaxLength!");
return -1;
}
size_t DecryptedLength;
int res = rsa_pkcs1_decrypt(
&m_Rsa, ctr_drbg_random, &m_Ctr_drbg, RSA_PRIVATE, &DecryptedLength,
a_EncryptedData, a_DecryptedData, a_DecryptedMaxLength
);
if (res != 0)
{
return -1;
}
return (int)DecryptedLength;
}
int cRSAPrivateKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength)
{
if (a_EncryptedMaxLength < m_Rsa.len)
{
LOGD("%s: Invalid a_EncryptedMaxLength: got %u, exp at least %u",
__FUNCTION__, (unsigned)a_EncryptedMaxLength, (unsigned)(m_Rsa.len)
);
ASSERT(!"Invalid a_DecryptedMaxLength!");
return -1;
}
if (a_PlainLength < m_Rsa.len)
{
LOGD("%s: Invalid a_PlainLength: got %u, exp at least %u",
__FUNCTION__, (unsigned)a_PlainLength, (unsigned)(m_Rsa.len)
);
ASSERT(!"Invalid a_PlainLength!");
return -1;
}
int res = rsa_pkcs1_encrypt(
&m_Rsa, ctr_drbg_random, &m_Ctr_drbg, RSA_PRIVATE,
a_PlainLength, a_PlainData, a_EncryptedData
);
if (res != 0)
{
return -1;
}
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);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cAESCFBDecryptor:
cAESCFBDecryptor::cAESCFBDecryptor(void) :
m_IVOffset(0),
m_IsValid(false)
{
}
cAESCFBDecryptor::~cAESCFBDecryptor()
{
// Clear the leftover in-memory data, so that they can't be accessed by a backdoor
memset(&m_Aes, 0, sizeof(m_Aes));
}
void cAESCFBDecryptor::Init(const Byte a_Key[16], const Byte a_IV[16])
{
ASSERT(!IsValid()); // Cannot Init twice
memcpy(m_IV, a_IV, 16);
aes_setkey_enc(&m_Aes, a_Key, 128);
m_IsValid = true;
}
void cAESCFBDecryptor::ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length)
{
ASSERT(IsValid()); // Must Init() first
// PolarSSL doesn't support AES-CFB8, need to implement it manually:
for (size_t i = 0; i < a_Length; i++)
{
Byte Buffer[sizeof(m_IV)];
aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer);
for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++)
{
m_IV[idx] = m_IV[idx + 1];
}
m_IV[sizeof(m_IV) - 1] = a_EncryptedIn[i];
a_DecryptedOut[i] = a_EncryptedIn[i] ^ Buffer[0];
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cAESCFBEncryptor:
cAESCFBEncryptor::cAESCFBEncryptor(void) :
m_IVOffset(0),
m_IsValid(false)
{
}
cAESCFBEncryptor::~cAESCFBEncryptor()
{
// Clear the leftover in-memory data, so that they can't be accessed by a backdoor
memset(&m_Aes, 0, sizeof(m_Aes));
}
void cAESCFBEncryptor::Init(const Byte a_Key[16], const Byte a_IV[16])
{
ASSERT(!IsValid()); // Cannot Init twice
ASSERT(m_IVOffset == 0);
memcpy(m_IV, a_IV, 16);
aes_setkey_enc(&m_Aes, a_Key, 128);
m_IsValid = true;
}
void cAESCFBEncryptor::ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length)
{
ASSERT(IsValid()); // Must Init() first
// PolarSSL doesn't do AES-CFB8, so we need to implement it ourselves:
for (size_t i = 0; i < a_Length; i++)
{
Byte Buffer[sizeof(m_IV)];
aes_crypt_ecb(&m_Aes, AES_ENCRYPT, m_IV, Buffer);
for (size_t idx = 0; idx < sizeof(m_IV) - 1; idx++)
{
m_IV[idx] = m_IV[idx + 1];
}
a_EncryptedOut[i] = a_PlainIn[i] ^ Buffer[0];
m_IV[sizeof(m_IV) - 1] = a_EncryptedOut[i];
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cSHA1Checksum:
cSHA1Checksum::cSHA1Checksum(void) :
m_DoesAcceptInput(true)
{
sha1_starts(&m_Sha1);
}
void cSHA1Checksum::Update(const Byte * a_Data, size_t a_Length)
{
ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed
sha1_update(&m_Sha1, a_Data, a_Length);
}
void cSHA1Checksum::Finalize(cSHA1Checksum::Checksum & a_Output)
{
ASSERT(m_DoesAcceptInput); // Not Finalize()-d yet, or Restart()-ed
sha1_finish(&m_Sha1, a_Output);
m_DoesAcceptInput = false;
}
void cSHA1Checksum::DigestToJava(const Checksum & a_Digest, AString & a_Out)
{
Checksum Digest;
memcpy(Digest, a_Digest, sizeof(Digest));
bool IsNegative = (Digest[0] >= 0x80);
if (IsNegative)
{
// Two's complement:
bool carry = true; // Add one to the whole number
for (int i = 19; i >= 0; i--)
{
Digest[i] = ~Digest[i];
if (carry)
{
carry = (Digest[i] == 0xff);
Digest[i]++;
}
}
}
a_Out.clear();
a_Out.reserve(40);
for (int i = 0; i < 20; i++)
{
AppendPrintf(a_Out, "%02x", Digest[i]);
}
while ((a_Out.length() > 0) && (a_Out[0] == '0'))
{
a_Out.erase(0, 1);
}
if (IsNegative)
{
a_Out.insert(0, "-");
}
}
void cSHA1Checksum::Restart(void)
{
sha1_starts(&m_Sha1);
m_DoesAcceptInput = true;
}

View File

@ -1,198 +0,0 @@
// Crypto.h
// Declares classes that wrap the cryptographic code library
#pragma once
#include "polarssl/rsa.h"
#include "polarssl/aes.h"
#include "polarssl/entropy.h"
#include "polarssl/ctr_drbg.h"
#include "polarssl/sha1.h"
#include "polarssl/pk.h"
/** Encapsulates an RSA private key used in PKI cryptography */
class cRSAPrivateKey
{
public:
/** Creates a new empty object, the key is not assigned */
cRSAPrivateKey(void);
/** Deep-copies the key from a_Other */
cRSAPrivateKey(const cRSAPrivateKey & a_Other);
~cRSAPrivateKey();
/** Generates a new key within this object, with the specified size in bits.
Returns true on success, false on failure. */
bool Generate(unsigned a_KeySizeBits = 1024);
/** Returns the public key part encoded in ASN1 DER encoding */
AString GetPubKeyDER(void);
/** Decrypts the data using RSAES-PKCS#1 algorithm.
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 RSAES-PKCS#1 algorithm.
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:
rsa_context m_Rsa;
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);
} ;
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
{
public:
Byte test;
cAESCFBDecryptor(void);
~cAESCFBDecryptor();
/** Initializes the decryptor with the specified Key / IV */
void Init(const Byte a_Key[16], const Byte a_IV[16]);
/** Decrypts a_Length bytes of the encrypted data; produces a_Length output bytes */
void ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length);
/** Returns true if the object has been initialized with the Key / IV */
bool IsValid(void) const { return m_IsValid; }
protected:
aes_context m_Aes;
/** The InitialVector, used by the CFB mode decryption */
Byte m_IV[16];
/** Current offset in the m_IV, used by the CFB mode decryption */
size_t m_IVOffset;
/** Indicates whether the object has been initialized with the Key / IV */
bool m_IsValid;
} ;
/** Encrypts data using the AES / CFB (128) algorithm */
class cAESCFBEncryptor
{
public:
cAESCFBEncryptor(void);
~cAESCFBEncryptor();
/** Initializes the decryptor with the specified Key / IV */
void Init(const Byte a_Key[16], const Byte a_IV[16]);
/** Encrypts a_Length bytes of the plain data; produces a_Length output bytes */
void ProcessData(Byte * a_EncryptedOut, const Byte * a_PlainIn, size_t a_Length);
/** Returns true if the object has been initialized with the Key / IV */
bool IsValid(void) const { return m_IsValid; }
protected:
aes_context m_Aes;
/** The InitialVector, used by the CFB mode encryption */
Byte m_IV[16];
/** Current offset in the m_IV, used by the CFB mode encryption */
size_t m_IVOffset;
/** Indicates whether the object has been initialized with the Key / IV */
bool m_IsValid;
} ;
/** Calculates a SHA1 checksum for data stream */
class cSHA1Checksum
{
public:
typedef Byte Checksum[20]; // The type used for storing the checksum
cSHA1Checksum(void);
/** Adds the specified data to the checksum */
void Update(const Byte * a_Data, size_t a_Length);
/** Calculates and returns the final checksum */
void Finalize(Checksum & a_Output);
/** Returns true if the object is accepts more input data, false if Finalize()-d (need to Restart()) */
bool DoesAcceptInput(void) const { return m_DoesAcceptInput; }
/** Converts a raw 160-bit SHA1 digest into a Java Hex representation
According to http://wiki.vg/wiki/index.php?title=Protocol_Encryption&oldid=2802
*/
static void DigestToJava(const Checksum & a_Digest, AString & a_JavaOut);
/** Clears the current context and start a new checksum calculation */
void Restart(void);
protected:
/** True if the object is accepts more input data, false if Finalize()-d (need to Restart()) */
bool m_DoesAcceptInput;
sha1_context m_Sha1;
} ;

View File

@ -99,7 +99,7 @@ public:
/** Returns true if there are no enchantments */
bool IsEmpty(void) const;
/** Converts enchantment name to the numeric representation; returns -1 if enchantment name not found; case insensitive */
/** Converts enchantment name or ID (number in string) to the numeric representation; returns -1 if enchantment name not found; case insensitive */
static int StringToEnchantmentID(const AString & a_EnchantmentName);
/** Returns true if a_Other contains exactly the same enchantments and levels */

View File

@ -0,0 +1,193 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Player.h"
#include "ArrowEntity.h"
#include "../Chunk.h"
cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5),
m_PickupState(psNoPickup),
m_DamageCoeff(2),
m_IsCritical(false),
m_Timer(0),
m_HitGroundTimer(0),
m_bIsCollected(false),
m_HitBlockPos(Vector3i(0, 0, 0))
{
SetSpeed(a_Speed);
SetMass(0.1);
SetYawFromSpeed();
SetPitchFromSpeed();
LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}",
m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(),
GetYaw(), GetPitch()
);
}
cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) :
super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5),
m_PickupState(psInSurvivalOrCreative),
m_DamageCoeff(2),
m_IsCritical((a_Force >= 1)),
m_Timer(0),
m_HitGroundTimer(0),
m_HasTeleported(false),
m_bIsCollected(false),
m_HitBlockPos(0, 0, 0)
{
}
bool cArrowEntity::CanPickup(const cPlayer & a_Player) const
{
switch (m_PickupState)
{
case psNoPickup: return false;
case psInSurvivalOrCreative: return (a_Player.IsGameModeSurvival() || a_Player.IsGameModeCreative());
case psInCreative: return a_Player.IsGameModeCreative();
}
ASSERT(!"Unhandled pickup state");
return false;
}
void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
if (a_HitFace == BLOCK_FACE_NONE) { return; }
super::OnHitSolidBlock(a_HitPos, a_HitFace);
int a_X = (int)a_HitPos.x, a_Y = (int)a_HitPos.y, a_Z = (int)a_HitPos.z;
switch (a_HitFace)
{
case BLOCK_FACE_XM: // Strangely, bounding boxes / block tracers return the actual block for these two directions, so AddFace not needed
case BLOCK_FACE_YM:
{
break;
}
default: AddFaceDirection(a_X, a_Y, a_Z, a_HitFace, true);
}
m_HitBlockPos = Vector3i(a_X, a_Y, a_Z);
// Broadcast arrow hit sound
m_World->BroadcastSoundEffect("random.bowhit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
}
void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer() && !a_EntityHit.IsBoat())
{
// Not an entity that interacts with an arrow
return;
}
int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5);
if (m_IsCritical)
{
Damage += m_World->GetTickRandomNumber(Damage / 2 + 2);
}
a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1);
// Broadcast successful hit sound
m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
Destroy();
}
void cArrowEntity::CollectedBy(cPlayer * a_Dest)
{
if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest)))
{
int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW);
if (NumAdded > 0) // Only play effects if there was space in inventory
{
m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest);
// Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
m_bIsCollected = true;
}
}
}
void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
m_Timer += a_Dt;
if (m_bIsCollected)
{
if (m_Timer > 500.f) // 0.5 seconds
{
Destroy();
return;
}
}
else if (m_Timer > 1000 * 60 * 5) // 5 minutes
{
Destroy();
return;
}
if (m_IsInGround)
{
// When an arrow hits, the client doesn't think its in the ground and keeps on moving, IF BroadcastMovementUpdate() and TeleportEntity was called during flight, AT ALL
// Fix is to simply not sync with the client and send a teleport to confirm pos after arrow has stabilised (around 1 sec after landing)
// We can afford to do this because xoft's algorithm for trajectory is near perfect, so things are pretty close anyway without sync
// Besides, this seems to be what the vanilla server does, note how arrows teleport half a second after they hit to the server position
if (!m_HasTeleported) // Sent a teleport already, don't do again
{
if (m_HitGroundTimer > 1000.f) // Send after a second, could be less, but just in case
{
m_World->BroadcastTeleportEntity(*this);
m_HasTeleported = true;
}
else
{
m_HitGroundTimer += a_Dt;
}
}
int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width;
int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width;
cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
if (Chunk == NULL)
{
// Inside an unloaded chunk, abort
return;
}
if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed?
{
m_IsInGround = false; // Yes, begin simulating physics again
}
}
}

View File

@ -0,0 +1,96 @@
//
// ArrowEntity.h
//
#pragma once
#include "ProjectileEntity.h"
// tolua_begin
class cArrowEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
/// Determines when the arrow can be picked up (depending on player gamemode). Corresponds to the MCA file "pickup" field
enum ePickupState
{
psNoPickup = 0,
psInSurvivalOrCreative = 1,
psInCreative = 2,
} ;
// tolua_end
CLASS_PROTODEF(cArrowEntity);
/// Creates a new arrow with psNoPickup state and default damage modifier coeff
cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
/// Creates a new arrow as shot by a player, initializes it from the player object
cArrowEntity(cPlayer & a_Player, double a_Force);
// tolua_begin
/// Returns whether the arrow can be picked up by players
ePickupState GetPickupState(void) const { return m_PickupState; }
/// Sets a new pickup state
void SetPickupState(ePickupState a_PickupState) { m_PickupState = a_PickupState; }
/// Returns the damage modifier coeff.
double GetDamageCoeff(void) const { return m_DamageCoeff; }
/// Sets the damage modifier coeff
void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; }
/// Returns true if the specified player can pick the arrow up
bool CanPickup(const cPlayer & a_Player) const;
/// Returns true if the arrow is set as critical
bool IsCritical(void) const { return m_IsCritical; }
/// Sets the IsCritical flag
void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; }
// tolua_end
protected:
/// Determines when the arrow can be picked up by players
ePickupState m_PickupState;
/// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow
double m_DamageCoeff;
/// If true, the arrow deals more damage
bool m_IsCritical;
/// Timer for pickup collection animation or five minute timeout
float m_Timer;
/// Timer for client arrow position confirmation via TeleportEntity
float m_HitGroundTimer;
// Whether the arrow has already been teleported into the proper position in the ground.
bool m_HasTeleported;
/// If true, the arrow is in the process of being collected - don't go to anyone else
bool m_bIsCollected;
/// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air
Vector3i m_HitBlockPos;
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
virtual void CollectedBy(cPlayer * a_Player) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
}; // tolua_export

View File

@ -33,9 +33,12 @@ void cBoat::SpawnOn(cClientHandle & a_ClientHandle)
void cBoat::DoTakeDamage(TakeDamageInfo & TDI)
bool cBoat::DoTakeDamage(TakeDamageInfo & TDI)
{
super::DoTakeDamage(TDI);
if (!super::DoTakeDamage(TDI))
{
return false;
}
if (GetHealth() == 0)
{
@ -50,6 +53,7 @@ void cBoat::DoTakeDamage(TakeDamageInfo & TDI)
}
Destroy(true);
}
return true;
}

View File

@ -26,7 +26,7 @@ public:
// cEntity overrides:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
virtual void DoTakeDamage(TakeDamageInfo & TDI) override;
virtual bool DoTakeDamage(TakeDamageInfo & TDI) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void HandleSpeedFromAttachee(float a_Forward, float a_Sideways) override;

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(Entities ${SOURCE})

View File

@ -47,6 +47,8 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_TicksSinceLastVoidDamage(0)
, m_IsSwimming(false)
, m_IsSubmerged(false)
, m_AirLevel(0)
, m_AirTickTimer(0)
, m_HeadYaw(0.0)
, m_Rot(0.0, 0.0, 0.0)
, m_Pos(a_X, a_Y, a_Z)
@ -54,6 +56,7 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_Mass (0.001) // Default 1g
, m_Width(a_Width)
, m_Height(a_Height)
, m_InvulnerableTicks(0)
{
cCSLock Lock(m_CSCount);
m_EntityCount++;
@ -288,17 +291,23 @@ void cEntity::SetPitchFromSpeed(void)
void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
bool cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
{
if (cRoot::Get()->GetPluginManager()->CallHookTakeDamage(*this, a_TDI))
{
return;
return false;
}
if (m_Health <= 0)
{
// Can't take damage if already dead
return;
return false;
}
if (m_InvulnerableTicks > 0)
{
// Entity is invulnerable
return false;
}
if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer()))
@ -320,17 +329,49 @@ void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
m_Health = 0;
}
if (IsMob() || IsPlayer()) // Knockback for only players and mobs
if ((IsMob() || IsPlayer()) && (a_TDI.Attacker != NULL)) // Knockback for only players and mobs
{
AddSpeed(a_TDI.Knockback * 2);
int KnockbackLevel = 0;
if (a_TDI.Attacker->GetEquippedWeapon().m_ItemType == E_ITEM_BOW)
{
KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchPunch);
}
else
{
KnockbackLevel = a_TDI.Attacker->GetEquippedWeapon().m_Enchantments.GetLevel(cEnchantments::enchKnockback);
}
Vector3d additionalSpeed(0, 0, 0);
switch (KnockbackLevel)
{
case 1:
{
additionalSpeed.Set(5, .3, 5);
break;
}
case 2:
{
additionalSpeed.Set(8, .3, 8);
break;
}
default:
{
additionalSpeed.Set(2, .3, 2);
break;
}
}
AddSpeed(a_TDI.Knockback * additionalSpeed);
}
m_World->BroadcastEntityStatus(*this, esGenericHurt);
m_InvulnerableTicks = 10;
if (m_Health <= 0)
{
KilledBy(a_TDI.Attacker);
}
return true;
}
@ -375,11 +416,8 @@ int cEntity::GetRawDamageAgainst(const cEntity & a_Receiver)
int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage)
bool cEntity::ArmorCoversAgainst(eDamageType a_DamageType)
{
// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
// Filter out damage types that are not protected by armor:
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Effects as of 2012_12_20
switch (a_DamageType)
{
@ -394,9 +432,34 @@ int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_Dama
case dtLightning:
case dtPlugin:
{
return 0;
return false;
}
case dtAttack:
case dtArrowAttack:
case dtCactusContact:
case dtLavaContact:
case dtFireContact:
case dtEnderPearl:
case dtExplosion:
{
return true;
}
}
ASSERT(!"Invalid damage type!");
return false;
}
int cEntity::GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_Damage)
{
// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
// Filter out damage types that are not protected by armor:
if (!ArmorCoversAgainst(a_DamageType)) return 0;
// Add up all armor points:
// Ref.: http://www.minecraftwiki.net/wiki/Armor#Defense_points as of 2012_12_20
@ -505,6 +568,11 @@ void cEntity::SetHealth(int a_Health)
void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
if (m_InvulnerableTicks > 0)
{
m_InvulnerableTicks--;
}
if (m_AttachedTo != NULL)
{
if ((m_Pos - m_AttachedTo->GetPosition()).Length() > 0.5)
@ -780,14 +848,12 @@ void cEntity::TickBurning(cChunk & a_Chunk)
// Remember the current burning state:
bool HasBeenBurning = (m_TicksLeftBurning > 0);
if (GetWorld()->GetWeather() == eWeather_Rain)
if (m_World->IsWeatherWet())
{
if (HasBeenBurning)
if (POSY_TOINT > m_World->GetHeight(POSX_TOINT, POSZ_TOINT))
{
m_TicksLeftBurning = 0;
OnFinishedBurning();
}
return;
}
}
// Do the burning damage:

View File

@ -262,14 +262,19 @@ public:
// tolua_end
/// Makes this entity take damage specified in the a_TDI. The TDI is sent through plugins first, then applied
virtual void DoTakeDamage(TakeDamageInfo & a_TDI);
/** Makes this entity take damage specified in the a_TDI.
The TDI is sent through plugins first, then applied.
If it returns false, the entity hasn't receive any damage. */
virtual bool DoTakeDamage(TakeDamageInfo & a_TDI);
// tolua_begin
/// Returns the hitpoints that this pawn can deal to a_Receiver using its equipped items
virtual int GetRawDamageAgainst(const cEntity & a_Receiver);
/** Returns whether armor will protect against the passed damage type **/
virtual bool ArmorCoversAgainst(eDamageType a_DamageType);
/// Returns the hitpoints out of a_RawDamage that the currently equipped armor would cover
virtual int GetArmorCoverAgainst(const cEntity * a_Attacker, eDamageType a_DamageType, int a_RawDamage);
@ -391,6 +396,12 @@ public:
virtual bool IsSubmerged(void) const{ return m_IsSubmerged; }
/** Gets remaining air of a monster */
int GetAirLevel(void) const { return m_AirLevel; }
/** Gets the invulnerable ticks from the entity */
int GetInvulnerableTicks(void) const { return m_InvulnerableTicks; }
/** Set the invulnerable ticks from the entity */
void SetInvulnerableTicks(int a_InvulnerableTicks) { m_InvulnerableTicks = a_InvulnerableTicks; }
// tolua_end
@ -479,29 +490,33 @@ protected:
int m_AirTickTimer;
private:
// Measured in degrees, [-180, +180)
/** Measured in degrees, [-180, +180) */
double m_HeadYaw;
// Measured in meter/second (m/s)
/** Measured in meter/second (m/s) */
Vector3d m_Speed;
// Measured in degrees, [-180, +180)
/** Measured in degrees, [-180, +180) */
Vector3d m_Rot;
/// Position of the entity's XZ center and Y bottom
/** Position of the entity's XZ center and Y bottom */
Vector3d m_Pos;
// Measured in meter / second
/** Measured in meter / second */
Vector3d m_WaterSpeed;
// Measured in Kilograms (Kg)
/** Measured in Kilograms (Kg) */
double m_Mass;
/// Width of the entity, in the XZ plane. Since entities are represented as cylinders, this is more of a diameter.
/** Width of the entity, in the XZ plane. Since entities are represented as cylinders, this is more of a diameter. */
double m_Width;
/// Height of the entity (Y axis)
/** Height of the entity (Y axis) */
double m_Height;
/** If a player hit a entity, the entity receive a invulnerable of 10 ticks.
While this ticks, a player can't hit this entity. */
int m_InvulnerableTicks;
} ; // tolua_export
typedef std::list<cEntity *> cEntityList;

View File

@ -0,0 +1,27 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "ExpBottleEntity.h"
#include "../World.h"
cExpBottleEntity::cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
}
void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
// Spawn an experience orb with a reward between 3 and 11.
m_World->BroadcastSoundParticleEffect(2002, POSX_TOINT, POSY_TOINT, POSZ_TOINT, 0);
m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8));
Destroy();
}

View File

@ -0,0 +1,33 @@
//
// ExpBottleEntity.h
//
#pragma once
#include "ProjectileEntity.h"
// tolua_begin
class cExpBottleEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cExpBottleEntity);
cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
}; // tolua_export

View File

@ -87,7 +87,9 @@ void cFallingBlock::Tick(float a_Dt, cChunk & a_Chunk)
AddSpeedY(MilliDt * -9.8f);
AddPosition(GetSpeed() * MilliDt);
if ((GetSpeedX() != 0) || (GetSpeedZ() != 0))
// If not static (One billionth precision) broadcast movement.
static const float epsilon = 0.000000001f;
if ((fabs(GetSpeedX()) > epsilon) || (fabs(GetSpeedZ()) > epsilon))
{
BroadcastMovementUpdate();
}

View File

@ -0,0 +1,50 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "FireChargeEntity.h"
#include "../World.h"
cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkFireCharge, a_Creator, a_X, a_Y, a_Z, 0.3125, 0.3125)
{
SetSpeed(a_Speed);
SetGravity(0);
}
void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ)
{
if (m_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR)
{
m_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 1);
}
}
void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
}
void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
// TODO: Some entities are immune to hits
a_EntityHit.StartBurning(5 * 20); // 5 seconds of burning
}

View File

@ -0,0 +1,36 @@
//
// FireChargeEntity.h
//
#pragma once
#include "ProjectileEntity.h"
// tolua_begin
class cFireChargeEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cFireChargeEntity);
cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
void Explode(int a_BlockX, int a_BlockY, int a_BlockZ);
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
} ; // tolua_export

View File

@ -0,0 +1,73 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "FireworkEntity.h"
#include "../World.h"
#include "../Chunk.h"
cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item) :
super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25),
m_ExplodeTimer(0),
m_FireworkItem(a_Item)
{
}
void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
int PosY = POSY_TOINT;
if ((PosY < 0) || (PosY >= cChunkDef::Height))
{
goto setspeed;
}
if (m_IsInGround)
{
if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) == E_BLOCK_AIR)
{
m_IsInGround = false;
}
else
{
return;
}
}
else
{
if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) != E_BLOCK_AIR)
{
OnHitSolidBlock(GetPosition(), BLOCK_FACE_YM);
return;
}
}
setspeed:
AddSpeedY(1);
AddPosition(GetSpeed() * (a_Dt / 1000));
}
void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
if (m_ExplodeTimer == m_FireworkItem.m_FireworkItem.m_FlightTimeInTicks)
{
m_World->BroadcastEntityStatus(*this, esFireworkExploding);
Destroy();
}
m_ExplodeTimer++;
}

View File

@ -0,0 +1,40 @@
//
// FireworkEntity.h
//
#pragma once
#include "ProjectileEntity.h"
// tolua_begin
class cFireworkEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cFireworkEntity);
cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item);
const cItem & GetItem(void) const { return m_FireworkItem; }
protected:
// cProjectileEntity overrides:
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
private:
int m_ExplodeTimer;
cItem m_FireworkItem;
}; // tolua_export

View File

@ -0,0 +1,44 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "GhastFireballEntity.h"
#include "../World.h"
cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkGhastFireball, a_Creator, a_X, a_Y, a_Z, 1, 1)
{
SetSpeed(a_Speed);
SetGravity(0);
}
void cGhastFireballEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ)
{
m_World->DoExplosionAt(1, a_BlockX, a_BlockY, a_BlockZ, true, esGhastFireball, this);
}
void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
}
void cGhastFireballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
}

View File

@ -0,0 +1,38 @@
//
// GhastFireballEntity.h
//
#pragma once
#include "ProjectileEntity.h"
// tolua_begin
class cGhastFireballEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cGhastFireballEntity);
cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
void Explode(int a_BlockX, int a_BlockY, int a_BlockZ);
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// TODO: Deflecting the fireballs by arrow- or sword- hits
} ; // tolua_export

View File

@ -902,18 +902,21 @@ bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
void cMinecart::DoTakeDamage(TakeDamageInfo & TDI)
bool cMinecart::DoTakeDamage(TakeDamageInfo & TDI)
{
if ((TDI.Attacker != NULL) && TDI.Attacker->IsPlayer() && ((cPlayer *)TDI.Attacker)->IsGameModeCreative())
{
Destroy();
TDI.FinalDamage = GetMaxHealth(); // Instant hit for creative
super::DoTakeDamage(TDI);
return; // No drops for creative
SetInvulnerableTicks(0);
return super::DoTakeDamage(TDI); // No drops for creative
}
m_LastDamage = TDI.FinalDamage;
super::DoTakeDamage(TDI);
if (!super::DoTakeDamage(TDI))
{
return false;
}
m_World->BroadcastEntityMetadata(*this);
@ -952,12 +955,13 @@ void cMinecart::DoTakeDamage(TakeDamageInfo & TDI)
default:
{
ASSERT(!"Unhandled minecart type when spawning pickup!");
return;
return true;
}
}
m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY(), GetPosZ());
}
return true;
}

View File

@ -36,7 +36,7 @@ public:
// cEntity overrides:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
virtual void DoTakeDamage(TakeDamageInfo & TDI) override;
virtual bool DoTakeDamage(TakeDamageInfo & TDI) override;
virtual void Destroyed() override;
int LastDamage(void) const { return m_LastDamage; }

View File

@ -802,14 +802,14 @@ void cPlayer::SetFlying(bool a_IsFlying)
void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
bool cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
{
if ((a_TDI.DamageType != dtInVoid) && (a_TDI.DamageType != dtPlugin))
{
if (IsGameModeCreative())
{
// No damage / health in creative mode if not void or plugin damage
return;
return false;
}
}
@ -822,17 +822,19 @@ void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
if (!m_Team->AllowsFriendlyFire())
{
// Friendly fire is disabled
return;
return false;
}
}
}
super::DoTakeDamage(a_TDI);
// Any kind of damage adds food exhaustion
AddFoodExhaustion(0.3f);
SendHealth();
if (super::DoTakeDamage(a_TDI))
{
// Any kind of damage adds food exhaustion
AddFoodExhaustion(0.3f);
SendHealth();
return true;
}
return false;
}
@ -891,6 +893,7 @@ void cPlayer::KilledBy(cEntity * a_Killer)
void cPlayer::Respawn(void)
{
m_Health = GetMaxHealth();
SetInvulnerableTicks(20);
// Reset food level:
m_FoodLevel = MAX_FOOD_LEVEL;

View File

@ -495,7 +495,7 @@ protected:
virtual void Destroyed(void);
/** Filters out damage for creative mode/friendly fire */
virtual void DoTakeDamage(TakeDamageInfo & TDI) override;
virtual bool DoTakeDamage(TakeDamageInfo & TDI) override;
/** Stops players from burning in creative mode */
virtual void TickBurning(cChunk & a_Chunk) override;

View File

@ -4,15 +4,24 @@
// Implements the cProjectileEntity class representing the common base class for projectiles, as well as individual projectile types
#include "Globals.h"
#include "../Bindings/PluginManager.h"
#include "ProjectileEntity.h"
#include "../ClientHandle.h"
#include "Player.h"
#include "../LineBlockTracer.h"
#include "../BoundingBox.h"
#include "../ChunkMap.h"
#include "../Chunk.h"
#include "ArrowEntity.h"
#include "ThrownEggEntity.h"
#include "ThrownEnderPearlEntity.h"
#include "ExpBottleEntity.h"
#include "ThrownSnowballEntity.h"
#include "FireChargeEntity.h"
#include "FireworkEntity.h"
#include "GhastFireballEntity.h"
@ -401,495 +410,3 @@ void cProjectileEntity::CollectedBy(cPlayer * a_Dest)
// Overriden in arrow
UNUSED(a_Dest);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cArrowEntity:
cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkArrow, a_Creator, a_X, a_Y, a_Z, 0.5, 0.5),
m_PickupState(psNoPickup),
m_DamageCoeff(2),
m_IsCritical(false),
m_Timer(0),
m_HitGroundTimer(0),
m_bIsCollected(false),
m_HitBlockPos(Vector3i(0, 0, 0))
{
SetSpeed(a_Speed);
SetMass(0.1);
SetYawFromSpeed();
SetPitchFromSpeed();
LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}",
m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(),
GetYaw(), GetPitch()
);
}
cArrowEntity::cArrowEntity(cPlayer & a_Player, double a_Force) :
super(pkArrow, &a_Player, a_Player.GetThrowStartPos(), a_Player.GetThrowSpeed(a_Force * 1.5 * 20), 0.5, 0.5),
m_PickupState(psInSurvivalOrCreative),
m_DamageCoeff(2),
m_IsCritical((a_Force >= 1)),
m_Timer(0),
m_HitGroundTimer(0),
m_bIsCollected(false),
m_HitBlockPos(0, 0, 0)
{
}
bool cArrowEntity::CanPickup(const cPlayer & a_Player) const
{
switch (m_PickupState)
{
case psNoPickup: return false;
case psInSurvivalOrCreative: return (a_Player.IsGameModeSurvival() || a_Player.IsGameModeCreative());
case psInCreative: return a_Player.IsGameModeCreative();
}
ASSERT(!"Unhandled pickup state");
return false;
}
void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
if (a_HitFace == BLOCK_FACE_NONE) { return; }
super::OnHitSolidBlock(a_HitPos, a_HitFace);
int a_X = (int)a_HitPos.x, a_Y = (int)a_HitPos.y, a_Z = (int)a_HitPos.z;
switch (a_HitFace)
{
case BLOCK_FACE_XM: // Strangely, bounding boxes / block tracers return the actual block for these two directions, so AddFace not needed
case BLOCK_FACE_YM:
{
break;
}
default: AddFaceDirection(a_X, a_Y, a_Z, a_HitFace, true);
}
m_HitBlockPos = Vector3i(a_X, a_Y, a_Z);
// Broadcast arrow hit sound
m_World->BroadcastSoundEffect("random.bowhit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
}
void cArrowEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
if (!a_EntityHit.IsMob() && !a_EntityHit.IsMinecart() && !a_EntityHit.IsPlayer() && !a_EntityHit.IsBoat())
{
// Not an entity that interacts with an arrow
return;
}
int Damage = (int)(GetSpeed().Length() / 20 * m_DamageCoeff + 0.5);
if (m_IsCritical)
{
Damage += m_World->GetTickRandomNumber(Damage / 2 + 2);
}
a_EntityHit.TakeDamage(dtRangedAttack, this, Damage, 1);
// Broadcast successful hit sound
m_World->BroadcastSoundEffect("random.successful_hit", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
Destroy();
}
void cArrowEntity::CollectedBy(cPlayer * a_Dest)
{
if ((m_IsInGround) && (!m_bIsCollected) && (CanPickup(*a_Dest)))
{
int NumAdded = a_Dest->GetInventory().AddItem(E_ITEM_ARROW);
if (NumAdded > 0) // Only play effects if there was space in inventory
{
m_World->BroadcastCollectPickup((const cPickup &)*this, *a_Dest);
// Also send the "pop" sound effect with a somewhat random pitch (fast-random using EntityID ;)
m_World->BroadcastSoundEffect("random.pop", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 0.5, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
m_bIsCollected = true;
}
}
}
void cArrowEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
m_Timer += a_Dt;
if (m_bIsCollected)
{
if (m_Timer > 500.f) // 0.5 seconds
{
Destroy();
return;
}
}
else if (m_Timer > 1000 * 60 * 5) // 5 minutes
{
Destroy();
return;
}
if (m_IsInGround)
{
// When an arrow hits, the client doesn't think its in the ground and keeps on moving, IF BroadcastMovementUpdate() and TeleportEntity was called during flight, AT ALL
// Fix is to simply not sync with the client and send a teleport to confirm pos after arrow has stabilised (around 1 sec after landing)
// We can afford to do this because xoft's algorithm for trajectory is near perfect, so things are pretty close anyway without sync
// Besides, this seems to be what the vanilla server does, note how arrows teleport half a second after they hit to the server position
if (m_HitGroundTimer != -1) // Sent a teleport already, don't do again
{
if (m_HitGroundTimer > 1000.f) // Send after a second, could be less, but just in case
{
m_World->BroadcastTeleportEntity(*this);
m_HitGroundTimer = -1;
}
else
{
m_HitGroundTimer += a_Dt;
}
}
int RelPosX = m_HitBlockPos.x - a_Chunk.GetPosX() * cChunkDef::Width;
int RelPosZ = m_HitBlockPos.z - a_Chunk.GetPosZ() * cChunkDef::Width;
cChunk * Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(RelPosX, RelPosZ);
if (Chunk == NULL)
{
// Inside an unloaded chunk, abort
return;
}
if (Chunk->GetBlock(RelPosX, m_HitBlockPos.y, RelPosZ) == E_BLOCK_AIR) // Block attached to was destroyed?
{
m_IsInGround = false; // Yes, begin simulating physics again
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cThrownEggEntity:
cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
}
void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
if (m_World->GetTickRandomNumber(7) == 1)
{
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
}
else if (m_World->GetTickRandomNumber(32) == 1)
{
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
}
Destroy();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cThrownEnderPearlEntity :
cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
}
void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
// Teleport the creator here, make them take 5 damage:
if (m_Creator != NULL)
{
// TODO: The coords might need some tweaking based on the block face
m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5);
m_Creator->TakeDamage(dtEnderPearl, this, 5, 0);
}
Destroy();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cThrownSnowballEntity :
cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
}
void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
Destroy();
}
void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
int TotalDamage = 0;
if (a_EntityHit.IsMob())
{
cMonster::eType MobType = ((cMonster &) a_EntityHit).GetMobType();
if (MobType == cMonster::mtBlaze)
{
TotalDamage = 3;
}
else if (MobType == cMonster::mtEnderDragon)
{
TotalDamage = 1;
}
}
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
Destroy(true);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cBottleOEnchantingEntity :
cExpBottleEntity::cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
}
void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
// Spawn an experience orb with a reward between 3 and 11.
m_World->BroadcastSoundParticleEffect(2002, POSX_TOINT, POSY_TOINT, POSZ_TOINT, 0);
m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8));
Destroy();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFireworkEntity :
cFireworkEntity::cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item) :
super(pkFirework, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25),
m_ExplodeTimer(0),
m_FireworkItem(a_Item)
{
}
void cFireworkEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
int RelX = POSX_TOINT - a_Chunk.GetPosX() * cChunkDef::Width;
int RelZ = POSZ_TOINT - a_Chunk.GetPosZ() * cChunkDef::Width;
int PosY = POSY_TOINT;
if ((PosY < 0) || (PosY >= cChunkDef::Height))
{
goto setspeed;
}
if (m_IsInGround)
{
if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) == E_BLOCK_AIR)
{
m_IsInGround = false;
}
else
{
return;
}
}
else
{
if (a_Chunk.GetBlock(RelX, POSY_TOINT + 1, RelZ) != E_BLOCK_AIR)
{
OnHitSolidBlock(GetPosition(), BLOCK_FACE_YM);
return;
}
}
setspeed:
AddSpeedY(1);
AddPosition(GetSpeed() * (a_Dt / 1000));
}
void cFireworkEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
if (m_ExplodeTimer == m_FireworkItem.m_FireworkItem.m_FlightTimeInTicks)
{
m_World->BroadcastEntityStatus(*this, esFireworkExploding);
Destroy();
}
m_ExplodeTimer++;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cGhastFireballEntity :
cGhastFireballEntity::cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkGhastFireball, a_Creator, a_X, a_Y, a_Z, 1, 1)
{
SetSpeed(a_Speed);
SetGravity(0);
}
void cGhastFireballEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ)
{
m_World->DoExplosionAt(1, a_BlockX, a_BlockY, a_BlockZ, true, esGhastFireball, this);
}
void cGhastFireballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
}
void cGhastFireballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFireChargeEntity :
cFireChargeEntity::cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkFireCharge, a_Creator, a_X, a_Y, a_Z, 0.3125, 0.3125)
{
SetSpeed(a_Speed);
SetGravity(0);
}
void cFireChargeEntity::Explode(int a_BlockX, int a_BlockY, int a_BlockZ)
{
if (m_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR)
{
m_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 1);
}
}
void cFireChargeEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
}
void cFireChargeEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
Destroy();
Explode((int)floor(a_HitPos.x), (int)floor(a_HitPos.y), (int)floor(a_HitPos.z));
// TODO: Some entities are immune to hits
a_EntityHit.StartBurning(5 * 20); // 5 seconds of burning
}

View File

@ -94,300 +94,4 @@ protected:
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
virtual void SpawnOn(cClientHandle & a_Client) override;
// tolua_begin
} ;
class cArrowEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
/// Determines when the arrow can be picked up (depending on player gamemode). Corresponds to the MCA file "pickup" field
enum ePickupState
{
psNoPickup = 0,
psInSurvivalOrCreative = 1,
psInCreative = 2,
} ;
// tolua_end
CLASS_PROTODEF(cArrowEntity);
/// Creates a new arrow with psNoPickup state and default damage modifier coeff
cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
/// Creates a new arrow as shot by a player, initializes it from the player object
cArrowEntity(cPlayer & a_Player, double a_Force);
// tolua_begin
/// Returns whether the arrow can be picked up by players
ePickupState GetPickupState(void) const { return m_PickupState; }
/// Sets a new pickup state
void SetPickupState(ePickupState a_PickupState) { m_PickupState = a_PickupState; }
/// Returns the damage modifier coeff.
double GetDamageCoeff(void) const { return m_DamageCoeff; }
/// Sets the damage modifier coeff
void SetDamageCoeff(double a_DamageCoeff) { m_DamageCoeff = a_DamageCoeff; }
/// Returns true if the specified player can pick the arrow up
bool CanPickup(const cPlayer & a_Player) const;
/// Returns true if the arrow is set as critical
bool IsCritical(void) const { return m_IsCritical; }
/// Sets the IsCritical flag
void SetIsCritical(bool a_IsCritical) { m_IsCritical = a_IsCritical; }
// tolua_end
protected:
/// Determines when the arrow can be picked up by players
ePickupState m_PickupState;
/// The coefficient applied to the damage that the arrow will deal, based on the bow enchantment. 2.0 for normal arrow
double m_DamageCoeff;
/// If true, the arrow deals more damage
bool m_IsCritical;
/// Timer for pickup collection animation or five minute timeout
float m_Timer;
/// Timer for client arrow position confirmation via TeleportEntity
float m_HitGroundTimer;
/// If true, the arrow is in the process of being collected - don't go to anyone else
bool m_bIsCollected;
/// Stores the block position that arrow is lodged into, sets m_IsInGround to false if it becomes air
Vector3i m_HitBlockPos;
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
virtual void CollectedBy(cPlayer * a_Player) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
// tolua_begin
} ;
class cThrownEggEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cThrownEggEntity);
cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// tolua_end
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
// tolua_begin
} ;
class cThrownEnderPearlEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cThrownEnderPearlEntity);
cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// tolua_end
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
// tolua_begin
} ;
class cThrownSnowballEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cThrownSnowballEntity);
cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// tolua_begin
} ;
class cExpBottleEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cExpBottleEntity);
cExpBottleEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
// tolua_begin
};
class cFireworkEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cFireworkEntity);
cFireworkEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const cItem & a_Item);
const cItem & GetItem(void) const { return m_FireworkItem; }
protected:
// cProjectileEntity overrides:
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
private:
int m_ExplodeTimer;
cItem m_FireworkItem;
// tolua_begin
};
class cGhastFireballEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cGhastFireballEntity);
cGhastFireballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
void Explode(int a_BlockX, int a_BlockY, int a_BlockZ);
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// TODO: Deflecting the fireballs by arrow- or sword- hits
// tolua_begin
} ;
class cFireChargeEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cFireChargeEntity);
cFireChargeEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
void Explode(int a_BlockX, int a_BlockY, int a_BlockZ);
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// tolua_begin
} ;
// tolua_end
} ; // tolua_export

View File

@ -0,0 +1,59 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "ThrownEggEntity.h"
#include "../World.h"
cThrownEggEntity::cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkEgg, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
}
void cThrownEggEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
TrySpawnChicken(a_HitPos);
Destroy();
}
void cThrownEggEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
int TotalDamage = 0;
// TODO: If entity is Ender Crystal, destroy it
TrySpawnChicken(a_HitPos);
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
Destroy(true);
}
void cThrownEggEntity::TrySpawnChicken(const Vector3d & a_HitPos)
{
if (m_World->GetTickRandomNumber(7) == 1)
{
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
}
else if (m_World->GetTickRandomNumber(32) == 1)
{
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
m_World->SpawnMob(a_HitPos.x, a_HitPos.y, a_HitPos.z, cMonster::mtChicken);
}
}

View File

@ -0,0 +1,37 @@
//
// ThrownEggEntity.h
//
#pragma once
#include "ProjectileEntity.h"
// tolua_begin
class cThrownEggEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cThrownEggEntity);
cThrownEggEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// Randomly decides whether to spawn a chicken where the egg lands.
void TrySpawnChicken(const Vector3d & a_HitPos);
} ; // tolua_export

View File

@ -0,0 +1,54 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "ThrownEnderPearlEntity.h"
cThrownEnderPearlEntity::cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkEnderPearl, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
}
void cThrownEnderPearlEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
// TODO: Tweak a_HitPos based on block face.
TeleportCreator(a_HitPos);
Destroy();
}
void cThrownEnderPearlEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
int TotalDamage = 0;
// TODO: If entity is Ender Crystal, destroy it
TeleportCreator(a_HitPos);
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
Destroy(true);
}
void cThrownEnderPearlEntity::TeleportCreator(const Vector3d & a_HitPos)
{
// Teleport the creator here, make them take 5 damage:
if (m_Creator != NULL)
{
m_Creator->TeleportToCoords(a_HitPos.x + 0.5, a_HitPos.y + 1.7, a_HitPos.z + 0.5);
m_Creator->TakeDamage(dtEnderPearl, this, 5, 0);
}
}

View File

@ -0,0 +1,37 @@
//
// ThrownEnderPearlEntity.h
//
#pragma once
#include "ProjectileEntity.h"
// tolua_begin
class cThrownEnderPearlEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cThrownEnderPearlEntity);
cThrownEnderPearlEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
// Teleports the creator where the ender pearl lands.
void TeleportCreator(const Vector3d & a_HitPos);
} ; // tolua_export

View File

@ -0,0 +1,48 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "ThrownSnowballEntity.h"
#include "../World.h"
cThrownSnowballEntity::cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed) :
super(pkSnowball, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
{
SetSpeed(a_Speed);
}
void cThrownSnowballEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace)
{
Destroy();
}
void cThrownSnowballEntity::OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
{
int TotalDamage = 0;
if (a_EntityHit.IsMob())
{
cMonster::eType MobType = ((cMonster &) a_EntityHit).GetMobType();
if (MobType == cMonster::mtBlaze)
{
TotalDamage = 3;
}
else if (MobType == cMonster::mtEnderDragon)
{
TotalDamage = 1;
}
}
// TODO: If entity is Ender Crystal, destroy it
a_EntityHit.TakeDamage(dtRangedAttack, this, TotalDamage, 1);
Destroy(true);
}

View File

@ -0,0 +1,34 @@
//
// ThrownSnowballEntity.h
//
#pragma once
#include "ProjectileEntity.h"
// tolua_begin
class cThrownSnowballEntity :
public cProjectileEntity
{
typedef cProjectileEntity super;
public:
// tolua_end
CLASS_PROTODEF(cThrownSnowballEntity);
cThrownSnowballEntity(cEntity * a_Creator, double a_X, double a_Y, double a_Z, const Vector3d & a_Speed);
protected:
// cProjectileEntity overrides:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) override;
virtual void OnHitEntity (cEntity & a_EntityHit, const Vector3d & a_HitPos) override;
} ; // tolua_export

View File

@ -91,7 +91,8 @@ int cFastRandom::m_SeedCounter = 0;
cFastRandom::cFastRandom(void) :
m_Seed(m_SeedCounter++)
m_Seed(m_SeedCounter++),
m_Counter(0)
{
}

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(Generating ${SOURCE})

View File

@ -283,7 +283,7 @@ void cPrefab::ParseCharMap(CharMap & a_CharMapOut, const char * a_CharMapDef)
if ((NumElements >= 3) && !CharDef[2].empty())
{
BlockMeta = (NIBBLETYPE)atoi(CharDef[2].c_str());
ASSERT((BlockMeta >= 0) && (BlockMeta <= 15));
ASSERT((BlockMeta <= 15));
}
a_CharMapOut[Src].m_BlockMeta = BlockMeta;
} // for itr - Lines[]

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(Generating_Prefabs ${SOURCE})

View File

@ -439,11 +439,11 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
// The data has been exported from the gallery Nether, area index 20, ID 162, created by STR_Warrior
{
// Size:
14, 9, 7, // SizeX = 14, SizeY = 9, SizeZ = 7
14, 11, 7, // SizeX = 14, SizeY = 11, SizeZ = 7
// Hitbox (relative to bounding box):
0, 0, 0, // MinX, MinY, MinZ
13, 18, 6, // MaxX, MaxY, MaxZ
13, 20, 6, // MaxX, MaxY, MaxZ
// Block definitions:
".: 0: 0\n" /* air */
@ -552,11 +552,33 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
/* * 01234567890123 */
/* 0 */ "mmmmmmmmhhhhhm"
/* 1 */ "mmmmmmhhh...hh"
/* 2 */ ".............h"
/* 3 */ ".............h"
/* 4 */ ".............h"
/* 2 */ "mm...........h"
/* 3 */ "mm...........h"
/* 4 */ "mm...........h"
/* 5 */ "mmmmmmhhh...hh"
/* 6 */ "mmmmmmmmhhhhhm",
/* 6 */ "mmmmmmmmhhhhhm"
// Level 9
/* z\x* 1111 */
/* * 01234567890123 */
/* 0 */ "mmmmmmmm.....m"
/* 1 */ "mmmmmm........"
/* 2 */ "mmmm.........."
/* 3 */ "mmmm.........."
/* 4 */ "mmmm.........."
/* 5 */ "mmmmmm........"
/* 6 */ "mmmmmmmm.....m"
// Level 10
/* z\x* 1111 */
/* * 01234567890123 */
/* 0 */ "mmmmmmmm.....m"
/* 1 */ "mmmmmm........"
/* 2 */ "mmmmmm........"
/* 3 */ "mmmmmm........"
/* 4 */ "mmmmmm........"
/* 5 */ "mmmmmm........"
/* 6 */ "mmmmmmmm.....m",
// Connectors:
"0: 0, 5, 3: 4\n" /* Type 0, direction X- */,
@ -3030,7 +3052,7 @@ const cPrefab::sDef g_NetherFortPrefabs[] =
false,
// DefaultWeight:
250,
1000,
// DepthWeight:
"0:0|2:0|4:0|6:0|8:0|10:0|12:0|14:0|16:0|18:0",

View File

@ -264,7 +264,21 @@ template class SizeChecker<UInt16, 2>;
// Same as assert but in all Self test builds
#ifdef SELF_TEST
#define assert_test(x) ( !!(x) || (assert(!#x), exit(1), 0))
#define assert_test(x) ( !!(x) || (assert(!#x), exit(1), 0))
#endif
// Allow both Older versions of MSVC and newer versions of everything use a shared_ptr:
// Note that we cannot typedef, because C++ doesn't allow (partial) templates to be typedeffed.
#if (defined(_MSC_VER) && (_MSC_VER < 1600))
// MSVC before 2010 doesn't have std::shared_ptr, but has std::tr1::shared_ptr, defined in <memory> included earlier
#define SharedPtr std::tr1::shared_ptr
#elif (defined(_MSC_VER) || (__cplusplus >= 201103L))
// C++11 has std::shared_ptr in <memory>, included earlier
#define SharedPtr std::shared_ptr
#else
// C++03 has std::tr1::shared_ptr in <tr1/memory>
#include <tr1/memory>
#define SharedPtr std::tr1::shared_ptr
#endif
@ -296,7 +310,7 @@ T Clamp(T a_Value, T a_Min, T a_Max)
#ifndef TOLUA_TEMPLATE_BIND
#define TOLUA_TEMPLATE_BIND(x)
#define TOLUA_TEMPLATE_BIND(x)
#endif

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(HTTPServer ${SOURCE})

View File

@ -64,7 +64,7 @@ public:
{
if (!IsValidItem(m_ItemType))
{
if (m_ItemType != E_BLOCK_AIR)
if ((m_ItemType != E_BLOCK_AIR) && (m_ItemType != E_ITEM_EMPTY))
{
LOGWARNING("%s: creating an invalid item type (%d), resetting to empty.", __FUNCTION__, a_ItemType);
}
@ -73,6 +73,10 @@ public:
}
// The constructor is disabled in code, because the compiler generates it anyway,
// but it needs to stay because ToLua needs to generate the binding for it
#if 0
/** Creates an exact copy of the item */
cItem(const cItem & a_CopyFrom) :
m_ItemType (a_CopyFrom.m_ItemType),
@ -85,6 +89,8 @@ public:
{
}
#endif
void Empty(void)
{

View File

@ -4,4 +4,9 @@ project (MCServer)
include_directories ("${PROJECT_SOURCE_DIR}/../")
add_library(Items ItemHandler)
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(Items ${SOURCE})

67
src/Items/ItemArmor.h Normal file
View File

@ -0,0 +1,67 @@
#pragma once
#include "ItemHandler.h"
#include "../World.h"
class cItemArmorHandler :
public cItemHandler
{
public:
cItemArmorHandler(int a_ItemType) :
cItemHandler(a_ItemType)
{
}
/** Move the armor to the armor slot of the player's inventory */
virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
{
int SlotNum;
if (ItemCategory::IsHelmet(a_Item.m_ItemType))
{
SlotNum = 0;
}
else if (ItemCategory::IsChestPlate(a_Item.m_ItemType))
{
SlotNum = 1;
}
else if (ItemCategory::IsLeggings(a_Item.m_ItemType))
{
SlotNum = 2;
}
else if (ItemCategory::IsBoots(a_Item.m_ItemType))
{
SlotNum = 3;
}
else
{
LOGWARNING("Used unknown armor: %i", a_Item.m_ItemType);
return false;
}
if (!a_Player->GetInventory().GetArmorSlot(SlotNum).IsEmpty())
{
return false;
}
a_Player->GetInventory().SetArmorSlot(SlotNum, a_Item.CopyOne());
cItem Item(a_Item);
Item.m_ItemCount--;
if (Item.m_ItemCount <= 0)
{
Item.Empty();
}
a_Player->GetInventory().SetHotbarSlot(a_Player->GetInventory().GetEquippedSlotNum(), Item);
return true;
}
} ;

View File

@ -9,7 +9,7 @@
#pragma once
#include "../Entities/ProjectileEntity.h"
#include "../Entities/ArrowEntity.h"

View File

@ -8,6 +8,7 @@
#include "../BlockInServerPluginInterface.h"
// Handlers:
#include "ItemArmor.h"
#include "ItemBed.h"
#include "ItemBoat.h"
#include "ItemBow.h"
@ -222,6 +223,31 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
{
return new cItemFoodHandler(a_ItemType);
}
// Armor:
case E_ITEM_LEATHER_CAP:
case E_ITEM_GOLD_HELMET:
case E_ITEM_CHAIN_HELMET:
case E_ITEM_IRON_HELMET:
case E_ITEM_DIAMOND_HELMET:
case E_ITEM_LEATHER_TUNIC:
case E_ITEM_GOLD_CHESTPLATE:
case E_ITEM_CHAIN_CHESTPLATE:
case E_ITEM_IRON_CHESTPLATE:
case E_ITEM_DIAMOND_CHESTPLATE:
case E_ITEM_LEATHER_PANTS:
case E_ITEM_GOLD_LEGGINGS:
case E_ITEM_CHAIN_LEGGINGS:
case E_ITEM_IRON_LEGGINGS:
case E_ITEM_DIAMOND_LEGGINGS:
case E_ITEM_LEATHER_BOOTS:
case E_ITEM_GOLD_BOOTS:
case E_ITEM_CHAIN_BOOTS:
case E_ITEM_IRON_BOOTS:
case E_ITEM_DIAMOND_BOOTS:
{
return new cItemArmorHandler(a_ItemType);
}
}
}

View File

@ -21,13 +21,13 @@ class cItemHandler
public:
cItemHandler(int a_ItemType);
// Force virtual destructor
/** Force virtual destructor */
virtual ~cItemHandler() {}
/// Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False
/** Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False */
virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir);
/// Called when the client sends the SHOOT status in the lclk packet
/** Called when the client sends the SHOOT status in the lclk packet */
virtual void OnItemShoot(cPlayer *, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
{
UNUSED(a_BlockX);
@ -36,7 +36,7 @@ public:
UNUSED(a_BlockFace);
}
/// Called every tick while the item is on the player's inventory (Used by maps) - For now, called only for equipped items
/** Called every tick while the item is on the player's inventory (Used by maps) - For now, called only for equipped items */
virtual void OnUpdate(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item)
{
UNUSED(a_World);
@ -44,16 +44,16 @@ public:
UNUSED(a_Item);
}
/// Called while the player diggs a block using this item
/** Called while the player diggs a block using this item */
virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace);
/// Called when the player destroys a block using this item. This also calls the drop function for the destroyed block
/** Called when the player destroys a block using this item. This also calls the drop function for the destroyed block */
virtual void OnBlockDestroyed(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_X, int a_Y, int a_Z);
/// Called after the player has eaten this item.
/** Called after the player has eaten this item. */
virtual void OnFoodEaten(cWorld *a_World, cPlayer *a_Player, cItem *a_Item);
/// Returns the maximum stack size for a given item
/** Returns the maximum stack size for a given item */
virtual char GetMaxStackSize(void);
struct FoodInfo
@ -70,22 +70,22 @@ public:
}
} ;
/// Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance)
/** Returns the FoodInfo for this item. (FoodRecovery, Saturation and PoisionChance) */
virtual FoodInfo GetFoodInfo();
/// Lets the player eat a selected item. Returns true if the player ate the item
/** Lets the player eat a selected item. Returns true if the player ate the item */
virtual bool EatItem(cPlayer *a_Player, cItem *a_Item);
/// Indicates if this item is a tool
/** Indicates if this item is a tool */
virtual bool IsTool(void);
/// Indicates if this item is food
/** Indicates if this item is food */
virtual bool IsFood(void);
/// Blocks simply get placed
/** Blocks simply get placed */
virtual bool IsPlaceable(void);
/** Called before a block is placed into a world.
/** Called before a block is placed into a world.
The handler should return true to allow placement, false to refuse.
Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block.
*/
@ -96,7 +96,7 @@ public:
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
);
/// Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can´t) DEFAULT: False
/** Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can<EFBFBD>t) DEFAULT: False */
virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType);
static cItemHandler * GetItemHandler(int a_ItemType);

View File

@ -33,7 +33,10 @@ public:
a_BlockY--;
}
if (a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, (cMonster::eType)(a_Item.m_ItemDamage)) >= 0)
cMonster::eType MonsterType = ItemDamageToMonsterType(a_Item.m_ItemDamage);
if (
(MonsterType != cMonster::mtInvalidType) && // Valid monster type
(a_World->SpawnMob(a_BlockX + 0.5, a_BlockY, a_BlockZ + 0.5, MonsterType) >= 0)) // Spawning succeeded
{
if (!a_Player->IsGameModeCreative())
{
@ -45,6 +48,41 @@ public:
return false;
}
/** Converts the Spawn egg item damage to the monster type to spawn.
Returns mtInvalidType for invalid damage values. */
static cMonster::eType ItemDamageToMonsterType(short a_ItemDamage)
{
switch (a_ItemDamage)
{
case E_META_SPAWN_EGG_BAT: return cMonster::mtBat;
case E_META_SPAWN_EGG_BLAZE: return cMonster::mtBlaze;
case E_META_SPAWN_EGG_CAVE_SPIDER: return cMonster::mtCaveSpider;
case E_META_SPAWN_EGG_CHICKEN: return cMonster::mtChicken;
case E_META_SPAWN_EGG_COW: return cMonster::mtCow;
case E_META_SPAWN_EGG_CREEPER: return cMonster::mtCreeper;
case E_META_SPAWN_EGG_ENDERMAN: return cMonster::mtEnderman;
case E_META_SPAWN_EGG_GHAST: return cMonster::mtGhast;
case E_META_SPAWN_EGG_HORSE: return cMonster::mtHorse;
case E_META_SPAWN_EGG_MAGMA_CUBE: return cMonster::mtMagmaCube;
case E_META_SPAWN_EGG_MOOSHROOM: return cMonster::mtMooshroom;
case E_META_SPAWN_EGG_OCELOT: return cMonster::mtOcelot;
case E_META_SPAWN_EGG_PIG: return cMonster::mtPig;
case E_META_SPAWN_EGG_SHEEP: return cMonster::mtSheep;
case E_META_SPAWN_EGG_SILVERFISH: return cMonster::mtSilverfish;
case E_META_SPAWN_EGG_SKELETON: return cMonster::mtSkeleton;
case E_META_SPAWN_EGG_SLIME: return cMonster::mtSlime;
case E_META_SPAWN_EGG_SPIDER: return cMonster::mtSpider;
case E_META_SPAWN_EGG_SQUID: return cMonster::mtSquid;
case E_META_SPAWN_EGG_VILLAGER: return cMonster::mtVillager;
case E_META_SPAWN_EGG_WITCH: return cMonster::mtWitch;
case E_META_SPAWN_EGG_WOLF: return cMonster::mtWolf;
case E_META_SPAWN_EGG_ZOMBIE: return cMonster::mtZombie;
case E_META_SPAWN_EGG_ZOMBIE_PIGMAN: return cMonster::mtZombiePigman;
}
return cMonster::mtInvalidType;
}
} ;

View File

@ -171,7 +171,6 @@ bool cLineBlockTracer::MoveToNextBlock(void)
double CoeffZ = (DestZ - m_StartZ) / m_DiffZ;
if (CoeffZ < Coeff)
{
Coeff = CoeffZ;
Direction = dirZ;
}
}

View File

@ -9,7 +9,6 @@
cMCLogger * cMCLogger::s_MCLogger = NULL;
bool g_ShouldColorOutput = false;
#ifdef _WIN32
#include <io.h> // Needed for _isatty(), not available on Linux
@ -33,7 +32,8 @@ cMCLogger * cMCLogger::GetInstance(void)
cMCLogger::cMCLogger(void)
cMCLogger::cMCLogger(void):
m_ShouldColorOutput(false)
{
AString FileName;
Printf(FileName, "LOG_%d.txt", (int)time(NULL));
@ -76,15 +76,15 @@ void cMCLogger::InitLog(const AString & a_FileName)
#ifdef _WIN32
// See whether we are writing to a console the default console attrib:
g_ShouldColorOutput = (_isatty(_fileno(stdin)) != 0);
if (g_ShouldColorOutput)
m_ShouldColorOutput = (_isatty(_fileno(stdin)) != 0);
if (m_ShouldColorOutput)
{
CONSOLE_SCREEN_BUFFER_INFO sbi;
GetConsoleScreenBufferInfo(g_Console, &sbi);
g_DefaultConsoleAttrib = sbi.wAttributes;
}
#elif defined (__linux) && !defined(ANDROID_NDK)
g_ShouldColorOutput = isatty(fileno(stdout));
m_ShouldColorOutput = isatty(fileno(stdout));
// TODO: Check if the terminal supports colors, somehow?
#endif
}
@ -178,7 +178,7 @@ void cMCLogger::Error(const char * a_Format, va_list a_ArgList)
void cMCLogger::SetColor(eColorScheme a_Scheme)
{
if (!g_ShouldColorOutput)
if (!m_ShouldColorOutput)
{
return;
}
@ -211,7 +211,7 @@ void cMCLogger::SetColor(eColorScheme a_Scheme)
void cMCLogger::ResetColor(void)
{
if (!g_ShouldColorOutput)
if (!m_ShouldColorOutput)
{
return;
}

View File

@ -52,6 +52,7 @@ private:
cCriticalSection m_CriticalSection;
cLog * m_Log;
static cMCLogger * s_MCLogger;
bool m_ShouldColorOutput;
/// Sets the specified color scheme in the terminal (TODO: if coloring available)

View File

@ -37,7 +37,7 @@ void cAggressiveMonster::InStateChasing(float a_Dt)
}
}
if (((float)m_FinalDestination.x != (float)m_Target->GetPosX()) || ((float)m_FinalDestination.z != (float)m_Target->GetPosZ()))
if (!IsMovingToTargetPosition())
{
MoveToPosition(m_Target->GetPosition());
}
@ -106,3 +106,18 @@ void cAggressiveMonster::Attack(float a_Dt)
bool cAggressiveMonster::IsMovingToTargetPosition()
{
static const float epsilon = 0.000000000001f;
// Difference between destination x and target x is negligible (to 10^-12 precision)
if (fabsf((float)m_FinalDestination.x - (float)m_Target->GetPosX()) < epsilon)
{
return false;
}
// Difference between destination z and target z is negligible (to 10^-12 precision)
else if (fabsf((float)m_FinalDestination.z - (float)m_Target->GetPosZ()) > epsilon)
{
return false;
}
return true;
}

View File

@ -22,6 +22,10 @@ public:
virtual void EventSeePlayer(cEntity *) override;
virtual void Attack(float a_Dt);
protected:
/** Whether this mob's destination is the same as its target's position. */
bool IsMovingToTargetPosition();
} ;

View File

@ -3,6 +3,7 @@
#include "Blaze.h"
#include "../World.h"
#include "../Entities/FireChargeEntity.h"

View File

@ -6,6 +6,7 @@ include_directories ("${PROJECT_SOURCE_DIR}/../")
file(GLOB SOURCE
"*.cpp"
"*.h"
)
add_library(Mobs ${SOURCE})

View File

@ -1,15 +1,14 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Cavespider.h"
#include "CaveSpider.h"
#include "../World.h"
cCavespider::cCavespider(void) :
super("Cavespider", mtCaveSpider, "mob.spider.say", "mob.spider.death", 0.7, 0.5)
cCaveSpider::cCaveSpider(void) :
super("CaveSpider", mtCaveSpider, "mob.spider.say", "mob.spider.death", 0.7, 0.5)
{
}
@ -17,7 +16,7 @@ cCavespider::cCavespider(void) :
void cCavespider::Tick(float a_Dt, cChunk & a_Chunk)
void cCaveSpider::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
@ -29,7 +28,7 @@ void cCavespider::Tick(float a_Dt, cChunk & a_Chunk)
void cCavespider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cCaveSpider::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
int LootingLevel = 0;
if (a_Killer != NULL)

View File

@ -1,4 +1,3 @@
#pragma once
#include "AggressiveMonster.h"
@ -7,13 +6,13 @@
class cCavespider :
class cCaveSpider :
public cAggressiveMonster
{
typedef cAggressiveMonster super;
public:
cCavespider(void);
cCaveSpider(void);
CLASS_PROTODEF(cCaveSpider);

Some files were not shown because too many files have changed in this diff Show More