summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTycho <work.tycho+git@gmail.com>2014-01-26 15:28:51 +0100
committerTycho <work.tycho+git@gmail.com>2014-01-26 15:28:51 +0100
commitbed2ee22e8283a2e8bd0790a89124839319c2fc3 (patch)
treeee9e66a7b7c63995797a502f209079c1422ce9c9 /src
parentRefactored cBlockHandler::OnUse and dependents (diff)
parentMerge pull request #589 from mc-server/minecartimprovements (diff)
downloadcuberite-bed2ee22e8283a2e8bd0790a89124839319c2fc3.tar
cuberite-bed2ee22e8283a2e8bd0790a89124839319c2fc3.tar.gz
cuberite-bed2ee22e8283a2e8bd0790a89124839319c2fc3.tar.bz2
cuberite-bed2ee22e8283a2e8bd0790a89124839319c2fc3.tar.lz
cuberite-bed2ee22e8283a2e8bd0790a89124839319c2fc3.tar.xz
cuberite-bed2ee22e8283a2e8bd0790a89124839319c2fc3.tar.zst
cuberite-bed2ee22e8283a2e8bd0790a89124839319c2fc3.zip
Diffstat (limited to 'src')
-rw-r--r--src/Authenticator.cpp8
-rw-r--r--src/Bindings/AllToLua.bat4
-rw-r--r--src/Bindings/CMakeLists.txt24
-rw-r--r--src/Bindings/PluginManager.cpp2
-rw-r--r--src/BlockEntities/CommandBlockEntity.cpp32
-rw-r--r--src/Blocks/BlockRail.h28
-rw-r--r--src/CMakeLists.txt105
-rw-r--r--src/ClientHandle.cpp39
-rw-r--r--src/Crypto.cpp435
-rw-r--r--src/Crypto.h169
-rw-r--r--src/Defines.h20
-rw-r--r--src/Entities/Entity.cpp134
-rw-r--r--src/Entities/Entity.h38
-rw-r--r--src/Entities/Minecart.cpp474
-rw-r--r--src/Entities/Minecart.h24
-rw-r--r--src/Entities/Pickup.cpp67
-rw-r--r--src/Entities/Player.cpp224
-rw-r--r--src/Entities/Player.h33
-rw-r--r--src/Generating/ChunkGenerator.cpp9
-rw-r--r--src/Globals.h3
-rw-r--r--src/Items/ItemLighter.h4
-rw-r--r--src/LeakFinder.cpp11
-rw-r--r--src/MobProximityCounter.cpp4
-rw-r--r--src/Mobs/AggressiveMonster.cpp67
-rw-r--r--src/Mobs/AggressiveMonster.h5
-rw-r--r--src/Mobs/Creeper.cpp53
-rw-r--r--src/Mobs/Creeper.h3
-rw-r--r--src/Mobs/IronGolem.h4
-rw-r--r--src/Mobs/Monster.cpp454
-rw-r--r--src/Mobs/Monster.h90
-rw-r--r--src/Mobs/PassiveAggressiveMonster.cpp3
-rw-r--r--src/Mobs/PassiveMonster.cpp16
-rw-r--r--src/Mobs/Sheep.cpp39
-rw-r--r--src/Mobs/Sheep.h3
-rw-r--r--src/Mobs/Skeleton.cpp11
-rw-r--r--src/Mobs/Squid.cpp3
-rw-r--r--src/Mobs/Squid.h3
-rw-r--r--src/Mobs/Villager.cpp2
-rw-r--r--src/Mobs/Wolf.cpp68
-rw-r--r--src/Mobs/Wolf.h1
-rw-r--r--src/Mobs/Zombie.cpp13
-rw-r--r--src/MonsterConfig.cpp20
-rw-r--r--src/OSSupport/BlockingTCPLink.cpp4
-rw-r--r--src/OSSupport/Errors.cpp53
-rw-r--r--src/OSSupport/Errors.h5
-rw-r--r--src/OSSupport/Event.cpp17
-rw-r--r--src/OSSupport/File.cpp9
-rw-r--r--src/OSSupport/File.h45
-rw-r--r--src/OSSupport/Socket.cpp52
-rw-r--r--src/OSSupport/Socket.h7
-rw-r--r--src/OSSupport/SocketThreads.cpp3
-rw-r--r--src/Protocol/Protocol132.cpp149
-rw-r--r--src/Protocol/Protocol132.h16
-rw-r--r--src/Protocol/Protocol14x.cpp2
-rw-r--r--src/Protocol/Protocol15x.cpp6
-rw-r--r--src/Protocol/Protocol17x.cpp91
-rw-r--r--src/Protocol/Protocol17x.h17
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp2
-rw-r--r--src/ReferenceManager.cpp43
-rw-r--r--src/ReferenceManager.h34
-rw-r--r--src/Server.cpp14
-rw-r--r--src/Server.h14
-rw-r--r--src/UI/Window.cpp31
-rw-r--r--src/World.cpp40
-rw-r--r--src/World.h12
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp19
-rw-r--r--src/WorldStorage/ScoreboardSerializer.cpp76
-rw-r--r--src/WorldStorage/WSSAnvil.cpp9
-rw-r--r--src/WorldStorage/WSSCompact.cpp182
-rw-r--r--src/main.cpp21
70 files changed, 2527 insertions, 1195 deletions
diff --git a/src/Authenticator.cpp b/src/Authenticator.cpp
index 4cbe3beed..bd6db1c11 100644
--- a/src/Authenticator.cpp
+++ b/src/Authenticator.cpp
@@ -43,7 +43,6 @@ cAuthenticator::~cAuthenticator()
-/// Read custom values from INI
void cAuthenticator::ReadINI(cIniFile & IniFile)
{
m_Server = IniFile.GetValueSet("Authentication", "Server", DEFAULT_AUTH_SERVER);
@@ -55,7 +54,6 @@ void cAuthenticator::ReadINI(cIniFile & IniFile)
-/// Queues a request for authenticating a user. If the auth fails, the user is kicked
void cAuthenticator::Authenticate(int a_ClientID, const AString & a_UserName, const AString & a_ServerHash)
{
if (!m_ShouldAuthenticate)
@@ -141,7 +139,9 @@ bool cAuthenticator::AuthFromAddress(const AString & a_Server, const AString & a
cBlockingTCPLink Link;
if (!Link.Connect(a_Server.c_str(), 80))
{
- LOGERROR("cAuthenticator: cannot connect to auth server \"%s\", kicking user \"%s\"", a_Server.c_str(), a_Server.c_str());
+ LOGWARNING("%s: cannot connect to auth server \"%s\", kicking user \"%s\"",
+ __FUNCTION__, a_Server.c_str(), a_UserName.c_str()
+ );
return false;
}
@@ -170,7 +170,7 @@ bool cAuthenticator::AuthFromAddress(const AString & a_Server, const AString & a
if (code == 302)
{
// redirect blabla
- LOGINFO("Need to redirect!");
+ LOGD("%s: Need to redirect, current level %d!", __FUNCTION__, a_Level);
if (a_Level > MAX_REDIRECTS)
{
LOGERROR("cAuthenticator: received too many levels of redirection from auth server \"%s\" for user \"%s\", bailing out and kicking the user", a_Server.c_str(), a_UserName.c_str());
diff --git a/src/Bindings/AllToLua.bat b/src/Bindings/AllToLua.bat
index b2a192880..f085af9e9 100644
--- a/src/Bindings/AllToLua.bat
+++ b/src/Bindings/AllToLua.bat
@@ -4,17 +4,21 @@
:: When called without any parameters, it will pause for a keypress at the end
:: Call with any parameter to disable the wait (for buildserver use)
+@echo off
+
:: Regenerate the files:
+echo Regenerating LUA bindings . . .
"tolua++.exe" -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
: Wait for keypress, if no param given:
+echo.
if %ALLTOLUA_WAIT%N == N pause
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
deleted file mode 100644
index 50b81e42a..000000000
--- a/src/Bindings/CMakeLists.txt
+++ /dev/null
@@ -1,24 +0,0 @@
-
-cmake_minimum_required (VERSION 2.6)
-project (MCServer)
-
-# NOTE: This CMake file is processed only for Unix builds; Windows(MSVC) builds handle all the subfolders in /src in a single file, /src/CMakeLists.txt
-
-include_directories ("${PROJECT_SOURCE_DIR}/../")
-
-ADD_CUSTOM_COMMAND(
- # add any new generated bindings here
- OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/Bindings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Bindings.h
-
- # command execuded to regerate bindings
- COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
- WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
-
- # add any new generation dependencies here
- DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/virtual_method_hooks.lua ${CMAKE_CURRENT_SOURCE_DIR}/AllToLua.pkg tolua
-)
-
-#add cpp files here
-add_library(Bindings PluginManager LuaState WebPlugin Bindings ManualBindings LuaWindow Plugin PluginLua WebPlugin)
-
-target_link_libraries(Bindings lua sqlite tolualib)
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 92c06487c..e582fde86 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -1740,7 +1740,7 @@ bool cPluginManager::DoWithPlugin(const AString & a_PluginName, cPluginCallback
{
// TODO: Implement locking for plugins
PluginMap::iterator itr = m_Plugins.find(a_PluginName);
- if (itr == m_Plugins.end())
+ if ((itr == m_Plugins.end()) || (itr->second == NULL))
{
return false;
}
diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp
index 7e9015d33..0bc6ca359 100644
--- a/src/BlockEntities/CommandBlockEntity.cpp
+++ b/src/BlockEntities/CommandBlockEntity.cpp
@@ -151,9 +151,13 @@ void cCommandBlockEntity::SendTo(cClientHandle & a_Client)
bool cCommandBlockEntity::LoadFromJson(const Json::Value & a_Value)
{
- m_Command = a_Value.get("Command", "").asString();
+ m_PosX = a_Value.get("x", 0).asInt();
+ m_PosY = a_Value.get("y", 0).asInt();
+ m_PosZ = a_Value.get("z", 0).asInt();
- m_LastOutput = a_Value.get("LastOutput", "").asString();
+ m_Command = a_Value.get("Command", "").asString();
+ m_LastOutput = a_Value.get("LastOutput", "").asString();
+ m_Result = a_Value.get("SuccessCount", 0).asInt();
return true;
}
@@ -164,9 +168,13 @@ bool cCommandBlockEntity::LoadFromJson(const Json::Value & a_Value)
void cCommandBlockEntity::SaveToJson(Json::Value & a_Value)
{
- a_Value["Command"] = m_Command;
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
- a_Value["LastOutput"] = m_LastOutput;
+ a_Value["Command"] = m_Command;
+ a_Value["LastOutput"] = m_LastOutput;
+ a_Value["SuccessCount"] = m_Result;
}
@@ -175,18 +183,24 @@ void cCommandBlockEntity::SaveToJson(Json::Value & a_Value)
void cCommandBlockEntity::Execute()
{
+ if (m_World != NULL)
+ {
+ if (!m_World->AreCommandBlocksEnabled())
+ {
+ return;
+ }
+ }
+
class CommandBlockOutCb :
public cCommandOutputCallback
{
- cCommandBlockEntity* m_CmdBlock;
+ cCommandBlockEntity * m_CmdBlock;
public:
- CommandBlockOutCb(cCommandBlockEntity* a_CmdBlock) : m_CmdBlock(a_CmdBlock) {}
+ CommandBlockOutCb(cCommandBlockEntity * a_CmdBlock) : m_CmdBlock(a_CmdBlock) {}
virtual void Out(const AString & a_Text)
{
- ASSERT(m_CmdBlock != NULL);
-
// Overwrite field
m_CmdBlock->SetLastOutput(a_Text);
}
@@ -194,7 +208,7 @@ void cCommandBlockEntity::Execute()
LOGD("cCommandBlockEntity: Executing command %s", m_Command.c_str());
- cServer* Server = cRoot::Get()->GetServer();
+ cServer * Server = cRoot::Get()->GetServer();
Server->ExecuteConsoleCommand(m_Command, CmdBlockOutCb);
diff --git a/src/Blocks/BlockRail.h b/src/Blocks/BlockRail.h
index 59d7182ba..30a8bc10c 100644
--- a/src/Blocks/BlockRail.h
+++ b/src/Blocks/BlockRail.h
@@ -186,25 +186,35 @@ public:
}
if (RailsCnt > 1)
{
- if (Neighbors[3] && Neighbors[0]) return E_META_RAIL_CURVED_ZP_XP;
- else if (Neighbors[3] && Neighbors[1]) return E_META_RAIL_CURVED_ZP_XM;
- else if (Neighbors[2] && Neighbors[0]) return E_META_RAIL_CURVED_ZM_XP;
- else if (Neighbors[2] && Neighbors[1]) return E_META_RAIL_CURVED_ZM_XM;
+ if (Neighbors[3] && Neighbors[0] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZP_XP;
+ else if (Neighbors[3] && Neighbors[1] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZP_XM;
+ else if (Neighbors[2] && Neighbors[0] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZM_XP;
+ else if (Neighbors[2] && Neighbors[1] && CanThisRailCurve()) return E_META_RAIL_CURVED_ZM_XM;
else if (Neighbors[7] && Neighbors[2]) return E_META_RAIL_ASCEND_ZP;
else if (Neighbors[3] && Neighbors[6]) return E_META_RAIL_ASCEND_ZM;
else if (Neighbors[5] && Neighbors[0]) return E_META_RAIL_ASCEND_XM;
else if (Neighbors[4] && Neighbors[1]) return E_META_RAIL_ASCEND_XP;
else if (Neighbors[0] && Neighbors[1]) return E_META_RAIL_XM_XP;
else if (Neighbors[2] && Neighbors[3]) return E_META_RAIL_ZM_ZP;
- ASSERT(!"Weird neighbor count");
+
+ if (CanThisRailCurve())
+ {
+ ASSERT(!"Weird neighbor count");
+ }
}
return Meta;
}
+ inline bool CanThisRailCurve(void)
+ {
+ return m_BlockType == E_BLOCK_RAIL;
+ }
+
+
bool IsUnstable(cChunkInterface * a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- if (a_ChunkInterface->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
+ if (!IsBlockRail(a_ChunkInterface->GetBlock(a_BlockX, a_BlockY, a_BlockZ)))
{
return false;
}
@@ -339,11 +349,11 @@ public:
{
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false);
NIBBLETYPE Meta;
- if (a_ChunkInterface->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
+ if (!IsBlockRail(a_ChunkInterface->GetBlock(a_BlockX, a_BlockY, a_BlockZ)))
{
- if ((a_ChunkInterface->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure != E_PURE_UPDOWN))
+ if (!IsBlockRail(a_ChunkInterface->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) || (a_Pure != E_PURE_UPDOWN))
{
- if ((a_ChunkInterface->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure == E_PURE_NONE))
+ if (!IsBlockRail(a_ChunkInterface->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)) || (a_Pure == E_PURE_NONE))
{
return true;
}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 275099540..944150a44 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -4,13 +4,106 @@ project (MCServer)
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 Bindings Items Blocks Protocol Generating)
+set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating)
set(FOLDERS ${FOLDERS} WorldStorage Mobs Entities Simulator UI BlockEntities)
if (NOT MSVC)
+
+ #Bindings needs to reference other folders so are done here
+
+ #lib dependecies are not included
+
+ set(BINDING_DEPENDECIES
+ ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/virtual_method_hooks.lua
+ ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/AllToLua.pkg
+ ChunkDef.h
+ BiomeDef.h
+ OSSupport/File.h
+ Bindings/LuaFunctions.h
+ Bindings/PluginManager.h
+ Bindings/Plugin.h
+ Bindings/PluginLua.h
+ Bindings/WebPlugin.h
+ Bindings/LuaWindow.h
+ BlockID.h
+ StringUtils.h
+ Defines.h
+ ChatColor.h
+ ClientHandle.h
+ Entities/Entity.h
+ Entities/Floater.h
+ Entities/Pawn.h
+ Entities/Player.h
+ Entities/Pickup.h
+ Entities/ProjectileEntity.h
+ Entities/TNTEntity.h
+ Entities/Effects.h
+ Server.h
+ World.h
+ Inventory.h
+ Enchantments.h
+ Item.h
+ ItemGrid.h
+ BlockEntities/BlockEntity.h
+ BlockEntities/BlockEntityWithItems.h
+ BlockEntities/ChestEntity.h
+ BlockEntities/DropSpenserEntity.h
+ BlockEntities/DispenserEntity.h
+ BlockEntities/DropperEntity.h
+ BlockEntities/FurnaceEntity.h
+ BlockEntities/HopperEntity.h
+ BlockEntities/JukeboxEntity.h
+ BlockEntities/NoteEntity.h
+ BlockEntities/SignEntity.h
+ WebAdmin.h
+ Root.h
+ Vector3f.h
+ Vector3d.h
+ Vector3i.h
+ Matrix4f.h
+ Cuboid.h
+ BoundingBox.h
+ Tracer.h
+ Group.h
+ BlockArea.h
+ Generating/ChunkDesc.h
+ CraftingRecipes.h
+ UI/Window.h
+ Mobs/Monster.h
+ )
+
+ include_directories(Bindings)
+ include_directories(.)
+
+ ADD_CUSTOM_COMMAND(
+ # add any new generated bindings here
+ OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Bindings.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/Bindings.h
+
+ # command execuded to regerate bindings
+ COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/
+
+ # add any new generation dependencies here
+ DEPENDS ${BINDING_DEPENDECIES}
+ )
+ #add cpp files here
+ add_library(Bindings Bindings/PluginManager Bindings/LuaState Bindings/WebPlugin Bindings/Bindings Bindings/ManualBindings Bindings/LuaWindow Bindings/Plugin Bindings/PluginLua Bindings/WebPlugin)
+
+ target_link_libraries(Bindings lua sqlite tolualib)
+
+ #clear file
+ file(WRITE ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/BindingDependecies.txt)
+ foreach(dependecy ${BINDING_DEPENDECIES})
+ #write each dependecy on a seperate line
+ file(APPEND ${CMAKE_CURRENT_SOURCE_DIR}/Bindings/BindingDependecies.txt "${dependecy}\n")
+ endforeach()
+
+ set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "Bindings.cpp Bindings.h")
+
foreach(folder ${FOLDERS})
add_subdirectory(${folder})
endforeach(folder)
@@ -44,6 +137,7 @@ else ()
# Add all subfolders as solution-folders:
list(APPEND FOLDERS "Resources")
+ list(APPEND FOLDERS "Bindings")
function(includefolder PATH)
FILE(GLOB FOLDER_FILES
"${PATH}/*.cpp"
@@ -78,6 +172,13 @@ else ()
"StackWalker.cpp LeakFinder.h" PROPERTIES COMPILE_FLAGS "/Yc\"Globals.h\""
)
list(APPEND SOURCE "Resources/MCServer.rc")
+
+ # Make MSVC generate the PDB files even for the release build:
+ set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi")
+ set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /Zi")
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /DEBUG")
+ set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG")
+ set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /DEBUG")
endif()
set(EXECUTABLE MCServer)
@@ -120,4 +221,4 @@ endif ()
if (WIN32)
target_link_libraries(${EXECUTABLE} expat tolualib ws2_32.lib Psapi.lib)
endif()
-target_link_libraries(${EXECUTABLE} md5 luaexpat iniFile jsoncpp cryptopp zlib lua sqlite)
+target_link_libraries(${EXECUTABLE} md5 luaexpat iniFile jsoncpp polarssl zlib lua sqlite)
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index fa3827d73..60d2efbb2 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -149,15 +149,6 @@ cClientHandle::~cClientHandle()
SendDisconnect("Server shut down? Kthnxbai");
}
- // Queue all remaining outgoing packets to cSocketThreads:
- {
- cCSLock Lock(m_CSOutgoingData);
- AString Data;
- m_OutgoingData.ReadAll(Data);
- m_OutgoingData.CommitRead();
- cRoot::Get()->GetServer()->WriteToClient(this, Data);
- }
-
// Close the socket as soon as it sends all outgoing data:
cRoot::Get()->GetServer()->RemoveClient(this);
@@ -611,25 +602,18 @@ void cClientHandle::HandleCommandBlockMessage(const char* a_Data, unsigned int a
}
}
- class cUpdateCommandBlock :
- public cCommandBlockCallback
- {
- AString m_Command;
- public:
- cUpdateCommandBlock(const AString & a_Command) : m_Command(a_Command) {}
-
- virtual bool Item(cCommandBlockEntity * a_CommandBlock) override
- {
- a_CommandBlock->SetCommand(m_Command);
- return false;
- }
- } CmdBlockCB (Command);
-
cWorld * World = m_Player->GetWorld();
- World->DoWithCommandBlockAt(BlockX, BlockY, BlockZ, CmdBlockCB);
+ if (World->AreCommandBlocksEnabled())
+ {
+ World->SetCommandBlockCommand(BlockX, BlockY, BlockZ, Command);
- SendChat(Printf("%s[INFO]%s Successfully set command block command", cChatColor::Green.c_str(), cChatColor::White.c_str()));
+ SendChat(Printf("%s[INFO]%s Successfully set command block command", cChatColor::Green.c_str(), cChatColor::White.c_str()));
+ }
+ else
+ {
+ SendChat(Printf("%s[INFO]%s Command blocks are not enabled on this server", cChatColor::Yellow.c_str(), cChatColor::White.c_str()));
+ }
}
@@ -665,7 +649,8 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, ch
// A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
return;
}
- m_Player->TossItem(false);
+
+ m_Player->TossEquippedItem();
return;
}
@@ -720,7 +705,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, ch
// A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
return;
}
- m_Player->TossItem(false, 64); // Toss entire slot - if there aren't enough items, the maximum will be ejected
+ m_Player->TossEquippedItem(64); // Toss entire slot - if there aren't enough items, the maximum will be ejected
return;
}
diff --git a/src/Crypto.cpp b/src/Crypto.cpp
new file mode 100644
index 000000000..2045d0385
--- /dev/null
+++ b/src/Crypto.cpp
@@ -0,0 +1,435 @@
+
+// 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;
+ }
+ size_t DecryptedLength;
+ int res = rsa_pkcs1_encrypt(
+ &m_Rsa, ctr_drbg_random, &m_Ctr_drbg, RSA_PUBLIC,
+ a_PlainLength, a_PlainData, a_EncryptedData
+ );
+ if (res != 0)
+ {
+ return -1;
+ }
+ return (int)DecryptedLength;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cAESCFBDecryptor:
+
+cAESCFBDecryptor::cAESCFBDecryptor(void) :
+ m_IsValid(false),
+ m_IVOffset(0)
+{
+}
+
+
+
+
+
+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_IsValid(false),
+ m_IVOffset(0)
+{
+}
+
+
+
+
+
+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;
+}
+
+
+
+
diff --git a/src/Crypto.h b/src/Crypto.h
new file mode 100644
index 000000000..a97f34fbf
--- /dev/null
+++ b/src/Crypto.h
@@ -0,0 +1,169 @@
+
+// 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"
+
+
+
+
+
+/** 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);
+} ;
+
+
+
+
+
+/** 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:
+ Byte test;
+
+ 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;
+} ;
+
+
+
+
diff --git a/src/Defines.h b/src/Defines.h
index 7a86f499e..3a26f4be6 100644
--- a/src/Defines.h
+++ b/src/Defines.h
@@ -5,8 +5,6 @@
-typedef unsigned char Byte;
-
/// List of slot numbers, used for inventory-painting
typedef std::vector<int> cSlotNums;
@@ -282,6 +280,24 @@ inline bool IsBlockLiquid(BLOCKTYPE a_BlockType)
+inline bool IsBlockRail(BLOCKTYPE a_BlockType)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_RAIL:
+ case E_BLOCK_ACTIVATOR_RAIL:
+ case E_BLOCK_DETECTOR_RAIL:
+ case E_BLOCK_POWERED_RAIL:
+ {
+ return true;
+ }
+ default: return false;
+ }
+}
+
+
+
+
inline bool IsBlockTypeOfDirt(BLOCKTYPE a_BlockType)
{
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 565c78dfd..e22f689d9 100644
--- a/src/Entities/Entity.cpp
+++ b/src/Entities/Entity.cpp
@@ -4,9 +4,7 @@
#include "../World.h"
#include "../Server.h"
#include "../Root.h"
-#include "../Vector3d.h"
#include "../Matrix4f.h"
-#include "../ReferenceManager.h"
#include "../ClientHandle.h"
#include "../Chunk.h"
#include "../Simulator/FluidSimulator.h"
@@ -32,8 +30,6 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
, m_MaxHealth(1)
, m_AttachedTo(NULL)
, m_Attachee(NULL)
- , m_Referencers(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCERS))
- , m_References(new cReferenceManager(cReferenceManager::RFMNGR_REFERENCES))
, m_bDirtyHead(true)
, m_bDirtyOrientation(true)
, m_bDirtyPosition(true)
@@ -61,6 +57,8 @@ 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_IsSubmerged(false)
+ , m_IsSwimming(false)
{
cCSLock Lock(m_CSCount);
m_EntityCount++;
@@ -73,7 +71,8 @@ cEntity::cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, d
cEntity::~cEntity()
{
- ASSERT(!m_World->HasEntity(m_UniqueID)); // Before deleting, the entity needs to have been removed from the world
+ // Before deleting, the entity needs to have been removed from the world, if ever added
+ ASSERT((m_World == NULL) || !m_World->HasEntity(m_UniqueID));
/*
// DEBUG:
@@ -99,8 +98,6 @@ cEntity::~cEntity()
LOGWARNING("ERROR: Entity deallocated without being destroyed");
ASSERT(!"Entity deallocated without being destroyed or unlinked");
}
- delete m_Referencers;
- delete m_References;
}
@@ -534,7 +531,17 @@ void cEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
TickInVoid(a_Chunk);
}
- else { m_TicksSinceLastVoidDamage = 0; }
+ else
+ m_TicksSinceLastVoidDamage = 0;
+
+ if (IsMob() || IsPlayer())
+ {
+ // Set swimming state
+ SetSwimState(a_Chunk);
+
+ // Handle drowning
+ HandleAir();
+ }
}
@@ -912,6 +919,87 @@ void cEntity::TickInVoid(cChunk & a_Chunk)
+void cEntity::SetSwimState(cChunk & a_Chunk)
+{
+ int RelY = (int)floor(m_LastPosY + 0.1);
+ if ((RelY < 0) || (RelY >= cChunkDef::Height - 1))
+ {
+ m_IsSwimming = false;
+ m_IsSubmerged = false;
+ return;
+ }
+
+ BLOCKTYPE BlockIn;
+ int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width;
+ int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width;
+
+ // Check if the player is swimming:
+ // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk
+ if (!a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn))
+ {
+ // This sometimes happens on Linux machines
+ // Ref.: http://forum.mc-server.org/showthread.php?tid=1244
+ LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}",
+ RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ()
+ );
+ m_IsSwimming = false;
+ m_IsSubmerged = false;
+ return;
+ }
+ m_IsSwimming = IsBlockWater(BlockIn);
+
+ // Check if the player is submerged:
+ VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY + 1, RelZ, BlockIn));
+ m_IsSubmerged = IsBlockWater(BlockIn);
+}
+
+
+
+
+
+void cEntity::HandleAir(void)
+{
+ // Ref.: http://www.minecraftwiki.net/wiki/Chunk_format
+ // See if the entity is /submerged/ water (block above is water)
+ // Get the type of block the entity is standing in:
+
+ if (IsSubmerged())
+ {
+ SetSpeedY(1); // Float in the water
+
+ // Either reduce air level or damage player
+ if (m_AirLevel < 1)
+ {
+ if (m_AirTickTimer < 1)
+ {
+ // Damage player
+ TakeDamage(dtDrowning, NULL, 1, 1, 0);
+ // Reset timer
+ m_AirTickTimer = DROWNING_TICKS;
+ }
+ else
+ {
+ m_AirTickTimer -= 1;
+ }
+ }
+ else
+ {
+ // Reduce air supply
+ m_AirLevel -= 1;
+ }
+ }
+ else
+ {
+ // Set the air back to maximum
+ m_AirLevel = MAX_AIR_LEVEL;
+ m_AirTickTimer = DROWNING_TICKS;
+ }
+}
+
+
+
+
+
/// Called when the entity starts burning
void cEntity::OnStartedBurning(void)
{
@@ -1429,33 +1517,3 @@ void cEntity::SetPosZ(double a_PosZ)
-
-//////////////////////////////////////////////////////////////////////////
-// Reference stuffs
-void cEntity::AddReference(cEntity * & a_EntityPtr)
-{
- m_References->AddReference(a_EntityPtr);
- a_EntityPtr->ReferencedBy(a_EntityPtr);
-}
-
-
-
-
-
-void cEntity::ReferencedBy(cEntity * & a_EntityPtr)
-{
- m_Referencers->AddReference(a_EntityPtr);
-}
-
-
-
-
-
-void cEntity::Dereference(cEntity * & a_EntityPtr)
-{
- m_Referencers->Dereference(a_EntityPtr);
-}
-
-
-
-
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index 91463bfd6..b2edfc2b4 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -4,6 +4,7 @@
#include "../Item.h"
#include "../Vector3d.h"
#include "../Vector3f.h"
+#include "../Vector3i.h"
@@ -28,12 +29,16 @@
return super::GetClass(); \
}
+#define POSX_TOINT (int)floor(GetPosX())
+#define POSY_TOINT (int)floor(GetPosY())
+#define POSZ_TOINT (int)floor(GetPosZ())
+#define POS_TOINT Vector3i(POSXTOINT, POSYTOINT, POSZTOINT)
+
class cWorld;
-class cReferenceManager;
class cClientHandle;
class cPlayer;
class cChunk;
@@ -110,6 +115,8 @@ public:
BURN_TICKS_PER_DAMAGE = 20, ///< How many ticks to wait between damaging an entity when it is burning
BURN_DAMAGE = 1, ///< How much damage to deal when the entity is burning
BURN_TICKS = 200, ///< How long to keep an entity burning after it has stood in lava / fire
+ MAX_AIR_LEVEL = 300, ///< Maximum air an entity can have
+ DROWNING_TICKS = 20, ///< Number of ticks per heart of damage
} ;
cEntity(eEntityType a_EntityType, double a_X, double a_Y, double a_Z, double a_Width, double a_Height);
@@ -344,7 +351,14 @@ public:
virtual bool IsRiding (void) const {return false; }
virtual bool IsSprinting(void) const {return false; }
virtual bool IsRclking (void) const {return false; }
- virtual bool IsInvisible(void) const {return false; }
+ virtual bool IsInvisible(void) const { return false; }
+
+ /** Returns whether the player is swimming or not */
+ virtual bool IsSwimming(void) const{ return m_IsSwimming; }
+ /** Return whether the player is under water or not */
+ virtual bool IsSubmerged(void) const{ return m_IsSubmerged; }
+ /** Gets remaining air of a monster */
+ int GetAirLevel(void) const { return m_AirLevel; }
// tolua_end
@@ -373,9 +387,6 @@ protected:
/// The entity which is attached to this entity (rider), NULL if none
cEntity * m_Attachee;
- cReferenceManager* m_Referencers;
- cReferenceManager* m_References;
-
// Flags that signal that we haven't updated the clients with the latest.
bool m_bDirtyHead;
bool m_bDirtyOrientation;
@@ -415,11 +426,18 @@ protected:
virtual void Destroyed(void) {} // Called after the entity has been destroyed
void SetWorld(cWorld * a_World) { m_World = a_World; }
-
- friend class cReferenceManager;
- void AddReference( cEntity*& a_EntityPtr );
- void ReferencedBy( cEntity*& a_EntityPtr );
- void Dereference( cEntity*& a_EntityPtr );
+
+ /** Called in each tick to handle air-related processing i.e. drowning */
+ virtual void HandleAir();
+ /** Called once per tick to set IsSwimming and IsSubmerged */
+ virtual void SetSwimState(cChunk & a_Chunk);
+
+ /** If an entity is currently swimming in or submerged under water */
+ bool m_IsSwimming, m_IsSubmerged;
+
+ /** Air level of a mobile */
+ int m_AirLevel;
+ int m_AirTickTimer;
private:
// Measured in degrees, [-180, +180)
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
index d0d384481..a650927b1 100644
--- a/src/Entities/Minecart.cpp
+++ b/src/Entities/Minecart.cpp
@@ -19,6 +19,70 @@
+class cMinecartCollisionCallback :
+ public cEntityCallback
+{
+public:
+ cMinecartCollisionCallback(Vector3d a_Pos, double a_Height, double a_Width, int a_UniqueID, int a_AttacheeUniqueID) :
+ m_Pos(a_Pos),
+ m_Height(a_Height),
+ m_Width(a_Width),
+ m_DoesInteserct(false),
+ m_CollidedEntityPos(0, 0, 0),
+ m_UniqueID(a_UniqueID),
+ m_AttacheeUniqueID(a_AttacheeUniqueID)
+ {
+ }
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ ASSERT(a_Entity != NULL);
+
+ if (!a_Entity->IsPlayer() && !a_Entity->IsMob() && !a_Entity->IsMinecart() && !a_Entity->IsBoat())
+ {
+ return false;
+ }
+ else if ((a_Entity->GetUniqueID() == m_UniqueID) || (a_Entity->GetUniqueID() == m_AttacheeUniqueID))
+ {
+ return false;
+ }
+
+ cBoundingBox bbEntity(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight());
+ cBoundingBox bbMinecart(Vector3d(m_Pos.x, floor(m_Pos.y), m_Pos.z), m_Width / 2, m_Height);
+
+ if (bbEntity.DoesIntersect(bbMinecart))
+ {
+ m_CollidedEntityPos = a_Entity->GetPosition();
+ m_DoesInteserct = true;
+ return true;
+ }
+ return false;
+ }
+
+ bool FoundIntersection(void) const
+ {
+ return m_DoesInteserct;
+ }
+
+ Vector3d GetCollidedEntityPosition(void) const
+ {
+ return m_CollidedEntityPos;
+ }
+
+protected:
+ bool m_DoesInteserct;
+
+ Vector3d m_CollidedEntityPos;
+
+ Vector3d m_Pos;
+ double m_Height, m_Width;
+ int m_UniqueID;
+ int m_AttacheeUniqueID;
+};
+
+
+
+
cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) :
super(etMinecart, a_X, a_Y, a_Z, 0.98, 0.7),
@@ -30,7 +94,7 @@ cMinecart::cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z) :
SetMass(20.f);
SetMaxHealth(6);
SetHealth(6);
- SetWidth(1.2);
+ SetWidth(1);
SetHeight(0.9);
}
@@ -96,10 +160,17 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
if (IsBlockRail(InsideType)) AddPosY(1); // Push cart upwards
}
+ bool WasDetectorRail = false;
if (IsBlockRail(InsideType))
{
- bool WasDetectorRail = false;
- SnapToRail(InsideMeta);
+ if (InsideType == E_BLOCK_RAIL)
+ {
+ SnapToRail(InsideMeta);
+ }
+ else
+ {
+ SnapToRail(InsideMeta & 0x07);
+ }
switch (InsideType)
{
@@ -116,12 +187,6 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
}
AddPosition(GetSpeed() * (a_Dt / 1000)); // Commit changes; as we use our own engine when on rails, this needs to be done, whereas it is normally in Entity.cpp
-
- if (m_bIsOnDetectorRail && !WasDetectorRail)
- {
- m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07);
- m_bIsOnDetectorRail = false;
- }
}
else
{
@@ -129,6 +194,17 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
SetPosY(floor(GetPosY()) + 0.35); // HandlePhysics overrides this if minecart can fall, else, it is to stop ground clipping minecart bottom when off-rail
super::HandlePhysics(a_Dt, *Chunk);
}
+
+ if (m_bIsOnDetectorRail && !Vector3i((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ())).Equals(m_DetectorRailPosition))
+ {
+ m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07);
+ m_bIsOnDetectorRail = false;
+ }
+ else if (WasDetectorRail)
+ {
+ m_bIsOnDetectorRail = true;
+ m_DetectorRailPosition = Vector3i((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
+ }
// Broadcast positioning changes to client
BroadcastMovementUpdate();
@@ -154,7 +230,9 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
SetSpeedY(0); // Don't move vertically as on ground
SetSpeedX(0); // Correct diagonal movement from curved rails
- if (TestBlockCollision(a_RailMeta)) return;
+ // Execute both the entity and block collision checks
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
if (GetSpeedZ() != 0) // Don't do anything if cart is stationary
{
@@ -178,7 +256,8 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
SetSpeedY(0);
SetSpeedZ(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
if (GetSpeedX() != 0)
{
@@ -281,94 +360,46 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
{
SetYaw(315); // Set correct rotation server side
SetPosY(floor(GetPosY()) + 0.55); // Levitate dat cart
+ SetSpeedY(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
+
+ // SnapToRail handles turning
- if (GetSpeedZ() > 0) // Cart moving south
- {
- int OldX = (int)floor(GetPosX());
- AddSpeedX(-GetSpeedZ() + 0.5); // See below
- AddPosX(-GetSpeedZ() * (a_Dt / 1000)); // Diagonally move southwest (which will make cart hit a southwest rail)
- // If we are already at southwest rail, set Z speed to zero as we can be moving so fast, MCS doesn't tick fast enough to active the handle for the rail...
- // ...and so we derail unexpectedly.
- if (GetPosX() <= OldX - 1) SetSpeedZ(0);
- }
- else if (GetSpeedX() > 0) // Cart moving east
- {
- int OldZ = (int)floor(GetPosZ());
- AddSpeedZ(-GetSpeedX() + 0.5);
- AddPosZ(-GetSpeedX() * (a_Dt / 1000)); // Diagonally move northeast
- if (GetPosZ() <= OldZ - 1) SetSpeedX(0);
- }
break;
}
case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST
{
SetYaw(225);
SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
- if (GetSpeedZ() > 0)
- {
- int OldX = (int)floor(GetPosX());
- AddSpeedX(GetSpeedZ() - 0.5);
- AddPosX(GetSpeedZ() * (a_Dt / 1000));
- if (GetPosX() >= OldX + 1) SetSpeedZ(0);
- }
- else if (GetSpeedX() < 0)
- {
- int OldZ = (int)floor(GetPosZ());
- AddSpeedZ(GetSpeedX() + 0.5);
- AddPosZ(GetSpeedX() * (a_Dt / 1000));
- if (GetPosZ() <= OldZ - 1) SetSpeedX(0);
- }
break;
}
case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST
{
SetYaw(135);
SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
- if (GetSpeedZ() < 0)
- {
- int OldX = (int)floor(GetPosX());
- AddSpeedX(GetSpeedZ() + 0.5);
- AddPosX(GetSpeedZ() * (a_Dt / 1000));
- if (GetPosX() <= OldX - 1) SetSpeedZ(0);
- }
- else if (GetSpeedX() > 0)
- {
- int OldZ = (int)floor(GetPosZ());
- AddSpeedZ(GetSpeedX() - 0.5);
- AddPosZ(GetSpeedX() * (a_Dt / 1000));
- if (GetPosZ() >= OldZ + 1) SetSpeedX(0);
- }
break;
}
case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST
{
SetYaw(45);
SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
- if (GetSpeedZ() < 0)
- {
- int OldX = (int)floor(GetPosX());
- AddSpeedX(-GetSpeedZ() - 0.5);
- AddPosX(-GetSpeedZ() * (a_Dt / 1000));
- if (GetPosX() >= OldX + 1) SetSpeedZ(0);
- }
- else if (GetSpeedX() < 0)
- {
- int OldZ = (int)floor(GetPosZ());
- AddSpeedZ(-GetSpeedX() - 0.5);
- AddPosZ(-GetSpeedX() * (a_Dt / 1000));
- if (GetPosZ() >= OldZ + 1) SetSpeedX(0);
- }
break;
}
default:
@@ -385,8 +416,8 @@ void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
{
// Initialise to 'slow down' values
- int AccelDecelSpeed = -1;
- int AccelDecelNegSpeed = 1;
+ int AccelDecelSpeed = -2;
+ int AccelDecelNegSpeed = 2;
if ((a_RailMeta & 0x8) == 0x8)
{
@@ -404,17 +435,18 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
SetSpeedY(0);
SetSpeedX(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
if (GetSpeedZ() != 0)
{
if (GetSpeedZ() > 0)
{
- AddSpeedZ(AccelDecelNegSpeed);
+ AddSpeedZ(AccelDecelSpeed);
}
else
{
- AddSpeedZ(AccelDecelSpeed);
+ AddSpeedZ(AccelDecelNegSpeed);
}
}
break;
@@ -426,7 +458,8 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
SetSpeedY(0);
SetSpeedZ(0);
- if (TestBlockCollision(a_RailMeta)) return;
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
if (GetSpeedX() != 0)
{
@@ -441,6 +474,87 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
}
break;
}
+ case E_META_RAIL_ASCEND_XM: // ASCEND EAST
+ {
+ SetYaw(180);
+ SetSpeedZ(0);
+
+ if (GetSpeedX() >= 0)
+ {
+ if (GetSpeedX() <= MAX_SPEED)
+ {
+ AddSpeedX(AccelDecelSpeed);
+ SetSpeedY(-GetSpeedX());
+ }
+ }
+ else
+ {
+ AddSpeedX(AccelDecelNegSpeed);
+ SetSpeedY(-GetSpeedX());
+ }
+ break;
+ }
+ case E_META_RAIL_ASCEND_XP: // ASCEND WEST
+ {
+ SetYaw(180);
+ SetSpeedZ(0);
+
+ if (GetSpeedX() > 0)
+ {
+ AddSpeedX(AccelDecelSpeed);
+ SetSpeedY(GetSpeedX());
+ }
+ else
+ {
+ if (GetSpeedX() >= MAX_SPEED_NEGATIVE)
+ {
+ AddSpeedX(AccelDecelNegSpeed);
+ SetSpeedY(GetSpeedX());
+ }
+ }
+ break;
+ }
+ case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH
+ {
+ SetYaw(270);
+ SetSpeedX(0);
+
+ if (GetSpeedZ() >= 0)
+ {
+ if (GetSpeedZ() <= MAX_SPEED)
+ {
+ AddSpeedZ(AccelDecelSpeed);
+ SetSpeedY(-GetSpeedZ());
+ }
+ }
+ else
+ {
+ AddSpeedZ(AccelDecelNegSpeed);
+ SetSpeedY(-GetSpeedZ());
+ }
+ break;
+ }
+ case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH
+ {
+ SetYaw(270);
+ SetSpeedX(0);
+
+ if (GetSpeedZ() > 0)
+ {
+ AddSpeedZ(AccelDecelSpeed);
+ SetSpeedY(GetSpeedZ());
+ }
+ else
+ {
+ if (GetSpeedZ() >= MAX_SPEED_NEGATIVE)
+ {
+ AddSpeedZ(AccelDecelNegSpeed);
+ SetSpeedY(GetSpeedZ());
+ }
+ }
+ break;
+ }
+ default: ASSERT(!"Unhandled powered rail metadata!"); break;
}
}
@@ -450,11 +564,17 @@ void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
{
- m_bIsOnDetectorRail = true;
- m_DetectorRailPosition = Vector3i((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
-
m_World->SetBlockMeta(m_DetectorRailPosition, a_RailMeta | 0x08);
+ // No special handling
+ HandleRailPhysics(a_RailMeta & 0x07, a_Dt);
+}
+
+
+
+
+void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
+{
HandleRailPhysics(a_RailMeta & 0x07, a_Dt);
}
@@ -482,6 +602,107 @@ void cMinecart::SnapToRail(NIBBLETYPE a_RailMeta)
SetPosX(floor(GetPosX()) + 0.5);
break;
}
+ // Curved rail physics: once minecart has reached more than half of the block in the direction that it is travelling in, jerk it in the direction of curvature
+ case E_META_RAIL_CURVED_ZM_XM:
+ {
+ if (GetPosZ() > floor(GetPosZ()) + 0.5)
+ {
+ if (GetSpeedZ() > 0)
+ {
+ SetSpeedX(-GetSpeedZ() * 0.7);
+ }
+
+ SetSpeedZ(0);
+ SetPosZ(floor(GetPosZ()) + 0.5);
+ }
+ else if (GetPosX() > floor(GetPosX()) + 0.5)
+ {
+ if (GetSpeedX() > 0)
+ {
+ SetSpeedZ(-GetSpeedX() * 0.7);
+ }
+
+ SetSpeedX(0);
+ SetPosX(floor(GetPosX()) + 0.5);
+ }
+ SetSpeedY(0);
+ break;
+ }
+ case E_META_RAIL_CURVED_ZM_XP:
+ {
+ if (GetPosZ() > floor(GetPosZ()) + 0.5)
+ {
+ if (GetSpeedZ() > 0)
+ {
+ SetSpeedX(GetSpeedZ() * 0.7);
+ }
+
+ SetSpeedZ(0);
+ SetPosZ(floor(GetPosZ()) + 0.5);
+ }
+ else if (GetPosX() < floor(GetPosX()) + 0.5)
+ {
+ if (GetSpeedX() < 0)
+ {
+ SetSpeedZ(GetSpeedX() * 0.7);
+ }
+
+ SetSpeedX(0);
+ SetPosX(floor(GetPosX()) + 0.5);
+ }
+ SetSpeedY(0);
+ break;
+ }
+ case E_META_RAIL_CURVED_ZP_XM:
+ {
+ if (GetPosZ() < floor(GetPosZ()) + 0.5)
+ {
+ if (GetSpeedZ() < 0)
+ {
+ SetSpeedX(GetSpeedZ() * 0.7);
+ }
+
+ SetSpeedZ(0);
+ SetPosZ(floor(GetPosZ()) + 0.5);
+ }
+ else if (GetPosX() > floor(GetPosX()) + 0.5)
+ {
+ if (GetSpeedX() > 0)
+ {
+ SetSpeedZ(GetSpeedX() * 0.7);
+ }
+
+ SetSpeedX(0);
+ SetPosX(floor(GetPosX()) + 0.5);
+ }
+ SetSpeedY(0);
+ break;
+ }
+ case E_META_RAIL_CURVED_ZP_XP:
+ {
+ if (GetPosZ() < floor(GetPosZ()) + 0.5)
+ {
+ if (GetSpeedZ() < 0)
+ {
+ SetSpeedX(-GetSpeedZ() * 0.7);
+ }
+
+ SetSpeedZ(0);
+ SetPosZ(floor(GetPosZ()) + 0.5);
+ }
+ else if (GetPosX() < floor(GetPosX()) + 0.5)
+ {
+ if (GetSpeedX() < 0)
+ {
+ SetSpeedZ(-GetSpeedX() * 0.7);
+ }
+
+ SetSpeedX(0);
+ SetPosX(floor(GetPosX()) + 0.5);
+ }
+ SetSpeedY(0);
+ break;
+ }
default: break;
}
}
@@ -513,7 +734,7 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
}
}
}
- else
+ else if (GetSpeedZ() < 0)
{
BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) - 1);
if (!IsBlockRail(Block) && g_BlockIsSolid[Block])
@@ -549,7 +770,7 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
}
}
}
- else
+ else if (GetSpeedX() < 0)
{
BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()) - 1, (int)floor(GetPosY()), (int)floor(GetPosZ()));
if (!IsBlockRail(Block) && g_BlockIsSolid[Block])
@@ -597,10 +818,93 @@ bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
+bool cMinecart::TestEntityCollision(NIBBLETYPE a_RailMeta)
+{
+ cMinecartCollisionCallback MinecartCollisionCallback(GetPosition(), GetHeight(), GetWidth(), GetUniqueID(), ((m_Attachee == NULL) ? -1 : m_Attachee->GetUniqueID()));
+ int ChunkX, ChunkZ;
+ cChunkDef::BlockToChunk((int)floor(GetPosX()), (int)floor(GetPosZ()), ChunkX, ChunkZ);
+ m_World->ForEachEntityInChunk(ChunkX, ChunkZ, MinecartCollisionCallback);
+
+ if (!MinecartCollisionCallback.FoundIntersection())
+ {
+ return false;
+ }
+
+ switch (a_RailMeta)
+ {
+ case E_META_RAIL_ZM_ZP:
+ {
+ if (MinecartCollisionCallback.GetCollidedEntityPosition().z >= GetPosZ())
+ {
+ if ((-GetSpeedZ() * 0.4) < 0.01)
+ {
+ AddSpeedZ(-4);
+ }
+ else
+ {
+ SetSpeedZ(-GetSpeedZ() * 0.4);
+ }
+ }
+ else
+ {
+ if ((GetSpeedZ() * 0.4) < 0.01)
+ {
+ AddSpeedZ(4);
+ }
+ else
+ {
+ SetSpeedZ(GetSpeedZ() * 0.4);
+ }
+ }
+ return true;
+ }
+ case E_META_RAIL_XM_XP:
+ {
+ if (MinecartCollisionCallback.GetCollidedEntityPosition().x >= GetPosX())
+ {
+ if ((-GetSpeedX() * 0.4) < 0.01)
+ {
+ AddSpeedX(-4);
+ }
+ else
+ {
+ SetSpeedX(-GetSpeedX() * 0.4);
+ }
+ }
+ else
+ {
+ if ((GetSpeedX() * 0.4) < 0.01)
+ {
+ AddSpeedX(4);
+ }
+ else
+ {
+ SetSpeedX(GetSpeedX() * 0.4);
+ }
+ }
+ return true;
+ }
+ case E_META_RAIL_CURVED_ZM_XM:
+ case E_META_RAIL_CURVED_ZM_XP:
+ case E_META_RAIL_CURVED_ZP_XM:
+ case E_META_RAIL_CURVED_ZP_XP:
+ {
+ // TODO - simply can't be bothered right now
+ break;
+ }
+ default: break;
+ }
+
+ return false;
+}
+
+
+
+
void cMinecart::DoTakeDamage(TakeDamageInfo & TDI)
{
- if (TDI.Attacker->IsPlayer() && ((cPlayer *)TDI.Attacker)->IsGameModeCreative())
+ if ((TDI.Attacker != NULL) && TDI.Attacker->IsPlayer() && ((cPlayer *)TDI.Attacker)->IsGameModeCreative())
{
Destroy();
TDI.FinalDamage = GetMaxHealth(); // Instant hit for creative
diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h
index 874d0204e..073e78953 100644
--- a/src/Entities/Minecart.h
+++ b/src/Entities/Minecart.h
@@ -15,20 +15,6 @@
-inline bool IsBlockRail(BLOCKTYPE a_BlockType)
- {
- return (
- (a_BlockType == E_BLOCK_RAIL) ||
- (a_BlockType == E_BLOCK_ACTIVATOR_RAIL) ||
- (a_BlockType == E_BLOCK_DETECTOR_RAIL) ||
- (a_BlockType == E_BLOCK_POWERED_RAIL)
- ) ;
- }
-
-
-
-
-
class cMinecart :
public cEntity
{
@@ -79,10 +65,16 @@ protected:
*/
void HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt);
- /** Snaps a minecart to a rail's axis, resetting its speed */
+ /** Handles activator rails - placeholder for future implementation */
+ void HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt);
+
+ /** Snaps a mincecart to a rail's axis, resetting its speed
+ For curved rails, it changes the cart's direction as well as snapping it to axis */
void SnapToRail(NIBBLETYPE a_RailMeta);
- /** Tests is a solid block is in front of a cart, and stops the cart (and returns true) if so; returns false if no obstruction*/
+ /** Tests if a solid block is in front of a cart, and stops the cart (and returns true) if so; returns false if no obstruction */
bool TestBlockCollision(NIBBLETYPE a_RailMeta);
+ /** Tests if this mincecart's bounding box is intersecting another entity's bounding box (collision) and pushes mincecart away */
+ bool TestEntityCollision(NIBBLETYPE a_RailMeta);
} ;
diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp
index 001e386a7..bfe162b69 100644
--- a/src/Entities/Pickup.cpp
+++ b/src/Entities/Pickup.cpp
@@ -6,19 +6,58 @@
#endif
#include "Pickup.h"
+#include "Player.h"
#include "../ClientHandle.h"
-#include "../Inventory.h"
#include "../World.h"
-#include "../Simulator/FluidSimulator.h"
#include "../Server.h"
-#include "Player.h"
#include "../Bindings/PluginManager.h"
-#include "../Item.h"
#include "../Root.h"
#include "../Chunk.h"
-#include "../Vector3d.h"
-#include "../Vector3f.h"
+
+
+
+class cPickupCombiningCallback :
+ public cEntityCallback
+{
+public:
+ cPickupCombiningCallback(Vector3d a_Position, cPickup * a_Pickup) :
+ m_Position(a_Position),
+ m_Pickup(a_Pickup),
+ m_FoundMatchingPickup(false)
+ {
+ }
+
+ virtual bool Item(cEntity * a_Entity) override
+ {
+ if (!a_Entity->IsPickup() || (a_Entity->GetUniqueID() == m_Pickup->GetUniqueID()) || a_Entity->IsDestroyed())
+ {
+ return false;
+ }
+
+ Vector3d EntityPos = a_Entity->GetPosition();
+ double Distance = (EntityPos - m_Position).Length();
+
+ if ((Distance < 1.2) && ((cPickup *)a_Entity)->GetItem().IsEqual(m_Pickup->GetItem()))
+ {
+ m_Pickup->GetItem().AddCount(((cPickup *)a_Entity)->GetItem().m_ItemCount);
+ a_Entity->Destroy();
+ m_FoundMatchingPickup = true;
+ }
+ return false;
+ }
+
+ inline bool FoundMatchingPickup()
+ {
+ return m_FoundMatchingPickup;
+ }
+
+protected:
+ bool m_FoundMatchingPickup;
+
+ Vector3d m_Position;
+ cPickup * m_Pickup;
+};
@@ -26,10 +65,10 @@
cPickup::cPickup(double a_PosX, double a_PosY, double a_PosZ, const cItem & a_Item, bool IsPlayerCreated, float a_SpeedX /* = 0.f */, float a_SpeedY /* = 0.f */, float a_SpeedZ /* = 0.f */)
: cEntity(etPickup, a_PosX, a_PosY, a_PosZ, 0.2, 0.2)
- , m_Timer( 0.f )
+ , m_Timer(0.f)
, m_Item(a_Item)
- , m_bCollected( false )
- , m_bIsPlayerCreated( IsPlayerCreated )
+ , m_bCollected(false)
+ , m_bIsPlayerCreated(IsPlayerCreated)
{
SetGravity(-10.5f);
SetMaxHealth(5);
@@ -89,6 +128,16 @@ void cPickup::Tick(float a_Dt, cChunk & a_Chunk)
return;
}
}
+
+ if (!IsDestroyed()) // Don't try to combine if someone has tried to combine me
+ {
+ cPickupCombiningCallback PickupCombiningCallback(GetPosition(), this);
+ m_World->ForEachEntity(PickupCombiningCallback); // Not ForEachEntityInChunk, otherwise pickups don't combine across chunk boundaries
+ if (PickupCombiningCallback.FoundMatchingPickup())
+ {
+ m_World->BroadcastEntityMetadata(*this);
+ }
+ }
}
}
}
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index e5def0156..82e31ae65 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -7,7 +7,6 @@
#include "../UI/Window.h"
#include "../UI/WindowOwner.h"
#include "../World.h"
-#include "Pickup.h"
#include "../Bindings/PluginManager.h"
#include "../BlockEntities/BlockEntity.h"
#include "../GroupManager.h"
@@ -27,8 +26,6 @@
#include "inifile/iniFile.h"
#include "json/json.h"
-#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x))
-
@@ -36,8 +33,6 @@
cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
: super(etPlayer, 0.6, 1.8)
- , m_AirLevel( MAX_AIR_LEVEL )
- , m_AirTickTimer(DROWNING_TICKS)
, m_bVisible(true)
, m_FoodLevel(MAX_FOOD_LEVEL)
, m_FoodSaturationLevel(5)
@@ -108,9 +103,23 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
a_PlayerName.c_str(), GetPosX(), GetPosY(), GetPosZ()
);
}
+
m_LastJumpHeight = (float)(GetPosY());
m_LastGroundHeight = (float)(GetPosY());
m_Stance = GetPosY() + 1.62;
+
+ if (m_GameMode == gmNotSet)
+ {
+ cWorld * World = cRoot::Get()->GetWorld(GetLoadedWorldName());
+ if (World == NULL)
+ {
+ World = cRoot::Get()->GetDefaultWorld();
+ }
+ if (World->IsGameModeCreative())
+ {
+ m_CanFly = true;
+ }
+ }
cRoot::Get()->GetServer()->PlayerCreated(this);
}
@@ -197,12 +206,6 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
super::Tick(a_Dt, a_Chunk);
- // Set player swimming state
- SetSwimState(a_Chunk);
-
- // Handle air drowning stuff
- HandleAir();
-
// Handle charging the bow:
if (m_IsChargingBow)
{
@@ -435,7 +438,7 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
cWorld * World = GetWorld();
if ((GetPosY() >= 0) && (GetPosY() < cChunkDef::Height))
{
- BLOCKTYPE BlockType = World->GetBlock(float2int(GetPosX()), float2int(GetPosY()), float2int(GetPosZ()));
+ BLOCKTYPE BlockType = World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
if (BlockType != E_BLOCK_AIR)
{
m_bTouchGround = true;
@@ -460,12 +463,10 @@ void cPlayer::SetTouchGround(bool a_bTouchGround)
if (Damage > 0)
{
- if (!IsGameModeCreative())
- {
- TakeDamage(dtFalling, NULL, Damage, Damage, 0);
- }
+ // cPlayer makes sure damage isn't applied in creative, no need to check here
+ TakeDamage(dtFalling, NULL, Damage, Damage, 0);
- // Mojang uses floor() to get X and Z positions, instead of just casting it to an (int)
+ // Fall particles
GetWorld()->BroadcastSoundParticleEffect(2006, (int)floor(GetPosX()), (int)GetPosY() - 1, (int)floor(GetPosZ()), Damage /* Used as particle effect speed modifier */);
}
@@ -787,7 +788,7 @@ void cPlayer::DoTakeDamage(TakeDamageInfo & a_TDI)
{
if (IsGameModeCreative())
{
- // No damage / health in creative mode
+ // No damage / health in creative mode if not void damage
return;
}
}
@@ -1420,59 +1421,68 @@ AString cPlayer::GetColor(void) const
-void cPlayer::TossItem(
- bool a_bDraggingItem,
- char a_Amount /* = 1 */,
- short a_CreateType /* = 0 */,
- short a_CreateHealth /* = 0 */
-)
+void cPlayer::TossEquippedItem(char a_Amount)
{
cItems Drops;
- if (a_CreateType != 0)
+ cItem DroppedItem(GetInventory().GetEquippedItem());
+ if (!DroppedItem.IsEmpty())
{
- // Just create item without touching the inventory (used in creative mode)
- Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth));
+ char NewAmount = a_Amount;
+ if (NewAmount > GetInventory().GetEquippedItem().m_ItemCount)
+ {
+ NewAmount = GetInventory().GetEquippedItem().m_ItemCount; // Drop only what's there
+ }
+
+ GetInventory().GetHotbarGrid().ChangeSlotCount(GetInventory().GetEquippedSlotNum() /* Returns hotbar subslot, which HotbarGrid takes */, -a_Amount);
+
+ DroppedItem.m_ItemCount = NewAmount;
+ Drops.push_back(DroppedItem);
}
- else
+
+ double vX = 0, vY = 0, vZ = 0;
+ EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY);
+ vY = -vY * 2 + 1.f;
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player
+}
+
+
+
+
+
+void cPlayer::TossHeldItem(char a_Amount)
+{
+ cItems Drops;
+ cItem & Item = GetDraggingItem();
+ if (!Item.IsEmpty())
{
- // Drop an item from the inventory:
- if (a_bDraggingItem)
+ char OriginalItemAmount = Item.m_ItemCount;
+ Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount);
+ Drops.push_back(Item);
+ if (OriginalItemAmount > a_Amount)
{
- cItem & Item = GetDraggingItem();
- if (!Item.IsEmpty())
- {
- char OriginalItemAmount = Item.m_ItemCount;
- Item.m_ItemCount = std::min(OriginalItemAmount, a_Amount);
- Drops.push_back(Item);
- if (OriginalItemAmount > a_Amount)
- {
- Item.m_ItemCount = OriginalItemAmount - (char)a_Amount;
- }
- else
- {
- Item.Empty();
- }
- }
+ Item.m_ItemCount = OriginalItemAmount - a_Amount;
}
else
{
- // Else drop equipped item
- cItem DroppedItem(GetInventory().GetEquippedItem());
- if (!DroppedItem.IsEmpty())
- {
- char NewAmount = a_Amount;
- if (NewAmount > GetInventory().GetEquippedItem().m_ItemCount)
- {
- NewAmount = GetInventory().GetEquippedItem().m_ItemCount; // Drop only what's there
- }
-
- GetInventory().GetHotbarGrid().ChangeSlotCount(GetInventory().GetEquippedSlotNum() /* Returns hotbar subslot, which HotbarGrid takes */, -a_Amount);
-
- DroppedItem.m_ItemCount = NewAmount;
- Drops.push_back(DroppedItem);
- }
+ Item.Empty();
}
}
+
+ double vX = 0, vY = 0, vZ = 0;
+ EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY);
+ vY = -vY * 2 + 1.f;
+ m_World->SpawnItemPickups(Drops, GetPosX(), GetEyeHeight(), GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because created by player
+}
+
+
+
+
+
+void cPlayer::TossPickup(const cItem & a_Item)
+{
+ cItems Drops;
+ Drops.push_back(a_Item);
+
double vX = 0, vY = 0, vZ = 0;
EulerToVector(-GetYaw(), GetPitch(), vZ, vX, vY);
vY = -vY * 2 + 1.f;
@@ -1621,27 +1631,12 @@ bool cPlayer::LoadFromDisk()
m_CurrentXp = (short) root.get("xpCurrent", 0).asInt();
m_IsFlying = root.get("isflying", 0).asBool();
- //SetExperience(root.get("experience", 0).asInt());
-
m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt();
if (m_GameMode == eGameMode_Creative)
{
m_CanFly = true;
}
- else if (m_GameMode == eGameMode_NotSet)
- {
- cWorld * World = cRoot::Get()->GetWorld(GetLoadedWorldName());
- if (World == NULL)
- {
- World = cRoot::Get()->GetDefaultWorld();
- }
-
- if (World->GetGameMode() == eGameMode_Creative)
- {
- m_CanFly = true;
- }
- }
m_Inventory.LoadFromJson(root["inventory"]);
@@ -1758,85 +1753,6 @@ void cPlayer::UseEquippedItem(void)
-void cPlayer::SetSwimState(cChunk & a_Chunk)
-{
- int RelY = (int)floor(m_LastPosY + 0.1);
- if ((RelY < 0) || (RelY >= cChunkDef::Height - 1))
- {
- m_IsSwimming = false;
- m_IsSubmerged = false;
- return;
- }
-
- BLOCKTYPE BlockIn;
- int RelX = (int)floor(m_LastPosX) - a_Chunk.GetPosX() * cChunkDef::Width;
- int RelZ = (int)floor(m_LastPosZ) - a_Chunk.GetPosZ() * cChunkDef::Width;
-
- // Check if the player is swimming:
- // Use Unbounded, because we're being called *after* processing super::Tick(), which could have changed our chunk
- if (!a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockIn))
- {
- // This sometimes happens on Linux machines
- // Ref.: http://forum.mc-server.org/showthread.php?tid=1244
- LOGD("SetSwimState failure: RelX = %d, RelZ = %d, LastPos = {%.02f, %.02f}, Pos = %.02f, %.02f}",
- RelX, RelY, m_LastPosX, m_LastPosZ, GetPosX(), GetPosZ()
- );
- m_IsSwimming = false;
- m_IsSubmerged = false;
- return;
- }
- m_IsSwimming = IsBlockWater(BlockIn);
-
- // Check if the player is submerged:
- VERIFY(a_Chunk.UnboundedRelGetBlockType(RelX, RelY + 1, RelZ, BlockIn));
- m_IsSubmerged = IsBlockWater(BlockIn);
-}
-
-
-
-
-
-void cPlayer::HandleAir(void)
-{
- // Ref.: http://www.minecraftwiki.net/wiki/Chunk_format
- // see if the player is /submerged/ water (block above is water)
- // Get the type of block the player's standing in:
-
- if (IsSubmerged())
- {
- // either reduce air level or damage player
- if (m_AirLevel < 1)
- {
- if (m_AirTickTimer < 1)
- {
- // damage player
- TakeDamage(dtDrowning, NULL, 1, 1, 0);
- // reset timer
- m_AirTickTimer = DROWNING_TICKS;
- }
- else
- {
- m_AirTickTimer -= 1;
- }
- }
- else
- {
- // reduce air supply
- m_AirLevel -= 1;
- }
- }
- else
- {
- // set the air back to maximum
- m_AirLevel = MAX_AIR_LEVEL;
- m_AirTickTimer = DROWNING_TICKS;
- }
-}
-
-
-
-
-
void cPlayer::HandleFood(void)
{
// Ref.: http://www.minecraftwiki.net/wiki/Hunger
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 52ba2065c..50f7560d6 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -31,8 +31,6 @@ public:
MAX_HEALTH = 20,
MAX_FOOD_LEVEL = 20,
EATING_TICKS = 30, ///< Number of ticks it takes to eat an item
- MAX_AIR_LEVEL = 300,
- DROWNING_TICKS = 10, //number of ticks per heart of damage
} ;
// tolua_end
@@ -224,7 +222,14 @@ public:
/// Returns the full color code to use for this player, based on their primary group or set in m_Color
AString GetColor(void) const;
- void TossItem(bool a_bDraggingItem, char a_Amount = 1, short a_CreateType = 0, short a_CreateHealth = 0);
+ /** tosses the item in the selected hotbar slot */
+ void TossEquippedItem(char a_Amount = 1);
+
+ /** tosses the item held in hand (when in UI windows) */
+ void TossHeldItem(char a_Amount = 1);
+
+ /** tosses a pickup newly created from a_Item */
+ void TossPickup(const cItem & a_Item);
/// Heals the player by the specified amount of HPs (positive only); sends health update
void Heal(int a_Health);
@@ -234,8 +239,6 @@ public:
int GetFoodTickTimer (void) const { return m_FoodTickTimer; }
double GetFoodExhaustionLevel (void) const { return m_FoodExhaustionLevel; }
int GetFoodPoisonedTicksRemaining(void) const { return m_FoodPoisonedTicksRemaining; }
-
- int GetAirLevel (void) const { return m_AirLevel; }
/// Returns true if the player is satiated, i. e. their foodlevel is at the max and they cannot eat anymore
bool IsSatiated(void) const { return (m_FoodLevel >= MAX_FOOD_LEVEL); }
@@ -346,12 +349,6 @@ public:
/// If true the player can fly even when he's not in creative.
void SetCanFly(bool a_CanFly);
- /// Returns whether the player is swimming or not
- virtual bool IsSwimming(void) const{ return m_IsSwimming; }
-
- /// Return whether the player is under water or not
- virtual bool IsSubmerged(void) const{ return m_IsSubmerged; }
-
/// Returns wheter the player can fly or not.
virtual bool CanFly(void) const { return m_CanFly; }
// tolua_end
@@ -382,12 +379,6 @@ protected:
XP_TO_LEVEL30 = 825
} ;
- /// Player's air level (for swimming)
- int m_AirLevel;
-
- /// used to time ticks between damage taken via drowning/suffocation
- int m_AirTickTimer;
-
bool m_bVisible;
// Food-related variables:
@@ -424,7 +415,7 @@ protected:
float m_LastBlockActionTime;
int m_LastBlockActionCnt;
eGameMode m_GameMode;
- std::string m_IP;
+ AString m_IP;
/// The item being dragged by the cursor while in a UI window
cItem m_DraggingItem;
@@ -483,12 +474,6 @@ protected:
/// Called in each tick if the player is fishing to make sure the floater dissapears when the player doesn't have a fishing rod as equipped item.
void HandleFloater(void);
-
- /// Called in each tick to handle air-related processing i.e. drowning
- void HandleAir();
-
- /// Called once per tick to set IsSwimming and IsSubmerged
- void SetSwimState(cChunk & a_Chunk);
/// Adds food exhaustion based on the difference between Pos and LastPos, sprinting status and swimming (in water block)
void ApplyFoodExhaustionFromMovement();
diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp
index baa5b76b8..ef38f1399 100644
--- a/src/Generating/ChunkGenerator.cpp
+++ b/src/Generating/ChunkGenerator.cpp
@@ -201,7 +201,7 @@ void cChunkGenerator::Execute(void)
while (!m_ShouldTerminate)
{
cCSLock Lock(m_CS);
- while (m_Queue.size() == 0)
+ while (m_Queue.empty())
{
if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC))
{
@@ -221,6 +221,13 @@ void cChunkGenerator::Execute(void)
LastReportTick = clock();
}
+ if (m_Queue.empty())
+ {
+ // Sometimes the queue remains empty
+ // If so, we can't do any front() operations on it!
+ continue;
+ }
+
cChunkCoords coords = m_Queue.front(); // Get next coord from queue
m_Queue.erase( m_Queue.begin() ); // Remove coordinate from queue
bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
diff --git a/src/Globals.h b/src/Globals.h
index 6c0b4e0fd..1e90d83e9 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -93,6 +93,9 @@ typedef unsigned long long UInt64;
typedef unsigned int UInt32;
typedef unsigned short UInt16;
+typedef unsigned char Byte;
+
+
diff --git a/src/Items/ItemLighter.h b/src/Items/ItemLighter.h
index 4281a2d0c..8f3389d95 100644
--- a/src/Items/ItemLighter.h
+++ b/src/Items/ItemLighter.h
@@ -42,6 +42,10 @@ public:
{
// Light a fire next to/on top of the block if air:
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
+ if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
+ {
+ break;
+ }
if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR)
{
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 0);
diff --git a/src/LeakFinder.cpp b/src/LeakFinder.cpp
index 9d7f185ba..42a5afe56 100644
--- a/src/LeakFinder.cpp
+++ b/src/LeakFinder.cpp
@@ -862,8 +862,10 @@ static int MyAllocHook(int nAllocType, void *pvData,
{
// RequestID was found
size_t temp = g_CurrentMemUsage;
- g_CurrentMemUsage -= nSize ;
- g_pCRTTable->Remove(lRequest);
+ if (g_pCRTTable->Remove(lRequest))
+ {
+ g_CurrentMemUsage -= nSize;
+ }
if (g_CurrentMemUsage > temp)
{
printf("********************************************\n");
@@ -896,8 +898,11 @@ static int MyAllocHook(int nAllocType, void *pvData,
// Try to find the RequestID in the Hash-Table, mark it that it was freed
lReallocRequest = pHead->lRequest;
size_t temp = g_CurrentMemUsage;
- g_CurrentMemUsage -= pHead->nDataSize;
bRet = g_pCRTTable->Remove(lReallocRequest);
+ if (bRet)
+ {
+ g_CurrentMemUsage -= pHead->nDataSize;
+ }
if (g_CurrentMemUsage > temp)
{
printf("********************************************\n");
diff --git a/src/MobProximityCounter.cpp b/src/MobProximityCounter.cpp
index 583a71579..6c44ea458 100644
--- a/src/MobProximityCounter.cpp
+++ b/src/MobProximityCounter.cpp
@@ -59,7 +59,7 @@ cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDist
{
if (toReturn.m_Begin == m_DistanceToMonster.end())
{
- if (a_DistanceMin == -1 || itr->first > a_DistanceMin)
+ if ((a_DistanceMin == 1) || (itr->first > a_DistanceMin))
{
toReturn.m_Begin = itr; // this is the first one with distance > a_DistanceMin;
}
@@ -67,7 +67,7 @@ cMobProximityCounter::sIterablePair cMobProximityCounter::getMobWithinThosesDist
if (toReturn.m_Begin != m_DistanceToMonster.end())
{
- if (a_DistanceMax != -1 && itr->first > a_DistanceMax)
+ if ((a_DistanceMax != 1) && (itr->first > a_DistanceMax))
{
toReturn.m_End = itr; // this is just after the last one with distance < a_DistanceMax
// Note : if we are not going through this, it's ok, toReturn.m_End will be end();
diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp
index cc7e7da2b..f2f0c404c 100644
--- a/src/Mobs/AggressiveMonster.cpp
+++ b/src/Mobs/AggressiveMonster.cpp
@@ -4,7 +4,6 @@
#include "AggressiveMonster.h"
#include "../World.h"
-#include "../Vector3f.h"
#include "../Entities/Player.h"
#include "../MersenneTwister.h"
@@ -13,8 +12,7 @@
cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height) :
- super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height),
- m_ChaseTime(999999)
+ super(a_ConfigName, a_MobType, a_SoundHurt, a_SoundDeath, a_Width, a_Height)
{
m_EMPersonality = AGGRESSIVE;
}
@@ -27,32 +25,23 @@ cAggressiveMonster::cAggressiveMonster(const AString & a_ConfigName, eType a_Mob
void cAggressiveMonster::InStateChasing(float a_Dt)
{
super::InStateChasing(a_Dt);
- m_ChaseTime += a_Dt;
+
if (m_Target != NULL)
{
if (m_Target->IsPlayer())
{
- cPlayer * Player = (cPlayer *) m_Target;
- if (Player->IsGameModeCreative())
+ if (((cPlayer *)m_Target)->IsGameModeCreative())
{
m_EMState = IDLE;
return;
}
}
- Vector3f Pos = Vector3f( GetPosition() );
- Vector3f Their = Vector3f( m_Target->GetPosition() );
- if ((Their - Pos).Length() <= m_AttackRange)
+ if (((float)m_FinalDestination.x != (float)m_Target->GetPosX()) || ((float)m_FinalDestination.z != (float)m_Target->GetPosZ()))
{
- Attack(a_Dt);
+ MoveToPosition(m_Target->GetPosition());
}
- MoveToPosition(Their + Vector3f(0, 0.65f, 0));
}
- else if (m_ChaseTime > 5.f)
- {
- m_ChaseTime = 0;
- m_EMState = IDLE;
- }
}
@@ -61,8 +50,11 @@ void cAggressiveMonster::InStateChasing(float a_Dt)
void cAggressiveMonster::EventSeePlayer(cEntity * a_Entity)
{
- super::EventSeePlayer(a_Entity);
- m_EMState = CHASING;
+ if (!((cPlayer *)a_Entity)->IsGameModeCreative())
+ {
+ super::EventSeePlayer(a_Entity);
+ m_EMState = CHASING;
+ }
}
@@ -73,25 +65,32 @@ void cAggressiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
- m_SeePlayerInterval += a_Dt;
-
- if (m_SeePlayerInterval > 1)
+ if (m_EMState == CHASING)
+ {
+ CheckEventLostPlayer();
+ }
+ else
{
- int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally
+ CheckEventSeePlayer();
+ }
+}
- m_SeePlayerInterval = 0.0;
- if (rem >= 2)
- {
- if (m_EMState == CHASING)
- {
- CheckEventLostPlayer();
- }
- else
- {
- CheckEventSeePlayer();
- }
- }
+
+
+
+
+void cAggressiveMonster::Attack(float a_Dt)
+{
+ super::Attack(a_Dt);
+
+ if ((m_Target != NULL) && (m_AttackInterval > 3.0))
+ {
+ // Setting this higher gives us more wiggle room for attackrate
+ m_AttackInterval = 0.0;
+ m_Target->TakeDamage(dtMobAttack, this, m_AttackDamage, 0);
}
}
+
+
diff --git a/src/Mobs/AggressiveMonster.h b/src/Mobs/AggressiveMonster.h
index 5a0d93f3d..9cee4e7a7 100644
--- a/src/Mobs/AggressiveMonster.h
+++ b/src/Mobs/AggressiveMonster.h
@@ -13,16 +13,15 @@ class cAggressiveMonster :
typedef cMonster super;
public:
+
cAggressiveMonster(const AString & a_ConfigName, eType a_MobType, const AString & a_SoundHurt, const AString & a_SoundDeath, double a_Width, double a_Height);
virtual void Tick (float a_Dt, cChunk & a_Chunk) override;
virtual void InStateChasing(float a_Dt) override;
virtual void EventSeePlayer(cEntity *) override;
+ virtual void Attack(float a_Dt) override;
-
-protected:
- float m_ChaseTime;
} ;
diff --git a/src/Mobs/Creeper.cpp b/src/Mobs/Creeper.cpp
index 4e11ae13e..2e1ece865 100644
--- a/src/Mobs/Creeper.cpp
+++ b/src/Mobs/Creeper.cpp
@@ -3,6 +3,7 @@
#include "Creeper.h"
#include "../World.h"
+#include "../Entities/ProjectileEntity.h"
@@ -11,7 +12,8 @@
cCreeper::cCreeper(void) :
super("Creeper", mtCreeper, "mob.creeper.say", "mob.creeper.say", 0.6, 1.8),
m_bIsBlowing(false),
- m_bIsCharged(false)
+ m_bIsCharged(false),
+ m_ExplodingTimer(0)
{
}
@@ -19,11 +21,34 @@ cCreeper::cCreeper(void) :
+void cCreeper::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ if (!ReachedFinalDestination())
+ {
+ m_ExplodingTimer = 0;
+ m_bIsBlowing = false;
+ m_World->BroadcastEntityMetadata(*this);
+ }
+}
+
+
+
+
+
void cCreeper::GetDrops(cItems & a_Drops, cEntity * a_Killer)
{
AddRandomDropItem(a_Drops, 0, 2, E_ITEM_GUNPOWDER);
- // TODO Check if killed by a skeleton, then drop random music disk
+ if ((a_Killer != NULL) && (a_Killer->IsProjectile()))
+ {
+ if (((cMonster *)((cProjectileEntity *)a_Killer)->GetCreator())->GetMobType() == mtSkeleton)
+ {
+ // 12 music discs. TickRand starts from 0, so range = 11. Disk IDs start at 2256, so add that. There.
+ AddRandomDropItem(a_Drops, 1, 1, (short)m_World->GetTickRandomNumber(11) + 2256);
+ }
+ }
}
@@ -45,3 +70,27 @@ void cCreeper::DoTakeDamage(TakeDamageInfo & a_TDI)
+
+void cCreeper::Attack(float a_Dt)
+{
+ UNUSED(a_Dt);
+
+ m_ExplodingTimer += 1;
+
+ if (!m_bIsBlowing)
+ {
+ m_World->BroadcastSoundEffect("random.fuse", (int)GetPosX() * 8, (int)GetPosY() * 8, (int)GetPosZ() * 8, 1.f, (float)(0.75 + ((float)((GetUniqueID() * 23) % 32)) / 64));
+ m_bIsBlowing = true;
+ m_World->BroadcastEntityMetadata(*this);
+ }
+
+ if (m_ExplodingTimer == 20)
+ {
+ m_World->DoExplosionAt((m_bIsCharged ? 5 : 3), GetPosX(), GetPosY(), GetPosZ(), false, esMonster, this);
+ Destroy();
+ }
+}
+
+
+
+
diff --git a/src/Mobs/Creeper.h b/src/Mobs/Creeper.h
index c3d4edeae..0f71e5ad2 100644
--- a/src/Mobs/Creeper.h
+++ b/src/Mobs/Creeper.h
@@ -19,6 +19,8 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+ virtual void Attack(float a_Dt) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
bool IsBlowing(void) const {return m_bIsBlowing; }
bool IsCharged(void) const {return m_bIsCharged; }
@@ -26,6 +28,7 @@ public:
private:
bool m_bIsBlowing, m_bIsCharged;
+ int m_ExplodingTimer;
} ;
diff --git a/src/Mobs/IronGolem.h b/src/Mobs/IronGolem.h
index d49ff4cab..41c60438c 100644
--- a/src/Mobs/IronGolem.h
+++ b/src/Mobs/IronGolem.h
@@ -18,6 +18,10 @@ public:
CLASS_PROTODEF(cIronGolem);
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+
+ // Iron golems do not drown
+ virtual void HandleAir(void) override {}
+ virtual void SetSwimState(cChunk & a_Chunk) override {}
} ;
diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp
index 98b6c1d28..42c7d2899 100644
--- a/src/Mobs/Monster.cpp
+++ b/src/Mobs/Monster.cpp
@@ -8,14 +8,9 @@
#include "../World.h"
#include "../Entities/Player.h"
#include "../Entities/ExpOrb.h"
-#include "../Defines.h"
#include "../MonsterConfig.h"
#include "../MersenneTwister.h"
-#include "../Vector3f.h"
-#include "../Vector3i.h"
-#include "../Vector3d.h"
-#include "../Tracer.h"
#include "../Chunk.h"
#include "../FastRandom.h"
@@ -79,17 +74,15 @@ cMonster::cMonster(const AString & a_ConfigName, eType a_MobType, const AString
, m_AttackRate(3)
, m_IdleInterval(0)
, m_bMovingToDestination(false)
- , m_DestinationTime( 0 )
- , m_DestroyTimer( 0 )
- , m_Jump(0)
+ , m_DestroyTimer(0)
, m_MobType(a_MobType)
, m_SoundHurt(a_SoundHurt)
, m_SoundDeath(a_SoundDeath)
- , m_SeePlayerInterval (0)
- , m_AttackDamage(1.0f)
- , m_AttackRange(2.0f)
+ , m_AttackDamage(1)
+ , m_AttackRange(2)
, m_AttackInterval(0)
, m_BurnsInDaylight(false)
+ , m_LastGroundHeight(POSY_TOINT)
{
if (!a_ConfigName.empty())
{
@@ -110,11 +103,105 @@ void cMonster::SpawnOn(cClientHandle & a_Client)
-void cMonster::MoveToPosition( const Vector3f & a_Position )
+void cMonster::TickPathFinding()
{
+ int PosX = (int)floor(GetPosX());
+ int PosY = (int)floor(GetPosY());
+ int PosZ = (int)floor(GetPosZ());
+
+ m_FinalDestination.y = (double)FindFirstNonAirBlockPosition(m_FinalDestination.x, m_FinalDestination.z);
+
+ std::vector<Vector3d> m_PotentialCoordinates;
+ m_TraversedCoordinates.push_back(Vector3i(PosX, PosY, PosZ));
+
+ static const struct // Define which directions to try to move to
+ {
+ int x, z;
+ } gCrossCoords[] =
+ {
+ { 1, 0},
+ {-1, 0},
+ { 0, 1},
+ { 0,-1},
+ } ;
+
+ for (size_t i = 0; i < ARRAYCOUNT(gCrossCoords); i++)
+ {
+ if ((gCrossCoords[i].x + PosX == PosX) && (gCrossCoords[i].z + PosZ == PosZ))
+ {
+ continue;
+ }
+
+ if (IsCoordinateInTraversedList(Vector3i(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ)))
+ {
+ continue;
+ }
+
+ BLOCKTYPE BlockAtY = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY, gCrossCoords[i].z + PosZ);
+ BLOCKTYPE BlockAtYP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 1, gCrossCoords[i].z + PosZ);
+ BLOCKTYPE BlockAtYPP = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY + 2, gCrossCoords[i].z + PosZ);
+ BLOCKTYPE BlockAtYM = m_World->GetBlock(gCrossCoords[i].x + PosX, PosY - 1, gCrossCoords[i].z + PosZ);
+
+ if (!g_BlockIsSolid[BlockAtY] && !g_BlockIsSolid[BlockAtYP] && !IsBlockLava(BlockAtYM))
+ {
+ m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY, gCrossCoords[i].z + PosZ));
+ }
+ else if (g_BlockIsSolid[BlockAtY] && !g_BlockIsSolid[BlockAtYP] && !g_BlockIsSolid[BlockAtYPP] && !IsBlockLava(BlockAtYM))
+ {
+ m_PotentialCoordinates.push_back(Vector3d((gCrossCoords[i].x + PosX), PosY + 1, gCrossCoords[i].z + PosZ));
+ }
+ }
+
+ if (!m_PotentialCoordinates.empty())
+ {
+ Vector3f ShortestCoords = m_PotentialCoordinates.front();
+ for (std::vector<Vector3d>::const_iterator itr = m_PotentialCoordinates.begin(); itr != m_PotentialCoordinates.end(); ++itr)
+ {
+ Vector3f Distance = m_FinalDestination - ShortestCoords;
+ Vector3f Distance2 = m_FinalDestination - *itr;
+ if (Distance.SqrLength() > Distance2.SqrLength())
+ {
+ ShortestCoords = *itr;
+ }
+ }
+
+ m_Destination = ShortestCoords;
+ m_Destination.z += 0.5f;
+ m_Destination.x += 0.5f;
+ }
+ else
+ {
+ FinishPathFinding();
+ }
+}
+
+
+
+
+
+void cMonster::MoveToPosition(const Vector3f & a_Position)
+{
+ FinishPathFinding();
+
+ m_FinalDestination = a_Position;
m_bMovingToDestination = true;
+ TickPathFinding();
+}
+
+
- m_Destination = a_Position;
+
+bool cMonster::IsCoordinateInTraversedList(Vector3i a_Coords)
+{
+ for (std::vector<Vector3i>::const_iterator itr = m_TraversedCoordinates.begin(); itr != m_TraversedCoordinates.end(); ++itr)
+ {
+ if (itr->Equals(a_Coords))
+ {
+ return true;
+ }
+ }
+
+ return false;
}
@@ -123,10 +210,24 @@ void cMonster::MoveToPosition( const Vector3f & a_Position )
bool cMonster::ReachedDestination()
{
- Vector3f Distance = (m_Destination) - GetPosition();
- if( Distance.SqrLength() < 2.f )
+ if ((m_Destination - GetPosition()).Length() < 0.5f)
+ {
return true;
+ }
+
+ return false;
+}
+
+
+
+bool cMonster::ReachedFinalDestination()
+{
+ if ((GetPosition() - m_FinalDestination).Length() <= m_AttackRange)
+ {
+ return true;
+ }
+
return false;
}
@@ -149,25 +250,35 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
return;
}
+ if ((m_Target != NULL) && m_Target->IsDestroyed())
+ m_Target = NULL;
+
// Burning in daylight
HandleDaylightBurning(a_Chunk);
-
- HandlePhysics(a_Dt,a_Chunk);
- BroadcastMovementUpdate();
a_Dt /= 1000;
if (m_bMovingToDestination)
{
- Vector3f Pos( GetPosition() );
- Vector3f Distance = m_Destination - Pos;
- if( !ReachedDestination() )
+ if (m_bOnGround)
+ {
+ m_Destination.y = FindFirstNonAirBlockPosition(m_Destination.x, m_Destination.z);
+
+ if (DoesPosYRequireJump(m_Destination.y))
+ {
+ m_bOnGround = false;
+ AddPosY(1.5); // Jump!!
+ }
+ }
+
+ Vector3f Distance = m_Destination - GetPosition();
+ if(!ReachedDestination() && !ReachedFinalDestination()) // If we haven't reached any sort of destination, move
{
Distance.y = 0;
Distance.Normalize();
Distance *= 3;
- SetSpeedX( Distance.x );
- SetSpeedZ( Distance.z );
+ SetSpeedX(Distance.x);
+ SetSpeedZ(Distance.z);
if (m_EMState == ESCAPING)
{ //Runs Faster when escaping :D otherwise they just walk away
@@ -177,40 +288,22 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
}
else
{
- m_bMovingToDestination = false;
- }
-
- if( GetSpeed().SqrLength() > 0.f )
- {
- if( m_bOnGround )
+ if (ReachedFinalDestination()) // If we have reached the ultimate, final destination, stop pathfinding and attack if appropriate
+ {
+ FinishPathFinding();
+ }
+ else
{
- Vector3f NormSpeed = Vector3f(GetSpeed()).NormalizeCopy();
- Vector3f NextBlock = Vector3f( GetPosition() ) + NormSpeed;
- int NextHeight;
- if (!m_World->TryGetHeight((int)NextBlock.x, (int)NextBlock.z, NextHeight))
- {
- // The chunk at NextBlock is not loaded
- return;
- }
- if( NextHeight > (GetPosY() - 1.0) && (NextHeight - GetPosY()) < 2.5 )
- {
- m_bOnGround = false;
- SetSpeedY(5.f); // Jump!!
- }
+ TickPathFinding(); // We have reached the next point in our path, calculate another point
}
}
}
- Vector3d Distance = m_Destination - GetPosition();
- if (Distance.SqrLength() > 0.1f)
- {
- double Rotation, Pitch;
- Distance.Normalize();
- VectorToEuler( Distance.x, Distance.y, Distance.z, Rotation, Pitch );
- SetHeadYaw (Rotation);
- SetYaw( Rotation );
- SetPitch( -Pitch );
- }
+ if (ReachedFinalDestination() && (m_Target != NULL))
+ Attack(a_Dt);
+
+ SetPitchAndYawFromDestination();
+ HandleFalling();
switch (m_EMState)
{
@@ -219,21 +312,113 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
// If enemy passive we ignore checks for player visibility
InStateIdle(a_Dt);
break;
- }
-
+ }
case CHASING:
{
// If we do not see a player anymore skip chasing action
InStateChasing(a_Dt);
break;
- }
-
+ }
case ESCAPING:
{
InStateEscaping(a_Dt);
break;
}
} // switch (m_EMState)
+
+ BroadcastMovementUpdate();
+}
+
+
+
+
+void cMonster::SetPitchAndYawFromDestination()
+{
+ Vector3d FinalDestination = m_FinalDestination;
+ if (m_Target != NULL)
+ {
+ if (m_Target->IsPlayer())
+ {
+ FinalDestination.y = ((cPlayer *)m_Target)->GetStance();
+ }
+ else
+ {
+ FinalDestination.y = GetHeight();
+ }
+ }
+
+ Vector3d Distance = FinalDestination - GetPosition();
+ if (Distance.SqrLength() > 0.1f)
+ {
+ {
+ double Rotation, Pitch;
+ Distance.Normalize();
+ VectorToEuler(Distance.x, Distance.y, Distance.z, Rotation, Pitch);
+ SetHeadYaw(Rotation);
+ SetPitch(-Pitch);
+ }
+
+ {
+ Vector3d BodyDistance = m_Destination - GetPosition();
+ double Rotation, Pitch;
+ Distance.Normalize();
+ VectorToEuler(BodyDistance.x, BodyDistance.y, BodyDistance.z, Rotation, Pitch);
+ SetYaw(Rotation);
+ }
+ }
+}
+
+
+
+
+void cMonster::HandleFalling()
+{
+ if (m_bOnGround)
+ {
+ int Damage = (m_LastGroundHeight - POSY_TOINT) - 3;
+
+ if (Damage > 0)
+ {
+ TakeDamage(dtFalling, NULL, Damage, Damage, 0);
+
+ // Fall particles
+ GetWorld()->BroadcastSoundParticleEffect(2006, POSX_TOINT, POSY_TOINT - 1, POSZ_TOINT, Damage /* Used as particle effect speed modifier */);
+ }
+
+ m_LastGroundHeight = (int)floor(GetPosY());
+ }
+}
+
+
+
+
+int cMonster::FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ)
+{
+ int PosY = (int)floor(GetPosY());
+
+ if (PosY < 0)
+ PosY = 0;
+ else if (PosY > cChunkDef::Height)
+ PosY = cChunkDef::Height;
+
+ if (!g_BlockIsSolid[m_World->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))])
+ {
+ while (!g_BlockIsSolid[m_World->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))] && (PosY > 0))
+ {
+ PosY--;
+ }
+
+ return PosY + 1;
+ }
+ else
+ {
+ while (g_BlockIsSolid[m_World->GetBlock((int)floor(a_PosX), PosY, (int)floor(a_PosZ))] && (PosY < cChunkDef::Height))
+ {
+ PosY++;
+ }
+
+ return PosY;
+ }
}
@@ -244,11 +429,13 @@ void cMonster::Tick(float a_Dt, cChunk & a_Chunk)
void cMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
{
super::DoTakeDamage(a_TDI);
- if((m_SoundHurt != "") && (m_Health > 0)) m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f);
+
+ if((m_SoundHurt != "") && (m_Health > 0))
+ m_World->BroadcastSoundEffect(m_SoundHurt, (int)(GetPosX() * 8), (int)(GetPosY() * 8), (int)(GetPosZ() * 8), 1.0f, 0.8f);
+
if (a_TDI.Attacker != NULL)
{
m_Target = a_TDI.Attacker;
- AddReference(m_Target);
}
}
@@ -330,55 +517,12 @@ void cMonster::KilledBy(cEntity * a_Killer)
-//----State Logic
-
-const char *cMonster::GetState()
-{
- switch(m_EMState)
- {
- case IDLE: return "Idle";
- case ATTACKING: return "Attacking";
- case CHASING: return "Chasing";
- default: return "Unknown";
- }
-}
-
-
-
-
-
-// for debugging
-void cMonster::SetState(const AString & a_State)
-{
- if (a_State.compare("Idle") == 0)
- {
- m_EMState = IDLE;
- }
- else if (a_State.compare("Attacking") == 0)
- {
- m_EMState = ATTACKING;
- }
- else if (a_State.compare("Chasing") == 0)
- {
- m_EMState = CHASING;
- }
- else
- {
- LOGD("cMonster::SetState(): Invalid state");
- ASSERT(!"Invalid state");
- }
-}
-
-
-
-
-
//Checks to see if EventSeePlayer should be fired
//monster sez: Do I see the player
void cMonster::CheckEventSeePlayer(void)
{
// TODO: Rewrite this to use cWorld's DoWithPlayers()
- cPlayer * Closest = FindClosestPlayer();
+ cPlayer * Closest = m_World->FindClosestPlayer(GetPosition(), (float)m_SightDistance, false);
if (Closest != NULL)
{
@@ -391,14 +535,10 @@ void cMonster::CheckEventSeePlayer(void)
void cMonster::CheckEventLostPlayer(void)
-{
- Vector3f pos;
- cTracer LineOfSight(GetWorld());
-
+{
if (m_Target != NULL)
{
- pos = m_Target->GetPosition();
- if ((pos - GetPosition()).Length() > m_SightDistance || LineOfSight.Trace(GetPosition(),(pos - GetPosition()), (int)(pos - GetPosition()).Length()))
+ if ((m_Target->GetPosition() - GetPosition()).Length() > m_SightDistance)
{
EventLosePlayer();
}
@@ -418,7 +558,6 @@ void cMonster::CheckEventLostPlayer(void)
void cMonster::EventSeePlayer(cEntity * a_SeenPlayer)
{
m_Target = a_SeenPlayer;
- AddReference(m_Target);
}
@@ -427,7 +566,6 @@ void cMonster::EventSeePlayer(cEntity * a_SeenPlayer)
void cMonster::EventLosePlayer(void)
{
- Dereference(m_Target);
m_Target = NULL;
m_EMState = IDLE;
}
@@ -436,28 +574,35 @@ void cMonster::EventLosePlayer(void)
-// What to do if in Idle State
void cMonster::InStateIdle(float a_Dt)
{
+ if (m_bMovingToDestination)
+ {
+ return; // Still getting there
+ }
+
m_IdleInterval += a_Dt;
+
if (m_IdleInterval > 1)
{
- // at this interval the results are predictable
+ // At this interval the results are predictable
int rem = m_World->GetTickRandomNumber(6) + 1;
- // LOGD("Moving: int: %3.3f rem: %i",idle_interval,rem);
- m_IdleInterval -= 1; // So nothing gets dropped when the server hangs for a few seconds
- Vector3f Dist;
- Dist.x = (float)(m_World->GetTickRandomNumber(10) - 5);
- Dist.z = (float)(m_World->GetTickRandomNumber(10) - 5);
+ m_IdleInterval -= 1; // So nothing gets dropped when the server hangs for a few seconds
+
+ Vector3d Dist;
+ Dist.x = (double)m_World->GetTickRandomNumber(10) - 5;
+ Dist.z = (double)m_World->GetTickRandomNumber(10) - 5;
+
if ((Dist.SqrLength() > 2) && (rem >= 3))
{
- m_Destination.x = (float)(GetPosX() + Dist.x);
- m_Destination.z = (float)(GetPosZ() + Dist.z);
- int PosY;
- if (m_World->TryGetHeight((int)m_Destination.x, (int)m_Destination.z, PosY))
+ Vector3d Destination(GetPosX() + Dist.x, 0, GetPosZ() + Dist.z);
+
+ int NextHeight = FindFirstNonAirBlockPosition(Destination.x, Destination.z);
+
+ if (IsNextYPosReachable(NextHeight))
{
- m_Destination.y = (float)PosY + 1.2f;
- MoveToPosition(m_Destination);
+ Destination.y = NextHeight;
+ MoveToPosition(Destination);
}
}
}
@@ -505,22 +650,6 @@ void cMonster::InStateEscaping(float a_Dt)
void cMonster::Attack(float a_Dt)
{
m_AttackInterval += a_Dt * m_AttackRate;
- if ((m_Target != NULL) && (m_AttackInterval > 3.0))
- {
- // Setting this higher gives us more wiggle room for attackrate
- m_AttackInterval = 0.0;
- ((cPawn *)m_Target)->TakeDamage(*this);
- }
-}
-
-
-
-
-
-// Checks for Players close by and if they are visible return the closest
-cPlayer * cMonster::FindClosestPlayer(void)
-{
- return m_World->FindClosestPlayer(GetPosition(), m_SightDistance);
}
@@ -536,42 +665,6 @@ void cMonster::GetMonsterConfig(const AString & a_Name)
-void cMonster::SetAttackRate(int ar)
-{
- m_AttackRate = (float)ar;
-}
-
-
-
-
-
-void cMonster::SetAttackRange(float ar)
-{
- m_AttackRange = ar;
-}
-
-
-
-
-
-void cMonster::SetAttackDamage(float ad)
-{
- m_AttackDamage = ad;
-}
-
-
-
-
-
-void cMonster::SetSightDistance(float sd)
-{
- m_SightDistance = sd;
-}
-
-
-
-
-
AString cMonster::MobTypeToString(cMonster::eType a_MobType)
{
// Mob types aren't sorted, so we need to search linearly:
@@ -635,6 +728,8 @@ cMonster::eType cMonster::StringToMobType(const AString & a_Name)
cMonster::eFamily cMonster::FamilyFromType(eType a_Type)
{
+ // Passive-agressive mobs are counted in mob spawning code as passive
+
switch (a_Type)
{
case mtBat: return mfAmbient;
@@ -699,7 +794,7 @@ cMonster * cMonster::NewMonsterFromType(cMonster::eType a_MobType)
case mtMagmaCube:
case mtSlime:
{
- toReturn = new cSlime (Random.NextInt(2) + 1);
+ toReturn = new cSlime(Random.NextInt(2) + 1);
break;
}
case mtSkeleton:
@@ -803,6 +898,13 @@ void cMonster::HandleDaylightBurning(cChunk & a_Chunk)
int RelX = (int)floor(GetPosX()) - GetChunkX() * cChunkDef::Width;
int RelZ = (int)floor(GetPosZ()) - GetChunkZ() * cChunkDef::Width;
+
+ if (!a_Chunk.IsLightValid())
+ {
+ m_World->QueueLightChunk(GetChunkX(), GetChunkZ());
+ return;
+ }
+
if (
(a_Chunk.GetSkyLight(RelX, RelY, RelZ) == 15) && // In the daylight
(a_Chunk.GetBlock(RelX, RelY, RelZ) != E_BLOCK_SOULSAND) && // Not on soulsand
diff --git a/src/Mobs/Monster.h b/src/Mobs/Monster.h
index dafb33574..1dd302cdc 100644
--- a/src/Mobs/Monster.h
+++ b/src/Mobs/Monster.h
@@ -10,7 +10,6 @@
-class Vector3f;
class cClientHandle;
class cWorld;
@@ -74,8 +73,6 @@ public:
enum MState{ATTACKING, IDLE, CHASING, ESCAPING} m_EMState;
enum MPersonality{PASSIVE,AGGRESSIVE,COWARDLY} m_EMPersonality;
- float m_SightDistance;
-
/** Creates the mob object.
* If a_ConfigName is not empty, the configuration is loaded using GetMonsterConfig()
* a_MobType is the type of the mob (also used in the protocol ( http://wiki.vg/Entities#Mobs , 2012_12_22))
@@ -100,14 +97,9 @@ public:
eType GetMobType(void) const {return m_MobType; }
eFamily GetMobFamily(void) const;
// tolua_end
-
-
- const char * GetState();
- void SetState(const AString & str);
virtual void CheckEventSeePlayer(void);
virtual void EventSeePlayer(cEntity * a_Player);
- virtual cPlayer * FindClosestPlayer(); // non static is easier. also virtual so other mobs can implement their own searching algo
/// Reads the monster configuration for the specified monster name and assigns it to this object.
void GetMonsterConfig(const AString & a_Name);
@@ -121,11 +113,11 @@ public:
virtual void Attack(float a_Dt);
- int GetAttackRate(){return (int)m_AttackRate;}
- void SetAttackRate(int ar);
- void SetAttackRange(float ar);
- void SetAttackDamage(float ad);
- void SetSightDistance(float sd);
+ int GetAttackRate() { return (int)m_AttackRate; }
+ void SetAttackRate(float a_AttackRate) { m_AttackRate = a_AttackRate; }
+ void SetAttackRange(int a_AttackRange) { m_AttackRange = a_AttackRange; }
+ void SetAttackDamage(int a_AttackDamage) { m_AttackDamage = a_AttackDamage; }
+ void SetSightDistance(int a_SightDistance) { m_SightDistance = a_SightDistance; }
/// Sets whether the mob burns in daylight. Only evaluated at next burn-decision tick
void SetBurnsInDaylight(bool a_BurnsInDaylight) { m_BurnsInDaylight = a_BurnsInDaylight; }
@@ -159,34 +151,80 @@ public:
protected:
- cEntity * m_Target;
- float m_AttackRate;
- float m_IdleInterval;
+ /* ======= PATHFINDING ======= */
- Vector3f m_Destination;
+ /** A pointer to the entity this mobile is aiming to reach */
+ cEntity * m_Target;
+ /** Coordinates of the next position that should be reached */
+ Vector3d m_Destination;
+ /** Coordinates for the ultimate, final destination. */
+ Vector3d m_FinalDestination;
+ /** Returns if the ultimate, final destination has been reached */
+ bool ReachedFinalDestination(void);
+
+ /** Stores if mobile is currently moving towards the ultimate, final destination */
bool m_bMovingToDestination;
- bool m_bPassiveAggressive;
+ /** Finds the first non-air block position (not the highest, as cWorld::GetHeight does)
+ If current Y is nonsolid, goes down to try to find a solid block, then returns that + 1
+ If current Y is solid, goes up to find first nonsolid block, and returns that */
+ int FindFirstNonAirBlockPosition(double a_PosX, double a_PosZ);
+ /** Returns if a monster can actually reach a given height by jumping or walking */
+ inline bool IsNextYPosReachable(int a_PosY)
+ {
+ return (
+ (a_PosY <= (int)floor(GetPosY())) ||
+ DoesPosYRequireJump(a_PosY)
+ );
+ }
+ /** Returns if a monster can reach a given height by jumping */
+ inline bool DoesPosYRequireJump(int a_PosY)
+ {
+ return ((a_PosY > (int)floor(GetPosY())) && (a_PosY == (int)floor(GetPosY()) + 1));
+ }
+
+ /** A semi-temporary list to store the traversed coordinates during active pathfinding so we don't visit them again */
+ std::vector<Vector3i> m_TraversedCoordinates;
+ /** Returns if coordinate is in the traversed list */
+ bool IsCoordinateInTraversedList(Vector3i a_Coords);
+
+ /** Finds the next place to go
+ This is based on the ultimate, final destination and the current position, as well as the traversed coordinates, and any environmental hazards */
+ void TickPathFinding(void);
+ /** Finishes a pathfinding task, be it due to failure or something else */
+ inline void FinishPathFinding(void)
+ {
+ m_TraversedCoordinates.clear();
+ m_bMovingToDestination = false;
+ }
+ /** Sets the body yaw and head yaw/pitch based on next/ultimate destinations */
+ void SetPitchAndYawFromDestination(void);
+
+ /* =========================== */
+ /* ========= FALLING ========= */
- float m_DestinationTime;
+ virtual void HandleFalling(void);
+ int m_LastGroundHeight;
+ /* =========================== */
+
+ float m_IdleInterval;
float m_DestroyTimer;
- float m_Jump;
eType m_MobType;
AString m_SoundHurt;
AString m_SoundDeath;
- float m_SeePlayerInterval;
- float m_AttackDamage;
- float m_AttackRange;
+ float m_AttackRate;
+ int m_AttackDamage;
+ int m_AttackRange;
float m_AttackInterval;
+ int m_SightDistance;
+ void HandleDaylightBurning(cChunk & a_Chunk);
bool m_BurnsInDaylight;
- void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0);
-
- void HandleDaylightBurning(cChunk & a_Chunk);
+ void AddRandomDropItem(cItems & a_Drops, unsigned int a_Min, unsigned int a_Max, short a_Item, short a_ItemHealth = 0);
} ; // tolua_export
diff --git a/src/Mobs/PassiveAggressiveMonster.cpp b/src/Mobs/PassiveAggressiveMonster.cpp
index 28de65905..4b45f9a2a 100644
--- a/src/Mobs/PassiveAggressiveMonster.cpp
+++ b/src/Mobs/PassiveAggressiveMonster.cpp
@@ -25,8 +25,7 @@ void cPassiveAggressiveMonster::DoTakeDamage(TakeDamageInfo & a_TDI)
if ((m_Target != NULL) && (m_Target->IsPlayer()))
{
- cPlayer * Player = (cPlayer *) m_Target;
- if (Player->GetGameMode() != 1)
+ if (!((cPlayer *)m_Target)->IsGameModeCreative())
{
m_EMState = CHASING;
}
diff --git a/src/Mobs/PassiveMonster.cpp b/src/Mobs/PassiveMonster.cpp
index 91ceb5a53..d774d3170 100644
--- a/src/Mobs/PassiveMonster.cpp
+++ b/src/Mobs/PassiveMonster.cpp
@@ -2,7 +2,6 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "PassiveMonster.h"
-#include "../MersenneTwister.h"
#include "../World.h"
@@ -36,20 +35,9 @@ void cPassiveMonster::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
- m_SeePlayerInterval += a_Dt;
-
- if (m_SeePlayerInterval > 1) // Check every second
+ if (m_EMState == ESCAPING)
{
- int rem = m_World->GetTickRandomNumber(3) + 1; // Check most of the time but miss occasionally
-
- m_SeePlayerInterval = 0.0;
- if (rem >= 2)
- {
- if (m_EMState == ESCAPING)
- {
- CheckEventLostPlayer();
- }
- }
+ CheckEventLostPlayer();
}
}
diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp
index bda4ccff8..702108ae4 100644
--- a/src/Mobs/Sheep.cpp
+++ b/src/Mobs/Sheep.cpp
@@ -13,7 +13,8 @@
cSheep::cSheep(int a_Color) :
super("Sheep", mtSheep, "mob.sheep.say", "mob.sheep.say", 0.6, 1.3),
m_IsSheared(false),
- m_WoolColor(a_Color)
+ m_WoolColor(a_Color),
+ m_TimeToStopEating(-1)
{
}
@@ -60,3 +61,39 @@ void cSheep::OnRightClicked(cPlayer & a_Player)
m_World->BroadcastEntityMetadata(*this);
}
}
+
+
+
+
+
+void cSheep::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ // The sheep should not move when he's eating so only handle the physics.
+ if (m_TimeToStopEating > 0)
+ {
+ HandlePhysics(a_Dt, a_Chunk);
+ m_TimeToStopEating--;
+ if (m_TimeToStopEating == 0)
+ {
+ if (m_World->GetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ()) == E_BLOCK_GRASS)
+ {
+ // The sheep ate the grass so we change it to dirt.
+ m_World->SetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ(), E_BLOCK_DIRT, 0);
+ m_IsSheared = false;
+ m_World->BroadcastEntityMetadata(*this);
+ }
+ }
+ }
+ else
+ {
+ super::Tick(a_Dt, a_Chunk);
+ if (m_World->GetTickRandomNumber(600) == 1)
+ {
+ if (m_World->GetBlock((int) GetPosX(), (int) GetPosY() - 1, (int) GetPosZ()) == E_BLOCK_GRASS)
+ {
+ m_World->BroadcastEntityStatus(*this, 10);
+ m_TimeToStopEating = 40;
+ }
+ }
+ }
+}
diff --git a/src/Mobs/Sheep.h b/src/Mobs/Sheep.h
index 8293a2c05..4eee3db1c 100644
--- a/src/Mobs/Sheep.h
+++ b/src/Mobs/Sheep.h
@@ -19,6 +19,8 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
bool IsSheared(void) const { return m_IsSheared; }
int GetFurColor(void) const { return m_WoolColor; }
@@ -26,6 +28,7 @@ private:
bool m_IsSheared;
int m_WoolColor;
+ int m_TimeToStopEating;
} ;
diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp
index 509c2191e..4c8e78988 100644
--- a/src/Mobs/Skeleton.cpp
+++ b/src/Mobs/Skeleton.cpp
@@ -30,15 +30,18 @@ void cSkeleton::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cSkeleton::MoveToPosition(const Vector3f & a_Position)
{
- m_Destination = a_Position;
-
// If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement.
- if (!IsOnFire() && m_World->GetTimeOfDay() < 13187 && m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15)
+ if (
+ !IsOnFire() &&
+ (m_World->GetTimeOfDay() < 13187) &&
+ (m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15)
+ )
{
m_bMovingToDestination = false;
return;
}
- m_bMovingToDestination = true;
+
+ super::MoveToPosition(a_Position);
}
diff --git a/src/Mobs/Squid.cpp b/src/Mobs/Squid.cpp
index a311108ae..5a27762ff 100644
--- a/src/Mobs/Squid.cpp
+++ b/src/Mobs/Squid.cpp
@@ -43,7 +43,8 @@ void cSquid::Tick(float a_Dt, cChunk & a_Chunk)
}
int RelX = (int)floor(Pos.x) - a_Chunk.GetPosX() * cChunkDef::Width;
int RelZ = (int)floor(Pos.z) - a_Chunk.GetPosZ() * cChunkDef::Width;
- if (!IsBlockWater(a_Chunk.GetBlock(RelX, RelY, RelZ)) && !IsOnFire())
+ BLOCKTYPE BlockType;
+ if (a_Chunk.UnboundedRelGetBlockType(RelX, RelY, RelZ, BlockType) && !IsBlockWater(BlockType) && !IsOnFire())
{
// Burn for 10 ticks, then decide again
StartBurning(10);
diff --git a/src/Mobs/Squid.h b/src/Mobs/Squid.h
index ad299b95c..a9dba8b70 100644
--- a/src/Mobs/Squid.h
+++ b/src/Mobs/Squid.h
@@ -21,6 +21,9 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+ // Squids do not drown (or float)
+ virtual void HandleAir(void) override {}
+ virtual void SetSwimState(cChunk & a_Chunk) override {}
} ;
diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp
index 7f89fb6cc..c8ff4f101 100644
--- a/src/Mobs/Villager.cpp
+++ b/src/Mobs/Villager.cpp
@@ -21,7 +21,7 @@ cVillager::cVillager(eVillagerType VillagerType) :
void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
{
super::DoTakeDamage(a_TDI);
- if (a_TDI.Attacker->IsPlayer())
+ if ((a_TDI.Attacker != NULL) && a_TDI.Attacker->IsPlayer())
{
if (m_World->GetTickRandomNumber(5) == 3)
{
diff --git a/src/Mobs/Wolf.cpp b/src/Mobs/Wolf.cpp
index 3d4e97c80..c0c7892e3 100644
--- a/src/Mobs/Wolf.cpp
+++ b/src/Mobs/Wolf.cpp
@@ -37,6 +37,26 @@ void cWolf::DoTakeDamage(TakeDamageInfo & a_TDI)
+void cWolf::Attack(float a_Dt)
+{
+ UNUSED(a_Dt);
+
+ if ((m_Target != NULL) && (m_Target->IsPlayer()))
+ {
+ if (((cPlayer *)m_Target)->GetName() != m_OwnerName)
+ {
+ super::Attack(a_Dt);
+ }
+ }
+ else
+ {
+ super::Attack(a_Dt);
+ }
+}
+
+
+
+
void cWolf::OnRightClicked(cPlayer & a_Player)
{
@@ -55,10 +75,12 @@ void cWolf::OnRightClicked(cPlayer & a_Player)
SetIsTame(true);
SetOwner(a_Player.GetName());
m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMED);
+ m_World->BroadcastParticleEffect("heart", (float) GetPosX(), (float) GetPosY(), (float) GetPosZ(), 0, 0, 0, 0, 5);
}
else
{
m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_WOLF_TAMING);
+ m_World->BroadcastParticleEffect("smoke", (float) GetPosX(), (float) GetPosY(), (float) GetPosZ(), 0, 0, 0, 0, 5);
}
}
}
@@ -102,13 +124,14 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
{
super::Tick(a_Dt, a_Chunk);
}
-
+
+ // The wolf is sitting so don't move him at all.
if (IsSitting())
{
m_bMovingToDestination = false;
}
- cPlayer * a_Closest_Player = FindClosestPlayer();
+ cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), (float)m_SightDistance);
if (a_Closest_Player != NULL)
{
switch (a_Closest_Player->GetEquippedItem().m_ItemType)
@@ -125,10 +148,18 @@ void cWolf::Tick(float a_Dt, cChunk & a_Chunk)
SetIsBegging(true);
m_World->BroadcastEntityMetadata(*this);
}
- Vector3f a_NewDestination = a_Closest_Player->GetPosition();
- a_NewDestination.y = a_NewDestination.y + 1; // Look at the head of the player, not his feet.
- m_Destination = Vector3f(a_NewDestination);
- m_bMovingToDestination = false;
+ // Don't move to the player if the wolf is sitting.
+ if (IsSitting())
+ {
+ m_bMovingToDestination = false;
+ }
+ else
+ {
+ m_bMovingToDestination = true;
+ }
+ Vector3d PlayerPos = a_Closest_Player->GetPosition();
+ PlayerPos.y++;
+ m_FinalDestination = PlayerPos;
break;
}
default:
@@ -163,23 +194,30 @@ void cWolf::TickFollowPlayer()
return false;
}
public:
- Vector3f OwnerPos;
+ Vector3d OwnerPos;
} Callback;
if (m_World->DoWithPlayer(m_OwnerName, Callback))
{
- // The player is present in the world, follow them:
+ // The player is present in the world, follow him:
double Distance = (Callback.OwnerPos - GetPosition()).Length();
- if (Distance < 3)
- {
- m_bMovingToDestination = false;
- }
- else if ((Distance > 30) && (!IsSitting()))
+ if (Distance > 30)
{
- TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z);
+ if (!IsSitting())
+ {
+ TeleportToCoords(Callback.OwnerPos.x, Callback.OwnerPos.y, Callback.OwnerPos.z);
+ }
}
else
{
- m_Destination = Callback.OwnerPos;
+ m_FinalDestination = Callback.OwnerPos;
+ if (IsSitting())
+ {
+ m_bMovingToDestination = false;
+ }
+ else
+ {
+ m_bMovingToDestination = true;
+ }
}
}
}
diff --git a/src/Mobs/Wolf.h b/src/Mobs/Wolf.h
index 040e2cf7a..9e5ad03c7 100644
--- a/src/Mobs/Wolf.h
+++ b/src/Mobs/Wolf.h
@@ -22,6 +22,7 @@ public:
virtual void OnRightClicked(cPlayer & a_Player) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void TickFollowPlayer();
+ virtual void Attack(float a_Dt) override;
// Get functions
bool IsSitting (void) const { return m_IsSitting; }
diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp
index a046fcc92..27e8ed5fb 100644
--- a/src/Mobs/Zombie.cpp
+++ b/src/Mobs/Zombie.cpp
@@ -34,15 +34,18 @@ void cZombie::GetDrops(cItems & a_Drops, cEntity * a_Killer)
void cZombie::MoveToPosition(const Vector3f & a_Position)
{
- m_Destination = a_Position;
-
- // If the destination is in the sun and if it is not night AND the skeleton isn't on fire then block the movement.
- if ((m_World->GetBlockSkyLight((int) a_Position.x, (int) a_Position.y, (int) a_Position.z) == 15) && (m_World->GetTimeOfDay() < 13187) && !IsOnFire())
+ // If the destination is in the sun and if it is not night AND the zombie isn't on fire then block the movement.
+ if (
+ !IsOnFire() &&
+ (m_World->GetTimeOfDay() < 13187) &&
+ (m_World->GetBlockSkyLight((int)a_Position.x, (int)a_Position.y, (int)a_Position.z) == 15)
+ )
{
m_bMovingToDestination = false;
return;
}
- m_bMovingToDestination = true;
+
+ super::MoveToPosition(a_Position);
}
diff --git a/src/MonsterConfig.cpp b/src/MonsterConfig.cpp
index 6165606f0..c06bd6b6f 100644
--- a/src/MonsterConfig.cpp
+++ b/src/MonsterConfig.cpp
@@ -12,9 +12,9 @@
struct cMonsterConfig::sAttributesStruct
{
AString m_Name;
- double m_SightDistance;
- double m_AttackDamage;
- double m_AttackRange;
+ int m_SightDistance;
+ int m_AttackDamage;
+ int m_AttackRange;
double m_AttackRate;
int m_MaxHealth;
};
@@ -67,9 +67,9 @@ void cMonsterConfig::Initialize()
sAttributesStruct Attributes;
AString Name = MonstersIniFile.GetKeyName(i);
Attributes.m_Name = Name;
- Attributes.m_AttackDamage = MonstersIniFile.GetValueF(Name, "AttackDamage", 0);
- Attributes.m_AttackRange = MonstersIniFile.GetValueF(Name, "AttackRange", 0);
- Attributes.m_SightDistance = MonstersIniFile.GetValueF(Name, "SightDistance", 0);
+ Attributes.m_AttackDamage = MonstersIniFile.GetValueI(Name, "AttackDamage", 0);
+ Attributes.m_AttackRange = MonstersIniFile.GetValueI(Name, "AttackRange", 0);
+ Attributes.m_SightDistance = MonstersIniFile.GetValueI(Name, "SightDistance", 0);
Attributes.m_AttackRate = MonstersIniFile.GetValueF(Name, "AttackRate", 0);
Attributes.m_MaxHealth = MonstersIniFile.GetValueI(Name, "MaxHealth", 1);
m_pState->AttributesList.push_front(Attributes);
@@ -87,10 +87,10 @@ void cMonsterConfig::AssignAttributes(cMonster * a_Monster, const AString & a_Na
{
if (itr->m_Name.compare(a_Name) == 0)
{
- a_Monster->SetAttackDamage ((float)itr->m_AttackDamage);
- a_Monster->SetAttackRange ((float)itr->m_AttackRange);
- a_Monster->SetSightDistance((float)itr->m_SightDistance);
- a_Monster->SetAttackRate ((int)itr->m_AttackRate);
+ a_Monster->SetAttackDamage (itr->m_AttackDamage);
+ a_Monster->SetAttackRange (itr->m_AttackRange);
+ a_Monster->SetSightDistance(itr->m_SightDistance);
+ a_Monster->SetAttackRate ((float)itr->m_AttackRate);
a_Monster->SetMaxHealth (itr->m_MaxHealth);
return;
}
diff --git a/src/OSSupport/BlockingTCPLink.cpp b/src/OSSupport/BlockingTCPLink.cpp
index 08aec0c65..af50eda5d 100644
--- a/src/OSSupport/BlockingTCPLink.cpp
+++ b/src/OSSupport/BlockingTCPLink.cpp
@@ -2,7 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "BlockingTCPLink.h"
-
+#include "Errors.h"
@@ -75,7 +75,7 @@ bool cBlockingTCPLink::Connect(const char * iAddress, unsigned int iPort)
server.sin_port = htons( (unsigned short)iPort);
if (connect(m_Socket, (struct sockaddr *)&server, sizeof(server)))
{
- LOGWARN("cTCPLink: Connection to \"%s:%d\" failed (%s)", iAddress, iPort, cSocket::GetErrorString( cSocket::GetLastError() ).c_str() );
+ LOGWARN("cTCPLink: Connection to \"%s:%d\" failed (%s)", iAddress, iPort,GetOSErrorString( cSocket::GetLastError() ).c_str() );
CloseSocket();
return false;
}
diff --git a/src/OSSupport/Errors.cpp b/src/OSSupport/Errors.cpp
new file mode 100644
index 000000000..2e05f1df1
--- /dev/null
+++ b/src/OSSupport/Errors.cpp
@@ -0,0 +1,53 @@
+
+#include "Globals.h"
+
+#include "Errors.h"
+
+AString GetOSErrorString( int a_ErrNo )
+{
+ char buffer[ 1024 ];
+ AString Out;
+
+ #ifdef _WIN32
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, a_ErrNo, 0, buffer, ARRAYCOUNT(buffer), NULL);
+ Printf(Out, "%d: %s", a_ErrNo, buffer);
+ if (!Out.empty() && (Out[Out.length() - 1] == '\n'))
+ {
+ Out.erase(Out.length() - 2);
+ }
+ return Out;
+
+ #else // _WIN32
+
+ // According to http://linux.die.net/man/3/strerror_r there are two versions of strerror_r():
+
+ #if ( _GNU_SOURCE ) && !defined(ANDROID_NDK) // GNU version of strerror_r()
+
+ char * res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) );
+ if( res != NULL )
+ {
+ Printf(Out, "%d: %s", a_ErrNo, res);
+ return Out;
+ }
+
+ #else // XSI version of strerror_r():
+
+ int res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) );
+ if( res == 0 )
+ {
+ Printf(Out, "%d: %s", a_ErrNo, buffer);
+ return Out;
+ }
+
+ #endif // strerror_r() version
+
+ else
+ {
+ Printf(Out, "Error %d while getting error string for error #%d!", errno, a_ErrNo);
+ return Out;
+ }
+
+ #endif // else _WIN32
+}
+
diff --git a/src/OSSupport/Errors.h b/src/OSSupport/Errors.h
new file mode 100644
index 000000000..8ce9deb10
--- /dev/null
+++ b/src/OSSupport/Errors.h
@@ -0,0 +1,5 @@
+
+#pragma once
+
+AString GetOSErrorString(int a_ErrNo);
+
diff --git a/src/OSSupport/Event.cpp b/src/OSSupport/Event.cpp
index cbacbba17..649a0a3cf 100644
--- a/src/OSSupport/Event.cpp
+++ b/src/OSSupport/Event.cpp
@@ -7,7 +7,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "Event.h"
-
+#include "Errors.h"
@@ -35,14 +35,16 @@ cEvent::cEvent(void)
m_Event = sem_open(EventName.c_str(), O_CREAT, 777, 0 );
if (m_Event == SEM_FAILED)
{
- LOGERROR("cEvent: Cannot create event, errno = %i. Aborting server.", errno);
+ AString error = GetOSErrorString(errno);
+ LOGERROR("cEvent: Cannot create event, err = %s. Aborting server.", error.c_str());
abort();
}
// Unlink the semaphore immediately - it will continue to function but will not pollute the namespace
// We don't store the name, so can't call this in the destructor
if (sem_unlink(EventName.c_str()) != 0)
{
- LOGWARN("ERROR: Could not unlink cEvent. (%i)", errno);
+ AString error = GetOSErrorString(errno);
+ LOGWARN("ERROR: Could not unlink cEvent. (%s)", error.c_str());
}
}
#endif // *nix
@@ -61,7 +63,8 @@ cEvent::~cEvent()
{
if (sem_close(m_Event) != 0)
{
- LOGERROR("ERROR: Could not close cEvent. (%i)", errno);
+ AString error = GetOSErrorString(errno);
+ LOGERROR("ERROR: Could not close cEvent. (%s)", error.c_str());
}
}
else
@@ -88,7 +91,8 @@ void cEvent::Wait(void)
int res = sem_wait(m_Event);
if (res != 0 )
{
- LOGWARN("cEvent: waiting for the event failed: %i, errno = %i. Continuing, but server may be unstable.", res, errno);
+ AString error = GetOSErrorString(errno);
+ LOGWARN("cEvent: waiting for the event failed: %i, err = %s. Continuing, but server may be unstable.", res, error.c_str());
}
#endif
}
@@ -108,7 +112,8 @@ void cEvent::Set(void)
int res = sem_post(m_Event);
if (res != 0)
{
- LOGWARN("cEvent: Could not set cEvent: %i, errno = %d", res, errno);
+ AString error = GetOSErrorString(errno);
+ LOGWARN("cEvent: Could not set cEvent: %i, err = %s", res, error.c_str());
}
#endif
}
diff --git a/src/OSSupport/File.cpp b/src/OSSupport/File.cpp
index 9f7c0d439..0ebd04915 100644
--- a/src/OSSupport/File.cpp
+++ b/src/OSSupport/File.cpp
@@ -450,3 +450,12 @@ int cFile::Printf(const char * a_Fmt, ...)
+
+void cFile::Flush(void)
+{
+ fflush(m_File);
+}
+
+
+
+
diff --git a/src/OSSupport/File.h b/src/OSSupport/File.h
index 01663a229..07fce6661 100644
--- a/src/OSSupport/File.h
+++ b/src/OSSupport/File.h
@@ -18,6 +18,8 @@ Usage:
2, Check if the file was opened using IsOpen()
3, Read / write
4, Destroy the instance
+
+For reading entire files into memory, just use the static cFile::ReadWholeFile()
*/
@@ -55,7 +57,7 @@ public:
static const char PathSeparator = '/';
#endif
- /// The mode in which to open the file
+ /** The mode in which to open the file */
enum eMode
{
fmRead, // Read-only. If the file doesn't exist, object will not be valid
@@ -63,13 +65,13 @@ public:
fmReadWrite // Read/write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning
} ;
- /// Simple constructor - creates an unopened file object, use Open() to open / create a real file
+ /** Simple constructor - creates an unopened file object, use Open() to open / create a real file */
cFile(void);
- /// Constructs and opens / creates the file specified, use IsOpen() to check for success
+ /** Constructs and opens / creates the file specified, use IsOpen() to check for success */
cFile(const AString & iFileName, eMode iMode);
- /// Auto-closes the file, if open
+ /** Auto-closes the file, if open */
~cFile();
bool Open(const AString & iFileName, eMode iMode);
@@ -77,60 +79,63 @@ public:
bool IsOpen(void) const;
bool IsEOF(void) const;
- /// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open
+ /** Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open */
int Read (void * iBuffer, int iNumBytes);
- /// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open
+ /** Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open */
int Write(const void * iBuffer, int iNumBytes);
- /// Seeks to iPosition bytes from file start, returns old position or -1 for failure; asserts if not open
+ /** Seeks to iPosition bytes from file start, returns old position or -1 for failure; asserts if not open */
int Seek (int iPosition);
- /// Returns the current position (bytes from file start) or -1 for failure; asserts if not open
+ /** Returns the current position (bytes from file start) or -1 for failure; asserts if not open */
int Tell (void) const;
- /// Returns the size of file, in bytes, or -1 for failure; asserts if not open
+ /** Returns the size of file, in bytes, or -1 for failure; asserts if not open */
int GetSize(void) const;
- /// Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error
+ /** Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error */
int ReadRestOfFile(AString & a_Contents);
// tolua_begin
- /// Returns true if the file specified exists
+ /** Returns true if the file specified exists */
static bool Exists(const AString & a_FileName);
- /// Deletes a file, returns true if successful
+ /** Deletes a file, returns true if successful */
static bool Delete(const AString & a_FileName);
- /// Renames a file or folder, returns true if successful. May fail if dest already exists (libc-dependant)!
+ /** Renames a file or folder, returns true if successful. May fail if dest already exists (libc-dependant)! */
static bool Rename(const AString & a_OrigPath, const AString & a_NewPath);
- /// Copies a file, returns true if successful.
+ /** Copies a file, returns true if successful. */
static bool Copy(const AString & a_SrcFileName, const AString & a_DstFileName);
- /// Returns true if the specified path is a folder
+ /** Returns true if the specified path is a folder */
static bool IsFolder(const AString & a_Path);
- /// Returns true if the specified path is a regular file
+ /** Returns true if the specified path is a regular file */
static bool IsFile(const AString & a_Path);
- /// Returns the size of the file, or a negative number on error
+ /** Returns the size of the file, or a negative number on error */
static int GetSize(const AString & a_FileName);
- /// Creates a new folder with the specified name. Returns true if successful. Path may be relative or absolute
+ /** Creates a new folder with the specified name. Returns true if successful. Path may be relative or absolute */
static bool CreateFolder(const AString & a_FolderPath);
- /// Returns the entire contents of the specified file as a string. Returns empty string on error.
+ /** Returns the entire contents of the specified file as a string. Returns empty string on error. */
static AString ReadWholeFile(const AString & a_FileName);
// tolua_end
- /// Returns the list of all items in the specified folder (files, folders, nix pipes, whatever's there).
+ /** Returns the list of all items in the specified folder (files, folders, nix pipes, whatever's there). */
static AStringVector GetFolderContents(const AString & a_Folder); // Exported in ManualBindings.cpp
int Printf(const char * a_Fmt, ...);
+ /** Flushes all the bufferef output into the file (only when writing) */
+ void Flush(void);
+
private:
#ifdef USE_STDIO_FILE
FILE * m_File;
diff --git a/src/OSSupport/Socket.cpp b/src/OSSupport/Socket.cpp
index d80c9bb3d..4226a7535 100644
--- a/src/OSSupport/Socket.cpp
+++ b/src/OSSupport/Socket.cpp
@@ -105,58 +105,6 @@ void cSocket::ShutdownReadWrite(void)
-
-AString cSocket::GetErrorString( int a_ErrNo )
-{
- char buffer[ 1024 ];
- AString Out;
-
- #ifdef _WIN32
-
- FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, a_ErrNo, 0, buffer, ARRAYCOUNT(buffer), NULL);
- Printf(Out, "%d: %s", a_ErrNo, buffer);
- if (!Out.empty() && (Out[Out.length() - 1] == '\n'))
- {
- Out.erase(Out.length() - 2);
- }
- return Out;
-
- #else // _WIN32
-
- // According to http://linux.die.net/man/3/strerror_r there are two versions of strerror_r():
-
- #if ( _GNU_SOURCE ) && !defined(ANDROID_NDK) // GNU version of strerror_r()
-
- char * res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) );
- if( res != NULL )
- {
- Printf(Out, "%d: %s", a_ErrNo, res);
- return Out;
- }
-
- #else // XSI version of strerror_r():
-
- int res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) );
- if( res == 0 )
- {
- Printf(Out, "%d: %s", a_ErrNo, buffer);
- return Out;
- }
-
- #endif // strerror_r() version
-
- else
- {
- Printf(Out, "Error %d while getting error string for error #%d!", errno, a_ErrNo);
- return Out;
- }
-
- #endif // else _WIN32
-}
-
-
-
-
int cSocket::GetLastError()
{
#ifdef _WIN32
diff --git a/src/OSSupport/Socket.h b/src/OSSupport/Socket.h
index 91c9ca5fd..4ca3d61f4 100644
--- a/src/OSSupport/Socket.h
+++ b/src/OSSupport/Socket.h
@@ -14,7 +14,7 @@
#endif
-
+#include "Errors.h"
class cSocket
@@ -57,11 +57,10 @@ public:
/// Initializes the network stack. Returns 0 on success, or another number as an error code.
static int WSAStartup(void);
- static AString GetErrorString(int a_ErrNo);
static int GetLastError();
static AString GetLastErrorString(void)
{
- return GetErrorString(GetLastError());
+ return GetOSErrorString(GetLastError());
}
/// Creates a new socket of the specified address family
@@ -115,4 +114,4 @@ public:
private:
xSocket m_Socket;
AString m_IPString;
-}; \ No newline at end of file
+};
diff --git a/src/OSSupport/SocketThreads.cpp b/src/OSSupport/SocketThreads.cpp
index b8069cf00..74932daf8 100644
--- a/src/OSSupport/SocketThreads.cpp
+++ b/src/OSSupport/SocketThreads.cpp
@@ -7,6 +7,7 @@
#include "Globals.h"
#include "SocketThreads.h"
+#include "Errors.h"
@@ -556,7 +557,7 @@ void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write)
if (Sent < 0)
{
int Err = cSocket::GetLastError();
- LOGWARNING("Error %d while writing to client \"%s\", disconnecting. \"%s\"", Err, m_Slots[i].m_Socket.GetIPString().c_str(), cSocket::GetErrorString(Err).c_str());
+ LOGWARNING("Error %d while writing to client \"%s\", disconnecting. \"%s\"", Err, m_Slots[i].m_Socket.GetIPString().c_str(), GetOSErrorString(Err).c_str());
m_Slots[i].m_Socket.CloseSocket();
if (m_Slots[i].m_Client != NULL)
{
diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp
index b4ca37d37..f5fd95c7e 100644
--- a/src/Protocol/Protocol132.cpp
+++ b/src/Protocol/Protocol132.cpp
@@ -28,13 +28,14 @@
#pragma warning(disable:4702)
#endif
-#include "cryptopp/randpool.h"
-
#ifdef _MSC_VER
#pragma warning(pop)
#endif
+
+
+
#define HANDLE_PACKET_READ(Proc, Type, Var) \
Type Var; \
{ \
@@ -49,17 +50,6 @@
-typedef unsigned char Byte;
-
-
-
-
-
-using namespace CryptoPP;
-
-
-
-
const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
@@ -93,81 +83,6 @@ enum
-// 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(byte a_Digest[20], AString & a_Out)
-{
- bool IsNegative = (a_Digest[0] >= 0x80);
- if (IsNegative)
- {
- // Two's complement:
- bool carry = true; // Add one to the whole number
- for (int i = 19; i >= 0; i--)
- {
- a_Digest[i] = ~a_Digest[i];
- if (carry)
- {
- carry = (a_Digest[i] == 0xff);
- a_Digest[i]++;
- }
- }
- }
- a_Out.clear();
- a_Out.reserve(40);
- for (int i = 0; i < 20; i++)
- {
- AppendPrintf(a_Out, "%02x", a_Digest[i]);
- }
- while ((a_Out.length() > 0) && (a_Out[0] == '0'))
- {
- a_Out.erase(0, 1);
- }
- if (IsNegative)
- {
- a_Out.insert(0, "-");
- }
-}
-
-
-
-
-
-/*
-// 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];
- CryptoPP::SHA1 Checksum;
- Checksum.Update((const byte *)"Notch", 5);
- Checksum.Final(Digest);
- DigestToJava(Digest, DigestNotch);
- Checksum.Restart();
- Checksum.Update((const byte *)"jeb_", 4);
- Checksum.Final(Digest);
- DigestToJava(Digest, DigestJeb);
- Checksum.Restart();
- Checksum.Update((const byte *)"simon", 5);
- Checksum.Final(Digest);
- DigestToJava(Digest, DigestSimon);
- printf("Notch: \"%s\"", DigestNotch.c_str());
- printf("jeb_: \"%s\"", DigestJeb.c_str());
- printf("simon: \"%s\"", DigestSimon.c_str());
- }
-} test;
-*/
-
-
-
-
-
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol132:
@@ -197,11 +112,11 @@ void cProtocol132::DataReceived(const char * a_Data, int a_Size)
{
if (m_IsEncrypted)
{
- byte Decrypted[512];
+ Byte Decrypted[512];
while (a_Size > 0)
{
int NumBytes = (a_Size > (int)sizeof(Decrypted)) ? (int)sizeof(Decrypted) : a_Size;
- m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes);
+ m_Decryptor.ProcessData(Decrypted, (Byte *)a_Data, NumBytes);
super::DataReceived((const char *)Decrypted, NumBytes);
a_Size -= NumBytes;
a_Data += NumBytes;
@@ -582,9 +497,7 @@ int cProtocol132::ParseHandshake(void)
return PARSE_OK; // Player is not allowed into the server
}
- // Send a 0xFD Encryption Key Request http://wiki.vg/Protocol#0xFD
- CryptoPP::StringSink sink(m_ServerPublicKey); // GCC won't allow inline instantiation in the following line, damned temporary refs
- cRoot::Get()->GetServer()->GetPublicKey().Save(sink);
+ // Send a 0xfd Encryption Key Request http://wiki.vg/Protocol#0xFD
SendEncryptionKeyRequest();
return PARSE_OK;
@@ -596,7 +509,7 @@ int cProtocol132::ParseHandshake(void)
int cProtocol132::ParseClientStatuses(void)
{
- HANDLE_PACKET_READ(ReadByte, byte, Status);
+ HANDLE_PACKET_READ(ReadByte, Byte, Status);
if ((Status & 1) == 0)
{
m_Client->HandleLogin(39, m_Username);
@@ -714,11 +627,11 @@ void cProtocol132::Flush(void)
int a_Size = m_DataToSend.size();
if (m_IsEncrypted)
{
- byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
+ Byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
while (a_Size > 0)
{
int NumBytes = (a_Size > (int)sizeof(Encrypted)) ? (int)sizeof(Encrypted) : a_Size;
- m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes);
+ m_Encryptor.ProcessData(Encrypted, (Byte *)a_Data, NumBytes);
super::SendData((const char *)Encrypted, NumBytes);
a_Size -= NumBytes;
a_Data += NumBytes;
@@ -880,8 +793,8 @@ void cProtocol132::SendEncryptionKeyRequest(void)
cCSLock Lock(m_CSPacket);
WriteByte(0xfd);
WriteString(cRoot::Get()->GetServer()->GetServerID());
- WriteShort((short)m_ServerPublicKey.size());
- SendData(m_ServerPublicKey.data(), m_ServerPublicKey.size());
+ WriteShort((short)(cRoot::Get()->GetServer()->GetPublicKeyDER().size()));
+ SendData(cRoot::Get()->GetServer()->GetPublicKeyDER().data(), cRoot::Get()->GetServer()->GetPublicKeyDER().size());
WriteShort(4);
WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
Flush();
@@ -894,13 +807,11 @@ void cProtocol132::SendEncryptionKeyRequest(void)
void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce)
{
// Decrypt EncNonce using privkey
- RSAES<PKCS1v15>::Decryptor rsaDecryptor(cRoot::Get()->GetServer()->GetPrivateKey());
- time_t CurTime = time(NULL);
- CryptoPP::RandomPool rng;
- rng.Put((const byte *)&CurTime, sizeof(CurTime));
+ cRSAPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey();
+
Int32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)];
- DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), (byte *)DecryptedNonce);
- if (!res.isValidCoding || (res.messageLength != 4))
+ int res = rsaDecryptor.Decrypt((const Byte *)a_EncNonce.data(), a_EncNonce.size(), (Byte *)DecryptedNonce, sizeof(DecryptedNonce));
+ if (res != 4)
{
LOGD("Bad nonce length");
m_Client->Kick("Hacked client");
@@ -914,9 +825,9 @@ void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const A
}
// Decrypt the symmetric encryption key using privkey:
- byte DecryptedKey[MAX_ENC_LEN];
- res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey);
- if (!res.isValidCoding || (res.messageLength != 16))
+ Byte DecryptedKey[MAX_ENC_LEN];
+ res = rsaDecryptor.Decrypt((const Byte *)a_EncKey.data(), a_EncKey.size(), DecryptedKey, sizeof(DecryptedKey));
+ if (res != 16)
{
LOGD("Bad key length");
m_Client->Kick("Hacked client");
@@ -932,6 +843,12 @@ void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const A
Flush();
}
+ #ifdef _DEBUG
+ AString DecryptedKeyHex;
+ CreateHexDump(DecryptedKeyHex, DecryptedKey, res, 16);
+ LOGD("Received encryption key, %d bytes:\n%s", res, DecryptedKeyHex.c_str());
+ #endif
+
StartEncryption(DecryptedKey);
return;
}
@@ -940,21 +857,21 @@ void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const A
-void cProtocol132::StartEncryption(const byte * a_Key)
+void cProtocol132::StartEncryption(const Byte * a_Key)
{
- m_Encryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
- m_Decryptor.SetKey(a_Key, 16, MakeParameters(Name::IV(), ConstByteArrayParameter(a_Key, 16))(Name::FeedbackSize(), 1));
+ m_Encryptor.Init(a_Key, a_Key);
+ m_Decryptor.Init(a_Key, a_Key);
m_IsEncrypted = true;
// Prepare the m_AuthServerID:
- CryptoPP::SHA1 Checksum;
+ cSHA1Checksum Checksum;
AString ServerID = cRoot::Get()->GetServer()->GetServerID();
- Checksum.Update((const byte *)ServerID.c_str(), ServerID.length());
+ Checksum.Update((const Byte *)ServerID.c_str(), ServerID.length());
Checksum.Update(a_Key, 16);
- Checksum.Update((const byte *)m_ServerPublicKey.c_str(), m_ServerPublicKey.length());
- byte Digest[20];
- Checksum.Final(Digest);
- DigestToJava(Digest, m_AuthServerID);
+ Checksum.Update((const Byte *)cRoot::Get()->GetServer()->GetPublicKeyDER().data(), cRoot::Get()->GetServer()->GetPublicKeyDER().size());
+ Byte Digest[20];
+ Checksum.Finalize(Digest);
+ cSHA1Checksum::DigestToJava(Digest, m_AuthServerID);
}
diff --git a/src/Protocol/Protocol132.h b/src/Protocol/Protocol132.h
index 80fc8740a..89f4636f5 100644
--- a/src/Protocol/Protocol132.h
+++ b/src/Protocol/Protocol132.h
@@ -20,13 +20,12 @@
#pragma warning(disable:4702)
#endif
-#include "cryptopp/modes.h"
-#include "cryptopp/aes.h"
-
#ifdef _MSC_VER
#pragma warning(pop)
#endif
+#include "../Crypto.h"
+
@@ -79,16 +78,15 @@ public:
protected:
bool m_IsEncrypted;
- CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor;
- CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor;
+
+ cAESCFBDecryptor m_Decryptor;
+ cAESCFBEncryptor m_Encryptor;
+
AString m_DataToSend;
/// The ServerID used for session authentication; set in StartEncryption(), used in GetAuthServerID()
AString m_AuthServerID;
- /// The server's public key, as used by SendEncryptionKeyRequest() and StartEncryption()
- AString m_ServerPublicKey;
-
virtual void SendData(const char * a_Data, int a_Size) override;
// DEBUG:
@@ -108,7 +106,7 @@ protected:
void HandleEncryptionKeyResponse(const AString & a_EncKey, const AString & a_EncNonce);
/// Starts the symmetric encryption with the specified key; also sets m_AuthServerID
- void StartEncryption(const byte * a_Key);
+ void StartEncryption(const Byte * a_Key);
} ;
diff --git a/src/Protocol/Protocol14x.cpp b/src/Protocol/Protocol14x.cpp
index 127ce9d4b..f82e6de45 100644
--- a/src/Protocol/Protocol14x.cpp
+++ b/src/Protocol/Protocol14x.cpp
@@ -33,8 +33,6 @@ Implements the 1.4.x protocol classes representing these protocols:
#pragma warning(disable:4702)
#endif
-#include "cryptopp/randpool.h"
-
#ifdef _MSC_VER
#pragma warning(pop)
#endif
diff --git a/src/Protocol/Protocol15x.cpp b/src/Protocol/Protocol15x.cpp
index c33aec7d5..264a596b9 100644
--- a/src/Protocol/Protocol15x.cpp
+++ b/src/Protocol/Protocol15x.cpp
@@ -37,9 +37,9 @@ enum
{
PACKET_WINDOW_OPEN = 0x64,
PACKET_PARTICLE_EFFECT = 0x3F,
- PACKET_SCOREBOARD_OBJECTIVE = 0x3B,
- PACKET_SCORE_UPDATE = 0x3C,
- PACKET_DISPLAY_OBJECTIVE = 0x3D
+ PACKET_SCOREBOARD_OBJECTIVE = 0xCE,
+ PACKET_SCORE_UPDATE = 0xCF,
+ PACKET_DISPLAY_OBJECTIVE = 0xD0
} ;
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index 9506332dc..9bb2cfbf0 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -53,6 +53,16 @@ Implements the 1.7.x protocol classes:
+// fwd: main.cpp:
+extern bool g_ShouldLogComm;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol172:
+
cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) :
super(a_Client),
m_ServerAddress(a_ServerAddress),
@@ -63,6 +73,13 @@ cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAdd
m_OutPacketLenBuffer(20), // 20 bytes is more than enough for one VarInt
m_IsEncrypted(false)
{
+ // Create the comm log file, if so requested:
+ if (g_ShouldLogComm)
+ {
+ cFile::CreateFolder("CommLogs");
+ AString FileName = Printf("CommLogs/%x__%s.log", (unsigned)time(NULL), a_Client->GetIPString().c_str());
+ m_CommLogFile.Open(FileName, cFile::fmWrite);
+ }
}
@@ -73,11 +90,11 @@ void cProtocol172::DataReceived(const char * a_Data, int a_Size)
{
if (m_IsEncrypted)
{
- byte Decrypted[512];
+ Byte Decrypted[512];
while (a_Size > 0)
{
int NumBytes = (a_Size > sizeof(Decrypted)) ? sizeof(Decrypted) : a_Size;
- m_Decryptor.ProcessData(Decrypted, (byte *)a_Data, NumBytes);
+ m_Decryptor.ProcessData(Decrypted, (Byte *)a_Data, NumBytes);
AddReceivedData((const char *)Decrypted, NumBytes);
a_Size -= NumBytes;
a_Data += NumBytes;
@@ -1065,6 +1082,29 @@ void cProtocol172::SendWindowProperty(const cWindow & a_Window, short a_Property
void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
{
+ // Write the incoming data into the comm log file:
+ if (g_ShouldLogComm)
+ {
+ if (m_ReceivedData.GetReadableSpace() > 0)
+ {
+ AString AllData;
+ int OldReadableSpace = m_ReceivedData.GetReadableSpace();
+ m_ReceivedData.ReadAll(AllData);
+ m_ReceivedData.ResetRead();
+ m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace);
+ AString Hex;
+ CreateHexDump(Hex, AllData.data(), AllData.size(), 16);
+ m_CommLogFile.Printf("Incoming data, %d (0x%x) bytes unparsed already present in buffer:\n%s\n",
+ AllData.size(), AllData.size(), Hex.c_str()
+ );
+ }
+ AString Hex;
+ CreateHexDump(Hex, a_Data, a_Size, 16);
+ m_CommLogFile.Printf("Incoming data: %d (0x%x) bytes: \n%s\n",
+ a_Size, a_Size, Hex.c_str()
+ );
+ }
+
if (!m_ReceivedData.Write(a_Data, a_Size))
{
// Too much data in the incoming queue, report to caller:
@@ -1100,6 +1140,22 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
return;
}
+ // Log the packet info into the comm log file:
+ if (g_ShouldLogComm)
+ {
+ AString PacketData;
+ bb.ReadAll(PacketData);
+ bb.ResetRead();
+ bb.ReadVarInt(PacketType);
+ ASSERT(PacketData.size() > 0);
+ PacketData.resize(PacketData.size() - 1);
+ AString PacketDataHex;
+ CreateHexDump(PacketDataHex, PacketData.data(), PacketData.size(), 16);
+ m_CommLogFile.Printf("Next incoming packet is type %u (0x%x), length %u (0x%x) at state %d. Payload:\n%s\n",
+ PacketType, PacketType, PacketLen, PacketLen, m_State, PacketDataHex.c_str()
+ );
+ }
+
if (!HandlePacket(bb, PacketType))
{
// Unknown packet, already been reported, but without the length. Log the length here:
@@ -1116,6 +1172,12 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
LOGD("Packet contents:\n%s", Out.c_str());
#endif // _DEBUG
+ // Put a message in the comm log:
+ if (g_ShouldLogComm)
+ {
+ m_CommLogFile.Printf("^^^^^^ Unhandled packet ^^^^^^\n\n\n");
+ }
+
return;
}
@@ -1125,6 +1187,16 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
LOGWARNING("Protocol 1.7: Wrong number of bytes read for packet 0x%x, state %d. Read %u bytes, packet contained %u bytes",
PacketType, m_State, bb.GetUsedSpace() - bb.GetReadableSpace(), PacketLen
);
+
+ // Put a message in the comm log:
+ if (g_ShouldLogComm)
+ {
+ m_CommLogFile.Printf("^^^^^^ Wrong number of bytes read for this packet (exp %d left, got %d left) ^^^^^^\n\n\n",
+ 1, bb.GetReadableSpace()
+ );
+ m_CommLogFile.Flush();
+ }
+
ASSERT(!"Read wrong number of bytes!");
m_Client->PacketError(PacketType);
}
@@ -1664,11 +1736,11 @@ void cProtocol172::SendData(const char * a_Data, int a_Size)
{
if (m_IsEncrypted)
{
- byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
+ Byte Encrypted[8192]; // Larger buffer, we may be sending lots of data (chunks)
while (a_Size > 0)
{
int NumBytes = (a_Size > sizeof(Encrypted)) ? sizeof(Encrypted) : a_Size;
- m_Encryptor.ProcessData(Encrypted, (byte *)a_Data, NumBytes);
+ m_Encryptor.ProcessData(Encrypted, (Byte *)a_Data, NumBytes);
m_Client->SendData((const char *)Encrypted, NumBytes);
a_Size -= NumBytes;
a_Data += NumBytes;
@@ -1807,6 +1879,17 @@ cProtocol172::cPacketizer::~cPacketizer()
m_Out.ReadAll(DataToSend);
m_Protocol.SendData(DataToSend.data(), DataToSend.size());
m_Out.CommitRead();
+
+ // Log the comm into logfile:
+ if (g_ShouldLogComm)
+ {
+ AString Hex;
+ ASSERT(DataToSend.size() > 0);
+ CreateHexDump(Hex, DataToSend.data() + 1, DataToSend.size() - 1, 16);
+ m_Protocol.m_CommLogFile.Printf("Outgoing packet: type %d (0x%x), length %u (0x%x), state %d. Payload:\n%s\n",
+ DataToSend[0], DataToSend[0], PacketLen, PacketLen, m_Protocol.m_State, Hex.c_str()
+ );
+ }
}
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index 3f440f313..72544b575 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -26,21 +26,20 @@ Declares the 1.7.x protocol classes:
#pragma warning(disable:4702)
#endif
-#include "cryptopp/modes.h"
-#include "cryptopp/aes.h"
-
#ifdef _MSC_VER
#pragma warning(pop)
#endif
+#include "../Crypto.h"
+
class cProtocol172 :
- public cProtocol // TODO
+ public cProtocol
{
- typedef cProtocol super; // TODO
+ typedef cProtocol super;
public:
@@ -220,8 +219,12 @@ protected:
cByteBuffer m_OutPacketLenBuffer;
bool m_IsEncrypted;
- CryptoPP::CFB_Mode<CryptoPP::AES>::Decryption m_Decryptor;
- CryptoPP::CFB_Mode<CryptoPP::AES>::Encryption m_Encryptor;
+
+ cAESCFBDecryptor m_Decryptor;
+ cAESCFBEncryptor m_Encryptor;
+
+ /** The logfile where the comm is logged, when g_ShouldLogComm is true */
+ cFile m_CommLogFile;
/// Adds the received (unencrypted) data to m_ReceivedData, parses complete packets
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index de5dd3fb9..32409c2aa 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -965,7 +965,7 @@ void cProtocolRecognizer::SendLengthlessServerPing(void)
m_Buffer.ResetRead();
if (m_Buffer.CanReadBytes(2))
{
- byte val;
+ Byte val;
m_Buffer.ReadByte(val); // Packet type - Serverlist ping
m_Buffer.ReadByte(val); // 0x01 magic value
ASSERT(val == 0x01);
diff --git a/src/ReferenceManager.cpp b/src/ReferenceManager.cpp
deleted file mode 100644
index 6a9ed0e43..000000000
--- a/src/ReferenceManager.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "ReferenceManager.h"
-#include "Entities/Entity.h"
-
-
-
-
-
-cReferenceManager::cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type )
- : m_Type( a_Type )
-{
-}
-
-cReferenceManager::~cReferenceManager()
-{
- if( m_Type == RFMNGR_REFERENCERS )
- {
- for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr )
- {
- *(*itr) = 0; // Set referenced pointer to 0
- }
- }
- else
- {
- for( std::list< cEntity** >::iterator itr = m_References.begin(); itr != m_References.end(); ++itr )
- {
- cEntity* Ptr = (*(*itr));
- if( Ptr ) Ptr->Dereference( *(*itr) );
- }
- }
-}
-
-void cReferenceManager::AddReference( cEntity*& a_EntityPtr )
-{
- m_References.push_back( &a_EntityPtr );
-}
-
-void cReferenceManager::Dereference( cEntity*& a_EntityPtr )
-{
- m_References.remove( &a_EntityPtr );
-} \ No newline at end of file
diff --git a/src/ReferenceManager.h b/src/ReferenceManager.h
deleted file mode 100644
index bcd451f72..000000000
--- a/src/ReferenceManager.h
+++ /dev/null
@@ -1,34 +0,0 @@
-
-#pragma once
-
-
-
-
-
-class cEntity;
-
-
-
-
-
-class cReferenceManager
-{
-public:
- enum ENUM_REFERENCE_MANAGER_TYPE
- {
- RFMNGR_REFERENCERS,
- RFMNGR_REFERENCES,
- };
- cReferenceManager( ENUM_REFERENCE_MANAGER_TYPE a_Type );
- ~cReferenceManager();
-
- void AddReference( cEntity*& a_EntityPtr );
- void Dereference( cEntity*& a_EntityPtr );
-private:
- ENUM_REFERENCE_MANAGER_TYPE m_Type;
- std::list< cEntity** > m_References;
-};
-
-
-
-
diff --git a/src/Server.cpp b/src/Server.cpp
index 34ee066cb..3f9f8a4ac 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -194,7 +194,7 @@ bool cServer::InitServer(cIniFile & a_SettingsIni)
m_PlayerCount = 0;
m_PlayerCountDiff = 0;
- m_FaviconData = Base64Encode(cFile::ReadWholeFile("favicon.png")); // Will return empty string if file nonexistant; client doesn't mind
+ m_FaviconData = Base64Encode(cFile::ReadWholeFile(FILE_IO_PREFIX + AString("favicon.png"))); // Will return empty string if file nonexistant; client doesn't mind
if (m_bIsConnected)
{
@@ -284,17 +284,9 @@ int cServer::GetNumPlayers(void)
void cServer::PrepareKeys(void)
{
- // TODO: Save and load key for persistence across sessions
- // But generating the key takes only a moment, do we even need that?
-
LOGD("Generating protocol encryption keypair...");
-
- time_t CurTime = time(NULL);
- CryptoPP::RandomPool rng;
- rng.Put((const byte *)&CurTime, sizeof(CurTime));
- m_PrivateKey.GenerateRandomWithKeySize(rng, 1024);
- CryptoPP::RSA::PublicKey pk(m_PrivateKey);
- m_PublicKey = pk;
+ VERIFY(m_PrivateKey.Generate(1024));
+ m_PublicKeyDER = m_PrivateKey.GetPubKeyDER();
}
diff --git a/src/Server.h b/src/Server.h
index bb55e81b6..15eafc00a 100644
--- a/src/Server.h
+++ b/src/Server.h
@@ -23,8 +23,7 @@
#pragma warning(disable:4702)
#endif
-#include "cryptopp/rsa.h"
-#include "cryptopp/randpool.h"
+#include "Crypto.h"
#ifdef _MSC_VER
#pragma warning(pop)
@@ -110,8 +109,8 @@ public: // tolua_export
/** Returns base64 encoded favicon data (obtained from favicon.png) */
const AString & GetFaviconData(void) const { return m_FaviconData; }
- CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
- CryptoPP::RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; }
+ cRSAPrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
+ const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; }
private:
@@ -180,8 +179,11 @@ private:
bool m_bRestarting;
- CryptoPP::RSA::PrivateKey m_PrivateKey;
- CryptoPP::RSA::PublicKey m_PublicKey;
+ /** The private key used for the assymetric encryption start in the protocols */
+ cRSAPrivateKey m_PrivateKey;
+
+ /** Public key for m_PrivateKey, ASN1-DER-encoded */
+ AString m_PublicKeyDER;
cRCONServer m_RCONServer;
diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp
index 3ffeff7a0..1a8456f70 100644
--- a/src/UI/Window.cpp
+++ b/src/UI/Window.cpp
@@ -13,7 +13,8 @@
#include "../BlockEntities/DropSpenserEntity.h"
#include "../BlockEntities/EnderChestEntity.h"
#include "../BlockEntities/HopperEntity.h"
-
+#include "../Root.h"
+#include "../Bindings/PluginManager.h"
@@ -169,6 +170,7 @@ void cWindow::Clicked(
const cItem & a_ClickedItem
)
{
+ cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
if (a_WindowID != m_WindowID)
{
LOGWARNING("%s: Wrong window ID (exp %d, got %d) received from \"%s\"; ignoring click.", __FUNCTION__, m_WindowID, a_WindowID, a_Player.GetName().c_str());
@@ -179,14 +181,35 @@ void cWindow::Clicked(
{
case caRightClickOutside:
{
+ if (PlgMgr->CallHookPlayerTossingItem(a_Player))
+ {
+ // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
+ return;
+ }
+ if (a_Player.IsGameModeCreative())
+ {
+ a_Player.TossPickup(a_ClickedItem);
+ }
+
// Toss one of the dragged items:
- a_Player.TossItem(true);
+ a_Player.TossHeldItem();
return;
}
case caLeftClickOutside:
{
+ if (PlgMgr->CallHookPlayerTossingItem(a_Player))
+ {
+ // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
+ return;
+ }
+
+ if (a_Player.IsGameModeCreative())
+ {
+ a_Player.TossPickup(a_ClickedItem);
+ }
+
// Toss all dragged items:
- a_Player.TossItem(true, a_Player.GetDraggingItem().m_ItemCount);
+ a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount);
return;
}
case caLeftClickOutsideHoldNothing:
@@ -263,7 +286,7 @@ bool cWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
if (a_Player.IsDraggingItem())
{
LOGD("Player holds item! Dropping it...");
- a_Player.TossItem(true, a_Player.GetDraggingItem().m_ItemCount);
+ a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount);
}
cClientHandle * ClientHandle = a_Player.GetClientHandle();
diff --git a/src/World.cpp b/src/World.cpp
index ee855c67d..f5e207520 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -22,6 +22,8 @@
#include "Entities/Player.h"
#include "Entities/TNTEntity.h"
+#include "BlockEntities/CommandBlockEntity.h"
+
// Simulators:
#include "Simulator/SimulatorManager.h"
#include "Simulator/FloodyFluidSimulator.h"
@@ -232,7 +234,11 @@ cWorld::cWorld(const AString & a_WorldName) :
m_WorldName(a_WorldName),
m_IniFileName(m_WorldName + "/world.ini"),
m_StorageSchema("Default"),
+#ifdef _arm_
+ m_StorageCompressionFactor(0),
+#else
m_StorageCompressionFactor(6),
+#endif
m_IsSpawnExplicitlySet(false),
m_WorldAgeSecs(0),
m_TimeOfDaySecs(0),
@@ -523,7 +529,7 @@ void cWorld::Start(void)
}
m_StorageSchema = IniFile.GetValueSet ("Storage", "Schema", m_StorageSchema);
- m_StorageCompressionFactor = IniFile.GetValueSetI ("Storage", "CompressionFactor", m_StorageCompressionFactor);
+ m_StorageCompressionFactor = IniFile.GetValueSetI("Storage", "CompressionFactor", m_StorageCompressionFactor);
m_MaxCactusHeight = IniFile.GetValueSetI("Plants", "MaxCactusHeight", 3);
m_MaxSugarcaneHeight = IniFile.GetValueSetI("Plants", "MaxSugarcaneHeight", 3);
m_IsCactusBonemealable = IniFile.GetValueSetB("Plants", "IsCactusBonemealable", false);
@@ -540,6 +546,7 @@ void cWorld::Start(void)
m_bEnabledPVP = IniFile.GetValueSetB("PVP", "Enabled", true);
m_IsDeepSnowEnabled = IniFile.GetValueSetB("Physics", "DeepSnow", false);
m_ShouldLavaSpawnFire = IniFile.GetValueSetB("Physics", "ShouldLavaSpawnFire", true);
+ m_bCommandBlocksEnabled = IniFile.GetValueSetB("Mechanics", "CommandBlocksEnabled", false);
m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode);
@@ -2333,7 +2340,7 @@ bool cWorld::FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCa
// TODO: This interface is dangerous!
-cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit)
+cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit, bool a_CheckLineOfSight)
{
cTracer LineOfSight(this);
@@ -2348,7 +2355,12 @@ cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit)
if (Distance < ClosestDistance)
{
- if (!LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length()))
+ if (a_CheckLineOfSight && !LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length()))
+ {
+ ClosestDistance = Distance;
+ ClosestPlayer = *itr;
+ }
+ else
{
ClosestDistance = Distance;
ClosestPlayer = *itr;
@@ -2528,6 +2540,28 @@ bool cWorld::UpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString
+bool cWorld::SetCommandBlockCommand(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Command)
+{
+ class cUpdateCommandBlock : public cCommandBlockCallback
+ {
+ AString m_Command;
+ public:
+ cUpdateCommandBlock(const AString & a_Command) : m_Command(a_Command) {}
+
+ virtual bool Item(cCommandBlockEntity * a_CommandBlock) override
+ {
+ a_CommandBlock->SetCommand(m_Command);
+ return false;
+ }
+ } CmdBlockCB (a_Command);
+
+ return DoWithCommandBlockAt(a_BlockX, a_BlockY, a_BlockZ, CmdBlockCB);
+}
+
+
+
+
+
void cWorld::ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay)
{
m_ChunkMap->ChunksStay(a_Chunks, a_Stay);
diff --git a/src/World.h b/src/World.h
index d489e574a..7b2782421 100644
--- a/src/World.h
+++ b/src/World.h
@@ -246,7 +246,7 @@ public:
bool FindAndDoWithPlayer(const AString & a_PlayerNameHint, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
// TODO: This interface is dangerous - rewrite to DoWithClosestPlayer(pos, sight, action)
- cPlayer * FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit);
+ cPlayer * FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit, bool a_CheckLineOfSight = true);
void SendPlayerList(cPlayer * a_DestPlayer); // Sends playerlist to the player
@@ -303,6 +303,9 @@ public:
/** Sets the sign text, asking plugins for permission first. a_Player is the player who this change belongs to, may be NULL. Returns true if sign text changed. Same as SetSignLines() */
bool UpdateSign(int a_X, int a_Y, int a_Z, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player = NULL); // Exported in ManualBindings.cpp
+ /** Sets the command block command. Returns true if command changed. */
+ bool SetCommandBlockCommand(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Command); // tolua_export
+
/** Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable. To be used only by cChunkStay! */
void ChunksStay(const cChunkCoordsList & a_Chunks, bool a_Stay = true);
@@ -531,6 +534,10 @@ public:
/// Returns the associated scoreboard instance
cScoreboard & GetScoreBoard(void) { return m_Scoreboard; }
+
+ bool AreCommandBlocksEnabled(void) const { return m_bCommandBlocksEnabled; }
+
+ void SetCommandBlocksEnabled(bool a_Flag) { m_bCommandBlocksEnabled = a_Flag; }
// tolua_end
@@ -780,6 +787,9 @@ private:
bool m_IsSaplingBonemealable;
bool m_IsSugarcaneBonemealable;
+ bool m_bCommandBlocksEnabled;
+
+
cChunkGenerator m_Generator;
cScoreboard m_Scoreboard;
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index c84128022..e46a28caa 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -652,20 +652,21 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
m_Writer.BeginList("TileEntities", TAG_Compound);
}
m_IsTagOpen = true;
-
+
// Add tile-entity into NBT:
switch (a_Entity->GetBlockType())
{
- case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
- case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
- case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break;
- case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
- case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break;
+ case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
+ case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
+ case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break;
+ case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
+ case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break;
case E_BLOCK_SIGN_POST:
- case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break;
- case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
- case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
+ case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break;
+ case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
+ case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity((cCommandBlockEntity *) a_Entity); break;
+
default:
{
ASSERT(!"Unhandled block entity saved into Anvil");
diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp
index dabc5e2e1..c65e13f98 100644
--- a/src/WorldStorage/ScoreboardSerializer.cpp
+++ b/src/WorldStorage/ScoreboardSerializer.cpp
@@ -13,11 +13,6 @@
-#define SCOREBOARD_INFLATE_MAX 16 KiB
-
-
-
-
cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard)
: m_ScoreBoard(a_ScoreBoard)
@@ -36,38 +31,22 @@ cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScore
bool cScoreboardSerializer::Load(void)
{
- cFile File;
-
- if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmReadWrite))
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
{
return false;
}
- AString Data;
-
- File.ReadRestOfFile(Data);
+ AString Uncompressed;
+ int res = UncompressStringGZIP(Data.data(), Data.size(), Uncompressed);
- File.Close();
-
- char Uncompressed[SCOREBOARD_INFLATE_MAX];
- z_stream strm;
- strm.zalloc = (alloc_func)NULL;
- strm.zfree = (free_func)NULL;
- strm.opaque = NULL;
- inflateInit(&strm);
- strm.next_out = (Bytef *)Uncompressed;
- strm.avail_out = sizeof(Uncompressed);
- strm.next_in = (Bytef *)Data.data();
- strm.avail_in = Data.size();
- int res = inflate(&strm, Z_FINISH);
- inflateEnd(&strm);
- if (res != Z_STREAM_END)
+ if (res != Z_OK)
{
return false;
}
// Parse the NBT data:
- cParsedNBT NBT(Uncompressed, strm.total_out);
+ cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
if (!NBT.IsValid())
{
// NBT Parsing failed
@@ -85,11 +64,8 @@ bool cScoreboardSerializer::Save(void)
{
cFastNBTWriter Writer;
- Writer.BeginCompound("");
- m_ScoreBoard->RegisterObjective("test","test",cObjective::E_TYPE_DUMMY)->AddScore("dot", 2);
SaveScoreboardToNBT(Writer);
- Writer.EndCompound();
Writer.Finish();
#ifdef _DEBUG
@@ -97,12 +73,22 @@ bool cScoreboardSerializer::Save(void)
ASSERT(TestParse.IsValid());
#endif // _DEBUG
- gzFile gz = gzopen((FILE_IO_PREFIX + m_Path).c_str(), "wb");
- if (gz != NULL)
+ cFile File;
+ if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite))
+ {
+ return false;
+ }
+
+ AString Compressed;
+ int res = CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
+
+ if (res != Z_OK)
{
- gzwrite(gz, Writer.GetResult().data(), Writer.GetResult().size());
+ return false;
}
- gzclose(gz);
+
+ File.Write(Compressed.data(), Compressed.size());
+ File.Close();
return true;
}
@@ -113,7 +99,8 @@ bool cScoreboardSerializer::Save(void)
void cScoreboardSerializer::SaveScoreboardToNBT(cFastNBTWriter & a_Writer)
{
- a_Writer.BeginCompound("Data");
+ a_Writer.BeginCompound("data");
+
a_Writer.BeginList("Objectives", TAG_Compound);
for (cScoreboard::cObjectiveMap::const_iterator it = m_ScoreBoard->m_Objectives.begin(); it != m_ScoreBoard->m_Objectives.end(); ++it)
@@ -130,7 +117,7 @@ void cScoreboardSerializer::SaveScoreboardToNBT(cFastNBTWriter & a_Writer)
a_Writer.EndCompound();
}
- a_Writer.EndList();
+ a_Writer.EndList(); // Objectives
a_Writer.BeginList("PlayerScores", TAG_Compound);
@@ -151,7 +138,7 @@ void cScoreboardSerializer::SaveScoreboardToNBT(cFastNBTWriter & a_Writer)
}
}
- a_Writer.EndList();
+ a_Writer.EndList(); // PlayerScores
a_Writer.BeginList("Teams", TAG_Compound);
@@ -182,8 +169,7 @@ void cScoreboardSerializer::SaveScoreboardToNBT(cFastNBTWriter & a_Writer)
a_Writer.EndCompound();
}
- a_Writer.EndList();
- a_Writer.EndCompound();
+ a_Writer.EndList(); // Teams
a_Writer.BeginCompound("DisplaySlots");
@@ -196,7 +182,9 @@ void cScoreboardSerializer::SaveScoreboardToNBT(cFastNBTWriter & a_Writer)
Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::E_DISPLAY_SLOT_NAME);
a_Writer.AddString("slot_2", (Objective == NULL) ? "" : Objective->GetName());
- a_Writer.EndCompound();
+ a_Writer.EndCompound(); // DisplaySlots
+
+ a_Writer.EndCompound(); // Data
}
@@ -205,7 +193,7 @@ void cScoreboardSerializer::SaveScoreboardToNBT(cFastNBTWriter & a_Writer)
bool cScoreboardSerializer::LoadScoreboardFromNBT(const cParsedNBT & a_NBT)
{
- int Data = a_NBT.FindChildByName(0, "Data");
+ int Data = a_NBT.FindChildByName(0, "data");
if (Data < 0)
{
return false;
@@ -321,13 +309,13 @@ bool cScoreboardSerializer::LoadScoreboardFromNBT(const cParsedNBT & a_NBT)
CurrLine = a_NBT.FindChildByName(Child, "AllowFriendlyFire");
if (CurrLine >= 0)
{
- AllowsFriendlyFire = a_NBT.GetInt(CurrLine);
+ AllowsFriendlyFire = (a_NBT.GetInt(CurrLine) != 0);
}
CurrLine = a_NBT.FindChildByName(Child, "SeeFriendlyInvisibles");
if (CurrLine >= 0)
{
- CanSeeFriendlyInvisible = a_NBT.GetInt(CurrLine);
+ CanSeeFriendlyInvisible = (a_NBT.GetInt(CurrLine) != 0);
}
cTeam * Team = m_ScoreBoard->RegisterTeam(Name, DisplayName, Prefix, Suffix);
@@ -347,7 +335,7 @@ bool cScoreboardSerializer::LoadScoreboardFromNBT(const cParsedNBT & a_NBT)
}
}
- int DisplaySlots = a_NBT.FindChildByName(0, "DisplaySlots");
+ int DisplaySlots = a_NBT.FindChildByName(Data, "DisplaySlots");
if (DisplaySlots < 0)
{
return false;
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index 8be6372e2..e2a882f65 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -1926,14 +1926,19 @@ bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_N
double Speed[3];
if (!LoadDoublesListFromNBT(Speed, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Motion")))
{
- return false;
+ // Provide default speed:
+ Speed[0] = 0;
+ Speed[1] = 0;
+ Speed[2] = 0;
}
a_Entity.SetSpeed(Speed[0], Speed[1], Speed[2]);
double Rotation[3];
if (!LoadDoublesListFromNBT(Rotation, 2, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Rotation")))
{
- return false;
+ // Provide default rotation:
+ Rotation[0] = 0;
+ Rotation[1] = 0;
}
a_Entity.SetYaw(Rotation[0]);
a_Entity.SetRoll(Rotation[1]);
diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp
index ea17a8ec1..4c0684dd8 100644
--- a/src/WorldStorage/WSSCompact.cpp
+++ b/src/WorldStorage/WSSCompact.cpp
@@ -10,6 +10,7 @@
#include "json/json.h"
#include "../StringCompression.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/FurnaceEntity.h"
#include "../BlockEntities/JukeboxEntity.h"
@@ -71,14 +72,15 @@ void cJsonChunkSerializer::BlockEntity(cBlockEntity * a_BlockEntity)
const char * SaveInto = NULL;
switch (a_BlockEntity->GetBlockType())
{
- case E_BLOCK_CHEST: SaveInto = "Chests"; break;
- case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break;
- case E_BLOCK_DROPPER: SaveInto = "Droppers"; break;
- case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break;
- case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break;
- case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break;
- case E_BLOCK_NOTE_BLOCK: SaveInto = "Notes"; break;
- case E_BLOCK_JUKEBOX: SaveInto = "Jukeboxes"; break;
+ case E_BLOCK_CHEST: SaveInto = "Chests"; break;
+ case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break;
+ case E_BLOCK_DROPPER: SaveInto = "Droppers"; break;
+ case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break;
+ case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break;
+ case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break;
+ case E_BLOCK_NOTE_BLOCK: SaveInto = "Notes"; break;
+ case E_BLOCK_JUKEBOX: SaveInto = "Jukeboxes"; break;
+ case E_BLOCK_COMMAND_BLOCK: SaveInto = "CommandBlocks"; break;
default:
{
@@ -263,126 +265,114 @@ bool cWSSCompact::EraseChunkData(const cChunkCoords & a_Chunk)
void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World)
{
- // Load chests
+ // Load chests:
Json::Value AllChests = a_Value.get("Chests", Json::nullValue);
if (!AllChests.empty())
{
for (Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr )
{
- Json::Value & Chest = *itr;
- cChestEntity * ChestEntity = new cChestEntity(0,0,0, a_World);
- if (!ChestEntity->LoadFromJson( Chest ) )
+ std::auto_ptr<cChestEntity> ChestEntity(new cChestEntity(0, 0, 0, a_World));
+ if (!ChestEntity->LoadFromJson(*itr))
{
- LOGERROR("ERROR READING CHEST FROM JSON!" );
- delete ChestEntity;
+ LOGWARNING("ERROR READING CHEST FROM JSON!" );
}
else
{
- a_BlockEntities.push_back( ChestEntity );
+ a_BlockEntities.push_back(ChestEntity.release());
}
} // for itr - AllChests[]
}
- // Load dispensers
+ // Load dispensers:
Json::Value AllDispensers = a_Value.get("Dispensers", Json::nullValue);
- if( !AllDispensers.empty() )
+ for (Json::Value::iterator itr = AllDispensers.begin(); itr != AllDispensers.end(); ++itr)
{
- for( Json::Value::iterator itr = AllDispensers.begin(); itr != AllDispensers.end(); ++itr )
+ std::auto_ptr<cDispenserEntity> DispenserEntity(new cDispenserEntity(0, 0, 0, a_World));
+ if (!DispenserEntity->LoadFromJson(*itr))
{
- Json::Value & Dispenser = *itr;
- cDispenserEntity * DispenserEntity = new cDispenserEntity(0,0,0, a_World);
- if( !DispenserEntity->LoadFromJson( Dispenser ) )
- {
- LOGERROR("ERROR READING DISPENSER FROM JSON!" );
- delete DispenserEntity;
- }
- else
- {
- a_BlockEntities.push_back( DispenserEntity );
- }
- } // for itr - AllDispensers[]
- }
+ LOGWARNING("ERROR READING DISPENSER FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(DispenserEntity.release());
+ }
+ } // for itr - AllDispensers[]
- // Load furnaces
+ // Load furnaces:
Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue);
- if( !AllFurnaces.empty() )
+ for (Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr)
{
- for( Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr )
+ // TODO: The block type and meta aren't correct, there's no way to get them here
+ std::auto_ptr<cFurnaceEntity> FurnaceEntity(new cFurnaceEntity(0, 0, 0, E_BLOCK_FURNACE, 0, a_World));
+ if (!FurnaceEntity->LoadFromJson(*itr))
{
- Json::Value & Furnace = *itr;
- // TODO: The block type and meta aren't correct, there's no way to get them here
- cFurnaceEntity * FurnaceEntity = new cFurnaceEntity(0, 0, 0, E_BLOCK_FURNACE, 0, a_World);
- if (!FurnaceEntity->LoadFromJson(Furnace))
- {
- LOGERROR("ERROR READING FURNACE FROM JSON!" );
- delete FurnaceEntity;
- }
- else
- {
- a_BlockEntities.push_back(FurnaceEntity);
- }
- } // for itr - AllFurnaces[]
- }
+ LOGWARNING("ERROR READING FURNACE FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(FurnaceEntity.release());
+ }
+ } // for itr - AllFurnaces[]
- // Load signs
+ // Load signs:
Json::Value AllSigns = a_Value.get("Signs", Json::nullValue);
- if( !AllSigns.empty() )
+ for (Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr)
{
- for( Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr )
+ std::auto_ptr<cSignEntity> SignEntity(new cSignEntity(E_BLOCK_SIGN_POST, 0, 0, 0, a_World));
+ if (!SignEntity->LoadFromJson(*itr))
{
- Json::Value & Sign = *itr;
- cSignEntity * SignEntity = new cSignEntity( E_BLOCK_SIGN_POST, 0,0,0, a_World);
- if ( !SignEntity->LoadFromJson( Sign ) )
- {
- LOGERROR("ERROR READING SIGN FROM JSON!" );
- delete SignEntity;
- }
- else
- {
- a_BlockEntities.push_back( SignEntity );
- }
- } // for itr - AllSigns[]
- }
+ LOGWARNING("ERROR READING SIGN FROM JSON!");
+ }
+ else
+ {
+ a_BlockEntities.push_back(SignEntity.release());
+ }
+ } // for itr - AllSigns[]
- // Load note blocks
+ // Load note blocks:
Json::Value AllNotes = a_Value.get("Notes", Json::nullValue);
- if( !AllNotes.empty() )
+ for( Json::Value::iterator itr = AllNotes.begin(); itr != AllNotes.end(); ++itr )
{
- for( Json::Value::iterator itr = AllNotes.begin(); itr != AllNotes.end(); ++itr )
+ std::auto_ptr<cNoteEntity> NoteEntity(new cNoteEntity(0, 0, 0, a_World));
+ if (!NoteEntity->LoadFromJson(*itr))
{
- Json::Value & Note = *itr;
- cNoteEntity * NoteEntity = new cNoteEntity(0, 0, 0, a_World);
- if ( !NoteEntity->LoadFromJson( Note ) )
- {
- LOGERROR("ERROR READING NOTE BLOCK FROM JSON!" );
- delete NoteEntity;
- }
- else
- {
- a_BlockEntities.push_back( NoteEntity );
- }
- } // for itr - AllNotes[]
- }
+ LOGWARNING("ERROR READING NOTE BLOCK FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(NoteEntity.release());
+ }
+ } // for itr - AllNotes[]
- // Load jukeboxes
+ // Load jukeboxes:
Json::Value AllJukeboxes = a_Value.get("Jukeboxes", Json::nullValue);
- if( !AllJukeboxes.empty() )
+ for( Json::Value::iterator itr = AllJukeboxes.begin(); itr != AllJukeboxes.end(); ++itr )
{
- for( Json::Value::iterator itr = AllJukeboxes.begin(); itr != AllJukeboxes.end(); ++itr )
+ std::auto_ptr<cJukeboxEntity> JukeboxEntity(new cJukeboxEntity(0, 0, 0, a_World));
+ if (!JukeboxEntity->LoadFromJson(*itr))
{
- Json::Value & Jukebox = *itr;
- cJukeboxEntity * JukeboxEntity = new cJukeboxEntity(0, 0, 0, a_World);
- if ( !JukeboxEntity->LoadFromJson( Jukebox ) )
- {
- LOGERROR("ERROR READING JUKEBOX FROM JSON!" );
- delete JukeboxEntity;
- }
- else
- {
- a_BlockEntities.push_back( JukeboxEntity );
- }
- } // for itr - AllJukeboxes[]
- }
+ LOGWARNING("ERROR READING JUKEBOX FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(JukeboxEntity.release());
+ }
+ } // for itr - AllJukeboxes[]
+
+ // Load command blocks:
+ Json::Value AllCommandBlocks = a_Value.get("CommandBlocks", Json::nullValue);
+ for( Json::Value::iterator itr = AllCommandBlocks.begin(); itr != AllCommandBlocks.end(); ++itr )
+ {
+ std::auto_ptr<cCommandBlockEntity> CommandBlockEntity(new cCommandBlockEntity(0, 0, 0, a_World));
+ if (!CommandBlockEntity->LoadFromJson(*itr))
+ {
+ LOGWARNING("ERROR READING COMMAND BLOCK FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(CommandBlockEntity.release());
+ }
+ } // for itr - AllCommandBlocks[]
}
diff --git a/src/main.cpp b/src/main.cpp
index 340149e0b..06b344c25 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -19,6 +19,13 @@ bool g_SERVER_TERMINATED = false; // Set to true when the server terminates, so
+/** If set to true, the protocols will log each player's communication to a separate logfile */
+bool g_ShouldLogComm;
+
+
+
+
+
/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window
#define ENABLE_LEAK_FINDER
@@ -216,12 +223,24 @@ int main( int argc, char **argv )
#ifndef _DEBUG
std::signal(SIGSEGV, NonCtrlHandler);
std::signal(SIGTERM, NonCtrlHandler);
- std::signal(SIGINT, NonCtrlHandler);
+ std::signal(SIGINT, NonCtrlHandler);
#endif
// DEBUG: test the dumpfile creation:
// *((int *)0) = 0;
+ // Check if comm logging is to be enabled:
+ for (int i = 0; i < argc; i++)
+ {
+ if (
+ (NoCaseCompare(argv[i], "/commlog") == 0) ||
+ (NoCaseCompare(argv[i], "/logcomm") == 0)
+ )
+ {
+ g_ShouldLogComm = true;
+ }
+ }
+
#if !defined(ANDROID_NDK)
try
#endif