summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Authenticator.cpp8
-rw-r--r--src/Bindings/AllToLua.bat4
-rw-r--r--src/Bindings/AllToLua.pkg4
-rw-r--r--src/Bindings/CMakeLists.txt19
-rw-r--r--src/Bindings/LuaState.cpp317
-rw-r--r--src/Bindings/LuaState.h272
-rw-r--r--src/Bindings/ManualBindings.cpp502
-rw-r--r--src/Bindings/Plugin.h4
-rw-r--r--src/Bindings/PluginLua.cpp170
-rw-r--r--src/Bindings/PluginLua.h46
-rw-r--r--src/Bindings/PluginManager.cpp110
-rw-r--r--src/Bindings/PluginManager.h66
-rw-r--r--src/Bindings/tolua++.h6
-rw-r--r--src/Bindings/tolua_base.h128
-rw-r--r--src/Bindings/virtual_method_hooks.lua8
-rw-r--r--src/BiomeDef.cpp131
-rw-r--r--src/BiomeDef.h107
-rw-r--r--src/BlockArea.cpp176
-rw-r--r--src/BlockArea.h27
-rw-r--r--src/BlockEntities/BlockEntity.cpp24
-rw-r--r--src/BlockEntities/BlockEntity.h6
-rw-r--r--src/BlockEntities/BlockEntityWithItems.h1
-rw-r--r--src/BlockEntities/CommandBlockEntity.cpp221
-rw-r--r--src/BlockEntities/CommandBlockEntity.h91
-rw-r--r--src/BlockEntities/FurnaceEntity.cpp12
-rw-r--r--src/BlockEntities/FurnaceEntity.h2
-rw-r--r--src/BlockEntities/HopperEntity.cpp5
-rw-r--r--src/BlockEntities/JukeboxEntity.h2
-rw-r--r--src/BlockEntities/NoteEntity.h2
-rw-r--r--src/BlockID.cpp228
-rw-r--r--src/BlockID.h5
-rw-r--r--src/BlockTracer.h25
-rw-r--r--src/Blocks/BlockChest.h10
-rw-r--r--src/Blocks/BlockCommandBlock.h32
-rw-r--r--src/Blocks/BlockComparator.h2
-rw-r--r--src/Blocks/BlockDoor.h2
-rw-r--r--src/Blocks/BlockDropSpenser.h2
-rw-r--r--src/Blocks/BlockEnderchest.h2
-rw-r--r--src/Blocks/BlockFenceGate.h4
-rw-r--r--src/Blocks/BlockFurnace.h2
-rw-r--r--src/Blocks/BlockGlowstone.h4
-rw-r--r--src/Blocks/BlockHandler.cpp2
-rw-r--r--src/Blocks/BlockHandler.h6
-rw-r--r--src/Blocks/BlockLever.h2
-rw-r--r--src/Blocks/BlockPiston.cpp2
-rw-r--r--src/Blocks/BlockPumpkin.h2
-rw-r--r--src/Blocks/BlockRail.h28
-rw-r--r--src/Blocks/BlockRedstone.h2
-rw-r--r--src/Blocks/BlockRedstoneRepeater.h2
-rw-r--r--src/Blocks/BlockStairs.h2
-rw-r--r--src/Blocks/BlockTorch.h6
-rw-r--r--src/ByteBuffer.cpp42
-rw-r--r--src/ByteBuffer.h2
-rw-r--r--src/CMakeLists.txt253
-rw-r--r--src/Chunk.cpp61
-rw-r--r--src/Chunk.h39
-rw-r--r--src/Chunk.inl.h34
-rw-r--r--src/ChunkDef.h95
-rw-r--r--src/ChunkMap.cpp19
-rw-r--r--src/ChunkMap.h23
-rw-r--r--src/ChunkSender.cpp2
-rw-r--r--src/ClientHandle.cpp167
-rw-r--r--src/ClientHandle.h16
-rw-r--r--src/CommandOutput.h1
-rw-r--r--src/Crypto.cpp509
-rw-r--r--src/Crypto.h200
-rw-r--r--src/Defines.h53
-rw-r--r--src/Enchantments.cpp79
-rw-r--r--src/Enchantments.h8
-rw-r--r--src/Entities/Entity.cpp209
-rw-r--r--src/Entities/Entity.h62
-rw-r--r--src/Entities/Floater.h10
-rw-r--r--src/Entities/Minecart.cpp879
-rw-r--r--src/Entities/Minecart.h67
-rw-r--r--src/Entities/Pickup.cpp67
-rw-r--r--src/Entities/Player.cpp337
-rw-r--r--src/Entities/Player.h53
-rw-r--r--src/Entities/ProjectileEntity.cpp17
-rw-r--r--src/Entities/ProjectileEntity.h6
-rw-r--r--src/ForEachChunkProvider.h14
-rw-r--r--src/Generating/CMakeLists.txt2
-rw-r--r--src/Generating/Caves.cpp9
-rw-r--r--src/Generating/ChunkGenerator.cpp57
-rw-r--r--src/Generating/ChunkGenerator.h63
-rw-r--r--src/Generating/CompoGen.cpp17
-rw-r--r--src/Generating/ComposableGenerator.cpp212
-rw-r--r--src/Generating/ComposableGenerator.h17
-rw-r--r--src/Generating/DistortedHeightmap.cpp3
-rw-r--r--src/Generating/FinishGen.cpp126
-rw-r--r--src/Generating/FinishGen.h25
-rw-r--r--src/Generating/HeiGen.cpp74
-rw-r--r--src/Generating/Noise3DGenerator.cpp4
-rw-r--r--src/Generating/Noise3DGenerator.h2
-rw-r--r--src/Generating/Ravines.cpp2
-rw-r--r--src/Generating/Trees.cpp49
-rw-r--r--src/Generating/Trees.h3
-rw-r--r--src/Globals.h25
-rw-r--r--src/HTTPServer/HTTPConnection.cpp10
-rw-r--r--src/HTTPServer/HTTPConnection.h31
-rw-r--r--src/HTTPServer/HTTPFormParser.cpp1
-rw-r--r--src/HTTPServer/HTTPMessage.cpp7
-rw-r--r--src/HTTPServer/HTTPMessage.h51
-rw-r--r--src/HTTPServer/MultipartParser.cpp2
-rw-r--r--src/HTTPServer/NameValueParser.cpp1
-rw-r--r--src/Inventory.cpp6
-rw-r--r--src/Item.cpp34
-rw-r--r--src/Item.h45
-rw-r--r--src/ItemGrid.cpp8
-rw-r--r--src/Items/ItemBed.h2
-rw-r--r--src/Items/ItemComparator.h2
-rw-r--r--src/Items/ItemFishingRod.h8
-rw-r--r--src/Items/ItemHandler.h8
-rw-r--r--src/Items/ItemLighter.h4
-rw-r--r--src/Items/ItemMinecart.h2
-rw-r--r--src/Items/ItemRedstoneDust.h2
-rw-r--r--src/Items/ItemRedstoneRepeater.h2
-rw-r--r--src/Items/ItemSeeds.h1
-rw-r--r--src/Items/ItemSign.h2
-rw-r--r--src/LeakFinder.cpp29
-rw-r--r--src/LightingThread.cpp5
-rw-r--r--src/LineBlockTracer.cpp3
-rw-r--r--src/Log.cpp8
-rw-r--r--src/Log.h8
-rw-r--r--src/MCServer.vcproj.user167
-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/Chicken.cpp1
-rw-r--r--src/Mobs/Chicken.h3
-rw-r--r--src/Mobs/Cow.cpp2
-rw-r--r--src/Mobs/Cow.h3
-rw-r--r--src/Mobs/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/Mooshroom.cpp2
-rw-r--r--src/Mobs/Mooshroom.h2
-rw-r--r--src/Mobs/PassiveAggressiveMonster.cpp3
-rw-r--r--src/Mobs/PassiveMonster.cpp28
-rw-r--r--src/Mobs/PassiveMonster.h3
-rw-r--r--src/Mobs/Pig.cpp2
-rw-r--r--src/Mobs/Pig.h3
-rw-r--r--src/Mobs/Sheep.cpp40
-rw-r--r--src/Mobs/Sheep.h5
-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.cpp158
-rw-r--r--src/Mobs/Villager.h26
-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.cpp15
-rw-r--r--src/OSSupport/CriticalSection.h5
-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/Queue.h178
-rw-r--r--src/OSSupport/Socket.cpp84
-rw-r--r--src/OSSupport/Socket.h32
-rw-r--r--src/OSSupport/SocketThreads.cpp390
-rw-r--r--src/OSSupport/SocketThreads.h101
-rw-r--r--src/OSSupport/Timer.cpp2
-rw-r--r--src/Piston.cpp1
-rw-r--r--src/Protocol/Protocol.h6
-rw-r--r--src/Protocol/Protocol125.cpp32
-rw-r--r--src/Protocol/Protocol125.h9
-rw-r--r--src/Protocol/Protocol132.cpp179
-rw-r--r--src/Protocol/Protocol132.h28
-rw-r--r--src/Protocol/Protocol14x.cpp15
-rw-r--r--src/Protocol/Protocol15x.cpp56
-rw-r--r--src/Protocol/Protocol15x.h3
-rw-r--r--src/Protocol/Protocol17x.cpp567
-rw-r--r--src/Protocol/Protocol17x.h43
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp56
-rw-r--r--src/Protocol/ProtocolRecognizer.h7
-rw-r--r--src/ReferenceManager.cpp43
-rw-r--r--src/ReferenceManager.h34
-rw-r--r--src/Resources/MCServer.rc (renamed from VC2008/MCServer.rc)0
-rw-r--r--src/Resources/icon.ico (renamed from VC2008/icon.ico)bin353118 -> 353118 bytes
-rw-r--r--src/Resources/icon_128.png (renamed from VC2008/icon_128.png)bin26758 -> 26758 bytes
-rw-r--r--src/Resources/icon_256.png (renamed from VC2008/icon_256.png)bin66137 -> 66137 bytes
-rw-r--r--src/Resources/resource_MCServer.h (renamed from VC2008/resource_MCServer.h)0
-rw-r--r--src/Root.cpp8
-rw-r--r--src/Scoreboard.cpp510
-rw-r--r--src/Scoreboard.h276
-rw-r--r--src/Server.cpp39
-rw-r--r--src/Server.h71
-rw-r--r--src/Simulator/FireSimulator.h2
-rw-r--r--src/Simulator/NoopFluidSimulator.h10
-rw-r--r--src/Simulator/RedstoneSimulator.cpp377
-rw-r--r--src/Simulator/RedstoneSimulator.h109
-rw-r--r--src/Simulator/SandSimulator.h2
-rw-r--r--src/Simulator/Simulator.h9
-rw-r--r--src/StackWalker.cpp6
-rw-r--r--src/StringCompression.cpp8
-rw-r--r--src/StringCompression.h2
-rw-r--r--src/StringUtils.cpp124
-rw-r--r--src/StringUtils.h14
-rw-r--r--src/UI/SlotArea.cpp23
-rw-r--r--src/UI/Window.cpp33
-rw-r--r--src/WebAdmin.cpp3
-rw-r--r--src/WebAdmin.h15
-rw-r--r--src/World.cpp277
-rw-r--r--src/World.h334
-rw-r--r--src/WorldStorage/CMakeLists.txt2
-rw-r--r--src/WorldStorage/EnchantmentSerializer.cpp88
-rw-r--r--src/WorldStorage/EnchantmentSerializer.h17
-rw-r--r--src/WorldStorage/FastNBT.cpp23
-rw-r--r--src/WorldStorage/FastNBT.h6
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp45
-rw-r--r--src/WorldStorage/NBTChunkSerializer.h2
-rw-r--r--src/WorldStorage/SchematicFileSerializer.cpp172
-rw-r--r--src/WorldStorage/SchematicFileSerializer.h29
-rw-r--r--src/WorldStorage/ScoreboardSerializer.cpp377
-rw-r--r--src/WorldStorage/ScoreboardSerializer.h52
-rw-r--r--src/WorldStorage/WSSAnvil.cpp79
-rw-r--r--src/WorldStorage/WSSAnvil.h21
-rw-r--r--src/WorldStorage/WSSCompact.cpp193
-rw-r--r--src/WorldStorage/WSSCompact.h7
-rw-r--r--src/WorldStorage/WorldStorage.cpp161
-rw-r--r--src/WorldStorage/WorldStorage.h44
-rw-r--r--src/main.cpp65
227 files changed, 10265 insertions, 3771 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/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index e9a5ea0c6..f65aed9bb 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -1,8 +1,6 @@
$#include "../Globals.h"
-$#include "tolua_base.h"
-
// Typedefs from Globals.h, so that we don't have to process that file:
typedef long long Int64;
typedef int Int32;
@@ -14,6 +12,7 @@ typedef unsigned short UInt16;
$cfile "../ChunkDef.h"
+$cfile "../BiomeDef.h"
$cfile "../../lib/inifile/iniFile.h"
@@ -32,6 +31,7 @@ $cfile "../Defines.h"
$cfile "../ChatColor.h"
$cfile "../ClientHandle.h"
$cfile "../Entities/Entity.h"
+$cfile "../Entities/Floater.h"
$cfile "../Entities/Pawn.h"
$cfile "../Entities/Player.h"
$cfile "../Entities/Pickup.h"
diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt
deleted file mode 100644
index 41c641d9d..000000000
--- a/src/Bindings/CMakeLists.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-
-cmake_minimum_required (VERSION 2.6)
-project (MCServer)
-
-include_directories ("${PROJECT_SOURCE_DIR}/../")
-
- ADD_CUSTOM_COMMAND(
-#add any new generated bindings here
- OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/Bindings.cpp ${CMAKE_CURRENT_BINARY_DIR}/Bindings.h
-#command execuded to regerate bindings
- COMMAND tolua -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
-#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/LuaState.cpp b/src/Bindings/LuaState.cpp
index cfa3f70ca..2fca7142c 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -18,7 +18,7 @@ extern "C"
// fwd: SQLite/lsqlite3.c
extern "C"
{
- LUALIB_API int luaopen_lsqlite3(lua_State * L);
+ int luaopen_lsqlite3(lua_State * L);
}
// fwd: LuaExpat/lxplib.c:
@@ -228,11 +228,14 @@ bool cLuaState::PushFunction(const char * a_FunctionName)
return false;
}
+ // Push the error handler for lua_pcall()
+ lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
+
lua_getglobal(m_LuaState, a_FunctionName);
if (!lua_isfunction(m_LuaState, -1))
{
LOGWARNING("Error in %s: Could not find function %s()", m_SubsystemName.c_str(), a_FunctionName);
- lua_pop(m_LuaState, 1);
+ lua_pop(m_LuaState, 2);
return false;
}
m_CurrentFunctionName.assign(a_FunctionName);
@@ -249,10 +252,13 @@ bool cLuaState::PushFunction(int a_FnRef)
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
+ // Push the error handler for lua_pcall()
+ lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
+
lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref()
if (!lua_isfunction(m_LuaState, -1))
{
- lua_pop(m_LuaState, 1);
+ lua_pop(m_LuaState, 2);
return false;
}
m_CurrentFunctionName = "<callback>";
@@ -264,27 +270,29 @@ bool cLuaState::PushFunction(int a_FnRef)
-bool cLuaState::PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName)
+bool cLuaState::PushFunction(const cTableRef & a_TableRef)
{
ASSERT(IsValid());
ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack
-
- lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef); // Get the table ref
+
+ // Push the error handler for lua_pcall()
+ lua_pushcfunction(m_LuaState, &ReportFnCallErrors);
+
+ lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_TableRef.GetTableRef()); // Get the table ref
if (!lua_istable(m_LuaState, -1))
{
// Not a table, bail out
- lua_pop(m_LuaState, 1);
+ lua_pop(m_LuaState, 2);
return false;
}
- lua_getfield(m_LuaState, -1, a_FnName);
+ lua_getfield(m_LuaState, -1, a_TableRef.GetFnName());
if (lua_isnil(m_LuaState, -1) || !lua_isfunction(m_LuaState, -1))
{
// Not a valid function, bail out
lua_pop(m_LuaState, 2);
return false;
}
- lua_remove(m_LuaState, -2); // Remove the table ref from the stack
- m_CurrentFunctionName = "<table_callback>";
+ Printf(m_CurrentFunctionName, "<table-callback %s>", a_TableRef.GetFnName());
m_NumCurrentFunctionArgs = 0;
return true;
}
@@ -297,7 +305,7 @@ void cLuaState::Push(const AString & a_String)
{
ASSERT(IsValid());
- tolua_pushcppstring(m_LuaState, a_String);
+ lua_pushlstring(m_LuaState, a_String.data(), a_String.size());
m_NumCurrentFunctionArgs += 1;
}
@@ -309,7 +317,7 @@ void cLuaState::Push(const AStringVector & a_Vector)
{
ASSERT(IsValid());
- lua_createtable(m_LuaState, a_Vector.size(), 0);
+ lua_createtable(m_LuaState, (int)a_Vector.size(), 0);
int newTable = lua_gettop(m_LuaState);
int index = 1;
for (AStringVector::const_iterator itr = a_Vector.begin(), end = a_Vector.end(); itr != end; ++itr, ++index)
@@ -468,6 +476,18 @@ void cLuaState::Push(cItems * a_Items)
+void cLuaState::Push(const cItems & a_Items)
+{
+ ASSERT(IsValid());
+
+ tolua_pushusertype(m_LuaState, (void *)&a_Items, "cItems");
+ m_NumCurrentFunctionArgs += 1;
+}
+
+
+
+
+
void cLuaState::Push(cClientHandle * a_Client)
{
ASSERT(IsValid());
@@ -612,18 +632,6 @@ void cLuaState::Push(cTNTEntity * a_TNTEntity)
-void cLuaState::Push(cCreeper * a_Creeper)
-{
- ASSERT(IsValid());
-
- tolua_pushusertype(m_LuaState, a_Creeper, "cCreeper");
- m_NumCurrentFunctionArgs += 1;
-}
-
-
-
-
-
void cLuaState::Push(Vector3i * a_Vector)
{
ASSERT(IsValid());
@@ -720,11 +728,13 @@ void cLuaState::GetReturn(int a_StackPos, double & a_ReturnedVal)
bool cLuaState::CallFunction(int a_NumResults)
{
ASSERT (m_NumCurrentFunctionArgs >= 0); // A function must be pushed to stack first
- ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1));
+ ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 1)); // The function to call
+ ASSERT(lua_isfunction(m_LuaState, -m_NumCurrentFunctionArgs - 2)); // The error handler
- int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, 0);
- if (ReportErrors(s))
+ int s = lua_pcall(m_LuaState, m_NumCurrentFunctionArgs, a_NumResults, -m_NumCurrentFunctionArgs - 2);
+ if (s != 0)
{
+ // The error has already been printed together with the stacktrace
LOGWARNING("Error in %s calling function %s()", m_SubsystemName.c_str(), m_CurrentFunctionName.c_str());
m_NumCurrentFunctionArgs = -1;
m_CurrentFunctionName.clear();
@@ -732,6 +742,10 @@ bool cLuaState::CallFunction(int a_NumResults)
}
m_NumCurrentFunctionArgs = -1;
m_CurrentFunctionName.clear();
+
+ // Remove the error handler from the stack:
+ lua_remove(m_LuaState, -a_NumResults - 1);
+
return true;
}
@@ -904,6 +918,40 @@ bool cLuaState::CheckParamString(int a_StartParam, int a_EndParam)
+bool cLuaState::CheckParamFunction(int a_StartParam, int a_EndParam)
+{
+ ASSERT(IsValid());
+
+ if (a_EndParam < 0)
+ {
+ a_EndParam = a_StartParam;
+ }
+
+ for (int i = a_StartParam; i <= a_EndParam; i++)
+ {
+ if (lua_isfunction(m_LuaState, i))
+ {
+ continue;
+ }
+ // Not the correct parameter
+ lua_Debug entry;
+ VERIFY(lua_getstack(m_LuaState, 0, &entry));
+ VERIFY(lua_getinfo (m_LuaState, "n", &entry));
+ AString ErrMsg = Printf("Error in function '%s' parameter #%d. Function expected, got %s",
+ (entry.name != NULL) ? entry.name : "?", i, GetTypeText(i).c_str()
+ );
+ LogStackTrace();
+ return false;
+ } // for i - Param
+
+ // All params checked ok
+ return true;
+}
+
+
+
+
+
bool cLuaState::CheckParamEnd(int a_Param)
{
tolua_Error tolua_err;
@@ -952,15 +1000,24 @@ bool cLuaState::ReportErrors(lua_State * a_LuaState, int a_Status)
void cLuaState::LogStackTrace(void)
{
+ LogStackTrace(m_LuaState);
+}
+
+
+
+
+
+void cLuaState::LogStackTrace(lua_State * a_LuaState)
+{
LOGWARNING("Stack trace:");
lua_Debug entry;
int depth = 0;
- while (lua_getstack(m_LuaState, depth, &entry))
+ while (lua_getstack(a_LuaState, depth, &entry))
{
- int status = lua_getinfo(m_LuaState, "Sln", &entry);
+ int status = lua_getinfo(a_LuaState, "Sln", &entry);
assert(status);
- LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?");
+ LOGWARNING(" %s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "(no name)");
depth++;
}
LOGWARNING("Stack trace end");
@@ -972,21 +1029,195 @@ void cLuaState::LogStackTrace(void)
AString cLuaState::GetTypeText(int a_StackPos)
{
- int Type = lua_type(m_LuaState, a_StackPos);
- switch (Type)
+ return lua_typename(m_LuaState, lua_type(m_LuaState, a_StackPos));
+}
+
+
+
+
+
+int cLuaState::CallFunctionWithForeignParams(
+ const AString & a_FunctionName,
+ cLuaState & a_SrcLuaState,
+ int a_SrcParamStart,
+ int a_SrcParamEnd
+)
+{
+ ASSERT(IsValid());
+ ASSERT(a_SrcLuaState.IsValid());
+
+ // Store the stack position before any changes
+ int OldTop = lua_gettop(m_LuaState);
+
+ // Push the function to call, including the error handler:
+ if (!PushFunction(a_FunctionName.c_str()))
+ {
+ LOGWARNING("Function '%s' not found", a_FunctionName.c_str());
+ lua_pop(m_LuaState, 2);
+ return -1;
+ }
+
+ // Copy the function parameters to the target state
+ if (CopyStackFrom(a_SrcLuaState, a_SrcParamStart, a_SrcParamEnd) < 0)
+ {
+ // Something went wrong, fix the stack and exit
+ lua_pop(m_LuaState, 2);
+ return -1;
+ }
+
+ // Call the function, with an error handler:
+ int s = lua_pcall(m_LuaState, a_SrcParamEnd - a_SrcParamStart + 1, LUA_MULTRET, OldTop);
+ if (ReportErrors(s))
+ {
+ LOGWARN("Error while calling function '%s' in '%s'", a_FunctionName.c_str(), m_SubsystemName.c_str());
+ // Fix the stack.
+ // We don't know how many values have been pushed, so just get rid of any that weren't there initially
+ int CurTop = lua_gettop(m_LuaState);
+ if (CurTop > OldTop)
+ {
+ lua_pop(m_LuaState, CurTop - OldTop);
+ }
+ return -1;
+ }
+
+ // Reset the internal checking mechanisms:
+ m_NumCurrentFunctionArgs = -1;
+
+ // Remove the error handler from the stack:
+ lua_remove(m_LuaState, OldTop + 1);
+
+ // Return the number of return values:
+ return lua_gettop(m_LuaState) - OldTop;
+}
+
+
+
+
+
+int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd)
+{
+ /*
+ // DEBUG:
+ LOGD("Copying stack values from %d to %d", a_SrcStart, a_SrcEnd);
+ a_SrcLuaState.LogStack("Src stack before copying:");
+ LogStack("Dst stack before copying:");
+ */
+ for (int i = a_SrcStart; i <= a_SrcEnd; ++i)
+ {
+ int t = lua_type(a_SrcLuaState, i);
+ switch (t)
+ {
+ case LUA_TNIL:
+ {
+ lua_pushnil(m_LuaState);
+ break;
+ }
+ case LUA_TSTRING:
+ {
+ AString s;
+ a_SrcLuaState.ToString(i, s);
+ Push(s);
+ break;
+ }
+ case LUA_TBOOLEAN:
+ {
+ bool b = (tolua_toboolean(a_SrcLuaState, i, false) != 0);
+ Push(b);
+ break;
+ }
+ case LUA_TNUMBER:
+ {
+ lua_Number d = tolua_tonumber(a_SrcLuaState, i, 0);
+ Push(d);
+ break;
+ }
+ case LUA_TUSERDATA:
+ {
+ // Get the class name:
+ const char * type = NULL;
+ if (lua_getmetatable(a_SrcLuaState, i) == 0)
+ {
+ LOGWARNING("%s: Unknown class in pos %d, cannot copy.", __FUNCTION__, i);
+ lua_pop(m_LuaState, i - a_SrcStart);
+ return -1;
+ }
+ lua_rawget(a_SrcLuaState, LUA_REGISTRYINDEX); // Stack +1
+ type = lua_tostring(a_SrcLuaState, -1);
+ lua_pop(a_SrcLuaState, 1); // Stack -1
+
+ // Copy the value:
+ void * ud = tolua_touserdata(a_SrcLuaState, i, NULL);
+ tolua_pushusertype(m_LuaState, ud, type);
+ }
+ default:
+ {
+ LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools and classes!",
+ __FUNCTION__, lua_typename(a_SrcLuaState, t), i
+ );
+ a_SrcLuaState.LogStack("Stack where copying failed:");
+ lua_pop(m_LuaState, i - a_SrcStart);
+ return -1;
+ }
+ }
+ }
+ return a_SrcEnd - a_SrcStart + 1;
+}
+
+
+
+
+
+void cLuaState::ToString(int a_StackPos, AString & a_String)
+{
+ size_t len;
+ const char * s = lua_tolstring(m_LuaState, a_StackPos, &len);
+ if (s != NULL)
{
- case LUA_TNONE: return "TNONE";
- case LUA_TNIL: return "TNIL";
- case LUA_TBOOLEAN: return "TBOOLEAN";
- case LUA_TLIGHTUSERDATA: return "TLIGHTUSERDATA";
- case LUA_TNUMBER: return "TNUMBER";
- case LUA_TSTRING: return "TSTRING";
- case LUA_TTABLE: return "TTABLE";
- case LUA_TFUNCTION: return "TFUNCTION";
- case LUA_TUSERDATA: return "TUSERDATA";
- case LUA_TTHREAD: return "TTHREAD";
+ a_String.assign(s, len);
}
- return Printf("Unknown (%d)", Type);
+}
+
+
+
+
+
+void cLuaState::LogStack(const char * a_Header)
+{
+ LogStack(m_LuaState, a_Header);
+}
+
+
+
+
+
+void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header)
+{
+ LOGD((a_Header != NULL) ? a_Header : "Lua C API Stack contents:");
+ for (int i = lua_gettop(a_LuaState); i >= 0; i--)
+ {
+ AString Value;
+ int Type = lua_type(a_LuaState, i);
+ switch (Type)
+ {
+ case LUA_TBOOLEAN: Value.assign((lua_toboolean(a_LuaState, i) != 0) ? "true" : "false"); break;
+ case LUA_TLIGHTUSERDATA: Printf(Value, "%p", lua_touserdata(a_LuaState, i)); break;
+ case LUA_TNUMBER: Printf(Value, "%f", (double)lua_tonumber(a_LuaState, i)); break;
+ case LUA_TSTRING: Printf(Value, "%s", lua_tostring(a_LuaState, i)); break;
+ default: break;
+ }
+ LOGD(" Idx %d: type %d (%s) %s", i, Type, lua_typename(a_LuaState, Type), Value.c_str());
+ } // for i - stack idx
+}
+
+
+
+
+
+int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
+{
+ LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
+ LogStackTrace(a_LuaState);
+ return 1; // We left the error message on the stack as the return value
}
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index 15b0cdeff..dda45bb28 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -60,23 +60,23 @@ class cBlockEntity;
-/// Encapsulates a Lua state and provides some syntactic sugar for common operations
+/** Encapsulates a Lua state and provides some syntactic sugar for common operations */
class cLuaState
{
public:
- /// Used for storing references to object in the global registry
+ /** Used for storing references to object in the global registry */
class cRef
{
public:
- /// Creates a reference in the specified LuaState for object at the specified StackPos
+ /** Creates a reference in the specified LuaState for object at the specified StackPos */
cRef(cLuaState & a_LuaState, int a_StackPos);
~cRef();
- /// Returns true if the reference is valid
+ /** Returns true if the reference is valid */
bool IsValid(void) const {return (m_Ref != LUA_REFNIL); }
- /// Allows to use this class wherever an int (i. e. ref) is to be used
+ /** Allows to use this class wherever an int (i. e. ref) is to be used */
operator int(void) const { return m_Ref; }
protected:
@@ -85,7 +85,24 @@ public:
} ;
- /// A dummy class that's used only to delimit function args from return values for cLuaState::Call()
+ /** Used for calling functions stored in a reference-stored table */
+ class cTableRef
+ {
+ int m_TableRef;
+ const char * m_FnName;
+ public:
+ cTableRef(int a_TableRef, const char * a_FnName) :
+ m_TableRef(a_TableRef),
+ m_FnName(a_FnName)
+ {
+ }
+
+ int GetTableRef(void) const { return m_TableRef; }
+ const char * GetFnName(void) const { return m_FnName; }
+ } ;
+
+
+ /** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */
class cRet
{
} ;
@@ -106,22 +123,22 @@ public:
~cLuaState();
- /// Allows this object to be used in the same way as a lua_State *, for example in the LuaLib functions
+ /** Allows this object to be used in the same way as a lua_State *, for example in the LuaLib functions */
operator lua_State * (void) { return m_LuaState; }
- /// Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor
+ /** Creates the m_LuaState, if not closed already. This state will be automatically closed in the destructor */
void Create(void);
- /// Closes the m_LuaState, if not closed already
+ /** Closes the m_LuaState, if not closed already */
void Close(void);
- /// Attaches the specified state. Operations will be carried out on this state, but it will not be closed in the destructor
+ /** Attaches the specified state. Operations will be carried out on this state, but it will not be closed in the destructor */
void Attach(lua_State * a_State);
- /// Detaches a previously attached state.
+ /** Detaches a previously attached state. */
void Detach(void);
- /// Returns true if the m_LuaState is valid
+ /** Returns true if the m_LuaState is valid */
bool IsValid(void) const { return (m_LuaState != NULL); }
/** Loads the specified file
@@ -130,27 +147,9 @@ public:
*/
bool LoadFile(const AString & a_FileName);
- /// Returns true if a_FunctionName is a valid Lua function that can be called
+ /** Returns true if a_FunctionName is a valid Lua function that can be called */
bool HasFunction(const char * a_FunctionName);
- /** Pushes the function of the specified name onto the stack.
- Returns true if successful. Logs a warning on failure (incl. m_SubsystemName)
- */
- bool PushFunction(const char * a_FunctionName);
-
- /** Pushes a function that has been saved into the global registry, identified by a_FnRef.
- Returns true if successful. Logs a warning on failure
- */
- bool PushFunction(int a_FnRef);
-
- /** Pushes a function that is stored in a table ref.
- Returns true if successful, false on failure. Doesn't log failure.
- */
- bool PushFunctionFromRefTable(cRef & a_TableRef, const char * a_FnName);
-
- /// Pushes a usertype of the specified class type onto the stack
- void PushUserType(void * a_Object, const char * a_Type);
-
// Push a value onto the stack
void Push(const AString & a_String);
void Push(const AStringVector & a_Vector);
@@ -165,6 +164,7 @@ public:
void Push(cMonster * a_Monster);
void Push(cItem * a_Item);
void Push(cItems * a_Items);
+ void Push(const cItems & a_Items);
void Push(cClientHandle * a_ClientHandle);
void Push(cPickup * a_Pickup);
void Push(cChunkDesc * a_ChunkDesc);
@@ -177,13 +177,12 @@ public:
void Push(cWebAdmin * a_WebAdmin);
void Push(const HTTPTemplateRequest * a_Request);
void Push(cTNTEntity * a_TNTEntity);
- void Push(cCreeper * a_Creeper);
void Push(Vector3i * a_Vector);
void Push(void * a_Ptr);
void Push(cHopperEntity * a_Hopper);
void Push(cBlockEntity * a_BlockEntity);
-
- /// Call any 0-param 0-return Lua function in a single line:
+
+ /** Call any 0-param 0-return Lua function in a single line: */
template <typename FnT>
bool Call(FnT a_FnName)
{
@@ -194,7 +193,7 @@ public:
return CallFunction(0);
}
- /// Call any 1-param 0-return Lua function in a single line:
+ /** Call any 1-param 0-return Lua function in a single line: */
template<
typename FnT,
typename ArgT1
@@ -209,7 +208,7 @@ public:
return CallFunction(0);
}
- /// Call any 2-param 0-return Lua function in a single line:
+ /** Call any 2-param 0-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2
>
@@ -224,7 +223,7 @@ public:
return CallFunction(0);
}
- /// Call any 3-param 0-return Lua function in a single line:
+ /** Call any 3-param 0-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3
>
@@ -240,12 +239,34 @@ public:
return CallFunction(0);
}
- /// Call any 1-param 1-return Lua function in a single line:
+ /** Call any 0-param 1-return Lua function in a single line: */
+ template<
+ typename FnT, typename RetT1
+ >
+ bool Call(FnT a_FnName, const cRet & a_Mark, RetT1 & a_Ret1)
+ {
+ UNUSED(a_Mark);
+ if (!PushFunction(a_FnName))
+ {
+ return false;
+ }
+ if (!CallFunction(1))
+ {
+ return false;
+ }
+ GetReturn(-1, a_Ret1);
+ lua_pop(m_LuaState, 1);
+ return true;
+ }
+
+ /** Call any 1-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename RetT1
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1)
{
+ int InitialTop = lua_gettop(m_LuaState);
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -257,15 +278,17 @@ public:
}
GetReturn(-1, a_Ret1);
lua_pop(m_LuaState, 1);
+ ASSERT(InitialTop == lua_gettop(m_LuaState));
return true;
}
- /// Call any 2-param 1-return Lua function in a single line:
+ /** Call any 2-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename RetT1
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -281,12 +304,13 @@ public:
return true;
}
- /// Call any 3-param 1-return Lua function in a single line:
+ /** Call any 3-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename RetT1
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -303,12 +327,13 @@ public:
return true;
}
- /// Call any 4-param 1-return Lua function in a single line:
+ /** Call any 4-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename RetT1
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -326,12 +351,13 @@ public:
return true;
}
- /// Call any 5-param 1-return Lua function in a single line:
+ /** Call any 5-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename RetT1
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -350,13 +376,14 @@ public:
return true;
}
- /// Call any 6-param 1-return Lua function in a single line:
+ /** Call any 6-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
typename RetT1
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -376,13 +403,14 @@ public:
return true;
}
- /// Call any 7-param 1-return Lua function in a single line:
+ /** Call any 7-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
typename ArgT7, typename RetT1
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -403,13 +431,14 @@ public:
return true;
}
- /// Call any 8-param 1-return Lua function in a single line:
+ /** Call any 8-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
typename ArgT7, typename ArgT8, typename RetT1
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -431,13 +460,14 @@ public:
return true;
}
- /// Call any 9-param 1-return Lua function in a single line:
+ /** Call any 9-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
typename ArgT7, typename ArgT8, typename ArgT9, typename RetT1
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -460,13 +490,14 @@ public:
return true;
}
- /// Call any 10-param 1-return Lua function in a single line:
+ /** Call any 10-param 1-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5, typename ArgT6,
typename ArgT7, typename ArgT8, typename ArgT9, typename ArgT10, typename RetT1
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, ArgT10 a_Arg10, const cRet & a_Mark, RetT1 & a_Ret1)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -490,12 +521,13 @@ public:
return true;
}
- /// Call any 1-param 2-return Lua function in a single line:
+ /** Call any 1-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename RetT1, typename RetT2
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -511,12 +543,13 @@ public:
return true;
}
- /// Call any 2-param 2-return Lua function in a single line:
+ /** Call any 2-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename RetT1, typename RetT2
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -533,13 +566,14 @@ public:
return true;
}
- /// Call any 3-param 2-return Lua function in a single line:
+ /** Call any 3-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3,
typename RetT1, typename RetT2
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -557,13 +591,14 @@ public:
return true;
}
- /// Call any 4-param 2-return Lua function in a single line:
+ /** Call any 4-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4,
typename RetT1, typename RetT2
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -582,13 +617,14 @@ public:
return true;
}
- /// Call any 5-param 2-return Lua function in a single line:
+ /** Call any 5-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename RetT1, typename RetT2
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -608,7 +644,7 @@ public:
return true;
}
- /// Call any 6-param 2-return Lua function in a single line:
+ /** Call any 6-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename ArgT6,
@@ -616,6 +652,7 @@ public:
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -636,7 +673,7 @@ public:
return true;
}
- /// Call any 7-param 2-return Lua function in a single line:
+ /** Call any 7-param 2-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename ArgT6, typename ArgT7,
@@ -644,6 +681,7 @@ public:
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -665,7 +703,7 @@ public:
return true;
}
- /// Call any 7-param 3-return Lua function in a single line:
+ /** Call any 7-param 3-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename ArgT6, typename ArgT7,
@@ -673,6 +711,7 @@ public:
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -695,7 +734,7 @@ public:
return true;
}
- /// Call any 8-param 3-return Lua function in a single line:
+ /** Call any 8-param 3-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename ArgT6, typename ArgT7, typename ArgT8,
@@ -703,6 +742,7 @@ public:
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -726,7 +766,7 @@ public:
return true;
}
- /// Call any 9-param 5-return Lua function in a single line:
+ /** Call any 9-param 5-return Lua function in a single line: */
template<
typename FnT, typename ArgT1, typename ArgT2, typename ArgT3, typename ArgT4, typename ArgT5,
typename ArgT6, typename ArgT7, typename ArgT8, typename ArgT9,
@@ -734,6 +774,7 @@ public:
>
bool Call(FnT a_FnName, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3, ArgT4 a_Arg4, ArgT5 a_Arg5, ArgT6 a_Arg6, ArgT7 a_Arg7, ArgT8 a_Arg8, ArgT9 a_Arg9, const cRet & a_Mark, RetT1 & a_Ret1, RetT2 & a_Ret2, RetT3 & a_Ret3, RetT4 & a_Ret4, RetT5 & a_Ret5)
{
+ UNUSED(a_Mark);
if (!PushFunction(a_FnName))
{
return false;
@@ -761,59 +802,71 @@ public:
}
- /// Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged
- void GetReturn(int a_StackPos, bool & a_ReturnedVal);
-
- /// Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged
- void GetReturn(int a_StackPos, AString & a_ReturnedVal);
-
- /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged
- void GetReturn(int a_StackPos, int & a_ReturnedVal);
-
- /// Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged
- void GetReturn(int a_StackPos, double & a_ReturnedVal);
-
- /**
- Calls the function that has been pushed onto the stack by PushFunction(),
- with arguments pushed by PushXXX().
- Returns true if successful, logs a warning on failure.
- */
- bool CallFunction(int a_NumReturnValues);
-
- /// Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions
+ /** Returns true if the specified parameters on the stack are of the specified usertable type; also logs warning if not. Used for static functions */
bool CheckParamUserTable(int a_StartParam, const char * a_UserTable, int a_EndParam = -1);
- /// Returns true if the specified parameters on the stack are of the specified usertype; also logs warning if not. Used for regular functions
+ /** Returns true if the specified parameters on the stack are of the specified usertype; also logs warning if not. Used for regular functions */
bool CheckParamUserType(int a_StartParam, const char * a_UserType, int a_EndParam = -1);
- /// Returns true if the specified parameters on the stack are tables; also logs warning if not
+ /** Returns true if the specified parameters on the stack are tables; also logs warning if not */
bool CheckParamTable(int a_StartParam, int a_EndParam = -1);
- /// Returns true if the specified parameters on the stack are numbers; also logs warning if not
+ /** Returns true if the specified parameters on the stack are numbers; also logs warning if not */
bool CheckParamNumber(int a_StartParam, int a_EndParam = -1);
- /// Returns true if the specified parameters on the stack are strings; also logs warning if not
+ /** Returns true if the specified parameters on the stack are strings; also logs warning if not */
bool CheckParamString(int a_StartParam, int a_EndParam = -1);
- /// Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters)
+ /** Returns true if the specified parameters on the stack are functions; also logs warning if not */
+ bool CheckParamFunction(int a_StartParam, int a_EndParam = -1);
+
+ /** Returns true if the specified parameter on the stack is nil (indicating an end-of-parameters) */
bool CheckParamEnd(int a_Param);
- /// If the status is nonzero, prints the text on the top of Lua stack and returns true
+ /** If the status is nonzero, prints the text on the top of Lua stack and returns true */
bool ReportErrors(int status);
- /// If the status is nonzero, prints the text on the top of Lua stack and returns true
+ /** If the status is nonzero, prints the text on the top of Lua stack and returns true */
static bool ReportErrors(lua_State * a_LuaState, int status);
- /// Logs all items in the current stack trace to the server console
+ /** Logs all items in the current stack trace to the server console */
void LogStackTrace(void);
- /// Returns the type of the item on the specified position in the stack
+ /** Logs all items in the current stack trace to the server console */
+ static void LogStackTrace(lua_State * a_LuaState);
+
+ /** Returns the type of the item on the specified position in the stack */
AString GetTypeText(int a_StackPos);
+ /** Calls the function specified by its name, with arguments copied off the foreign state.
+ If successful, keeps the return values on the stack and returns their number.
+ If unsuccessful, returns a negative number and keeps the stack position unchanged. */
+ int CallFunctionWithForeignParams(
+ const AString & a_FunctionName,
+ cLuaState & a_SrcLuaState,
+ int a_SrcParamStart,
+ int a_SrcParamEnd
+ );
+
+ /** Copies objects on the stack from the specified state.
+ Only numbers, bools, strings and userdatas are copied.
+ If successful, returns the number of objects copied.
+ If failed, returns a negative number and rewinds the stack position. */
+ int CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_SrcEnd);
+
+ /** Reads the value at the specified stack position as a string and sets it to a_String. */
+ void ToString(int a_StackPos, AString & a_String);
+
+ /** Logs all the elements' types on the API stack, with an optional header for the listing. */
+ void LogStack(const char * a_Header);
+
+ /** Logs all the elements' types on the API stack, with an optional header for the listing. */
+ static void LogStack(lua_State * a_LuaState, const char * a_Header = NULL);
+
protected:
lua_State * m_LuaState;
- /// If true, the state is owned by this object and will be auto-Closed. False => attached state
+ /** If true, the state is owned by this object and will be auto-Closed. False => attached state */
bool m_IsOwned;
/** The subsystem name is used for reporting errors to the console, it is either "plugin %s" or "LuaScript"
@@ -821,11 +874,52 @@ protected:
*/
AString m_SubsystemName;
- /// Name of the currently pushed function (for the Push / Call chain)
+ /** Name of the currently pushed function (for the Push / Call chain) */
AString m_CurrentFunctionName;
- /// Number of arguments currently pushed (for the Push / Call chain)
+ /** Number of arguments currently pushed (for the Push / Call chain) */
int m_NumCurrentFunctionArgs;
+
+
+ /** Pushes the function of the specified name onto the stack.
+ Returns true if successful. Logs a warning on failure (incl. m_SubsystemName)
+ */
+ bool PushFunction(const char * a_FunctionName);
+
+ /** Pushes a function that has been saved into the global registry, identified by a_FnRef.
+ Returns true if successful. Logs a warning on failure
+ */
+ bool PushFunction(int a_FnRef);
+
+ /** Pushes a function that is stored in a referenced table by name
+ Returns true if successful. Logs a warning on failure
+ */
+ bool PushFunction(const cTableRef & a_TableRef);
+
+ /** Pushes a usertype of the specified class type onto the stack */
+ void PushUserType(void * a_Object, const char * a_Type);
+
+ /** Retrieve value returned at a_StackPos, if it is a valid bool. If not, a_ReturnedVal is unchanged */
+ void GetReturn(int a_StackPos, bool & a_ReturnedVal);
+
+ /** Retrieve value returned at a_StackPos, if it is a valid string. If not, a_ReturnedVal is unchanged */
+ void GetReturn(int a_StackPos, AString & a_ReturnedVal);
+
+ /** Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged */
+ void GetReturn(int a_StackPos, int & a_ReturnedVal);
+
+ /** Retrieve value returned at a_StackPos, if it is a valid number. If not, a_ReturnedVal is unchanged */
+ void GetReturn(int a_StackPos, double & a_ReturnedVal);
+
+ /**
+ Calls the function that has been pushed onto the stack by PushFunction(),
+ with arguments pushed by PushXXX().
+ Returns true if successful, logs a warning on failure.
+ */
+ bool CallFunction(int a_NumReturnValues);
+
+ /** Used as the error reporting function for function calls */
+ static int ReportFnCallErrors(lua_State * a_LuaState);
} ;
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 2e19c2581..e7c66c6fb 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -13,7 +13,9 @@
#include "../Entities/Player.h"
#include "../WebAdmin.h"
#include "../ClientHandle.h"
+#include "../BlockArea.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/DropperEntity.h"
#include "../BlockEntities/FurnaceEntity.h"
@@ -21,6 +23,7 @@
#include "../BlockEntities/NoteEntity.h"
#include "md5/md5.h"
#include "../LineBlockTracer.h"
+#include "../WorldStorage/SchematicFileSerializer.h"
@@ -984,6 +987,77 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S)
+class cLuaScheduledWorldTask :
+ public cWorld::cTask
+{
+public:
+ cLuaScheduledWorldTask(cPluginLua & a_Plugin, int a_FnRef) :
+ m_Plugin(a_Plugin),
+ m_FnRef(a_FnRef)
+ {
+ }
+
+protected:
+ cPluginLua & m_Plugin;
+ int m_FnRef;
+
+ // cWorld::cTask overrides:
+ virtual void Run(cWorld & a_World) override
+ {
+ m_Plugin.Call(m_FnRef, &a_World);
+ }
+};
+
+
+
+
+
+static int tolua_cWorld_ScheduleTask(lua_State * tolua_S)
+{
+ // Binding for cWorld::ScheduleTask
+ // Params: function, Ticks
+
+ // Retrieve the cPlugin from the LuaState:
+ cPluginLua * Plugin = GetLuaPlugin(tolua_S);
+ if (Plugin == NULL)
+ {
+ // An error message has been already printed in GetLuaPlugin()
+ return 0;
+ }
+
+ // Retrieve the args:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cWorld") ||
+ !L.CheckParamNumber (2) ||
+ !L.CheckParamFunction(3)
+ )
+ {
+ return 0;
+ }
+ cWorld * World = (cWorld *)tolua_tousertype(tolua_S, 1, NULL);
+ if (World == NULL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
+ }
+
+ // Create a reference to the function:
+ int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
+ if (FnRef == LUA_REFNIL)
+ {
+ return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
+ }
+
+ int DelayTicks = (int)tolua_tonumber(tolua_S, 2, 0);
+
+ World->ScheduleTask(DelayTicks, new cLuaScheduledWorldTask(*Plugin, FnRef));
+ return 0;
+}
+
+
+
+
+
static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S)
{
cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0);
@@ -991,7 +1065,6 @@ static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S)
const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins();
lua_newtable(tolua_S);
- int newTable = lua_gettop(tolua_S);
int index = 1;
cPluginManager::PluginMap::const_iterator iter = AllPlugins.begin();
while (iter != AllPlugins.end())
@@ -1137,16 +1210,17 @@ static int tolua_cPluginManager_AddHook(lua_State * tolua_S)
{
/*
Function signatures:
- cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) -- (1) recommended
- cPluginManager:Get():AddHook(HOOK_TYPE, CallbackFunction) -- (2) accepted silently
- cPluginManager:Get():AddHook(Plugin, HOOK_TYPE) -- (3) old style (#121), accepted but complained about
- cPluginManager.AddHook(Plugin, HOOK_TYPE) -- (4) old style (#121) mangled, accepted but complained about
+ cPluginManager:AddHook(HOOK_TYPE, CallbackFunction) -- (1) recommended
+ cPluginManager.AddHook(HOOK_TYPE, CallbackFunction) -- (2) accepted silently (#401 deprecates this)
+ cPluginManager:Get():AddHook(HOOK_TYPE, CallbackFunction) -- (3) accepted silently
+ cPluginManager:Get():AddHook(Plugin, HOOK_TYPE) -- (4) old style (#121), accepted but complained about in the console
+ cPluginManager.AddHook(Plugin, HOOK_TYPE) -- (5) old style (#121) mangled, accepted but complained about in the console
*/
cLuaState S(tolua_S);
cPluginManager * PlgMgr = cPluginManager::Get();
- // If the first param is a cPluginManager, use it instead of the global one:
+ // If the first param is a cPluginManager instance, use it instead of the global one:
int ParamIdx = 1;
tolua_Error err;
if (tolua_isusertype(S, 1, "cPluginManager", 0, &err))
@@ -1161,6 +1235,11 @@ static int tolua_cPluginManager_AddHook(lua_State * tolua_S)
}
ParamIdx += 1;
}
+ else if (tolua_isusertable(S, 1, "cPluginManager", 0, &err))
+ {
+ // Style 1, use the global PlgMgr, but increment ParamIdx
+ ParamIdx++;
+ }
if (lua_isnumber(S, ParamIdx) && lua_isfunction(S, ParamIdx + 1))
{
@@ -1177,7 +1256,7 @@ static int tolua_cPluginManager_AddHook(lua_State * tolua_S)
AString ParamDesc;
Printf(ParamDesc, "%s, %s, %s", S.GetTypeText(1).c_str(), S.GetTypeText(2).c_str(), S.GetTypeText(3).c_str());
- LOGWARNING("cPluginManager.AddHook(): bad parameters. Expected HOOK_TYPE and CallbackFunction, got %s. Hook not added.", ParamDesc.c_str());
+ LOGWARNING("cPluginManager:AddHook(): bad parameters. Expected HOOK_TYPE and CallbackFunction, got %s. Hook not added.", ParamDesc.c_str());
S.LogStackTrace();
return 0;
}
@@ -1463,6 +1542,85 @@ static int tolua_cPluginManager_BindConsoleCommand(lua_State * L)
+static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S)
+{
+ /*
+ Function signature:
+ cPluginManager:CallPlugin("PluginName", "FunctionName", args...)
+ */
+
+ // Check the parameters:
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserTable(1, "cPluginManager") ||
+ !L.CheckParamString(2, 3))
+ {
+ return 0;
+ }
+
+ // Retrieve the plugin name and function name
+ AString PluginName, FunctionName;
+ L.ToString(2, PluginName);
+ L.ToString(3, FunctionName);
+ if (PluginName.empty() || FunctionName.empty())
+ {
+ LOGWARNING("cPluginManager:CallPlugin(): Invalid plugin name or function name");
+ L.LogStackTrace();
+ return 0;
+ }
+
+ // If requesting calling the current plugin, refuse:
+ cPluginLua * ThisPlugin = GetLuaPlugin(L);
+ if (ThisPlugin == NULL)
+ {
+ return 0;
+ }
+ if (ThisPlugin->GetName() == PluginName)
+ {
+ LOGWARNING("cPluginManager::CallPlugin(): Calling self is not implemented (why would it?)");
+ L.LogStackTrace();
+ return 0;
+ }
+
+ // Call the destination plugin using a plugin callback:
+ class cCallback :
+ public cPluginManager::cPluginCallback
+ {
+ public:
+ int m_NumReturns;
+
+ cCallback(const AString & a_FunctionName, cLuaState & a_SrcLuaState) :
+ m_FunctionName(a_FunctionName),
+ m_SrcLuaState(a_SrcLuaState),
+ m_NumReturns(0)
+ {
+ }
+ protected:
+ const AString & m_FunctionName;
+ cLuaState & m_SrcLuaState;
+
+ virtual bool Item(cPlugin * a_Plugin) override
+ {
+ m_NumReturns = ((cPluginLua *)a_Plugin)->CallFunctionFromForeignState(
+ m_FunctionName, m_SrcLuaState, 4, lua_gettop(m_SrcLuaState)
+ );
+ return true;
+ }
+ } Callback(FunctionName, L);
+ if (!cPluginManager::Get()->DoWithPlugin(PluginName, Callback))
+ {
+ // TODO 2014_01_20 _X: This might be too much logging, plugins cannot know if other plugins are loaded (async)
+ LOGWARNING("cPluginManager::CallPlugin: No such plugin name (\"%s\")", PluginName.c_str());
+ L.LogStackTrace();
+ return 0;
+ }
+ return Callback.m_NumReturns;
+}
+
+
+
+
+
static int tolua_cPlayer_GetGroups(lua_State* tolua_S)
{
cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0);
@@ -1657,112 +1815,28 @@ static int tolua_cPluginLua_AddTab(lua_State* tolua_S)
-// Perhaps use this as well for copying tables https://github.com/keplerproject/rings/pull/1
-static int copy_lua_values(lua_State * a_Source, lua_State * a_Destination, int i, int top)
-{
- for(; i <= top; ++i )
- {
- int t = lua_type(a_Source, i);
- switch (t) {
- case LUA_TSTRING: /* strings */
- {
- const char * s = lua_tostring(a_Source, i);
- LOGD("%i push string: %s", i, s);
- tolua_pushstring(a_Destination, s);
- }
- break;
- case LUA_TBOOLEAN: /* booleans */
- {
- int b = tolua_toboolean(a_Source, i, false);
- LOGD("%i push bool: %i", i, b);
- tolua_pushboolean(a_Destination, b );
- }
- break;
- case LUA_TNUMBER: /* numbers */
- {
- lua_Number d = tolua_tonumber(a_Source, i, 0);
- LOGD("%i push number: %0.2f", i, d);
- tolua_pushnumber(a_Destination, d );
- }
- break;
- case LUA_TUSERDATA:
- {
- const char * type = 0;
- if (lua_getmetatable(a_Source,i))
- {
- lua_rawget(a_Source, LUA_REGISTRYINDEX);
- type = lua_tostring(a_Source, -1);
- lua_pop(a_Source, 1); // Pop.. something?! I don't knooow~~ T_T
- }
-
- // don't need tolua_tousertype we already have the type
- void * ud = tolua_touserdata(a_Source, i, 0);
- LOGD("%i push usertype: %p of type '%s'", i, ud, type);
- if( type == 0 )
- {
- LOGERROR("Call(): Something went wrong when trying to get usertype name!");
- return 0;
- }
- tolua_pushusertype(a_Destination, ud, type);
- }
- break;
- default: /* other values */
- LOGERROR("Call(): Unsupported value: '%s'. Can only use numbers and strings!", lua_typename(a_Source, t));
- return 0;
- }
- }
- return 1;
-}
-
-
-
-
-static int tolua_cPlugin_Call(lua_State* tolua_S)
+static int tolua_cPlugin_Call(lua_State * tolua_S)
{
- cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0);
- lua_State* targetState = self->GetLuaState();
- int targetTop = lua_gettop(targetState);
-
- int top = lua_gettop(tolua_S);
- LOGD("total in stack: %i", top );
-
- std::string funcName = tolua_tostring(tolua_S, 2, "");
- LOGD("Func name: %s", funcName.c_str() );
-
- lua_getglobal(targetState, funcName.c_str());
- if(!lua_isfunction(targetState,-1))
- {
- LOGWARN("Error could not find function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() );
- lua_pop(targetState,1);
- return 0;
- }
-
- if( copy_lua_values(tolua_S, targetState, 3, top) == 0 ) // Start at 3 because 1 and 2 are the plugin and function name respectively
- {
- // something went wrong, exit
- return 0;
- }
+ cLuaState L(tolua_S);
- int s = lua_pcall(targetState, top - 2, LUA_MULTRET, 0);
- if (cLuaState::ReportErrors(targetState, s))
- {
- LOGWARN("Error while calling function '%s' in plugin '%s'", funcName.c_str(), self->GetName().c_str() );
- return 0;
- }
-
- int nresults = lua_gettop(targetState) - targetTop;
- LOGD("num results: %i", nresults);
- int ttop = lua_gettop(targetState);
- if( copy_lua_values(targetState, tolua_S, targetTop+1, ttop) == 0 ) // Start at targetTop+1 and I have no idea why xD
+ // Log the obsoletion warning:
+ LOGWARNING("cPlugin:Call() is obsolete and unsafe, use cPluginManager:CallPlugin() instead.");
+ L.LogStackTrace();
+
+ // Retrieve the params: plugin and the function name to call
+ cPluginLua * TargetPlugin = (cPluginLua *) tolua_tousertype(tolua_S, 1, 0);
+ AString FunctionName = tolua_tostring(tolua_S, 2, "");
+
+ // Call the function:
+ int NumReturns = TargetPlugin->CallFunctionFromForeignState(FunctionName, L, 3, lua_gettop(L));
+ if (NumReturns < 0)
{
- // something went wrong, exit
+ LOGWARNING("cPlugin::Call() failed to call destination function");
+ L.LogStackTrace();
return 0;
}
-
- lua_pop(targetState, nresults); // I have no idea what I'm doing, but it works
-
- return nresults;
+ return NumReturns;
}
@@ -1877,7 +1951,6 @@ static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S)
const cWebPlugin::TabNameList & TabNames = self->GetTabNames();
lua_newtable(tolua_S);
- int newTable = lua_gettop(tolua_S);
int index = 1;
cWebPlugin::TabNameList::const_iterator iter = TabNames.begin();
while(iter != TabNames.end())
@@ -1898,6 +1971,35 @@ static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S)
+static int tolua_cClientHandle_SendPluginMessage(lua_State * L)
+{
+ cLuaState S(L);
+ if (
+ !S.CheckParamUserType(1, "cClientHandle") ||
+ !S.CheckParamString(2, 3) ||
+ !S.CheckParamEnd(4)
+ )
+ {
+ return 0;
+ }
+ cClientHandle * Client = (cClientHandle *)tolua_tousertype(L, 1, NULL);
+ if (Client == NULL)
+ {
+ LOGWARNING("ClientHandle is nil in cClientHandle:SendPluginMessage()");
+ S.LogStackTrace();
+ return 0;
+ }
+ AString Channel, Message;
+ Channel.assign(lua_tostring(L, 2), lua_strlen(L, 2));
+ Message.assign(lua_tostring(L, 3), lua_strlen(L, 3));
+ Client->SendPluginMessage(Channel, Message);
+ return 0;
+}
+
+
+
+
+
static int Lua_ItemGrid_GetSlotCoords(lua_State * L)
{
tolua_Error tolua_err;
@@ -1947,118 +2049,72 @@ public:
virtual bool OnNextBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, char a_EntryFace) override
{
- if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlock"))
+ bool res = false;
+ if (!m_LuaState.Call(
+ cLuaState::cTableRef(m_TableRef, "OnNextBlock"),
+ a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_EntryFace,
+ cLuaState::Return, res
+ ))
{
// No such function in the table, skip the callback
return false;
}
- m_LuaState.Push(a_BlockX);
- m_LuaState.Push(a_BlockY);
- m_LuaState.Push(a_BlockZ);
- m_LuaState.Push(a_BlockType);
- m_LuaState.Push(a_BlockMeta);
- m_LuaState.Push(a_EntryFace);
- if (!m_LuaState.CallFunction(1))
- {
- return false;
- }
- bool res = false;
- if (lua_isboolean(m_LuaState, -1))
- {
- res = (lua_toboolean(m_LuaState, -1) != 0);
- }
- lua_pop(m_LuaState, 1);
return res;
}
virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) override
{
- if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNextBlockNoData"))
+ bool res = false;
+ if (!m_LuaState.Call(
+ cLuaState::cTableRef(m_TableRef, "OnNextBlockNoData"),
+ a_BlockX, a_BlockY, a_BlockZ, a_EntryFace,
+ cLuaState::Return, res
+ ))
{
// No such function in the table, skip the callback
return false;
}
- m_LuaState.Push(a_BlockX);
- m_LuaState.Push(a_BlockY);
- m_LuaState.Push(a_BlockZ);
- m_LuaState.Push(a_EntryFace);
- if (!m_LuaState.CallFunction(1))
- {
- return false;
- }
- bool res = false;
- if (lua_isboolean(m_LuaState, -1))
- {
- res = (lua_toboolean(m_LuaState, -1) != 0);
- }
- lua_pop(m_LuaState, 1);
return res;
}
virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override
{
- if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnOutOfWorld"))
+ bool res = false;
+ if (!m_LuaState.Call(
+ cLuaState::cTableRef(m_TableRef, "OnOutOfWorld"),
+ a_BlockX, a_BlockY, a_BlockZ,
+ cLuaState::Return, res
+ ))
{
// No such function in the table, skip the callback
return false;
}
- m_LuaState.Push(a_BlockX);
- m_LuaState.Push(a_BlockY);
- m_LuaState.Push(a_BlockZ);
- if (!m_LuaState.CallFunction(1))
- {
- return false;
- }
- bool res = false;
- if (lua_isboolean(m_LuaState, -1))
- {
- res = (lua_toboolean(m_LuaState, -1) != 0);
- }
- lua_pop(m_LuaState, 1);
return res;
}
virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) override
{
- if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnIntoWorld"))
+ bool res = false;
+ if (!m_LuaState.Call(
+ cLuaState::cTableRef(m_TableRef, "OnIntoWorld"),
+ a_BlockX, a_BlockY, a_BlockZ,
+ cLuaState::Return, res
+ ))
{
// No such function in the table, skip the callback
return false;
}
- m_LuaState.Push(a_BlockX);
- m_LuaState.Push(a_BlockY);
- m_LuaState.Push(a_BlockZ);
- if (!m_LuaState.CallFunction(1))
- {
- return false;
- }
- bool res = false;
- if (lua_isboolean(m_LuaState, -1))
- {
- res = (lua_toboolean(m_LuaState, -1) != 0);
- }
- lua_pop(m_LuaState, 1);
return res;
}
virtual void OnNoMoreHits(void) override
{
- if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoMoreHits"))
- {
- // No such function in the table, skip the callback
- return;
- }
- m_LuaState.CallFunction(0);
+ m_LuaState.Call(cLuaState::cTableRef(m_TableRef, "OnNoMoreHits"));
}
virtual void OnNoChunk(void) override
{
- if (!m_LuaState.PushFunctionFromRefTable(m_TableRef, "OnNoChunk"))
- {
- // No such function in the table, skip the callback
- return;
- }
- m_LuaState.CallFunction(0);
+ m_LuaState.Call(cLuaState::cTableRef(m_TableRef, "OnNoChunk"));
}
protected:
@@ -2180,6 +2236,63 @@ static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S)
+
+static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S)
+{
+ // function cBlockArea::LoadFromSchematicFile
+ // Exported manually because function has been moved to SchematicFileSerilizer.cpp
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cBlockArea") ||
+ !L.CheckParamString (2) ||
+ !L.CheckParamEnd (3)
+ )
+ {
+ return 0;
+ }
+ cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, 0);
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::LoadFromSchematicFile'", NULL);
+ return 0;
+ }
+
+ AString Filename = tolua_tostring(tolua_S, 2, 0);
+ bool res = cSchematicFileSerializer::LoadFromSchematicFile(*self,Filename);
+ tolua_pushboolean(tolua_S, res);
+ return 1;
+}
+
+
+
+
+static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S)
+{
+ // function cBlockArea::SaveToSchematicFile
+ // Exported manually because function has been moved to SchematicFileSerilizer.cpp
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cBlockArea") ||
+ !L.CheckParamString (2) ||
+ !L.CheckParamEnd (3)
+ )
+ {
+ return 0;
+ }
+ cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, 0);
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::SaveToSchematicFile'", NULL);
+ return 0;
+ }
+ AString Filename = tolua_tostring(tolua_S, 2, 0);
+ bool res = cSchematicFileSerializer::SaveToSchematicFile(*self,Filename);
+ tolua_pushboolean(tolua_S, res);
+ return 1;
+}
+
+
+
void ManualBindings::Bind(lua_State * tolua_S)
{
tolua_beginmodule(tolua_S, NULL);
@@ -2195,6 +2308,11 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents);
tolua_endmodule(tolua_S);
+ tolua_beginmodule(tolua_S, "cBlockArea");
+ tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile);
+ tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cHopperEntity");
tolua_function(tolua_S, "GetOutputBlockPos", tolua_cHopperEntity_GetOutputBlockPos);
tolua_endmodule(tolua_S);
@@ -2211,16 +2329,17 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cWorld");
- tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
- tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
- tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
- tolua_function(tolua_S, "DoWithDropSpenserAt", tolua_DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>);
- tolua_function(tolua_S, "DoWithDropperAt", tolua_DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>);
- tolua_function(tolua_S, "DoWithEntityByID", tolua_DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>);
- tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
- tolua_function(tolua_S, "DoWithNoteBlockAt", tolua_DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>);
- tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
- tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
+ tolua_function(tolua_S, "DoWithBlockEntityAt", tolua_DoWithXYZ<cWorld, cBlockEntity, &cWorld::DoWithBlockEntityAt>);
+ tolua_function(tolua_S, "DoWithChestAt", tolua_DoWithXYZ<cWorld, cChestEntity, &cWorld::DoWithChestAt>);
+ tolua_function(tolua_S, "DoWithDispenserAt", tolua_DoWithXYZ<cWorld, cDispenserEntity, &cWorld::DoWithDispenserAt>);
+ tolua_function(tolua_S, "DoWithDropSpenserAt", tolua_DoWithXYZ<cWorld, cDropSpenserEntity, &cWorld::DoWithDropSpenserAt>);
+ tolua_function(tolua_S, "DoWithDropperAt", tolua_DoWithXYZ<cWorld, cDropperEntity, &cWorld::DoWithDropperAt>);
+ tolua_function(tolua_S, "DoWithEntityByID", tolua_DoWithID< cWorld, cEntity, &cWorld::DoWithEntityByID>);
+ tolua_function(tolua_S, "DoWithFurnaceAt", tolua_DoWithXYZ<cWorld, cFurnaceEntity, &cWorld::DoWithFurnaceAt>);
+ tolua_function(tolua_S, "DoWithNoteBlockAt", tolua_DoWithXYZ<cWorld, cNoteEntity, &cWorld::DoWithNoteBlockAt>);
+ tolua_function(tolua_S, "DoWithCommandBlockAt", tolua_DoWithXYZ<cWorld, cCommandBlockEntity, &cWorld::DoWithCommandBlockAt>);
+ tolua_function(tolua_S, "DoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::DoWithPlayer>);
+ tolua_function(tolua_S, "FindAndDoWithPlayer", tolua_DoWith< cWorld, cPlayer, &cWorld::FindAndDoWithPlayer>);
tolua_function(tolua_S, "ForEachBlockEntityInChunk", tolua_ForEachInChunk<cWorld, cBlockEntity, &cWorld::ForEachBlockEntityInChunk>);
tolua_function(tolua_S, "ForEachChestInChunk", tolua_ForEachInChunk<cWorld, cChestEntity, &cWorld::ForEachChestInChunk>);
tolua_function(tolua_S, "ForEachEntity", tolua_ForEach< cWorld, cEntity, &cWorld::ForEachEntity>);
@@ -2231,6 +2350,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "GetBlockTypeMeta", tolua_cWorld_GetBlockTypeMeta);
tolua_function(tolua_S, "GetSignLines", tolua_cWorld_GetSignLines);
tolua_function(tolua_S, "QueueTask", tolua_cWorld_QueueTask);
+ tolua_function(tolua_S, "ScheduleTask", tolua_cWorld_ScheduleTask);
tolua_function(tolua_S, "SetSignLines", tolua_cWorld_SetSignLines);
tolua_function(tolua_S, "TryGetHeight", tolua_cWorld_TryGetHeight);
tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines);
@@ -2244,6 +2364,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "AddHook", tolua_cPluginManager_AddHook);
tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand);
tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand);
+ tolua_function(tolua_S, "CallPlugin", tolua_cPluginManager_CallPlugin);
tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand);
tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand);
tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins);
@@ -2286,6 +2407,7 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_beginmodule(tolua_S, "cClientHandle");
tolua_constant(tolua_S, "MAX_VIEW_DISTANCE", cClientHandle::MAX_VIEW_DISTANCE);
tolua_constant(tolua_S, "MIN_VIEW_DISTANCE", cClientHandle::MIN_VIEW_DISTANCE);
+ tolua_function(tolua_S, "SendPluginMessage", tolua_cClientHandle_SendPluginMessage);
tolua_endmodule(tolua_S);
tolua_beginmodule(tolua_S, "cItemGrid");
diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h
index 9a3c2383e..f4a117721 100644
--- a/src/Bindings/Plugin.h
+++ b/src/Bindings/Plugin.h
@@ -68,6 +68,8 @@ public:
virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual bool OnPlayerEating (cPlayer & a_Player) = 0;
+ virtual bool OnPlayerFished (cPlayer & a_Player, const cItems & a_Reward) = 0;
+ virtual bool OnPlayerFishing (cPlayer & a_Player, cItems & a_Reward) = 0;
virtual bool OnPlayerJoined (cPlayer & a_Player) = 0;
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0;
virtual bool OnPlayerMoved (cPlayer & a_Player) = 0;
@@ -82,6 +84,8 @@ public:
virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0;
virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0;
+ virtual bool OnPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) = 0;
+ virtual bool OnPluginsLoaded (void) = 0;
virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) = 0;
virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) = 0;
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index 0e5a66cd6..7b2362887 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -88,14 +88,37 @@ bool cPluginLua::Initialize(void)
std::string PluginPath = FILE_IO_PREFIX + GetLocalFolder() + "/";
- // Load all files for this plugin, and execute them
+ // List all Lua files for this plugin. Info.lua has a special handling - make it the last to load:
AStringVector Files = cFile::GetFolderContents(PluginPath.c_str());
- for (AStringVector::const_iterator itr = Files.begin(); itr != Files.end(); ++itr)
+ AStringVector LuaFiles;
+ bool HasInfoLua = false;
+ for (AStringVector::const_iterator itr = Files.begin(), end = Files.end(); itr != end; ++itr)
{
- if (itr->rfind(".lua") == AString::npos)
+ if (itr->rfind(".lua") != AString::npos)
{
- continue;
+ if (*itr == "Info.lua")
+ {
+ HasInfoLua = true;
+ }
+ else
+ {
+ LuaFiles.push_back(*itr);
+ }
}
+ }
+ std::sort(LuaFiles.begin(), LuaFiles.end());
+
+ // Warn if there are no Lua files in the plugin folder:
+ if (LuaFiles.empty())
+ {
+ LOGWARNING("No lua files found: plugin %s is missing.", GetName().c_str());
+ Close();
+ return false;
+ }
+
+ // Load all files in the list, including the Info.lua as last, if it exists:
+ for (AStringVector::const_iterator itr = LuaFiles.begin(), end = LuaFiles.end(); itr != end; ++itr)
+ {
AString Path = PluginPath + *itr;
if (!m_LuaState.LoadFile(Path))
{
@@ -103,8 +126,17 @@ bool cPluginLua::Initialize(void)
return false;
}
} // for itr - Files[]
+ if (HasInfoLua)
+ {
+ AString Path = PluginPath + "Info.lua";
+ if (!m_LuaState.LoadFile(Path))
+ {
+ Close();
+ return false;
+ }
+ }
- // Call intialize function
+ // Call the Initialize function:
bool res = false;
if (!m_LuaState.Call("Initialize", this, cLuaState::Return, res))
{
@@ -112,7 +144,6 @@ bool cPluginLua::Initialize(void)
Close();
return false;
}
-
if (!res)
{
LOGINFO("Plugin %s: Initialize() call failed, plugin is temporarily disabled.", GetName().c_str());
@@ -423,7 +454,7 @@ bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool &
{
case esOther: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
case esPrimedTNT: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cTNTEntity *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
- case esCreeper: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cCreeper *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
+ case esMonster: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (cMonster *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
case esBed: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
case esEnderCrystal: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, (Vector3i *)a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
case esGhastFireball: m_LuaState.Call((int)(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, a_SourceData, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break;
@@ -630,6 +661,46 @@ bool cPluginLua::OnPlayerEating(cPlayer & a_Player)
+bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHED];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Player, a_Reward, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHING];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Player, a_Reward, cLuaState::Return, res);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginLua::OnPlayerJoined(cPlayer & a_Player)
{
cCSLock Lock(m_CriticalSection);
@@ -910,6 +981,44 @@ bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_Block
+bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGIN_MESSAGE];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ m_LuaState.Call((int)(**itr), &a_Client, a_Channel, a_Message);
+ if (res)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginLua::OnPluginsLoaded(void)
+{
+ cCSLock Lock(m_CriticalSection);
+ bool res = false;
+ cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED];
+ for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
+ {
+ bool ret = false;
+ m_LuaState.Call((int)(**itr), cLuaState::Return, ret);
+ res = res || ret;
+ }
+ return res;
+}
+
+
+
+
+
bool cPluginLua::OnPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
{
cCSLock Lock(m_CriticalSection);
@@ -1324,6 +1433,8 @@ const char * cPluginLua::GetHookFnName(int a_HookType)
case cPluginManager::HOOK_PLAYER_USED_ITEM: return "OnPlayerUsedItem";
case cPluginManager::HOOK_PLAYER_USING_BLOCK: return "OnPlayerUsingBlock";
case cPluginManager::HOOK_PLAYER_USING_ITEM: return "OnPlayerUsingItem";
+ case cPluginManager::HOOK_PLUGIN_MESSAGE: return "OnPluginMessage";
+ case cPluginManager::HOOK_PLUGINS_LOADED: return "OnPluginsLoaded";
case cPluginManager::HOOK_POST_CRAFTING: return "OnPostCrafting";
case cPluginManager::HOOK_PRE_CRAFTING: return "OnPreCrafting";
case cPluginManager::HOOK_SPAWNED_ENTITY: return "OnSpawnedEntity";
@@ -1337,8 +1448,17 @@ const char * cPluginLua::GetHookFnName(int a_HookType)
case cPluginManager::HOOK_WEATHER_CHANGED: return "OnWeatherChanged";
case cPluginManager::HOOK_WEATHER_CHANGING: return "OnWeatherChanging";
case cPluginManager::HOOK_WORLD_TICK: return "OnWorldTick";
- default: return NULL;
+
+ case cPluginManager::HOOK_NUM_HOOKS:
+ {
+ // Satisfy a warning that all enum values should be used in a switch
+ // but don't want a default branch, so that we catch new hooks missing from this list.
+ break;
+ }
} // switch (a_Hook)
+ LOGWARNING("Requested name of an unknown hook type function: %d (max is %d)", a_HookType, cPluginManager::HOOK_MAX);
+ ASSERT(!"Unknown hook requested!");
+ return NULL;
}
@@ -1367,6 +1487,40 @@ bool cPluginLua::AddHookRef(int a_HookType, int a_FnRefIdx)
+int cPluginLua::CallFunctionFromForeignState(
+ const AString & a_FunctionName,
+ cLuaState & a_ForeignState,
+ int a_ParamStart,
+ int a_ParamEnd
+)
+{
+ cCSLock Lock(m_CriticalSection);
+
+ // Call the function:
+ int NumReturns = m_LuaState.CallFunctionWithForeignParams(a_FunctionName, a_ForeignState, a_ParamStart, a_ParamEnd);
+ if (NumReturns < 0)
+ {
+ // The call has failed, an error has already been output to the log, so just silently bail out with the same error
+ return NumReturns;
+ }
+
+ // Copy all the return values:
+ int Top = lua_gettop(m_LuaState);
+ int res = a_ForeignState.CopyStackFrom(m_LuaState, Top - NumReturns + 1, Top);
+
+ // Remove the return values off this stack:
+ if (NumReturns > 0)
+ {
+ lua_pop(m_LuaState, NumReturns);
+ }
+
+ return res;
+}
+
+
+
+
+
AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request )
{
cCSLock Lock(m_CriticalSection);
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index e1e274c72..c13f31424 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -65,6 +65,8 @@ public:
virtual bool OnPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual bool OnPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual bool OnPlayerEating (cPlayer & a_Player) override;
+ virtual bool OnPlayerFished (cPlayer & a_Player, const cItems & a_Reward) override;
+ virtual bool OnPlayerFishing (cPlayer & a_Player, cItems & a_Reward) override;
virtual bool OnPlayerJoined (cPlayer & a_Player) override;
virtual bool OnPlayerMoved (cPlayer & a_Player) override;
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override;
@@ -79,6 +81,8 @@ public:
virtual bool OnPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
virtual bool OnPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
virtual bool OnPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
+ virtual bool OnPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) override;
+ virtual bool OnPluginsLoaded (void) override;
virtual bool OnPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
virtual bool OnPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe) override;
virtual bool OnSpawnedEntity (cWorld & a_World, cEntity & a_Entity) override;
@@ -101,7 +105,7 @@ public:
virtual void ClearConsoleCommands(void) override;
- /// Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121)
+ /** Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) */
bool CanAddOldStyleHook(int a_HookType);
// cWebPlugin override
@@ -111,26 +115,26 @@ public:
virtual AString HandleWebRequest(const HTTPRequest * a_Request ) override;
bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // >> EXPORTED IN MANUALBINDINGS <<
- /// Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap.
+ /** Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. */
void BindCommand(const AString & a_Command, int a_FnRef);
- /// Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap.
+ /** Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap. */
void BindConsoleCommand(const AString & a_Command, int a_FnRef);
cLuaState & GetLuaState(void) { return m_LuaState; }
cCriticalSection & GetCriticalSection(void) { return m_CriticalSection; }
- /// Removes a previously referenced object (luaL_unref())
+ /** Removes a previously referenced object (luaL_unref()) */
void Unreference(int a_LuaRef);
- /// Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true
+ /** Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true */
bool CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse);
- /// Calls the plugin-specified "cLuaWindow slot changed" callback.
+ /** Calls the plugin-specified "cLuaWindow slot changed" callback. */
void CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum);
- /// Returns the name of Lua function that should handle the specified hook type in the older (#121) API
+ /** Returns the name of Lua function that should handle the specified hook type in the older (#121) API */
static const char * GetHookFnName(int a_HookType);
/** Adds a Lua function to be called for the specified hook.
@@ -139,37 +143,47 @@ public:
*/
bool AddHookRef(int a_HookType, int a_FnRefIdx);
+ /** Calls a function in this plugin's LuaState with parameters copied over from a_ForeignState.
+ The values that the function returns are placed onto a_ForeignState.
+ Returns the number of values returned, if successful, or negative number on failure. */
+ int CallFunctionFromForeignState(
+ const AString & a_FunctionName,
+ cLuaState & a_ForeignState,
+ int a_ParamStart,
+ int a_ParamEnd
+ );
+
// The following templates allow calls to arbitrary Lua functions residing in the plugin:
- /// Call a Lua function with 0 args
+ /** Call a Lua function with 0 args */
template <typename FnT> bool Call(FnT a_Fn)
{
cCSLock Lock(m_CriticalSection);
return m_LuaState.Call(a_Fn);
}
- /// Call a Lua function with 1 arg
+ /** Call a Lua function with 1 arg */
template <typename FnT, typename ArgT0> bool Call(FnT a_Fn, ArgT0 a_Arg0)
{
cCSLock Lock(m_CriticalSection);
return m_LuaState.Call(a_Fn, a_Arg0);
}
- /// Call a Lua function with 2 args
+ /** Call a Lua function with 2 args */
template <typename FnT, typename ArgT0, typename ArgT1> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1)
{
cCSLock Lock(m_CriticalSection);
return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1);
}
- /// Call a Lua function with 3 args
+ /** Call a Lua function with 3 args */
template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2)
{
cCSLock Lock(m_CriticalSection);
return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2);
}
- /// Call a Lua function with 4 args
+ /** Call a Lua function with 4 args */
template <typename FnT, typename ArgT0, typename ArgT1, typename ArgT2, typename ArgT3> bool Call(FnT a_Fn, ArgT0 a_Arg0, ArgT1 a_Arg1, ArgT2 a_Arg2, ArgT3 a_Arg3)
{
cCSLock Lock(m_CriticalSection);
@@ -177,13 +191,13 @@ public:
}
protected:
- /// Maps command name into Lua function reference
+ /** Maps command name into Lua function reference */
typedef std::map<AString, int> CommandMap;
- /// Provides an array of Lua function references
+ /** Provides an array of Lua function references */
typedef std::vector<cLuaState::cRef *> cLuaRefs;
- /// Maps hook types into arrays of Lua function references to call for each hook type
+ /** Maps hook types into arrays of Lua function references to call for each hook type */
typedef std::map<int, cLuaRefs> cHookMap;
cCriticalSection m_CriticalSection;
@@ -194,7 +208,7 @@ protected:
cHookMap m_HookMap;
- /// Releases all Lua references and closes the LuaState
+ /** Releases all Lua references and closes the LuaState */
void Close(void);
} ; // tolua_export
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 832dc4249..e582fde86 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -118,7 +118,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
int KeyNum = a_SettingsIni.FindKey("Plugins");
// If it does, how many plugins are there?
- unsigned int NumPlugins = ((KeyNum != -1) ? (a_SettingsIni.GetNumValues(KeyNum)) : 0);
+ int NumPlugins = ((KeyNum != -1) ? (a_SettingsIni.GetNumValues(KeyNum)) : 0);
if (KeyNum == -1)
{
@@ -126,7 +126,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
}
else if (NumPlugins > 0)
{
- for(unsigned int i = 0; i < NumPlugins; i++)
+ for (int i = 0; i < NumPlugins; i++)
{
AString ValueName = a_SettingsIni.GetValueName(KeyNum, i);
if (ValueName.compare("Plugin") == 0)
@@ -136,7 +136,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
{
if (m_Plugins.find(PluginFile) != m_Plugins.end())
{
- LoadPlugin( PluginFile );
+ LoadPlugin(PluginFile);
}
}
}
@@ -155,6 +155,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
{
LOG("-- Loaded 1 Plugin --");
}
+ CallHookPluginsLoaded();
}
@@ -168,9 +169,9 @@ void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni)
a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify");
a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx");
a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump");
- a_SettingsIni.SetValue("Plugins", "Plugin", "Core");
- a_SettingsIni.SetValue("Plugins", "Plugin", "TransAPI");
- a_SettingsIni.SetValue("Plugins", "Plugin", "ChatLog");
+ a_SettingsIni.AddValue("Plugins", "Plugin", "Core");
+ a_SettingsIni.AddValue("Plugins", "Plugin", "TransAPI");
+ a_SettingsIni.AddValue("Plugins", "Plugin", "ChatLog");
}
@@ -693,6 +694,48 @@ bool cPluginManager::CallHookPlayerEating(cPlayer & a_Player)
+bool cPluginManager::CallHookPlayerFished(cPlayer & a_Player, const cItems a_Reward)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_FISHED);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerFished(a_Player, a_Reward))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPlayerFishing(cPlayer & a_Player, cItems a_Reward)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_FISHING);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPlayerFishing(a_Player, a_Reward))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginManager::CallHookPlayerJoined(cPlayer & a_Player)
{
HookMap::iterator Plugins = m_Hooks.find(HOOK_PLAYER_JOINED);
@@ -987,6 +1030,46 @@ bool cPluginManager::CallHookPlayerUsingItem(cPlayer & a_Player, int a_BlockX, i
+bool cPluginManager::CallHookPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLUGIN_MESSAGE);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnPluginMessage(a_Client, a_Channel, a_Message))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
+bool cPluginManager::CallHookPluginsLoaded(void)
+{
+ HookMap::iterator Plugins = m_Hooks.find(HOOK_PLUGINS_LOADED);
+ if (Plugins == m_Hooks.end())
+ {
+ return false;
+ }
+ bool res = false;
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ res = !(*itr)->OnPluginsLoaded() || res;
+ }
+ return res;
+}
+
+
+
+
+
bool cPluginManager::CallHookPostCrafting(const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe)
{
HookMap::iterator Plugins = m_Hooks.find(HOOK_POST_CRAFTING);
@@ -1653,6 +1736,21 @@ bool cPluginManager::IsValidHookType(int a_HookType)
+bool cPluginManager::DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback)
+{
+ // TODO: Implement locking for plugins
+ PluginMap::iterator itr = m_Plugins.find(a_PluginName);
+ if ((itr == m_Plugins.end()) || (itr->second == NULL))
+ {
+ return false;
+ }
+ return a_Callback.Item(itr->second);
+}
+
+
+
+
+
bool cPluginManager::AddPlugin(cPlugin * a_Plugin)
{
m_Plugins[a_Plugin->GetDirectory()] = a_Plugin;
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index 04d6470c7..d8b838d80 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -80,6 +80,8 @@ public: // tolua_export
HOOK_PLAYER_BREAKING_BLOCK,
HOOK_PLAYER_BROKEN_BLOCK,
HOOK_PLAYER_EATING,
+ HOOK_PLAYER_FISHED,
+ HOOK_PLAYER_FISHING,
HOOK_PLAYER_JOINED,
HOOK_PLAYER_LEFT_CLICK,
HOOK_PLAYER_MOVING,
@@ -94,6 +96,8 @@ public: // tolua_export
HOOK_PLAYER_USED_ITEM,
HOOK_PLAYER_USING_BLOCK,
HOOK_PLAYER_USING_ITEM,
+ HOOK_PLUGIN_MESSAGE,
+ HOOK_PLUGINS_LOADED,
HOOK_POST_CRAFTING,
HOOK_PRE_CRAFTING,
HOOK_SPAWNED_ENTITY,
@@ -118,7 +122,7 @@ public: // tolua_export
} ;
// tolua_end
- /// Used as a callback for enumerating bound commands
+ /** Used as a callback for enumerating bound commands */
class cCommandEnumCallback
{
public:
@@ -128,7 +132,11 @@ public: // tolua_export
virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) = 0;
} ;
- /// Returns the instance of the Plugin Manager (there is only ever one)
+ /** The interface used for enumerating and extern-calling plugins */
+ typedef cItemCallback<cPlugin> cPluginCallback;
+
+
+ /** Returns the instance of the Plugin Manager (there is only ever one) */
static cPluginManager * Get(void); // tolua_export
typedef std::map< AString, cPlugin * > PluginMap;
@@ -139,7 +147,7 @@ public: // tolua_export
void FindPlugins(); // tolua_export
void ReloadPlugins(); // tolua_export
- /// Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add
+ /** Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add */
void AddHook(cPlugin * a_Plugin, int a_HookType);
unsigned int GetNumPlugins() const; // tolua_export
@@ -167,6 +175,8 @@ public: // tolua_export
bool CallHookPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
bool CallHookPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
bool CallHookPlayerEating (cPlayer & a_Player);
+ bool CallHookPlayerFished (cPlayer & a_Player, const cItems a_Reward);
+ bool CallHookPlayerFishing (cPlayer & a_Player, cItems a_Reward);
bool CallHookPlayerJoined (cPlayer & a_Player);
bool CallHookPlayerMoving (cPlayer & a_Player);
bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
@@ -181,6 +191,8 @@ public: // tolua_export
bool CallHookPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
bool CallHookPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
bool CallHookPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
+ bool CallHookPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message);
+ bool CallHookPluginsLoaded (void);
bool CallHookPostCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
bool CallHookPreCrafting (const cPlayer * a_Player, const cCraftingGrid * a_Grid, cCraftingRecipe * a_Recipe);
bool CallHookSpawnedEntity (cWorld & a_World, cEntity & a_Entity);
@@ -198,46 +210,46 @@ public: // tolua_export
bool DisablePlugin(const AString & a_PluginName); // tolua_export
bool LoadPlugin (const AString & a_PluginName); // tolua_export
- /// Removes all hooks the specified plugin has registered
+ /** Removes all hooks the specified plugin has registered */
void RemoveHooks(cPlugin * a_Plugin);
- /// Removes the plugin from the internal structures and deletes its object.
+ /** Removes the plugin from the internal structures and deletes its object. */
void RemovePlugin(cPlugin * a_Plugin);
- /// Removes all command bindings that the specified plugin has made
+ /** Removes all command bindings that the specified plugin has made */
void RemovePluginCommands(cPlugin * a_Plugin);
- /// Binds a command to the specified plugin. Returns true if successful, false if command already bound.
+ /** Binds a command to the specified plugin. Returns true if successful, false if command already bound. */
bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param
- /// Calls a_Callback for each bound command, returns true if all commands were enumerated
+ /** Calls a_Callback for each bound command, returns true if all commands were enumerated */
bool ForEachCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Returns true if the command is in the command map
+ /** Returns true if the command is in the command map */
bool IsCommandBound(const AString & a_Command); // tolua_export
- /// Returns the permission needed for the specified command; empty string if command not found
+ /** Returns the permission needed for the specified command; empty string if command not found */
AString GetCommandPermission(const AString & a_Command); // tolua_export
- /// Executes the command, as if it was requested by a_Player. Checks permissions first. Returns true if executed.
+ /** Executes the command, as if it was requested by a_Player. Checks permissions first. Returns true if executed. */
bool ExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export
- /// Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found)
+ /** Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found) */
bool ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export
- /// Removes all console command bindings that the specified plugin has made
+ /** Removes all console command bindings that the specified plugin has made */
void RemovePluginConsoleCommands(cPlugin * a_Plugin);
- /// Binds a console command to the specified plugin. Returns true if successful, false if command already bound.
+ /** Binds a console command to the specified plugin. Returns true if successful, false if command already bound. */
bool BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param
- /// Calls a_Callback for each bound console command, returns true if all commands were enumerated
+ /** Calls a_Callback for each bound console command, returns true if all commands were enumerated */
bool ForEachConsoleCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Returns true if the console command is in the command map
+ /** Returns true if the console command is in the command map */
bool IsConsoleCommandBound(const AString & a_Command); // tolua_export
- /// Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback
+ /** Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback */
bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
/** Appends all commands beginning with a_Text (case-insensitive) into a_Results.
@@ -245,9 +257,13 @@ public: // tolua_export
*/
void TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player);
- /// Returns true if the specified hook type is within the allowed range
+ /** Returns true if the specified hook type is within the allowed range */
static bool IsValidHookType(int a_HookType);
+ /** Calls the specified callback with the plugin object of the specified plugin.
+ Returns false if plugin not found, and the value that the callback has returned otherwise. */
+ bool DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback);
+
private:
friend class cRoot;
@@ -271,24 +287,24 @@ private:
bool m_bReloadPlugins;
cPluginManager();
- ~cPluginManager();
+ virtual ~cPluginManager();
- /// Reloads all plugins, defaulting to settings.ini for settings location
+ /** Reloads all plugins, defaulting to settings.ini for settings location */
void ReloadPluginsNow(void);
- /// Reloads all plugins with a cIniFile object expected to be initialised to settings.ini
+ /** Reloads all plugins with a cIniFile object expected to be initialised to settings.ini */
void ReloadPluginsNow(cIniFile & a_SettingsIni);
- /// Unloads all plugins
+ /** Unloads all plugins */
void UnloadPluginsNow(void);
- /// Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini
+ /** Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini */
void InsertDefaultPlugins(cIniFile & a_SettingsIni);
- /// Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again.
+ /** Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again. */
bool AddPlugin(cPlugin * a_Plugin);
- /// Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns true if the command is handled.
+ /** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns true if the command is handled. */
bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions, bool & a_WasCommandForbidden);
bool HandleCommand(cPlayer * a_Player, const AString & a_Command, bool a_ShouldCheckPermissions)
{
diff --git a/src/Bindings/tolua++.h b/src/Bindings/tolua++.h
index 4dfd06318..73e65b746 100644
--- a/src/Bindings/tolua++.h
+++ b/src/Bindings/tolua++.h
@@ -2,12 +2,18 @@
// tolua++.h
// Redirection file, needed because ToLua++ generates the Bindings.cpp file with >> #include "tolua++.h" <<
+// Only used from Bindings.cpp
#include "tolua++/include/tolua++.h"
+#ifdef _MSC_VER
+ // Disable specific warnings for the generated Bindings.cpp file:
+ #pragma warning(disable: 4800) // 'int' : forcing value to bool 'true' or 'false' (performance warning)
+#endif // _MSC_VER
+
diff --git a/src/Bindings/tolua_base.h b/src/Bindings/tolua_base.h
deleted file mode 100644
index 6a76f97b1..000000000
--- a/src/Bindings/tolua_base.h
+++ /dev/null
@@ -1,128 +0,0 @@
-#ifndef TOLUA_BASE_H
-#define TOLUA_BASE_H
-
-#pragma warning(disable:4800) // This file is ONLY included by Bindings.cpp and it throws lots of C4800 warnings
-
-#include "tolua++/include/tolua++.h"
-
-
-
-
-
-class ToluaBase {
-
- int lua_instance;
-
-protected:
-
- lua_State* lua_state;
-
- void lua_stacktrace(lua_State* L) const
- {
- lua_Debug entry;
- int depth = 0;
-
- while (lua_getstack(L, depth, &entry))
- {
- lua_getinfo(L, "Sln", &entry);
-
- LOGERROR("%s(%d): %s", entry.short_src, entry.currentline, entry.name ? entry.name : "?");
- depth++;
- }
- }
-
-
- bool report_errors(int status) const
- {
- if ( status!=0 )
- {
- const char* s = lua_tostring(lua_state, -1);
- LOGERROR("-- %s", s );
- //lua_pop(lua_state, 1);
- LOGERROR("Stack:");
- lua_stacktrace( lua_state );
- return true;
- }
- return false;
- }
-
- bool push_method(const char* name, lua_CFunction f) const {
-
- if (!lua_state) return false;
-
- lua_getref(lua_state, lua_instance);
- lua_pushstring(lua_state, name);
- //LOGINFO("1. push_method() Stack size: %i", lua_gettop( lua_state ) );
- lua_gettable(lua_state, -2);
- //LOGINFO("2. push_method() Stack size: %i", lua_gettop( lua_state ) );
-
- if (lua_isnil(lua_state, -1)) {
-
- // pop the table
- lua_pop(lua_state, 2);
- return false;
-
- } else {
-
- if (f) {
- if (lua_iscfunction(lua_state, -1)) {
- lua_pop(lua_state, 2);
- return false;
- };
- /* // not for now
- lua_pushcfunction(lua_state, f);
- if (lua_rawequal(lua_state, -1, -2)) {
-
- // avoid recursion, pop both functions and the table
- lua_pop(lua_state, 3);
- return false;
- };
-
- // pop f
- lua_pop(lua_state, 1);
- */
- };
-
- // swap table with function
- lua_insert(lua_state, -2);
- };
-
- return true;
- };
-
- void dbcall(lua_State* L, int nargs, int nresults) const {
-
- // using lua_call for now
- int s = lua_pcall(L, nargs, nresults, 0);
- report_errors( s );
- };
-public:
-
- int GetInstance() { return lua_instance; }
- lua_State* GetLuaState() { return lua_state; }
-
- void tolua__set_instance(lua_State* L, lua_Object lo) {
-
- lua_state = L;
-
- lua_pushvalue(L, lo);
- lua_instance = lua_ref(lua_state, 1);
- };
-
- ToluaBase() {
-
- lua_state = NULL;
- };
-
- ~ToluaBase() {
-
- if (lua_state) {
-
- lua_unref(lua_state, lua_instance);
- };
- };
-};
-
-#endif
-
-
diff --git a/src/Bindings/virtual_method_hooks.lua b/src/Bindings/virtual_method_hooks.lua
index 15ff1d7f8..c610d424f 100644
--- a/src/Bindings/virtual_method_hooks.lua
+++ b/src/Bindings/virtual_method_hooks.lua
@@ -504,3 +504,11 @@ end
+
+function post_output_hook()
+ print("Bindings have been generated.")
+end
+
+
+
+
diff --git a/src/BiomeDef.cpp b/src/BiomeDef.cpp
new file mode 100644
index 000000000..89a1cdefb
--- /dev/null
+++ b/src/BiomeDef.cpp
@@ -0,0 +1,131 @@
+
+// BiomeDef.cpp
+
+// Implements biome helper functions
+
+#include "Globals.h"
+#include "BiomeDef.h"
+
+
+EMCSBiome StringToBiome(const AString & a_BiomeString)
+{
+ // If it is a number, return it:
+ int res = atoi(a_BiomeString.c_str());
+ if ((res != 0) || (a_BiomeString.compare("0") == 0))
+ {
+ // It was a valid number
+ return (EMCSBiome)res;
+ }
+
+ // Convert using the built-in map:
+ static struct {
+ EMCSBiome m_Biome;
+ const char * m_String;
+ } BiomeMap[] =
+ {
+ {biOcean, "Ocean"} ,
+ {biPlains, "Plains"},
+ {biDesert, "Desert"},
+ {biExtremeHills, "ExtremeHills"},
+ {biForest, "Forest"},
+ {biTaiga, "Taiga"},
+ {biSwampland, "Swampland"},
+ {biRiver, "River"},
+ {biNether, "Hell"},
+ {biNether, "Nether"},
+ {biEnd, "Sky"},
+ {biEnd, "End"},
+ {biFrozenOcean, "FrozenOcean"},
+ {biFrozenRiver, "FrozenRiver"},
+ {biIcePlains, "IcePlains"},
+ {biIcePlains, "Tundra"},
+ {biIceMountains, "IceMountains"},
+ {biMushroomIsland, "MushroomIsland"},
+ {biMushroomShore, "MushroomShore"},
+ {biBeach, "Beach"},
+ {biDesertHills, "DesertHills"},
+ {biForestHills, "ForestHills"},
+ {biTaigaHills, "TaigaHills"},
+ {biExtremeHillsEdge, "ExtremeHillsEdge"},
+ {biJungle, "Jungle"},
+ {biJungleHills, "JungleHills"},
+
+ // Release 1.7 biomes:
+ {biJungleEdge, "JungleEdge"},
+ {biDeepOcean, "DeepOcean"},
+ {biStoneBeach, "StoneBeach"},
+ {biColdBeach, "ColdBeach"},
+ {biBirchForest, "BirchForest"},
+ {biBirchForestHills, "BirchForestHills"},
+ {biRoofedForest, "RoofedForest"},
+ {biColdTaiga, "ColdTaiga"},
+ {biColdTaigaHills, "ColdTaigaHills"},
+ {biMegaTaiga, "MegaTaiga"},
+ {biMegaTaigaHills, "MegaTaigaHills"},
+ {biExtremeHillsPlus, "ExtremeHillsPlus"},
+ {biSavanna, "Savanna"},
+ {biSavannaPlateau, "SavannaPlateau"},
+ {biMesa, "Mesa"},
+ {biMesaPlateauF, "MesaPlateauF"},
+ {biMesaPlateau, "MesaPlateau"},
+
+ // Release 1.7 variants:
+ {biSunflowerPlains, "SunflowerPlains"},
+ {biDesertM, "DesertM"},
+ {biExtremeHillsM, "ExtremeHillsM"},
+ {biFlowerForest, "FlowerForest"},
+ {biTaigaM, "TaigaM"},
+ {biSwamplandM, "SwamplandM"},
+ {biIcePlainsSpikes, "IcePlainsSpikes"},
+ {biJungleM, "JungleM"},
+ {biJungleEdgeM, "JungleEdgeM"},
+ {biBirchForestM, "BirchForestM"},
+ {biBirchForestHillsM, "BirchForestHillsM"},
+ {biRoofedForestM, "RoofedForestM"},
+ {biColdTaigaM, "ColdTaigaM"},
+ {biMegaSpruceTaiga, "MegaSpruceTaiga"},
+ {biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"},
+ {biExtremeHillsPlusM, "ExtremeHillsPlusM"},
+ {biSavannaM, "SavannaM"},
+ {biSavannaPlateauM, "SavannaPlateauM"},
+ {biMesaBryce, "MesaBryce"},
+ {biMesaPlateauFM, "MesaPlateauFM"},
+ {biMesaPlateauM, "MesaPlateauM"},
+ } ;
+
+ for (size_t i = 0; i < ARRAYCOUNT(BiomeMap); i++)
+ {
+ if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0)
+ {
+ return BiomeMap[i].m_Biome;
+ }
+ } // for i - BiomeMap[]
+ return (EMCSBiome)-1;
+}
+
+
+
+
+
+bool IsBiomeNoDownfall(EMCSBiome a_Biome)
+{
+ switch (a_Biome)
+ {
+ case biDesert:
+ case biDesertHills:
+ case biDesertM:
+ case biSavanna:
+ case biSavannaM:
+ case biSavannaPlateau:
+ case biSavannaPlateauM:
+ case biNether:
+ case biEnd:
+ {
+ return true;
+ }
+ default:
+ {
+ return false;
+ }
+ }
+}
diff --git a/src/BiomeDef.h b/src/BiomeDef.h
new file mode 100644
index 000000000..df1e387f0
--- /dev/null
+++ b/src/BiomeDef.h
@@ -0,0 +1,107 @@
+
+// BiomeDef.h
+
+// Defines relevant information and methods related to biomes
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// tolua_begin
+/** Biome IDs
+The first batch corresponds to the clientside biomes, used by MineCraft.
+BiomeIDs over 255 are used by MCServer internally and are translated to MC biomes before sending them to client
+*/
+enum EMCSBiome
+{
+ biOcean = 0,
+ biPlains = 1,
+ biDesert = 2,
+ biExtremeHills = 3,
+ biForest = 4,
+ biTaiga = 5,
+ biSwampland = 6,
+ biRiver = 7,
+ biHell = 8, // same as Nether
+ biNether = 8,
+ biSky = 9, // same as biEnd
+ biEnd = 9,
+ biFrozenOcean = 10,
+ biFrozenRiver = 11,
+ biIcePlains = 12,
+ biTundra = 12, // same as Ice Plains
+ biIceMountains = 13,
+ biMushroomIsland = 14,
+ biMushroomShore = 15,
+ biBeach = 16,
+ biDesertHills = 17,
+ biForestHills = 18,
+ biTaigaHills = 19,
+ biExtremeHillsEdge = 20,
+ biJungle = 21,
+ biJungleHills = 22,
+
+ // Release 1.7 biomes:
+ biJungleEdge = 23,
+ biDeepOcean = 24,
+ biStoneBeach = 25,
+ biColdBeach = 26,
+ biBirchForest = 27,
+ biBirchForestHills = 28,
+ biRoofedForest = 29,
+ biColdTaiga = 30,
+ biColdTaigaHills = 31,
+ biMegaTaiga = 32,
+ biMegaTaigaHills = 33,
+ biExtremeHillsPlus = 34,
+ biSavanna = 35,
+ biSavannaPlateau = 36,
+ biMesa = 37,
+ biMesaPlateauF = 38,
+ biMesaPlateau = 39,
+
+ // Automatically capture the maximum consecutive biome value into biMaxBiome:
+ biNumBiomes, // True number of biomes, since they are zero-based
+ biMaxBiome = biNumBiomes - 1, // The maximum biome value
+
+ // Add this number to the biomes to get the variant
+ biVariant = 128,
+
+ // Release 1.7 biome variants:
+ biSunflowerPlains = 129,
+ biDesertM = 130,
+ biExtremeHillsM = 131,
+ biFlowerForest = 132,
+ biTaigaM = 133,
+ biSwamplandM = 134,
+ biIcePlainsSpikes = 140,
+ biJungleM = 149,
+ biJungleEdgeM = 151,
+ biBirchForestM = 155,
+ biBirchForestHillsM = 156,
+ biRoofedForestM = 157,
+ biColdTaigaM = 158,
+ biMegaSpruceTaiga = 160,
+ biMegaSpruceTaigaHills = 161,
+ biExtremeHillsPlusM = 162,
+ biSavannaM = 163,
+ biSavannaPlateauM = 164,
+ biMesaBryce = 165,
+ biMesaPlateauFM = 166,
+ biMesaPlateauM = 167,
+} ;
+
+/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns -1 on failure.
+extern EMCSBiome StringToBiome(const AString & a_BiomeString);
+
+/// Returns true if the biome has no downfall - deserts and savannas
+extern bool IsBiomeNoDownfall(EMCSBiome a_Biome);
+
+
+// tolua_end
diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp
index 1148908c6..194e2d68a 100644
--- a/src/BlockArea.cpp
+++ b/src/BlockArea.cpp
@@ -6,9 +6,7 @@
#include "Globals.h"
#include "BlockArea.h"
-#include "World.h"
#include "OSSupport/GZipFile.h"
-#include "WorldStorage/FastNBT.h"
#include "Blocks/BlockHandler.h"
@@ -28,6 +26,8 @@ template<typename Combinator> void InternalMergeBlocks(
Combinator a_Combinator
)
{
+ UNUSED(a_SrcSizeY);
+ UNUSED(a_DstSizeY);
for (int y = 0; y < a_SizeY; y++)
{
int SrcBaseY = (y + a_SrcOffY) * a_SrcSizeX * a_SrcSizeZ;
@@ -264,7 +264,7 @@ void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ)
-bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes)
+bool cBlockArea::Read(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes)
{
// Normalize the coords:
if (a_MinBlockX > a_MaxBlockX)
@@ -325,7 +325,7 @@ bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_
cChunkDef::AbsoluteToRelative(a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ, MaxChunkX, MaxChunkZ);
// Query block data:
- if (!a_World->ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader))
+ if (!a_ForEachChunkProvider->ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader))
{
Clear();
return false;
@@ -338,7 +338,7 @@ bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_
-bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
+bool cBlockArea::Write(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
{
ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have?
a_DataTypes = a_DataTypes & GetDataTypes(); // For release builds, silently cut off the datatypes that I don't have
@@ -355,7 +355,7 @@ bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a
a_MinBlockY = cChunkDef::Height - m_SizeY;
}
- return a_World->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
+ return a_ForEachChunkProvider->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
}
@@ -446,85 +446,12 @@ void cBlockArea::DumpToRawFile(const AString & a_FileName)
-bool cBlockArea::LoadFromSchematicFile(const AString & a_FileName)
-{
- // Un-GZip the contents:
- AString Contents;
- cGZipFile File;
- if (!File.Open(a_FileName, cGZipFile::fmRead))
- {
- LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str());
- return false;
- }
- int NumBytesRead = File.ReadRestOfFile(Contents);
- if (NumBytesRead < 0)
- {
- LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead);
- return false;
- }
- File.Close();
-
- // Parse the NBT:
- cParsedNBT NBT(Contents.data(), Contents.size());
- if (!NBT.IsValid())
- {
- LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str());
- return false;
- }
-
- return LoadFromSchematicNBT(NBT);
-}
-bool cBlockArea::SaveToSchematicFile(const AString & a_FileName)
-{
- cFastNBTWriter Writer("Schematic");
- Writer.AddShort("Width", m_SizeX);
- Writer.AddShort("Height", m_SizeY);
- Writer.AddShort("Length", m_SizeZ);
- Writer.AddString("Materials", "Alpha");
- if (HasBlockTypes())
- {
- Writer.AddByteArray("Blocks", (const char *)m_BlockTypes, GetBlockCount());
- }
- else
- {
- AString Dummy(GetBlockCount(), 0);
- Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
- }
- if (HasBlockMetas())
- {
- Writer.AddByteArray("Data", (const char *)m_BlockMetas, GetBlockCount());
- }
- else
- {
- AString Dummy(GetBlockCount(), 0);
- Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
- }
- // TODO: Save entities and block entities
- Writer.BeginList("Entities", TAG_Compound);
- Writer.EndList();
- Writer.BeginList("TileEntities", TAG_Compound);
- Writer.EndList();
- Writer.Finish();
-
- // Save to file
- cGZipFile File;
- if (!File.Open(a_FileName, cGZipFile::fmWrite))
- {
- LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str());
- return false;
- }
- if (!File.Write(Writer.GetResult()))
- {
- LOG("Cannot write data to file \"%s\".", a_FileName.c_str());
- return false;
- }
- return true;
-}
+
@@ -826,7 +753,7 @@ void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int
int yd = dy - dx / 2;
int zd = dz - dx / 2;
- while (true)
+ for (;;)
{
RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
@@ -858,7 +785,7 @@ void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int
int xd = dx - dy / 2;
int zd = dz - dy / 2;
- while (true)
+ for (;;)
{
RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
@@ -892,7 +819,7 @@ void cBlockArea::RelLine(int a_RelX1, int a_RelY1, int a_RelZ1, int a_RelX2, int
int xd = dx - dz / 2;
int yd = dy - dz / 2;
- while (true)
+ for (;;)
{
RelSetData(a_RelX1, a_RelY1, a_RelZ1, a_DataTypes, a_BlockType, a_BlockMeta, a_BlockLight, a_BlockSkyLight);
@@ -2016,89 +1943,6 @@ void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMa
}
-
-
-
-bool cBlockArea::LoadFromSchematicNBT(cParsedNBT & a_NBT)
-{
- int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
- if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String))
- {
- AString Materials = a_NBT.GetString(TMaterials);
- if (Materials.compare("Alpha") != 0)
- {
- LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str());
- return false;
- }
- }
- int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width");
- int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height");
- int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length");
- if (
- (TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) ||
- (a_NBT.GetType(TSizeX) != TAG_Short) ||
- (a_NBT.GetType(TSizeY) != TAG_Short) ||
- (a_NBT.GetType(TSizeZ) != TAG_Short)
- )
- {
- LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)",
- TSizeX, TSizeY, TSizeZ,
- a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ)
- );
- return false;
- }
-
- int SizeX = a_NBT.GetShort(TSizeX);
- int SizeY = a_NBT.GetShort(TSizeY);
- int SizeZ = a_NBT.GetShort(TSizeZ);
- if ((SizeX < 1) || (SizeY < 1) || (SizeZ < 1))
- {
- LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ);
- return false;
- }
-
- int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks");
- int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data");
- if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray))
- {
- LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes);
- return false;
- }
- bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray);
-
- Clear();
- SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (baTypes | baMetas) : baTypes);
-
- // Copy the block types and metas:
- int NumBytes = m_SizeX * m_SizeY * m_SizeZ;
- if (a_NBT.GetDataLength(TBlockTypes) < NumBytes)
- {
- LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
- NumBytes, a_NBT.GetDataLength(TBlockTypes)
- );
- NumBytes = a_NBT.GetDataLength(TBlockTypes);
- }
- memcpy(m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes);
-
- if (AreMetasPresent)
- {
- int NumBytes = m_SizeX * m_SizeY * m_SizeZ;
- if (a_NBT.GetDataLength(TBlockMetas) < NumBytes)
- {
- LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
- NumBytes, a_NBT.GetDataLength(TBlockMetas)
- );
- NumBytes = a_NBT.GetDataLength(TBlockMetas);
- }
- memcpy(m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes);
- }
-
- return true;
-}
-
-
-
-
void cBlockArea::RelSetData(
int a_RelX, int a_RelY, int a_RelZ,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
diff --git a/src/BlockArea.h b/src/BlockArea.h
index 075cc99ec..59bc0f241 100644
--- a/src/BlockArea.h
+++ b/src/BlockArea.h
@@ -12,18 +12,7 @@
#pragma once
-
-
-
-
-// fwd: World.h
-class cWorld;
-
-// fwd: FastNBT.h
-class cParsedNBT;
-
-
-
+#include "ForEachChunkProvider.h"
// tolua_begin
@@ -68,13 +57,13 @@ public:
void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ);
/// Reads an area of blocks specified. Returns true if successful. All coords are inclusive.
- bool Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas);
+ bool Read(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas);
// TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write
// A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again
/// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all
- bool Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas);
+ bool Write(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas);
/// Copies this object's contents into the specified BlockArea.
void CopyTo(cBlockArea & a_Into) const;
@@ -85,12 +74,6 @@ public:
/// For testing purposes only, dumps the area into a file.
void DumpToRawFile(const AString & a_FileName);
- /// Loads an area from a .schematic file. Returns true if successful
- bool LoadFromSchematicFile(const AString & a_FileName);
-
- /// Saves the area into a .schematic file. Returns true if successful
- bool SaveToSchematicFile(const AString & a_FileName);
-
/// Crops the internal contents by the specified amount of blocks from each border.
void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
@@ -233,6 +216,7 @@ public:
protected:
friend class cChunkDesc;
+ friend class cSchematicFileSerializer;
class cChunkReader :
public cChunkDataCallback
@@ -291,9 +275,6 @@ protected:
// Expand helpers:
void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
-
- /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful.
- bool LoadFromSchematicNBT(cParsedNBT & a_NBT);
/// Sets the specified datatypes at the specified location.
void RelSetData(
diff --git a/src/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp
index 5d7e4a37a..97b5c1a66 100644
--- a/src/BlockEntities/BlockEntity.cpp
+++ b/src/BlockEntities/BlockEntity.cpp
@@ -6,6 +6,7 @@
#include "Globals.h"
#include "BlockEntity.h"
#include "ChestEntity.h"
+#include "CommandBlockEntity.h"
#include "DispenserEntity.h"
#include "DropperEntity.h"
#include "EnderChestEntity.h"
@@ -23,17 +24,18 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE
{
switch (a_BlockType)
{
- case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_DISPENSER: return new cDispenserEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_ENDER_CHEST: return new cEnderChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
- case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
- case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
- case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_CHEST: return new cChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_COMMAND_BLOCK: return new cCommandBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_DISPENSER: return new cDispenserEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_DROPPER: return new cDropperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_ENDER_CHEST: return new cEnderChestEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_LIT_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
+ case E_BLOCK_FURNACE: return new cFurnaceEntity (a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, a_World);
+ case E_BLOCK_HOPPER: return new cHopperEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_SIGN_POST: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_WALLSIGN: return new cSignEntity (a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_NOTE_BLOCK: return new cNoteEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
+ case E_BLOCK_JUKEBOX: return new cJukeboxEntity (a_BlockX, a_BlockY, a_BlockZ, a_World);
}
LOGD("%s: Requesting creation of an unknown block entity - block type %d (%s)",
__FUNCTION__, a_BlockType, ItemTypeToString(a_BlockType).c_str()
diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h
index 0d358b556..7c6688f8d 100644
--- a/src/BlockEntities/BlockEntity.h
+++ b/src/BlockEntities/BlockEntity.h
@@ -87,7 +87,11 @@ public:
virtual void SendTo(cClientHandle & a_Client) = 0;
/// Ticks the entity; returns true if the chunk should be marked as dirty as a result of this ticking. By default does nothing.
- virtual bool Tick(float a_Dt, cChunk & a_Chunk) { return false; }
+ virtual bool Tick(float a_Dt, cChunk & /* a_Chunk */)
+ {
+ UNUSED(a_Dt);
+ return false;
+ }
protected:
/// Position in absolute block coordinates
diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h
index f35412e03..bf6289a2f 100644
--- a/src/BlockEntities/BlockEntityWithItems.h
+++ b/src/BlockEntities/BlockEntityWithItems.h
@@ -73,6 +73,7 @@ protected:
// cItemGrid::cListener overrides:
virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum)
{
+ UNUSED(a_SlotNum);
ASSERT(a_Grid == &m_Contents);
if (m_World != NULL)
{
diff --git a/src/BlockEntities/CommandBlockEntity.cpp b/src/BlockEntities/CommandBlockEntity.cpp
new file mode 100644
index 000000000..0bc6ca359
--- /dev/null
+++ b/src/BlockEntities/CommandBlockEntity.cpp
@@ -0,0 +1,221 @@
+
+// CommandBlockEntity.cpp
+
+// Implements the cCommandBlockEntity class representing a single command block in the world
+
+#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+#include "json/json.h"
+#include "CommandBlockEntity.h"
+#include "../Entities/Player.h"
+#include "../WorldStorage/FastNBT.h"
+
+#include "../CommandOutput.h"
+#include "../Root.h"
+#include "../Server.h" // ExecuteConsoleCommand()
+
+
+
+
+
+cCommandBlockEntity::cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World) :
+ super(E_BLOCK_COMMAND_BLOCK, a_X, a_Y, a_Z, a_World),
+ m_ShouldExecute(false),
+ m_IsPowered(false)
+{}
+
+
+
+
+
+
+void cCommandBlockEntity::UsedBy(cPlayer * a_Player)
+{
+ // Nothing to do
+ UNUSED(a_Player);
+}
+
+
+
+
+
+void cCommandBlockEntity::SetCommand(const AString & a_Cmd)
+{
+ m_Command = a_Cmd;
+
+ /*
+ Vanilla requires that the server send a Block Entity Update after a command has been set
+ Therefore, command blocks don't support on-the-fly (when window is open) updating of a command and therefore...
+ ...the following code can't be put in UsedBy just before the window opens
+
+ Just documenting my experience in getting this to work :P
+ */
+ m_World->BroadcastBlockEntity(GetPosX(), GetPosY(), GetPosZ());
+}
+
+
+
+
+
+void cCommandBlockEntity::SetLastOutput(const AString & a_LastOut)
+{
+ m_World->BroadcastBlockEntity(GetPosX(), GetPosY(), GetPosZ());
+ m_LastOutput = a_LastOut;
+}
+
+
+
+
+
+void cCommandBlockEntity::SetResult(const NIBBLETYPE a_Result)
+{
+ m_Result = a_Result;
+}
+
+
+
+
+
+const AString & cCommandBlockEntity::GetCommand(void) const
+{
+ return m_Command;
+}
+
+
+
+
+
+const AString & cCommandBlockEntity::GetLastOutput(void) const
+{
+ return m_LastOutput;
+}
+
+
+
+
+
+NIBBLETYPE cCommandBlockEntity::GetResult(void) const
+{
+ return m_Result;
+}
+
+
+
+
+
+void cCommandBlockEntity::Activate(void)
+{
+ m_ShouldExecute = true;
+}
+
+
+
+
+
+void cCommandBlockEntity::SetRedstonePower(bool a_IsPowered)
+{
+ if (a_IsPowered && !m_IsPowered)
+ {
+ Activate();
+ }
+ m_IsPowered = a_IsPowered;
+}
+
+
+
+
+
+bool cCommandBlockEntity::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ if (!m_ShouldExecute)
+ {
+ return false;
+ }
+
+ m_ShouldExecute = false;
+ Execute();
+ return true;
+}
+
+
+
+
+
+void cCommandBlockEntity::SendTo(cClientHandle & a_Client)
+{
+ a_Client.SendUpdateBlockEntity(*this);
+}
+
+
+
+
+
+bool cCommandBlockEntity::LoadFromJson(const Json::Value & a_Value)
+{
+ 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_Command = a_Value.get("Command", "").asString();
+ m_LastOutput = a_Value.get("LastOutput", "").asString();
+ m_Result = a_Value.get("SuccessCount", 0).asInt();
+
+ return true;
+}
+
+
+
+
+
+void cCommandBlockEntity::SaveToJson(Json::Value & a_Value)
+{
+ a_Value["x"] = m_PosX;
+ a_Value["y"] = m_PosY;
+ a_Value["z"] = m_PosZ;
+
+ a_Value["Command"] = m_Command;
+ a_Value["LastOutput"] = m_LastOutput;
+ a_Value["SuccessCount"] = m_Result;
+}
+
+
+
+
+
+void cCommandBlockEntity::Execute()
+{
+ if (m_World != NULL)
+ {
+ if (!m_World->AreCommandBlocksEnabled())
+ {
+ return;
+ }
+ }
+
+ class CommandBlockOutCb :
+ public cCommandOutputCallback
+ {
+ cCommandBlockEntity * m_CmdBlock;
+
+ public:
+ CommandBlockOutCb(cCommandBlockEntity * a_CmdBlock) : m_CmdBlock(a_CmdBlock) {}
+
+ virtual void Out(const AString & a_Text)
+ {
+ // Overwrite field
+ m_CmdBlock->SetLastOutput(a_Text);
+ }
+ } CmdBlockOutCb(this);
+
+ LOGD("cCommandBlockEntity: Executing command %s", m_Command.c_str());
+
+ cServer * Server = cRoot::Get()->GetServer();
+
+ Server->ExecuteConsoleCommand(m_Command, CmdBlockOutCb);
+
+ // TODO 2014-01-18 xdot: Update the signal strength.
+ m_Result = 0;
+}
+
+
+
+
diff --git a/src/BlockEntities/CommandBlockEntity.h b/src/BlockEntities/CommandBlockEntity.h
new file mode 100644
index 000000000..12157670f
--- /dev/null
+++ b/src/BlockEntities/CommandBlockEntity.h
@@ -0,0 +1,91 @@
+
+// CommandBlockEntity.h
+
+// Declares the cCommandBlockEntity class representing a single command block in the world
+
+
+
+
+
+#pragma once
+
+#include "BlockEntity.h"
+
+
+
+
+
+namespace Json
+{
+ class Value;
+}
+
+
+
+
+
+// tolua_begin
+
+class cCommandBlockEntity :
+ public cBlockEntity
+{
+ typedef cBlockEntity super;
+
+public:
+
+ // tolua_end
+
+ /// Creates a new empty command block entity
+ cCommandBlockEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
+
+ bool LoadFromJson( const Json::Value& a_Value );
+ virtual void SaveToJson(Json::Value& a_Value ) override;
+
+ virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
+ virtual void SendTo(cClientHandle & a_Client) override;
+ virtual void UsedBy(cPlayer * a_Player) override;
+
+ void SetLastOutput(const AString & a_LastOut);
+
+ void SetResult(const NIBBLETYPE a_Result);
+
+ // tolua_begin
+
+ /// Sets the internal redstone power flag to "on" or "off", depending on the parameter. Calls Activate() if appropriate
+ void SetRedstonePower(bool a_IsPowered);
+
+ /// Sets the command block to execute a command in the next tick
+ void Activate(void);
+
+ /// Sets the command
+ void SetCommand(const AString & a_Cmd);
+
+ /// Retrieves stored command
+ const AString & GetCommand(void) const;
+
+ /// Retrieves the last line of output generated by the command block
+ const AString & GetLastOutput(void) const;
+
+ /// Retrieves the result (signal strength) of the last operation
+ NIBBLETYPE GetResult(void) const;
+
+ // tolua_end
+
+private:
+
+ /// Executes the associated command
+ void Execute();
+
+ bool m_ShouldExecute;
+ bool m_IsPowered;
+
+ AString m_Command;
+
+ AString m_LastOutput;
+
+ NIBBLETYPE m_Result;
+} ; // tolua_export
+
+
+
+
diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp
index b1409f5cc..c6643bcff 100644
--- a/src/BlockEntities/FurnaceEntity.cpp
+++ b/src/BlockEntities/FurnaceEntity.cpp
@@ -70,8 +70,8 @@ void cFurnaceEntity::UsedBy(cPlayer * a_Player)
if (a_Player->GetWindow() != Window)
{
a_Player->OpenWindow(Window);
- BroadcastProgress(PROGRESSBAR_FUEL, m_LastProgressFuel);
- BroadcastProgress(PROGRESSBAR_SMELTING, m_LastProgressCook);
+ BroadcastProgress(PROGRESSBAR_FUEL, (short)m_LastProgressFuel);
+ BroadcastProgress(PROGRESSBAR_SMELTING, (short)m_LastProgressCook);
}
}
}
@@ -307,7 +307,7 @@ void cFurnaceEntity::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
/// Updates the current recipe, based on the current input
void cFurnaceEntity::UpdateInput(void)
{
- if (!m_Contents.GetSlot(fsInput).IsStackableWith(m_LastInput))
+ if (!m_Contents.GetSlot(fsInput).IsEqual(m_LastInput))
{
// The input is different from what we had before, reset the cooking time
m_TimeCooked = 0;
@@ -417,7 +417,7 @@ bool cFurnaceEntity::CanCookInputToOutput(void) const
return true;
}
- if (!m_Contents.GetSlot(fsOutput).IsStackableWith(*m_CurrentRecipe->Out))
+ if (!m_Contents.GetSlot(fsOutput).IsEqual(*m_CurrentRecipe->Out))
{
// The output slot is blocked with something that cannot be stacked with the recipe's output
return false;
@@ -445,14 +445,14 @@ void cFurnaceEntity::UpdateProgressBars(void)
int CurFuel = (m_FuelBurnTime > 0) ? (200 - 200 * m_TimeBurned / m_FuelBurnTime) : 0;
if ((CurFuel / 8) != (m_LastProgressFuel / 8))
{
- BroadcastProgress(PROGRESSBAR_FUEL, CurFuel);
+ BroadcastProgress(PROGRESSBAR_FUEL, (short)CurFuel);
m_LastProgressFuel = CurFuel;
}
int CurCook = (m_NeedCookTime > 0) ? (200 * m_TimeCooked / m_NeedCookTime) : 0;
if ((CurCook / 8) != (m_LastProgressCook / 8))
{
- BroadcastProgress(PROGRESSBAR_SMELTING, CurCook);
+ BroadcastProgress(PROGRESSBAR_SMELTING, (short)CurCook);
m_LastProgressCook = CurCook;
}
}
diff --git a/src/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h
index 8b695d61a..b08187300 100644
--- a/src/BlockEntities/FurnaceEntity.h
+++ b/src/BlockEntities/FurnaceEntity.h
@@ -95,7 +95,7 @@ public:
// tolua_end
- void SetBurnTimes(int a_FuelBurnTime, int a_TimeBurned) {m_FuelBurnTime = a_FuelBurnTime; m_TimeBurned = 0; }
+ void SetBurnTimes(int a_FuelBurnTime, int a_TimeBurned) {m_FuelBurnTime = a_FuelBurnTime; m_TimeBurned = a_TimeBurned; }
void SetCookTimes(int a_NeedCookTime, int a_TimeCooked) {m_NeedCookTime = a_NeedCookTime; m_TimeCooked = a_TimeCooked; }
protected:
diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp
index 0aca3209f..2255cad64 100644
--- a/src/BlockEntities/HopperEntity.cpp
+++ b/src/BlockEntities/HopperEntity.cpp
@@ -407,7 +407,7 @@ bool cHopperEntity::MoveItemsFromSlot(cBlockEntityWithItems & a_Entity, int a_Sl
m_Contents.SetSlot(i, One);
return true;
}
- else if (m_Contents.GetSlot(i).IsStackableWith(One))
+ else if (m_Contents.GetSlot(i).IsEqual(One))
{
if (cPluginManager::Get()->CallHookHopperPullingItem(*m_World, *this, i, a_Entity, a_SlotNum))
{
@@ -488,7 +488,6 @@ bool cHopperEntity::MoveItemsToFurnace(cChunk & a_Chunk, int a_BlockX, int a_Blo
// Feed the fuel slot of the furnace
return MoveItemsToSlot(*Furnace, cFurnaceEntity::fsFuel);
}
- return false;
}
@@ -545,7 +544,7 @@ bool cHopperEntity::MoveItemsToSlot(cBlockEntityWithItems & a_Entity, int a_DstS
}
for (int i = 0; i < ContentsWidth * ContentsHeight; i++)
{
- if (m_Contents.GetSlot(i).IsStackableWith(DestSlot))
+ if (m_Contents.GetSlot(i).IsEqual(DestSlot))
{
if (cPluginManager::Get()->CallHookHopperPushingItem(*m_World, *this, i, a_Entity, a_DstSlotNum))
{
diff --git a/src/BlockEntities/JukeboxEntity.h b/src/BlockEntities/JukeboxEntity.h
index fcafdc479..996de965b 100644
--- a/src/BlockEntities/JukeboxEntity.h
+++ b/src/BlockEntities/JukeboxEntity.h
@@ -45,7 +45,7 @@ public:
// tolua_end
virtual void UsedBy(cPlayer * a_Player) override;
- virtual void SendTo(cClientHandle & a_Client) override { };
+ virtual void SendTo(cClientHandle &) override { };
private:
int m_Record;
diff --git a/src/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h
index e2d088f44..cf78aeac6 100644
--- a/src/BlockEntities/NoteEntity.h
+++ b/src/BlockEntities/NoteEntity.h
@@ -52,7 +52,7 @@ public:
// tolua_end
virtual void UsedBy(cPlayer * a_Player) override;
- virtual void SendTo(cClientHandle & a_Client) override { };
+ virtual void SendTo(cClientHandle &) override { };
private:
char m_Pitch;
diff --git a/src/BlockID.cpp b/src/BlockID.cpp
index 05d4c6595..fbb5a0720 100644
--- a/src/BlockID.cpp
+++ b/src/BlockID.cpp
@@ -20,7 +20,7 @@ bool g_BlockPistonBreakable[256];
bool g_BlockIsSnowable[256];
bool g_BlockRequiresSpecialTool[256];
bool g_BlockIsSolid[256];
-bool g_BlockIsTorchPlaceable[256];
+bool g_BlockFullyOccupiesVoxel[256];
@@ -267,106 +267,6 @@ AString ItemToFullString(const cItem & a_Item)
-EMCSBiome StringToBiome(const AString & a_BiomeString)
-{
- // If it is a number, return it:
- int res = atoi(a_BiomeString.c_str());
- if ((res != 0) || (a_BiomeString.compare("0") == 0))
- {
- // It was a valid number
- return (EMCSBiome)res;
- }
-
- // Convert using the built-in map:
- static struct {
- EMCSBiome m_Biome;
- const char * m_String;
- } BiomeMap[] =
- {
- {biOcean, "Ocean"} ,
- {biPlains, "Plains"},
- {biDesert, "Desert"},
- {biExtremeHills, "ExtremeHills"},
- {biForest, "Forest"},
- {biTaiga, "Taiga"},
- {biSwampland, "Swampland"},
- {biRiver, "River"},
- {biNether, "Hell"},
- {biNether, "Nether"},
- {biEnd, "Sky"},
- {biEnd, "End"},
- {biFrozenOcean, "FrozenOcean"},
- {biFrozenRiver, "FrozenRiver"},
- {biIcePlains, "IcePlains"},
- {biIcePlains, "Tundra"},
- {biIceMountains, "IceMountains"},
- {biMushroomIsland, "MushroomIsland"},
- {biMushroomShore, "MushroomShore"},
- {biBeach, "Beach"},
- {biDesertHills, "DesertHills"},
- {biForestHills, "ForestHills"},
- {biTaigaHills, "TaigaHills"},
- {biExtremeHillsEdge, "ExtremeHillsEdge"},
- {biJungle, "Jungle"},
- {biJungleHills, "JungleHills"},
-
- // Release 1.7 biomes:
- {biJungleEdge, "JungleEdge"},
- {biDeepOcean, "DeepOcean"},
- {biStoneBeach, "StoneBeach"},
- {biColdBeach, "ColdBeach"},
- {biBirchForest, "BirchForest"},
- {biBirchForestHills, "BirchForestHills"},
- {biRoofedForest, "RoofedForest"},
- {biColdTaiga, "ColdTaiga"},
- {biColdTaigaHills, "ColdTaigaHills"},
- {biMegaTaiga, "MegaTaiga"},
- {biMegaTaigaHills, "MegaTaigaHills"},
- {biExtremeHillsPlus, "ExtremeHillsPlus"},
- {biSavanna, "Savanna"},
- {biSavannaPlateau, "SavannaPlateau"},
- {biMesa, "Mesa"},
- {biMesaPlateauF, "MesaPlateauF"},
- {biMesaPlateau, "MesaPlateau"},
-
- // Release 1.7 variants:
- {biSunflowerPlains, "SunflowerPlains"},
- {biDesertM, "DesertM"},
- {biExtremeHillsM, "ExtremeHillsM"},
- {biFlowerForest, "FlowerForest"},
- {biTaigaM, "TaigaM"},
- {biSwamplandM, "SwamplandM"},
- {biIcePlainsSpikes, "IcePlainsSpikes"},
- {biJungleM, "JungleM"},
- {biJungleEdgeM, "JungleEdgeM"},
- {biBirchForestM, "BirchForestM"},
- {biBirchForestHillsM, "BirchForestHillsM"},
- {biRoofedForestM, "RoofedForestM"},
- {biColdTaigaM, "ColdTaigaM"},
- {biMegaSpruceTaiga, "MegaSpruceTaiga"},
- {biMegaSpruceTaigaHills, "MegaSpruceTaigaHills"},
- {biExtremeHillsPlusM, "ExtremeHillsPlusM"},
- {biSavannaM, "SavannaM"},
- {biSavannaPlateauM, "SavannaPlateauM"},
- {biMesaBryce, "MesaBryce"},
- {biMesaPlateauFM, "MesaPlateauFM"},
- {biMesaPlateauM, "MesaPlateauM"},
- } ;
-
- for (size_t i = 0; i < ARRAYCOUNT(BiomeMap); i++)
- {
- if (NoCaseCompare(BiomeMap[i].m_String, a_BiomeString) == 0)
- {
- return BiomeMap[i].m_Biome;
- }
- } // for i - BiomeMap[]
- return (EMCSBiome)-1;
-}
-
-
-
-
-
int StringToMobType(const AString & a_MobString)
{
static struct {
@@ -591,7 +491,7 @@ public:
memset(g_BlockTransparent, 0x00, sizeof(g_BlockTransparent));
memset(g_BlockOneHitDig, 0x00, sizeof(g_BlockOneHitDig));
memset(g_BlockPistonBreakable, 0x00, sizeof(g_BlockPistonBreakable));
- memset(g_BlockIsTorchPlaceable, 0x00, sizeof(g_BlockIsTorchPlaceable));
+ memset(g_BlockFullyOccupiesVoxel, 0x00, sizeof(g_BlockFullyOccupiesVoxel));
// Setting bools to true must be done manually, see http://forum.mc-server.org/showthread.php?tid=629&pid=5415#pid5415
for (size_t i = 0; i < ARRAYCOUNT(g_BlockIsSnowable); i++)
@@ -867,6 +767,8 @@ public:
g_BlockIsSolid[E_BLOCK_MELON_STEM] = false;
g_BlockIsSolid[E_BLOCK_NETHER_PORTAL] = false;
g_BlockIsSolid[E_BLOCK_PISTON_EXTENSION] = false;
+ g_BlockIsSolid[E_BLOCK_POTATOES] = false;
+ g_BlockIsSolid[E_BLOCK_POWERED_RAIL] = false;
g_BlockIsSolid[E_BLOCK_RAIL] = false;
g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_OFF] = false;
g_BlockIsSolid[E_BLOCK_REDSTONE_TORCH_ON] = false;
@@ -891,67 +793,67 @@ public:
g_BlockIsSolid[E_BLOCK_WOODEN_SLAB] = false;
// Torch placeable blocks:
- g_BlockIsTorchPlaceable[E_BLOCK_BEDROCK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_COAL] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_BLOCK_OF_REDSTONE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_BOOKCASE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_BRICK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_CLAY] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_COAL_ORE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_COBBLESTONE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_COMMAND_BLOCK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_CRAFTING_TABLE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_BLOCK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_DIAMOND_ORE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_DIRT] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_DISPENSER] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_STONE_SLAB] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_DOUBLE_WOODEN_SLAB] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_DROPPER] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_BLOCK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_EMERALD_ORE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_END_STONE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_FURNACE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_GLOWSTONE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_GOLD_BLOCK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_GOLD_ORE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_GRASS] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_GRAVEL] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_HARDENED_CLAY] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_HAY_BALE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_HUGE_BROWN_MUSHROOM] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_HUGE_RED_MUSHROOM] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_IRON_BLOCK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_IRON_ORE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_JACK_O_LANTERN] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_JUKEBOX] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_BLOCK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_LAPIS_ORE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_LOG] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_MELON] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_MOSSY_COBBLESTONE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_MYCELIUM] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_NETHERRACK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_NETHER_BRICK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_NETHER_QUARTZ_ORE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_NOTE_BLOCK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_OBSIDIAN] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_PACKED_ICE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_PLANKS] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_PUMPKIN] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_QUARTZ_BLOCK] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_OFF] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_LAMP_ON] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_REDSTONE_ORE_GLOWING] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_SANDSTONE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_SAND] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_SILVERFISH_EGG] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_SPONGE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_STAINED_CLAY] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_WOOL] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_STONE] = true;
- g_BlockIsTorchPlaceable[E_BLOCK_STONE_BRICKS] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_BEDROCK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_BLOCK_OF_COAL] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_BLOCK_OF_REDSTONE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_BOOKCASE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_BRICK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_CLAY] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_COAL_ORE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_COBBLESTONE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_COMMAND_BLOCK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_CRAFTING_TABLE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_DIAMOND_BLOCK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_DIAMOND_ORE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_DIRT] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_DISPENSER] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_DOUBLE_STONE_SLAB] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_DOUBLE_WOODEN_SLAB] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_DROPPER] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_EMERALD_BLOCK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_EMERALD_ORE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_END_STONE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_FURNACE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_GLOWSTONE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_GOLD_BLOCK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_GOLD_ORE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_GRASS] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_GRAVEL] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_HARDENED_CLAY] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_HAY_BALE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_HUGE_BROWN_MUSHROOM] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_HUGE_RED_MUSHROOM] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_IRON_BLOCK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_IRON_ORE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_JACK_O_LANTERN] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_JUKEBOX] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_LAPIS_BLOCK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_LAPIS_ORE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_LOG] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_MELON] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_MOSSY_COBBLESTONE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_MYCELIUM] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_NETHERRACK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_NETHER_BRICK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_NETHER_QUARTZ_ORE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_NOTE_BLOCK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_OBSIDIAN] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_PACKED_ICE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_PLANKS] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_PUMPKIN] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_QUARTZ_BLOCK] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_REDSTONE_LAMP_OFF] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_REDSTONE_LAMP_ON] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_REDSTONE_ORE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_REDSTONE_ORE_GLOWING] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_SANDSTONE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_SAND] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_SILVERFISH_EGG] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_SPONGE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_STAINED_CLAY] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_WOOL] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_STONE] = true;
+ g_BlockFullyOccupiesVoxel[E_BLOCK_STONE_BRICKS] = true;
}
} BlockPropertiesInitializer;
diff --git a/src/BlockID.h b/src/BlockID.h
index 288719ccf..b31c589b9 100644
--- a/src/BlockID.h
+++ b/src/BlockID.h
@@ -876,9 +876,6 @@ extern AString ItemTypeToString(short a_ItemType);
/// Translates a full item into a fully-specified string (including meta and count). If the ItemType is not recognized, the ItemType number is output into the string.
extern AString ItemToFullString(const cItem & a_Item);
-/// Translates a biome string to biome enum. Takes either a number or a biome alias (built-in). Returns -1 on failure.
-extern EMCSBiome StringToBiome(const AString & a_BiomeString);
-
/// Translates a mob string ("ocelot") to mobtype (E_ENTITY_TYPE_OCELOT)
extern int StringToMobType(const AString & a_MobString);
@@ -909,7 +906,7 @@ extern bool g_BlockPistonBreakable[256];
extern bool g_BlockIsSnowable[256];
extern bool g_BlockRequiresSpecialTool[256];
extern bool g_BlockIsSolid[256];
-extern bool g_BlockIsTorchPlaceable[256];
+extern bool g_BlockFullyOccupiesVoxel[256];
diff --git a/src/BlockTracer.h b/src/BlockTracer.h
index d0a34811d..40d80da1a 100644
--- a/src/BlockTracer.h
+++ b/src/BlockTracer.h
@@ -36,7 +36,14 @@ public:
/** Called on each block encountered along the path, including the first block (path start), if chunk data is not loaded
When this callback returns true, the tracing is aborted.
*/
- virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace) { return false; }
+ virtual bool OnNextBlockNoData(int a_BlockX, int a_BlockY, int a_BlockZ, char a_EntryFace)
+ {
+ UNUSED(a_BlockX);
+ UNUSED(a_BlockY);
+ UNUSED(a_BlockZ);
+ UNUSED(a_EntryFace);
+ return false;
+ }
/** Called when the path goes out of world, either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height)
The coords specify the exact point at which the path exited the world.
@@ -44,7 +51,13 @@ public:
Note that some paths can go out of the world and come back again (parabola),
in such a case this callback is followed by OnIntoWorld() and further OnNextBlock() calls
*/
- virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ) { return false; }
+ virtual bool OnOutOfWorld(double a_BlockX, double a_BlockY, double a_BlockZ)
+ {
+ UNUSED(a_BlockX);
+ UNUSED(a_BlockY);
+ UNUSED(a_BlockZ);
+ return false;
+ }
/** Called when the path goes into the world, from either below (a_BlockY < 0) or above (a_BlockY >= cChunkDef::Height)
The coords specify the exact point at which the path entered the world.
@@ -52,7 +65,13 @@ public:
Note that some paths can go out of the world and come back again (parabola),
in such a case this callback is followed by further OnNextBlock() calls
*/
- virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ) { return false; }
+ virtual bool OnIntoWorld(double a_BlockX, double a_BlockY, double a_BlockZ)
+ {
+ UNUSED(a_BlockX);
+ UNUSED(a_BlockY);
+ UNUSED(a_BlockZ);
+ return false;
+ }
/** Called when the path is sure not to hit any more blocks.
Note that for some shapes this might never happen (line with constant Y)
diff --git a/src/Blocks/BlockChest.h b/src/Blocks/BlockChest.h
index 488c58ac5..88f7c4b32 100644
--- a/src/Blocks/BlockChest.h
+++ b/src/Blocks/BlockChest.h
@@ -42,13 +42,13 @@ public:
{
return false;
}
- double rot = a_Player->GetRotation();
+ double yaw = a_Player->GetYaw();
if (
(Area.GetRelBlockType(0, 0, 1) == E_BLOCK_CHEST) ||
(Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
)
{
- a_BlockMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
+ a_BlockMeta = ((yaw >= -90) && (yaw < 90)) ? 2 : 3;
return true;
}
if (
@@ -56,12 +56,12 @@ public:
(Area.GetRelBlockType(2, 0, 1) == E_BLOCK_CHEST)
)
{
- a_BlockMeta = (rot < 0) ? 4 : 5;
+ a_BlockMeta = (yaw < 0) ? 4 : 5;
return true;
}
// Single chest, get meta from rotation only
- a_BlockMeta = RotationToMetaData(rot);
+ a_BlockMeta = RotationToMetaData(yaw);
return true;
}
@@ -80,7 +80,7 @@ public:
return;
}
- double rot = a_Player->GetRotation();
+ double rot = a_Player->GetYaw(); // FIXME: Rename rot to yaw
// Choose meta from player rotation, choose only between 2 or 3
NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
if (
diff --git a/src/Blocks/BlockCommandBlock.h b/src/Blocks/BlockCommandBlock.h
new file mode 100644
index 000000000..cf0103765
--- /dev/null
+++ b/src/Blocks/BlockCommandBlock.h
@@ -0,0 +1,32 @@
+
+#pragma once
+
+#include "BlockEntity.h"
+
+
+
+
+
+class cBlockCommandBlockHandler :
+ public cBlockEntityHandler
+{
+public:
+ cBlockCommandBlockHandler(BLOCKTYPE a_BlockType)
+ : cBlockEntityHandler(a_BlockType)
+ {
+ }
+
+ virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
+ {
+ a_Pickups.push_back(cItem(E_BLOCK_AIR, 8, 0));
+ }
+
+ virtual const char * GetStepSound(void) override
+ {
+ return "step.stone";
+ }
+} ;
+
+
+
+
diff --git a/src/Blocks/BlockComparator.h b/src/Blocks/BlockComparator.h
index e7e18bac9..5a8e54eda 100644
--- a/src/Blocks/BlockComparator.h
+++ b/src/Blocks/BlockComparator.h
@@ -53,7 +53,7 @@ public:
) override
{
a_BlockType = m_BlockType;
- a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation());
+ a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetYaw());
return true;
}
diff --git a/src/Blocks/BlockDoor.h b/src/Blocks/BlockDoor.h
index 03a79d47d..1e86446dd 100644
--- a/src/Blocks/BlockDoor.h
+++ b/src/Blocks/BlockDoor.h
@@ -42,7 +42,7 @@ public:
}
a_BlockType = m_BlockType;
- a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation());
+ a_BlockMeta = PlayerYawToMetaData(a_Player->GetYaw());
return true;
}
diff --git a/src/Blocks/BlockDropSpenser.h b/src/Blocks/BlockDropSpenser.h
index b7f20825d..ce9cc0c5d 100644
--- a/src/Blocks/BlockDropSpenser.h
+++ b/src/Blocks/BlockDropSpenser.h
@@ -31,7 +31,7 @@ public:
a_BlockType = m_BlockType;
// FIXME: Do not use cPiston class for dispenser placement!
- a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch());
+ a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
return true;
}
} ;
diff --git a/src/Blocks/BlockEnderchest.h b/src/Blocks/BlockEnderchest.h
index 50d8e38e0..b648248d0 100644
--- a/src/Blocks/BlockEnderchest.h
+++ b/src/Blocks/BlockEnderchest.h
@@ -30,7 +30,7 @@ public:
) override
{
a_BlockType = m_BlockType;
- a_BlockMeta = RotationToMetaData(a_Player->GetRotation());
+ a_BlockMeta = RotationToMetaData(a_Player->GetYaw());
return true;
}
diff --git a/src/Blocks/BlockFenceGate.h b/src/Blocks/BlockFenceGate.h
index 6423a7cb0..779f12ee1 100644
--- a/src/Blocks/BlockFenceGate.h
+++ b/src/Blocks/BlockFenceGate.h
@@ -25,7 +25,7 @@ public:
) override
{
a_BlockType = m_BlockType;
- a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation());
+ a_BlockMeta = PlayerYawToMetaData(a_Player->GetYaw());
return true;
}
@@ -33,7 +33,7 @@ public:
virtual void OnUse(cWorld * a_World, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
{
NIBBLETYPE OldMetaData = a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
- NIBBLETYPE NewMetaData = PlayerYawToMetaData(a_Player->GetRotation());
+ NIBBLETYPE NewMetaData = PlayerYawToMetaData(a_Player->GetYaw());
OldMetaData ^= 4; // Toggle the gate
if ((OldMetaData & 1) == (NewMetaData & 1))
{
diff --git a/src/Blocks/BlockFurnace.h b/src/Blocks/BlockFurnace.h
index 5b067d7b7..693eac331 100644
--- a/src/Blocks/BlockFurnace.h
+++ b/src/Blocks/BlockFurnace.h
@@ -35,7 +35,7 @@ public:
a_BlockType = m_BlockType;
// FIXME: Do not use cPiston class for furnace placement!
- a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), 0);
+ a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), 0);
return true;
}
diff --git a/src/Blocks/BlockGlowstone.h b/src/Blocks/BlockGlowstone.h
index 5f0d95dee..6c198efc4 100644
--- a/src/Blocks/BlockGlowstone.h
+++ b/src/Blocks/BlockGlowstone.h
@@ -20,8 +20,8 @@ public:
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
{
// Reset meta to 0
- // TODO: More drops?
- a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, 1, 0));
+ MTRand r1;
+ a_Pickups.push_back(cItem(E_ITEM_GLOWSTONE_DUST, (char)(2 + r1.randInt(2)), 0));
}
} ;
diff --git a/src/Blocks/BlockHandler.cpp b/src/Blocks/BlockHandler.cpp
index ff1022e12..b9c0887ce 100644
--- a/src/Blocks/BlockHandler.cpp
+++ b/src/Blocks/BlockHandler.cpp
@@ -14,6 +14,7 @@
#include "BlockChest.h"
#include "BlockCloth.h"
#include "BlockCobWeb.h"
+#include "BlockCommandBlock.h"
#include "BlockComparator.h"
#include "BlockCrops.h"
#include "BlockDeadBush.h"
@@ -116,6 +117,7 @@ cBlockHandler * cBlockHandler::CreateBlockHandler(BLOCKTYPE a_BlockType)
case E_BLOCK_CAULDRON: return new cBlockCauldronHandler (a_BlockType);
case E_BLOCK_CHEST: return new cBlockChestHandler (a_BlockType);
case E_BLOCK_COAL_ORE: return new cBlockOreHandler (a_BlockType);
+ case E_BLOCK_COMMAND_BLOCK: return new cBlockCommandBlockHandler (a_BlockType);
case E_BLOCK_ACTIVE_COMPARATOR: return new cBlockComparatorHandler (a_BlockType);
case E_BLOCK_COBBLESTONE: return new cBlockStoneHandler (a_BlockType);
case E_BLOCK_COBBLESTONE_STAIRS: return new cBlockStairsHandler (a_BlockType);
diff --git a/src/Blocks/BlockHandler.h b/src/Blocks/BlockHandler.h
index 107d36476..a732aa797 100644
--- a/src/Blocks/BlockHandler.h
+++ b/src/Blocks/BlockHandler.h
@@ -99,7 +99,11 @@ public:
virtual bool DoesIgnoreBuildCollision(void);
/// <summary>Similar to DoesIgnoreBuildCollision(void), but is used for cases where block meta/player item-in-hand is needed to determine collision (thin snow)</summary>
- virtual bool DoesIgnoreBuildCollision(cPlayer * a_Player, NIBBLETYPE a_Meta) { return DoesIgnoreBuildCollision(); }
+ virtual bool DoesIgnoreBuildCollision(cPlayer *, NIBBLETYPE a_Meta)
+ {
+ UNUSED(a_Meta);
+ return DoesIgnoreBuildCollision();
+ }
/// <summary>Returns if this block drops if it gets destroyed by an unsuitable situation. Default: true</summary>
virtual bool DoesDropOnUnsuitable(void);
diff --git a/src/Blocks/BlockLever.h b/src/Blocks/BlockLever.h
index 15fe2071c..5b0c89127 100644
--- a/src/Blocks/BlockLever.h
+++ b/src/Blocks/BlockLever.h
@@ -20,7 +20,7 @@ public:
// Flip the ON bit on/off using the XOR bitwise operation
NIBBLETYPE Meta = (a_World->GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) ^ 0x08);
- a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
+ a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LEVER, Meta); // SetMeta doesn't work for unpowering levers, so setblock
a_World->BroadcastSoundEffect("random.click", a_BlockX * 8, a_BlockY * 8, a_BlockZ * 8, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f);
}
diff --git a/src/Blocks/BlockPiston.cpp b/src/Blocks/BlockPiston.cpp
index 3e1ca1d15..88259d96e 100644
--- a/src/Blocks/BlockPiston.cpp
+++ b/src/Blocks/BlockPiston.cpp
@@ -60,7 +60,7 @@ bool cBlockPistonHandler::GetPlacementBlockTypeMeta(
)
{
a_BlockType = m_BlockType;
- a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetRotation(), a_Player->GetPitch());
+ a_BlockMeta = cPiston::RotationPitchToMetaData(a_Player->GetYaw(), a_Player->GetPitch());
return true;
}
diff --git a/src/Blocks/BlockPumpkin.h b/src/Blocks/BlockPumpkin.h
index 724241935..5187f24eb 100644
--- a/src/Blocks/BlockPumpkin.h
+++ b/src/Blocks/BlockPumpkin.h
@@ -86,7 +86,7 @@ public:
) override
{
a_BlockType = m_BlockType;
- a_BlockMeta = PlayerYawToMetaData(a_Player->GetRotation());
+ a_BlockMeta = PlayerYawToMetaData(a_Player->GetYaw());
return true;
}
diff --git a/src/Blocks/BlockRail.h b/src/Blocks/BlockRail.h
index 55cadfa48..da3783d08 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(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
+ if (!IsBlockRail(a_World->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_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_RAIL)
+ if (!IsBlockRail(a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)))
{
- if ((a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure != E_PURE_UPDOWN))
+ if (!IsBlockRail(a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)) || (a_Pure != E_PURE_UPDOWN))
{
- if ((a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ) != E_BLOCK_RAIL) || (a_Pure == E_PURE_NONE))
+ if (!IsBlockRail(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)) || (a_Pure == E_PURE_NONE))
{
return true;
}
diff --git a/src/Blocks/BlockRedstone.h b/src/Blocks/BlockRedstone.h
index 1bd9995f2..5ffb77fb6 100644
--- a/src/Blocks/BlockRedstone.h
+++ b/src/Blocks/BlockRedstone.h
@@ -20,7 +20,7 @@ public:
virtual bool CanBeAt(int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
- return ((a_RelY > 0) && g_BlockIsTorchPlaceable[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]);
+ return ((a_RelY > 0) && g_BlockFullyOccupiesVoxel[a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)]);
}
diff --git a/src/Blocks/BlockRedstoneRepeater.h b/src/Blocks/BlockRedstoneRepeater.h
index 1fcddd4f8..713664659 100644
--- a/src/Blocks/BlockRedstoneRepeater.h
+++ b/src/Blocks/BlockRedstoneRepeater.h
@@ -25,7 +25,7 @@ public:
) override
{
a_BlockType = m_BlockType;
- a_BlockMeta = RepeaterRotationToMetaData(a_Player->GetRotation());
+ a_BlockMeta = RepeaterRotationToMetaData(a_Player->GetYaw());
return true;
}
diff --git a/src/Blocks/BlockStairs.h b/src/Blocks/BlockStairs.h
index fa378e4b5..e463612f5 100644
--- a/src/Blocks/BlockStairs.h
+++ b/src/Blocks/BlockStairs.h
@@ -26,7 +26,7 @@ public:
) override
{
a_BlockType = m_BlockType;
- a_BlockMeta = RotationToMetaData(a_Player->GetRotation());
+ a_BlockMeta = RotationToMetaData(a_Player->GetYaw());
switch (a_BlockFace)
{
case BLOCK_FACE_TOP: break;
diff --git a/src/Blocks/BlockTorch.h b/src/Blocks/BlockTorch.h
index 9e543dfd7..faba9c4f5 100644
--- a/src/Blocks/BlockTorch.h
+++ b/src/Blocks/BlockTorch.h
@@ -98,7 +98,7 @@ public:
static bool CanBePlacedOn(BLOCKTYPE a_BlockType, char a_BlockFace)
{
- if ( !g_BlockIsTorchPlaceable[a_BlockType] )
+ if ( !g_BlockFullyOccupiesVoxel[a_BlockType] )
{
return (a_BlockFace == BLOCK_FACE_TOP); // Allow placement only when torch upright (for glass, etc.); exceptions won't even be sent by client, no need to handle
}
@@ -127,7 +127,7 @@ public:
{
return i;
}
- else if ((g_BlockIsTorchPlaceable[BlockInQuestion]) && (i != BLOCK_FACE_BOTTOM))
+ else if ((g_BlockFullyOccupiesVoxel[BlockInQuestion]) && (i != BLOCK_FACE_BOTTOM))
{
// Otherwise, if block in that direction is torch placeable and we haven't gotten to it via the bottom face, return that face
return i;
@@ -161,7 +161,7 @@ public:
// No need to check for upright orientation, it was done when the torch was placed
return true;
}
- else if ( !g_BlockIsTorchPlaceable[BlockInQuestion] )
+ else if ( !g_BlockFullyOccupiesVoxel[BlockInQuestion] )
{
return false;
}
diff --git a/src/ByteBuffer.cpp b/src/ByteBuffer.cpp
index 64c03d0d3..d2d3beb97 100644
--- a/src/ByteBuffer.cpp
+++ b/src/ByteBuffer.cpp
@@ -54,6 +54,7 @@ public:
{
TestRead();
TestWrite();
+ TestWrap();
}
void TestRead(void)
@@ -61,11 +62,11 @@ public:
cByteBuffer buf(50);
buf.Write("\x05\xac\x02\x00", 4);
UInt32 v1;
- ASSERT(buf.ReadVarInt(v1) && (v1 == 5));
+ assert(buf.ReadVarInt(v1) && (v1 == 5));
UInt32 v2;
- ASSERT(buf.ReadVarInt(v2) && (v2 == 300));
+ assert(buf.ReadVarInt(v2) && (v2 == 300));
UInt32 v3;
- ASSERT(buf.ReadVarInt(v3) && (v3 == 0));
+ assert(buf.ReadVarInt(v3) && (v3 == 0));
}
void TestWrite(void)
@@ -76,9 +77,30 @@ public:
buf.WriteVarInt(0);
AString All;
buf.ReadAll(All);
- ASSERT(All.size() == 4);
- ASSERT(memcmp(All.data(), "\x05\xac\x02\x00", All.size()) == 0);
+ assert(All.size() == 4);
+ assert(memcmp(All.data(), "\x05\xac\x02\x00", All.size()) == 0);
}
+
+ void TestWrap(void)
+ {
+ cByteBuffer buf(3);
+ for (int i = 0; i < 1000; i++)
+ {
+ int FreeSpace = buf.GetFreeSpace();
+ assert(buf.GetReadableSpace() == 0);
+ assert(FreeSpace > 0);
+ assert(buf.Write("a", 1));
+ assert(buf.CanReadBytes(1));
+ assert(buf.GetReadableSpace() == 1);
+ unsigned char v = 0;
+ assert(buf.ReadByte(v));
+ assert(v == 'a');
+ assert(buf.GetReadableSpace() == 0);
+ buf.CommitRead();
+ assert(buf.GetFreeSpace() == FreeSpace); // We're back to normal
+ }
+ }
+
} g_ByteBufferTest;
#endif
@@ -159,7 +181,7 @@ bool cByteBuffer::Write(const char * a_Bytes, int a_Count)
int CurReadableSpace = GetReadableSpace();
int WrittenBytes = 0;
- if (GetFreeSpace() < a_Count)
+ if (CurFreeSpace < a_Count)
{
return false;
}
@@ -773,7 +795,7 @@ void cByteBuffer::ReadAll(AString & a_Data)
-bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, int a_NumBytes)
+bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes)
{
if (!a_Dst.CanWriteBytes(a_NumBytes) || !CanReadBytes(a_NumBytes))
{
@@ -781,9 +803,10 @@ bool cByteBuffer::ReadToByteBuffer(cByteBuffer & a_Dst, int a_NumBytes)
return false;
}
char buf[1024];
- while (a_NumBytes > 0)
+ // > 0 without generating warnings about unsigned comparisons where size_t is unsigned
+ while (a_NumBytes != 0)
{
- int num = (a_NumBytes > sizeof(buf)) ? sizeof(buf) : a_NumBytes;
+ size_t num = (a_NumBytes > sizeof(buf)) ? sizeof(buf) : a_NumBytes;
VERIFY(ReadBuf(buf, num));
VERIFY(a_Dst.Write(buf, num));
a_NumBytes -= num;
@@ -863,3 +886,4 @@ void cByteBuffer::CheckValid(void) const
+
diff --git a/src/ByteBuffer.h b/src/ByteBuffer.h
index 06c846fa9..cbce119b1 100644
--- a/src/ByteBuffer.h
+++ b/src/ByteBuffer.h
@@ -110,7 +110,7 @@ public:
void ReadAll(AString & a_Data);
/// Reads the specified number of bytes and writes it into the destinatio bytebuffer. Returns true on success.
- bool ReadToByteBuffer(cByteBuffer & a_Dst, int a_NumBytes);
+ bool ReadToByteBuffer(cByteBuffer & a_Dst, size_t a_NumBytes);
/// Removes the bytes that have been read from the ringbuffer
void CommitRead(void);
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 00c3059b5..944150a44 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,83 +1,224 @@
-cmake_minimum_required (VERSION 2.6)
+cmake_minimum_required (VERSION 2.8.2)
project (MCServer)
-if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++03")
-endif()
-
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 WIN32)
-foreach(folder ${FOLDERS})
- add_subdirectory(${folder})
-endforeach(folder)
-file(GLOB SOURCE
- "*.cpp"
-)
-else()
+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)
-function(includefolder PATH)
- FILE(GLOB FOLDER_FILES
- "${PATH}/*.cpp"
- "${PATH}/*.h"
+ #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)
+
+ file(GLOB SOURCE
+ "*.cpp"
)
- source_group("${PATH}" FILES ${FOLDER_FILES})
-endfunction(includefolder)
+ list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/StackWalker.cpp" "${PROJECT_SOURCE_DIR}/LeakFinder.cpp")
+
+ # If building a windows version, but not using MSVC, add the resources directly to the makefile:
+ if (WIN32)
+ FILE(GLOB ResourceFiles
+ "Resources/*.rc"
+ )
+ list(APPEND SOURCE "${ResourceFiles}")
+ endif()
+
+
+else ()
+
+ # Generate the Bindings if they don't exist:
+ if (NOT EXISTS "${PROJECT_SOURCE_DIR}/Bindings/Bindings.cpp")
+ message("Bindings.cpp not found, generating now")
+ set(tolua_executable ${PROJECT_SOURCE_DIR}/Bindings/tolua++.exe)
+ execute_process(
+ COMMAND ${tolua_executable} -L virtual_method_hooks.lua -o Bindings.cpp -H Bindings.h AllToLua.pkg
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/Bindings
+ )
+ endif()
+
+ # Add all subfolders as solution-folders:
+ list(APPEND FOLDERS "Resources")
+ list(APPEND FOLDERS "Bindings")
+ function(includefolder PATH)
+ FILE(GLOB FOLDER_FILES
+ "${PATH}/*.cpp"
+ "${PATH}/*.h"
+ "${PATH}/*.rc"
+ )
+ source_group("${PATH}" FILES ${FOLDER_FILES})
+ endfunction(includefolder)
+
+ foreach(folder ${FOLDERS})
+ includefolder(${folder})
+ endforeach(folder)
+
+ file(GLOB_RECURSE SOURCE
+ "*.cpp"
+ "*.h"
+ )
-foreach(folder ${FOLDERS})
- includefolder(${folder})
-endforeach(folder)
+ include_directories("${PROJECT_SOURCE_DIR}")
-file(GLOB_RECURSE SOURCE
- "*.cpp"
- "*.h"
-)
+ source_group("" FILES ${SOURCE})
-include_directories("${PROJECT_SOURCE_DIR}")
+ # Precompiled headers (1st part)
+ SET_SOURCE_FILES_PROPERTIES(
+ Globals.cpp PROPERTIES COMPILE_FLAGS "/Yc\"Globals.h\""
+ )
+ # CMake cannot "remove" the precompiled header flags, so we use a dummy precompiled header compatible with just this one file:
+ SET_SOURCE_FILES_PROPERTIES(
+ Bindings/Bindings.cpp PROPERTIES COMPILE_FLAGS "/Yc\"string.h\" /Fp\"$(IntDir)/Bindings.pch\""
+ )
+ SET_SOURCE_FILES_PROPERTIES(
+ "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)
-source_group("" FILES ${SOURCE})
+add_executable(${EXECUTABLE} ${SOURCE})
-#precompiledheaders
-file(GLOB_RECURSE HEADERS
- "*.h"
+# Output the executable into the $/MCServer folder, so that it has access to external resources:
+set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/MCServer)
+SET_TARGET_PROPERTIES(${EXECUTABLE} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_SOURCE_DIR}/MCServer
+ RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_SOURCE_DIR}/MCServer
+ RUNTIME_OUTPUT_DIRECTORY_DEBUGPROFILE ${CMAKE_SOURCE_DIR}/MCServer
+ RUNTIME_OUTPUT_DIRECTORY_RELEASEPROFILE ${CMAKE_SOURCE_DIR}/MCServer
)
-foreach(header ${HEADERS})
- set(FLAGS "/Yu ${header} /Yc ${header}")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS}")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS}")
- set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS}")
- set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS}")
- set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS}")
- set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS}")
- set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_PROFILE} ${FLAGS}")
- set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_PROFILE} ${FLAGS}")
-endforeach()
-endif()
+# Make the debug executable have a "_debug" suffix
+SET_TARGET_PROPERTIES(${EXECUTABLE} PROPERTIES DEBUG_POSTFIX "_debug")
-list(REMOVE_ITEM SOURCE "${PROJECT_SOURCE_DIR}/StackWalker.cpp" "${PROJECT_SOURCE_DIR}/LeakFinder.cpp")
+# Make the profiled executables have a "_profile" postfix
+SET_TARGET_PROPERTIES(${EXECUTABLE} PROPERTIES DEBUGPROFILE_POSTFIX "_debug_profile")
+SET_TARGET_PROPERTIES(${EXECUTABLE} PROPERTIES RELEASEPROFILE_POSTFIX "_profile")
-if(UNIX)
- set(EXECUTABLE ../MCServer/MCServer)
-else()
- set(EXECUTABLE MCServer)
-endif()
-add_executable(${EXECUTABLE} ${SOURCE})
+# Precompiled headers (2nd part)
+if (MSVC)
+ SET_TARGET_PROPERTIES(
+ ${EXECUTABLE} PROPERTIES COMPILE_FLAGS "/Yu\"Globals.h\""
+ OBJECT_DEPENDS "$(IntDir)/$(TargetName.pch)"
+ )
+endif ()
+
-if(NOT WIN32)
-target_link_libraries(${EXECUTABLE} OSSupport HTTPServer Bindings Items Blocks)
-target_link_libraries(${EXECUTABLE} Protocol Generating WorldStorage)
-target_link_libraries(${EXECUTABLE} Mobs Entities Simulator UI BlockEntities)
+if (NOT MSVC)
+ target_link_libraries(${EXECUTABLE} OSSupport HTTPServer Bindings Items Blocks)
+ target_link_libraries(${EXECUTABLE} Protocol Generating WorldStorage)
+ target_link_libraries(${EXECUTABLE} Mobs Entities Simulator UI BlockEntities)
+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)
+target_link_libraries(${EXECUTABLE} md5 luaexpat iniFile jsoncpp polarssl zlib lua sqlite)
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index c446db9a6..a72464ec3 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -527,9 +527,11 @@ void cChunk::SpawnMobs(cMobSpawner& a_MobSpawner)
// MG TODO : check that "Level" really means Y
+ /*
NIBBLETYPE SkyLight = 0;
NIBBLETYPE BlockLight = 0;
+ */
if (IsLightValid())
{
@@ -1296,6 +1298,7 @@ void cChunk::CreateBlockEntities(void)
switch (BlockType)
{
case E_BLOCK_CHEST:
+ case E_BLOCK_COMMAND_BLOCK:
case E_BLOCK_DISPENSER:
case E_BLOCK_DROPPER:
case E_BLOCK_ENDER_CHEST:
@@ -1423,6 +1426,7 @@ void cChunk::SetBlock(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType,
switch (a_BlockType)
{
case E_BLOCK_CHEST:
+ case E_BLOCK_COMMAND_BLOCK:
case E_BLOCK_DISPENSER:
case E_BLOCK_DROPPER:
case E_BLOCK_ENDER_CHEST:
@@ -1741,7 +1745,14 @@ bool cChunk::AddClient(cClientHandle* a_Client)
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr )
{
- LOGD("cChunk: Entity #%d (%s) at [%i, %i, %i] spawning for player \"%s\"", (*itr)->GetUniqueID(), (*itr)->GetClass(), m_PosX, m_PosY, m_PosZ, a_Client->GetUsername().c_str());
+ /*
+ // DEBUG:
+ LOGD("cChunk: Entity #%d (%s) at [%i, %i, %i] spawning for player \"%s\"",
+ (*itr)->GetUniqueID(), (*itr)->GetClass(),
+ m_PosX, m_PosY, m_PosZ,
+ a_Client->GetUsername().c_str()
+ );
+ */
(*itr)->SpawnOn(*a_Client);
}
return true;
@@ -1766,7 +1777,13 @@ void cChunk::RemoveClient( cClientHandle* a_Client )
{
for (cEntityList::iterator itr = m_Entities.begin(); itr != m_Entities.end(); ++itr )
{
- LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"", m_PosX, m_PosZ, (*itr)->GetUniqueID(), a_Client->GetUsername().c_str() );
+ /*
+ // DEBUG:
+ LOGD("chunk [%i, %i] destroying entity #%i for player \"%s\"",
+ m_PosX, m_PosZ,
+ (*itr)->GetUniqueID(), a_Client->GetUsername().c_str()
+ );
+ */
a_Client->SendDestroyEntity(*(*itr));
}
}
@@ -2253,6 +2270,38 @@ bool cChunk::DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBl
+bool cChunk::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback)
+{
+ // The blockentity list is locked by the parent chunkmap's CS
+ for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2)
+ {
+ ++itr2;
+ if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ))
+ {
+ continue;
+ }
+ if ((*itr)->GetBlockType() != E_BLOCK_COMMAND_BLOCK)
+ {
+ // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out
+ return false;
+ }
+
+ // The correct block entity is here,
+ if (a_Callback.Item((cCommandBlockEntity *)*itr))
+ {
+ return false;
+ }
+ return true;
+ } // for itr - m_BlockEntitites[]
+
+ // Not found:
+ return false;
+}
+
+
+
+
+
bool cChunk::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
{
// The blockentity list is locked by the parent chunkmap's CS
@@ -2324,8 +2373,8 @@ BLOCKTYPE cChunk::GetBlock(int a_BlockIdx) const
void cChunk::GetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta)
{
int Idx = cChunkDef::MakeIndexNoCheck(a_RelX, a_RelY, a_RelZ);
- a_BlockType = cChunkDef::GetBlock (m_BlockTypes, a_RelX, a_RelY, a_RelZ);
- a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, a_RelX, a_RelY, a_RelZ);
+ a_BlockType = cChunkDef::GetBlock (m_BlockTypes, Idx);
+ a_BlockMeta = cChunkDef::GetNibble(m_BlockMeta, Idx);
}
@@ -2898,10 +2947,6 @@ NIBBLETYPE cChunk::GetTimeAlteredLight(NIBBLETYPE a_Skylight) const
-#if !C_CHUNK_USE_INLINE
-# include "cChunk.inl.h"
-#endif
-
diff --git a/src/Chunk.h b/src/Chunk.h
index 05a96d419..4ac38c46c 100644
--- a/src/Chunk.h
+++ b/src/Chunk.h
@@ -12,19 +12,6 @@
-#define C_CHUNK_USE_INLINE 1
-
-// Do not touch
-#if C_CHUNK_USE_INLINE
- #define __C_CHUNK_INLINE__ inline
-#else
- #define __C_CHUNK_INLINE__
-#endif
-
-
-
-
-
namespace Json
{
class Value;
@@ -53,12 +40,13 @@ class cFluidSimulatorData;
class cMobCensus;
class cMobSpawner;
-typedef std::list<cClientHandle *> cClientHandleList;
-typedef cItemCallback<cEntity> cEntityCallback;
-typedef cItemCallback<cChestEntity> cChestCallback;
-typedef cItemCallback<cDispenserEntity> cDispenserCallback;
-typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
-typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
+typedef std::list<cClientHandle *> cClientHandleList;
+typedef cItemCallback<cEntity> cEntityCallback;
+typedef cItemCallback<cChestEntity> cChestCallback;
+typedef cItemCallback<cDispenserEntity> cDispenserCallback;
+typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
+typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
+typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
@@ -250,6 +238,9 @@ public:
/// Calls the callback for the noteblock at the specified coords; returns false if there's no noteblock at those coords or callback returns true, returns true if found
bool DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBlockCallback & a_Callback);
+ /// Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found
+ bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback);
+
/// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible
@@ -436,8 +427,6 @@ private:
void RemoveBlockEntity(cBlockEntity * a_BlockEntity);
void AddBlockEntity (cBlockEntity * a_BlockEntity);
- void SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff);
-
/// Creates a block entity for each block that needs a block entity and doesn't have one in the list
void CreateBlockEntities(void);
@@ -482,11 +471,3 @@ typedef std::list<cChunkPtr> cChunkPtrList;
-
-#if C_CHUNK_USE_INLINE
- #include "Chunk.inl.h"
-#endif
-
-
-
-
diff --git a/src/Chunk.inl.h b/src/Chunk.inl.h
deleted file mode 100644
index fb9c4dad1..000000000
--- a/src/Chunk.inl.h
+++ /dev/null
@@ -1,34 +0,0 @@
-
-#ifndef __C_CHUNK_INL_H__
-#define __C_CHUNK_INL_H__
-
-#ifndef MAX
-# define MAX(a,b) (((a)>(b))?(a):(b))
-#endif
-
-
-
-
-
-__C_CHUNK_INLINE__
-void cChunk::SpreadLightOfBlock(NIBBLETYPE * a_LightBuffer, int a_X, int a_Y, int a_Z, char a_Falloff)
-{
- unsigned char CurrentLight = cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z );
- cChunkDef::SetNibble( a_LightBuffer, a_X-1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X-1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) );
- cChunkDef::SetNibble( a_LightBuffer, a_X+1, a_Y, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X+1, a_Y, a_Z ), MAX(0,CurrentLight-a_Falloff) ) );
- cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y-1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y-1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) );
- cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y+1, a_Z, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y+1, a_Z ), MAX(0,CurrentLight-a_Falloff) ) );
- cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z-1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z-1 ), MAX(0,CurrentLight-a_Falloff) ) );
- cChunkDef::SetNibble( a_LightBuffer, a_X, a_Y, a_Z+1, MAX(cChunkDef::GetNibble( a_LightBuffer, a_X, a_Y, a_Z+1 ), MAX(0,CurrentLight-a_Falloff) ) );
- MarkDirty();
-}
-
-
-
-
-
-#endif
-
-
-
-
diff --git a/src/ChunkDef.h b/src/ChunkDef.h
index 8c37e7907..d1288994c 100644
--- a/src/ChunkDef.h
+++ b/src/ChunkDef.h
@@ -10,6 +10,7 @@
#pragma once
#include "Vector3i.h"
+#include "BiomeDef.h"
@@ -57,97 +58,6 @@ typedef unsigned char HEIGHTTYPE;
-
-
-// tolua_begin
-/** Biome IDs
-The first batch corresponds to the clientside biomes, used by MineCraft.
-BiomeIDs over 255 are used by MCServer internally and are translated to MC biomes before sending them to client
-*/
-enum EMCSBiome
-{
- biOcean = 0,
- biPlains = 1,
- biDesert = 2,
- biExtremeHills = 3,
- biForest = 4,
- biTaiga = 5,
- biSwampland = 6,
- biRiver = 7,
- biHell = 8, // same as Nether
- biNether = 8,
- biSky = 9, // same as biEnd
- biEnd = 9,
- biFrozenOcean = 10,
- biFrozenRiver = 11,
- biIcePlains = 12,
- biTundra = 12, // same as Ice Plains
- biIceMountains = 13,
- biMushroomIsland = 14,
- biMushroomShore = 15,
- biBeach = 16,
- biDesertHills = 17,
- biForestHills = 18,
- biTaigaHills = 19,
- biExtremeHillsEdge = 20,
- biJungle = 21,
- biJungleHills = 22,
-
- // Release 1.7 biomes:
- biJungleEdge = 23,
- biDeepOcean = 24,
- biStoneBeach = 25,
- biColdBeach = 26,
- biBirchForest = 27,
- biBirchForestHills = 28,
- biRoofedForest = 29,
- biColdTaiga = 30,
- biColdTaigaHills = 31,
- biMegaTaiga = 32,
- biMegaTaigaHills = 33,
- biExtremeHillsPlus = 34,
- biSavanna = 35,
- biSavannaPlateau = 36,
- biMesa = 37,
- biMesaPlateauF = 38,
- biMesaPlateau = 39,
-
- // Automatically capture the maximum consecutive biome value into biMaxBiome:
- biNumBiomes, // True number of biomes, since they are zero-based
- biMaxBiome = biNumBiomes - 1, // The maximum biome value
-
- // Add this number to the biomes to get the variant
- biVariant = 128,
-
- // Release 1.7 biome variants:
- biSunflowerPlains = 129,
- biDesertM = 130,
- biExtremeHillsM = 131,
- biFlowerForest = 132,
- biTaigaM = 133,
- biSwamplandM = 134,
- biIcePlainsSpikes = 140,
- biJungleM = 149,
- biJungleEdgeM = 151,
- biBirchForestM = 155,
- biBirchForestHillsM = 156,
- biRoofedForestM = 157,
- biColdTaigaM = 158,
- biMegaSpruceTaiga = 160,
- biMegaSpruceTaigaHills = 161,
- biExtremeHillsPlusM = 162,
- biSavannaM = 163,
- biSavannaPlateauM = 164,
- biMesaBryce = 165,
- biMesaPlateauFM = 166,
- biMesaPlateauM = 167,
-} ;
-
-// tolua_end
-
-
-
-
/// Constants used throughout the code, useful typedefs and utility functions
class cChunkDef
{
@@ -182,6 +92,7 @@ public:
/// Converts absolute block coords into relative (chunk + block) coords:
inline static void AbsoluteToRelative(/* in-out */ int & a_X, int & a_Y, int & a_Z, /* out */ int & a_ChunkX, int & a_ChunkZ )
{
+ UNUSED(a_Y);
BlockToChunk(a_X, a_Z, a_ChunkX, a_ChunkZ);
a_X = a_X - a_ChunkX * Width;
@@ -609,8 +520,10 @@ public:
// Illegal in C++03: typedef std::list< cCoordWithData<X> > cCoordWithDataList<X>;
typedef cCoordWithData<int> cCoordWithInt;
+typedef cCoordWithData<BLOCKTYPE> cCoordWithBlock;
typedef std::list<cCoordWithInt> cCoordWithIntList;
typedef std::vector<cCoordWithInt> cCoordWithIntVector;
+typedef std::vector<cCoordWithBlock> cCoordWithBlockVector;
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 86fbceff7..5797eb453 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -991,7 +991,7 @@ bool cChunkMap::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ)
int cChunkMap::GetHeight(int a_BlockX, int a_BlockZ)
{
- while (true)
+ for (;;)
{
cCSLock Lock(m_CSLayers);
int ChunkX, ChunkZ, BlockY = 0;
@@ -1968,6 +1968,23 @@ bool cChunkMap::DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNot
+bool cChunkMap::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback)
+{
+ int ChunkX, ChunkZ;
+ int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ;
+ cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ);
+ cCSLock Lock(m_CSLayers);
+ cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ);
+ if ((Chunk == NULL) && !Chunk->IsValid())
+ {
+ return false;
+ }
+ return Chunk->DoWithCommandBlockAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
bool cChunkMap::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
{
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index e688d1f93..e9d8ee30b 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -23,6 +23,7 @@ class cDropperEntity;
class cDropSpenserEntity;
class cFurnaceEntity;
class cNoteEntity;
+class cCommandBlockEntity;
class cPawn;
class cPickup;
class cChunkDataSerializer;
@@ -32,15 +33,16 @@ class cMobSpawner;
typedef std::list<cClientHandle *> cClientHandleList;
typedef cChunk * cChunkPtr;
-typedef cItemCallback<cEntity> cEntityCallback;
-typedef cItemCallback<cBlockEntity> cBlockEntityCallback;
-typedef cItemCallback<cChestEntity> cChestCallback;
-typedef cItemCallback<cDispenserEntity> cDispenserCallback;
-typedef cItemCallback<cDropperEntity> cDropperCallback;
-typedef cItemCallback<cDropSpenserEntity> cDropSpenserCallback;
-typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
-typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
-typedef cItemCallback<cChunk> cChunkCallback;
+typedef cItemCallback<cEntity> cEntityCallback;
+typedef cItemCallback<cBlockEntity> cBlockEntityCallback;
+typedef cItemCallback<cChestEntity> cChestCallback;
+typedef cItemCallback<cDispenserEntity> cDispenserCallback;
+typedef cItemCallback<cDropperEntity> cDropperCallback;
+typedef cItemCallback<cDropSpenserEntity> cDropSpenserCallback;
+typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
+typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
+typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
+typedef cItemCallback<cChunk> cChunkCallback;
@@ -236,6 +238,9 @@ public:
/// Calls the callback for the noteblock at the specified coords; returns false if there's no noteblock at those coords or callback returns true, returns true if found
bool DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBlockCallback & a_Callback); // Lua-accessible
+ /// Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found
+ bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Lua-accessible
+
/// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Lua-accessible
diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp
index fe3ee9b42..2425adf18 100644
--- a/src/ChunkSender.cpp
+++ b/src/ChunkSender.cpp
@@ -264,7 +264,7 @@ void cChunkSender::BlockEntity(cBlockEntity * a_Entity)
-void cChunkSender::Entity(cEntity * a_Entity)
+void cChunkSender::Entity(cEntity *)
{
// Nothing needed yet, perhaps in the future when we save entities into chunks we'd like to send them upon load, too ;)
}
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 99df47bfb..ad3f15adc 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -8,6 +8,7 @@
#include "Entities/Player.h"
#include "Inventory.h"
#include "BlockEntities/ChestEntity.h"
+#include "BlockEntities/CommandBlockEntity.h"
#include "BlockEntities/SignEntity.h"
#include "UI/Window.h"
#include "Item.h"
@@ -119,9 +120,6 @@ cClientHandle::~cClientHandle()
LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), this);
- // Remove from cSocketThreads, we're not to be called anymore:
- cRoot::Get()->GetServer()->ClientDestroying(this);
-
{
cCSLock Lock(m_CSChunkLists);
m_LoadedChunks.clear();
@@ -150,17 +148,7 @@ 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);
- }
-
- // Queue the socket to close as soon as it sends all outgoing data:
- cRoot::Get()->GetServer()->QueueClientClose(this);
+ // Close the socket as soon as it sends all outgoing data:
cRoot::Get()->GetServer()->RemoveClient(this);
delete m_Protocol;
@@ -266,6 +254,12 @@ void cClientHandle::Authenticate(void)
m_Player->Initialize(World);
m_State = csAuthenticated;
+ // Query player team
+ m_Player->UpdateTeam();
+
+ // Send scoreboard data
+ World->GetScoreBoard().SendTo(*this);
+
cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player);
}
@@ -543,6 +537,88 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ,
+void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString & a_Message)
+{
+ if (a_Channel == "MC|AdvCdm") // Command block, set text, Client -> Server
+ {
+ const char* Data = a_Message.c_str();
+ HandleCommandBlockMessage(Data, a_Message.size());
+ return;
+ }
+ else if (a_Channel == "MC|Brand") // Client <-> Server branding exchange
+ {
+ // We are custom,
+ // We are awesome,
+ // We are MCServer.
+ SendPluginMessage("MC|Brand", "MCServer");
+ return;
+ }
+
+ cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message);
+}
+
+
+
+
+
+void cClientHandle::HandleCommandBlockMessage(const char* a_Data, unsigned int a_Length)
+{
+ if (a_Length < 14)
+ {
+ SendChat(Printf("%s[INFO]%s Failure setting command block command; bad request", cChatColor::Red.c_str(), cChatColor::White.c_str()));
+ LOGD("Malformed MC|AdvCdm packet.");
+ return;
+ }
+
+ cByteBuffer Buffer(a_Length);
+ Buffer.Write(a_Data, a_Length);
+
+ int BlockX, BlockY, BlockZ;
+
+ AString Command;
+
+ char Mode;
+
+ Buffer.ReadChar(Mode);
+
+ switch (Mode)
+ {
+ case 0x00:
+ {
+ Buffer.ReadBEInt(BlockX);
+ Buffer.ReadBEInt(BlockY);
+ Buffer.ReadBEInt(BlockZ);
+
+ Buffer.ReadVarUTF8String(Command);
+ break;
+ }
+
+ default:
+ {
+ SendChat(Printf("%s[INFO]%s Failure setting command block command; unhandled mode", cChatColor::Red.c_str(), cChatColor::White.c_str()));
+ LOGD("Unhandled MC|AdvCdm packet mode.");
+ return;
+ }
+ }
+
+ cWorld * World = m_Player->GetWorld();
+
+ 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()));
+ }
+ else
+ {
+ SendChat(Printf("%s[INFO]%s Command blocks are not enabled on this server", cChatColor::Yellow.c_str(), cChatColor::White.c_str()));
+ }
+}
+
+
+
+
+
void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status)
{
LOGD("HandleLeftClick: {%i, %i, %i}; Face: %i; Stat: %i",
@@ -572,7 +648,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;
}
@@ -620,6 +697,17 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, ch
return;
}
+ case DIG_STATUS_DROP_STACK:
+ {
+ if (PlgMgr->CallHookPlayerTossingItem(*m_Player))
+ {
+ // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
+ return;
+ }
+ m_Player->TossEquippedItem(64); // Toss entire slot - if there aren't enough items, the maximum will be ejected
+ return;
+ }
+
default:
{
ASSERT(!"Unhandled DIG_STATUS");
@@ -1017,7 +1105,7 @@ void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsO
return;
}
- m_Player->SetRotation (a_Rotation);
+ m_Player->SetYaw (a_Rotation);
m_Player->SetHeadYaw (a_Rotation);
m_Player->SetPitch (a_Pitch);
m_Player->SetTouchGround(a_IsOnGround);
@@ -1049,7 +1137,7 @@ void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_
m_Player->SetStance (a_Stance);
m_Player->SetTouchGround(a_IsOnGround);
m_Player->SetHeadYaw (a_Rotation);
- m_Player->SetRotation (a_Rotation);
+ m_Player->SetYaw (a_Rotation);
m_Player->SetPitch (a_Pitch);
}
@@ -1409,6 +1497,7 @@ void cClientHandle::SendData(const char * a_Data, int a_Size)
void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket)
{
+ UNUSED(a_World);
ASSERT(m_Player != NULL);
if (a_SendRespawnPacket)
@@ -1968,6 +2057,15 @@ void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player)
+void cClientHandle::SendPluginMessage(const AString & a_Channel, const AString & a_Message)
+{
+ m_Protocol->SendPluginMessage(a_Channel, a_Message);
+}
+
+
+
+
+
void cClientHandle::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
{
m_Protocol->SendRemoveEntityEffect(a_Entity, a_EffectID);
@@ -2004,6 +2102,33 @@ void cClientHandle::SendExperienceOrb(const cExpOrb & a_ExpOrb)
+void cClientHandle::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
+{
+ m_Protocol->SendScoreboardObjective(a_Name, a_DisplayName, a_Mode);
+}
+
+
+
+
+
+void cClientHandle::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
+{
+ m_Protocol->SendScoreUpdate(a_Objective, a_Player, a_Score, a_Mode);
+}
+
+
+
+
+
+void cClientHandle::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display)
+{
+ m_Protocol->SendDisplayObjective(a_Objective, a_Display);
+}
+
+
+
+
+
void cClientHandle::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
{
m_Protocol->SendSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch);
@@ -2102,6 +2227,14 @@ void cClientHandle::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+void cClientHandle::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
+{
+ m_Protocol->SendUpdateBlockEntity(a_BlockEntity);
+}
+
+
+
+
void cClientHandle::SendUpdateSign(
int a_BlockX, int a_BlockY, int a_BlockZ,
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 26d5e74b7..e1f326543 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -16,6 +16,7 @@
#include "OSSupport/SocketThreads.h"
#include "ChunkDef.h"
#include "ByteBuffer.h"
+#include "Scoreboard.h"
@@ -120,10 +121,14 @@ public:
void SendPlayerMoveLook (void);
void SendPlayerPosition (void);
void SendPlayerSpawn (const cPlayer & a_Player);
+ void SendPluginMessage (const AString & a_Channel, const AString & a_Message); // Exported in ManualBindings.cpp
void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID);
void SendRespawn (void);
void SendExperience (void);
void SendExperienceOrb (const cExpOrb & a_ExpOrb);
+ void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode);
+ void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode);
+ void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display);
void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch); // a_Src coords are Block * 8
void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data);
void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock);
@@ -135,6 +140,7 @@ public:
void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ);
void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay);
void SendUnloadChunk (int a_ChunkX, int a_ChunkZ);
+ void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity);
void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4);
void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ );
void SendWeather (eWeather a_Weather);
@@ -170,7 +176,13 @@ public:
void HandleCreativeInventory(short a_SlotNum, const cItem & a_HeldItem);
void HandleDisconnect (const AString & a_Reason);
void HandleEntityAction (int a_EntityID, char a_ActionID);
+
+ /** Called when the protocol handshake has been received (for protocol versions that support it;
+ otherwise the first instant when a username is received).
+ Returns true if the player is to be let in, false if they were disconnected
+ */
bool HandleHandshake (const AString & a_Username);
+
void HandleKeepAlive (int a_KeepAliveID);
void HandleLeftClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
void HandlePing (void);
@@ -178,6 +190,7 @@ public:
void HandlePlayerLook (float a_Rotation, float a_Pitch, bool a_IsOnGround);
void HandlePlayerMoveLook (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround); // While m_bPositionConfirmed (normal gameplay)
void HandlePlayerPos (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround);
+ void HandlePluginMessage (const AString & a_Channel, const AString & a_Message);
void HandleRespawn (void);
void HandleRightClick (int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_HeldItem);
void HandleSlotSelected (short a_SlotNum);
@@ -316,6 +329,9 @@ private:
/// Handles the DIG_FINISHED dig packet:
void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta);
+
+ /// Handles the "MC|AdvCdm" plugin message
+ void HandleCommandBlockMessage(const char* a_Data, unsigned int a_Length);
// cSocketThreads::cCallback overrides:
virtual void DataReceived (const char * a_Data, int a_Size) override; // Data is received from the client
diff --git a/src/CommandOutput.h b/src/CommandOutput.h
index bdf675238..3763d625f 100644
--- a/src/CommandOutput.h
+++ b/src/CommandOutput.h
@@ -38,6 +38,7 @@ class cNullCommandOutputCallback :
virtual void Out(const AString & a_Text) override
{
// Do nothing
+ UNUSED(a_Text);
}
} ;
diff --git a/src/Crypto.cpp b/src/Crypto.cpp
new file mode 100644
index 000000000..7a06d7fa3
--- /dev/null
+++ b/src/Crypto.cpp
@@ -0,0 +1,509 @@
+
+// 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_EncryptedMaxLength < m_Rsa.len)
+ {
+ LOGD("%s: Invalid a_PlainLength: got %u, exp at least %u",
+ __FUNCTION__, (unsigned)a_PlainLength, (unsigned)(m_Rsa.len)
+ );
+ ASSERT(!"Invalid a_PlainLength!");
+ return -1;
+ }
+ int res = rsa_pkcs1_encrypt(
+ &m_Rsa, ctr_drbg_random, &m_Ctr_drbg, RSA_PRIVATE,
+ a_PlainLength, a_PlainData, a_EncryptedData
+ );
+ if (res != 0)
+ {
+ return -1;
+ }
+ return (int)m_Rsa.len;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cPublicKey:
+
+cPublicKey::cPublicKey(const AString & a_PublicKeyDER)
+{
+ pk_init(&m_Pk);
+ if (pk_parse_public_key(&m_Pk, (const Byte *)a_PublicKeyDER.data(), a_PublicKeyDER.size()) != 0)
+ {
+ ASSERT(!"Cannot parse PubKey");
+ return;
+ }
+ InitRnd();
+}
+
+
+
+
+
+cPublicKey::~cPublicKey()
+{
+ pk_free(&m_Pk);
+}
+
+
+
+
+
+int cPublicKey::Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength)
+{
+ size_t DecryptedLen = a_DecryptedMaxLength;
+ int res = pk_decrypt(&m_Pk,
+ a_EncryptedData, a_EncryptedLength,
+ a_DecryptedData, &DecryptedLen, a_DecryptedMaxLength,
+ ctr_drbg_random, &m_Ctr_drbg
+ );
+ if (res != 0)
+ {
+ return res;
+ }
+ return (int)DecryptedLen;
+}
+
+
+
+
+
+int cPublicKey::Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength)
+{
+ size_t EncryptedLength = a_EncryptedMaxLength;
+ int res = pk_encrypt(&m_Pk,
+ a_PlainData, a_PlainLength, a_EncryptedData, &EncryptedLength, a_EncryptedMaxLength,
+ ctr_drbg_random, &m_Ctr_drbg
+ );
+ if (res != 0)
+ {
+ return res;
+ }
+ return (int)EncryptedLength;
+}
+
+
+
+
+
+void cPublicKey::InitRnd(void)
+{
+ entropy_init(&m_Entropy);
+ const unsigned char pers[] = "rsa_genkey";
+ ctr_drbg_init(&m_Ctr_drbg, entropy_func, &m_Entropy, pers, sizeof(pers) - 1);
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cAESCFBDecryptor:
+
+cAESCFBDecryptor::cAESCFBDecryptor(void) :
+ m_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..d68f7ec24
--- /dev/null
+++ b/src/Crypto.h
@@ -0,0 +1,200 @@
+
+// Crypto.h
+
+// Declares classes that wrap the cryptographic code library
+
+
+
+
+
+#pragma once
+
+#include "polarssl/rsa.h"
+#include "polarssl/aes.h"
+#include "polarssl/entropy.h"
+#include "polarssl/ctr_drbg.h"
+#include "polarssl/sha1.h"
+#include "polarssl/pk.h"
+
+
+
+
+
+/** Encapsulates an RSA private key used in PKI cryptography */
+class cRSAPrivateKey
+{
+public:
+ /** Creates a new empty object, the key is not assigned */
+ cRSAPrivateKey(void);
+
+ /** Deep-copies the key from a_Other */
+ cRSAPrivateKey(const cRSAPrivateKey & a_Other);
+
+ ~cRSAPrivateKey();
+
+ /** Generates a new key within this object, with the specified size in bits.
+ Returns true on success, false on failure. */
+ bool Generate(unsigned a_KeySizeBits = 1024);
+
+ /** Returns the public key part encoded in ASN1 DER encoding */
+ AString GetPubKeyDER(void);
+
+ /** Decrypts the data using RSAES-PKCS#1 algorithm.
+ Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
+ Returns the number of bytes decrypted, or negative number for error. */
+ int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength);
+
+ /** Encrypts the data using RSAES-PKCS#1 algorithm.
+ Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
+ Returns the number of bytes decrypted, or negative number for error. */
+ int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength);
+
+protected:
+ rsa_context m_Rsa;
+ entropy_context m_Entropy;
+ ctr_drbg_context m_Ctr_drbg;
+
+ /** Initializes the m_Entropy and m_Ctr_drbg contexts
+ Common part of this object's construction, called from all constructors. */
+ void InitRnd(void);
+} ;
+
+
+
+
+
+class cPublicKey
+{
+public:
+ cPublicKey(const AString & a_PublicKeyDER);
+ ~cPublicKey();
+
+ /** Decrypts the data using the stored public key
+ Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
+ Returns the number of bytes decrypted, or negative number for error. */
+ int Decrypt(const Byte * a_EncryptedData, size_t a_EncryptedLength, Byte * a_DecryptedData, size_t a_DecryptedMaxLength);
+
+ /** Encrypts the data using the stored public key
+ Both a_EncryptedData and a_DecryptedData must be at least <KeySizeBytes> bytes large.
+ Returns the number of bytes decrypted, or negative number for error. */
+ int Encrypt(const Byte * a_PlainData, size_t a_PlainLength, Byte * a_EncryptedData, size_t a_EncryptedMaxLength);
+
+protected:
+ pk_context m_Pk;
+ entropy_context m_Entropy;
+ ctr_drbg_context m_Ctr_drbg;
+
+ /** Initializes the m_Entropy and m_Ctr_drbg contexts
+ Common part of this object's construction, called from all constructors. */
+ void InitRnd(void);
+} ;
+
+
+
+
+
+/** Decrypts data using the AES / CFB (128) algorithm */
+class cAESCFBDecryptor
+{
+public:
+ Byte test;
+
+ cAESCFBDecryptor(void);
+ ~cAESCFBDecryptor();
+
+ /** Initializes the decryptor with the specified Key / IV */
+ void Init(const Byte a_Key[16], const Byte a_IV[16]);
+
+ /** Decrypts a_Length bytes of the encrypted data; produces a_Length output bytes */
+ void ProcessData(Byte * a_DecryptedOut, const Byte * a_EncryptedIn, size_t a_Length);
+
+ /** Returns true if the object has been initialized with the Key / IV */
+ bool IsValid(void) const { return m_IsValid; }
+
+protected:
+ aes_context m_Aes;
+
+ /** The InitialVector, used by the CFB mode decryption */
+ Byte m_IV[16];
+
+ /** Current offset in the m_IV, used by the CFB mode decryption */
+ size_t m_IVOffset;
+
+ /** Indicates whether the object has been initialized with the Key / IV */
+ bool m_IsValid;
+} ;
+
+
+
+
+
+/** Encrypts data using the AES / CFB (128) algorithm */
+class cAESCFBEncryptor
+{
+public:
+ 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 534802d55..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;
@@ -41,8 +39,8 @@ extern bool g_BlockRequiresSpecialTool[256];
/// Is this block solid (player cannot walk through)?
extern bool g_BlockIsSolid[256];
-/// Can torches be placed on this block?
-extern bool g_BlockIsTorchPlaceable[256];
+/// Does this block fully occupy it's voxel - is it a 'full' block?
+extern bool g_BlockFullyOccupiesVoxel[256];
/// Experience Orb setup
enum
@@ -85,6 +83,7 @@ enum
DIG_STATUS_STARTED = 0,
DIG_STATUS_CANCELLED = 1,
DIG_STATUS_FINISHED = 2,
+ DIG_STATUS_DROP_STACK= 3,
DIG_STATUS_DROP_HELD = 4,
DIG_STATUS_SHOOT_EAT = 5,
} ;
@@ -281,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)
{
@@ -563,34 +580,6 @@ namespace ItemCategory
}
}
-
-
-
-
-/// Returns true if the biome has no downfall - deserts and savannas
-inline bool IsBiomeNoDownfall(EMCSBiome a_Biome)
-{
- switch (a_Biome)
- {
- case biDesert:
- case biDesertHills:
- case biDesertM:
- case biSavanna:
- case biSavannaM:
- case biSavannaPlateau:
- case biSavannaPlateauM:
- case biNether:
- case biEnd:
- {
- return true;
- }
- default:
- {
- return false;
- }
- }
-}
-
// tolua_end
diff --git a/src/Enchantments.cpp b/src/Enchantments.cpp
index 95ca201f0..1d8188e96 100644
--- a/src/Enchantments.cpp
+++ b/src/Enchantments.cpp
@@ -213,87 +213,8 @@ bool cEnchantments::operator !=(const cEnchantments & a_Other) const
-void cEnchantments::WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const
-{
- // Write the enchantments into the specified NBT writer
- // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments")
-
- a_Writer.BeginList(a_ListTagName, TAG_Compound);
- for (cMap::const_iterator itr = m_Enchantments.begin(), end = m_Enchantments.end(); itr != end; ++itr)
- {
- a_Writer.BeginCompound("");
- a_Writer.AddShort("id", itr->first);
- a_Writer.AddShort("lvl", itr->second);
- a_Writer.EndCompound();
- } // for itr - m_Enchantments[]
- a_Writer.EndList();
-}
-
-void cEnchantments::ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx)
-{
- // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments)
-
- // Verify that the tag is a list:
- if (a_NBT.GetType(a_EnchListTagIdx) != TAG_List)
- {
- LOGWARNING("%s: Invalid EnchListTag type: exp %d, got %d. Enchantments not parsed",
- __FUNCTION__, TAG_List, a_NBT.GetType(a_EnchListTagIdx)
- );
- ASSERT(!"Bad EnchListTag type");
- return;
- }
-
- // Verify that the list is of Compounds:
- if (a_NBT.GetChildrenType(a_EnchListTagIdx) != TAG_Compound)
- {
- LOGWARNING("%s: Invalid NBT list children type: exp %d, got %d. Enchantments not parsed",
- __FUNCTION__, TAG_Compound, a_NBT.GetChildrenType(a_EnchListTagIdx)
- );
- ASSERT(!"Bad EnchListTag children type");
- return;
- }
-
- Clear();
-
- // Iterate over all the compound children, parse an enchantment from each:
- for (int tag = a_NBT.GetFirstChild(a_EnchListTagIdx); tag >= 0; tag = a_NBT.GetNextSibling(tag))
- {
- // tag is the compound inside the "ench" list tag
- ASSERT(a_NBT.GetType(tag) == TAG_Compound);
-
- // Search for the id and lvl tags' values:
- int id = -1, lvl = -1;
- for (int ch = a_NBT.GetFirstChild(tag); ch >= 0; ch = a_NBT.GetNextSibling(ch))
- {
- if (a_NBT.GetType(ch) != TAG_Short)
- {
- continue;
- }
- if (a_NBT.GetName(ch) == "id")
- {
- id = a_NBT.GetShort(ch);
- }
- else if (a_NBT.GetName(ch) == "lvl")
- {
- lvl = a_NBT.GetShort(ch);
- }
- } // for ch - children of the compound tag
-
- if ((id == -1) || (lvl <= 0))
- {
- // Failed to parse either the id or the lvl, skip this compound
- continue;
- }
-
- // Store the enchantment:
- m_Enchantments[id] = lvl;
- } // for tag - children of the ench list tag
-}
-
-
-
diff --git a/src/Enchantments.h b/src/Enchantments.h
index 7581b87b5..e984df92e 100644
--- a/src/Enchantments.h
+++ b/src/Enchantments.h
@@ -8,6 +8,7 @@
#pragma once
+#include "WorldStorage/EnchantmentSerializer.h"
@@ -20,7 +21,6 @@ class cParsedNBT;
-// tolua_begin
/** Class that stores item enchantments or stored-enchantments
The enchantments may be serialized to a stringspec and read back from such stringspec.
@@ -29,10 +29,12 @@ mapping each enchantment's id onto its level. ID may be either a number or the e
Level value of 0 means no such enchantment, and it will not be stored in the m_Enchantments.
Serialization will never put zero-level enchantments into the stringspec and will always use numeric IDs.
*/
+// tolua_begin
class cEnchantments
{
public:
/// Individual enchantment IDs, corresponding to their NBT IDs ( http://www.minecraftwiki.net/wiki/Data_Values#Enchantment_IDs )
+
enum
{
enchProtection = 0,
@@ -97,10 +99,10 @@ public:
bool operator !=(const cEnchantments & a_Other) const;
/// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments")
- void WriteToNBTCompound(cFastNBTWriter & a_Writer, const AString & a_ListTagName) const;
+ friend void EnchantmentSerializer::WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName);
/// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments)
- void ParseFromNBT(const cParsedNBT & a_NBT, int a_EnchListTagIdx);
+ friend void EnchantmentSerializer::ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx);
protected:
/// Maps enchantment ID -> enchantment level
diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp
index 8a74c9da4..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,15 +71,19 @@ 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:
LOGD("Deleting entity %d at pos {%.2f, %.2f, %.2f} ~ [%d, %d]; ptr %p",
m_UniqueID,
m_Pos.x, m_Pos.y, m_Pos.z,
(int)(m_Pos.x / cChunkDef::Width), (int)(m_Pos.z / cChunkDef::Width),
this
);
-
+ */
+
if (m_AttachedTo != NULL)
{
Detach();
@@ -96,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;
}
@@ -138,9 +138,13 @@ bool cEntity::Initialize(cWorld * a_World)
return false;
}
+ /*
+ // DEBUG:
LOGD("Initializing entity #%d (%s) at {%.02f, %.02f, %.02f}",
m_UniqueID, GetClass(), m_Pos.x, m_Pos.y, m_Pos.z
);
+ */
+
m_IsInitialized = true;
m_World = a_World;
m_World->AddEntity(this);
@@ -256,16 +260,16 @@ void cEntity::TakeDamage(eDamageType a_DamageType, cEntity * a_Attacker, int a_R
-void cEntity::SetRotationFromSpeed(void)
+void cEntity::SetYawFromSpeed(void)
{
const double EPS = 0.0000001;
if ((abs(m_Speed.x) < EPS) && (abs(m_Speed.z) < EPS))
{
// atan2() may overflow or is undefined, pick any number
- SetRotation(0);
+ SetYaw(0);
return;
}
- SetRotation(atan2(m_Speed.x, m_Speed.z) * 180 / PI);
+ SetYaw(atan2(m_Speed.x, m_Speed.z) * 180 / PI);
}
@@ -321,7 +325,10 @@ void cEntity::DoTakeDamage(TakeDamageInfo & a_TDI)
m_Health = 0;
}
- AddSpeed(a_TDI.Knockback * 2);
+ if (IsMob() || IsPlayer()) // Knockback for only players and mobs
+ {
+ AddSpeed(a_TDI.Knockback * 2);
+ }
m_World->BroadcastEntityStatus(*this, ENTITY_STATUS_HURT);
@@ -524,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();
+ }
}
@@ -614,9 +631,12 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
m_bOnGround = true;
+ /*
+ // DEBUG:
LOGD("Entity #%d (%s) is inside a block at {%d, %d, %d}",
m_UniqueID, GetClass(), BlockX, BlockY, BlockZ
);
+ */
}
if (!m_bOnGround)
@@ -626,11 +646,6 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
fallspeed = m_Gravity * a_Dt / 3; // Fall 3x slower in water.
}
- else if (IsBlockRail(BlockBelow) && IsMinecart()) // Rails aren't solid, except for Minecarts
- {
- fallspeed = 0;
- m_bOnGround = true;
- }
else if (BlockIn == E_BLOCK_COBWEB)
{
NextSpeed.y *= 0.05; // Reduce overall falling speed
@@ -645,41 +660,18 @@ void cEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
}
else
{
- if (IsMinecart())
+ // Friction
+ if (NextSpeed.SqrLength() > 0.0004f)
{
- if (!IsBlockRail(BlockBelow))
+ NextSpeed.x *= 0.7f / (1 + a_Dt);
+ if (fabs(NextSpeed.x) < 0.05)
{
- // Friction if minecart is off track, otherwise, Minecart.cpp handles this
- if (NextSpeed.SqrLength() > 0.0004f)
- {
- NextSpeed.x *= 0.7f / (1 + a_Dt);
- if (fabs(NextSpeed.x) < 0.05)
- {
- NextSpeed.x = 0;
- }
- NextSpeed.z *= 0.7f / (1 + a_Dt);
- if (fabs(NextSpeed.z) < 0.05)
- {
- NextSpeed.z = 0;
- }
- }
+ NextSpeed.x = 0;
}
- }
- else
- {
- // Friction for non-minecarts
- if (NextSpeed.SqrLength() > 0.0004f)
+ NextSpeed.z *= 0.7f / (1 + a_Dt);
+ if (fabs(NextSpeed.z) < 0.05)
{
- NextSpeed.x *= 0.7f / (1 + a_Dt);
- if (fabs(NextSpeed.x) < 0.05)
- {
- NextSpeed.x = 0;
- }
- NextSpeed.z *= 0.7f / (1 + a_Dt);
- if (fabs(NextSpeed.z) < 0.05)
- {
- NextSpeed.z = 0;
- }
+ NextSpeed.z = 0;
}
}
}
@@ -927,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)
{
@@ -1104,9 +1177,11 @@ void cEntity::AttachTo(cEntity * a_AttachTo)
// Already attached to that entity, nothing to do here
return;
}
-
- // Detach from any previous entity:
- Detach();
+ if (m_AttachedTo != NULL)
+ {
+ // Detach from any previous entity:
+ Detach();
+ }
// Attach to the new entity:
m_AttachedTo = a_AttachTo;
@@ -1442,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 9cb36eb14..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);
@@ -154,8 +161,7 @@ public:
double GetPosX (void) const { return m_Pos.x; }
double GetPosY (void) const { return m_Pos.y; }
double GetPosZ (void) const { return m_Pos.z; }
- const Vector3d & GetRot (void) const { return m_Rot; }
- double GetRotation (void) const { return m_Rot.x; } // OBSOLETE, use GetYaw() instead
+ const Vector3d & GetRot (void) const { return m_Rot; } // OBSOLETE, use individual GetYaw(), GetPitch, GetRoll() components
double GetYaw (void) const { return m_Rot.x; }
double GetPitch (void) const { return m_Rot.y; }
double GetRoll (void) const { return m_Rot.z; }
@@ -177,8 +183,7 @@ public:
void SetPosZ (double a_PosZ);
void SetPosition(double a_PosX, double a_PosY, double a_PosZ);
void SetPosition(const Vector3d & a_Pos) { SetPosition(a_Pos.x, a_Pos.y, a_Pos.z); }
- void SetRot (const Vector3f & a_Rot);
- void SetRotation(double a_Rotation) { SetYaw(a_Rotation); } // OBSOLETE, use SetYaw() instead
+ void SetRot (const Vector3f & a_Rot); // OBSOLETE, use individual SetYaw(), SetPitch(), SetRoll() components
void SetYaw (double a_Yaw);
void SetPitch (double a_Pitch);
void SetRoll (double a_Roll);
@@ -223,7 +228,7 @@ public:
void SetGravity(float a_Gravity) { m_Gravity = a_Gravity; }
/// Sets the rotation to match the speed vector (entity goes "face-forward")
- void SetRotationFromSpeed(void);
+ void SetYawFromSpeed(void);
/// Sets the pitch to match the speed vector (entity gies "face-forward")
void SetPitchFromSpeed(void);
@@ -327,7 +332,7 @@ public:
void AttachTo(cEntity * a_AttachTo);
/// Detaches from the currently attached entity, if any
- void Detach(void);
+ virtual void Detach(void);
/// Makes sure head yaw is not over the specified range.
void WrapHeadYaw();
@@ -346,15 +351,26 @@ 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
/// Called when the specified player right-clicks this entity
- virtual void OnRightClicked(cPlayer & a_Player) {};
+ virtual void OnRightClicked(cPlayer &) {};
/// Returns the list of drops for this pawn when it is killed. May check a_Killer for special handling (sword of looting etc.). Called from KilledBy().
- virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) {}
+ virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL)
+ {
+ UNUSED(a_Drops);
+ UNUSED(a_Killer);
+ }
protected:
static cCriticalSection m_CSCount;
@@ -371,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;
@@ -413,18 +426,27 @@ 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 (MAX 360°)
+ // Measured in degrees, [-180, +180)
double m_HeadYaw;
+
// Measured in meter/second (m/s)
Vector3d m_Speed;
- // Measured in degrees (MAX 360°)
+
+ // Measured in degrees, [-180, +180)
Vector3d m_Rot;
/// Position of the entity's XZ center and Y bottom
diff --git a/src/Entities/Floater.h b/src/Entities/Floater.h
index 4bbe3f352..162b74e75 100644
--- a/src/Entities/Floater.h
+++ b/src/Entities/Floater.h
@@ -7,21 +7,25 @@
+// tolua_begin
class cFloater :
public cEntity
{
typedef cFloater super;
public:
-
+
+ //tolua_end
cFloater(double a_X, double a_Y, double a_Z, Vector3d a_Speed, int a_PlayerID, int a_CountDownTime);
virtual void SpawnOn(cClientHandle & a_Client) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
-
+
+ // tolua_begin
bool CanPickup(void) const { return m_CanPickupItem; }
int GetOwnerID(void) const { return m_PlayerID; }
int GetAttachedMobID(void) const { return m_AttachedMobID; }
+ // tolua_end
protected:
// Position
@@ -37,4 +41,4 @@ protected:
// Entity IDs
int m_PlayerID;
int m_AttachedMobID;
-} ; \ No newline at end of file
+} ; // tolua_export \ No newline at end of file
diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp
index f75e23d8b..a650927b1 100644
--- a/src/Entities/Minecart.cpp
+++ b/src/Entities/Minecart.cpp
@@ -2,6 +2,7 @@
// Minecart.cpp
// Implements the cMinecart class representing a minecart in the world
+// Handles physics when a minecart is on any type of rail (overrides simulator in Entity.cpp)
// Indiana Jones!
#include "Globals.h"
@@ -10,6 +11,74 @@
#include "../ClientHandle.h"
#include "../Chunk.h"
#include "Player.h"
+#include "../BoundingBox.h"
+
+#define MAX_SPEED 8
+#define MAX_SPEED_NEGATIVE -MAX_SPEED
+
+
+
+
+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;
+};
@@ -18,11 +87,15 @@
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),
m_Payload(a_Payload),
- m_LastDamage(0)
+ m_LastDamage(0),
+ m_DetectorRailPosition(0, 0, 0),
+ m_bIsOnDetectorRail(false)
{
SetMass(20.f);
SetMaxHealth(6);
SetHealth(6);
+ SetWidth(1);
+ SetHeight(0.9);
}
@@ -45,6 +118,7 @@ void cMinecart::SpawnOn(cClientHandle & a_ClientHandle)
}
}
a_ClientHandle.SendSpawnVehicle(*this, 10, SubType); // 10 = Minecarts, SubType = What type of Minecart
+ a_ClientHandle.SendEntityMetadata(*this);
}
@@ -53,6 +127,11 @@ void cMinecart::SpawnOn(cClientHandle & a_ClientHandle)
void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
{
+ if (IsDestroyed()) // Mainly to stop detector rails triggering again after minecart is dead
+ {
+ return;
+ }
+
int PosY = (int)floor(GetPosY());
if ((PosY <= 0) || (PosY >= cChunkDef::Height))
{
@@ -70,286 +149,753 @@ void cMinecart::HandlePhysics(float a_Dt, cChunk & a_Chunk)
// Inside an unloaded chunk, bail out all processing
return;
}
- BLOCKTYPE BelowType = Chunk->GetBlock(RelPosX, PosY - 1, RelPosZ);
- BLOCKTYPE InsideType = Chunk->GetBlock(RelPosX, PosY, RelPosZ);
- if (IsBlockRail(BelowType))
+ BLOCKTYPE InsideType;
+ NIBBLETYPE InsideMeta;
+ Chunk->GetBlockTypeMeta(RelPosX, PosY, RelPosZ, InsideType, InsideMeta);
+
+ if (!IsBlockRail(InsideType))
{
- HandleRailPhysics(a_Dt, *Chunk);
+ Chunk->GetBlockTypeMeta(RelPosX, PosY + 1, RelPosZ, InsideType, InsideMeta); // When an descending minecart hits a flat rail, it goes through the ground; check for this
+ if (IsBlockRail(InsideType)) AddPosY(1); // Push cart upwards
}
- else
+
+ bool WasDetectorRail = false;
+ if (IsBlockRail(InsideType))
{
- if (IsBlockRail(InsideType))
+ if (InsideType == E_BLOCK_RAIL)
{
- SetPosY(PosY + 1);
- HandleRailPhysics(a_Dt, *Chunk);
+ SnapToRail(InsideMeta);
}
else
{
- super::HandlePhysics(a_Dt, *Chunk);
- BroadcastMovementUpdate();
+ SnapToRail(InsideMeta & 0x07);
}
+
+ switch (InsideType)
+ {
+ case E_BLOCK_RAIL: HandleRailPhysics(InsideMeta, a_Dt); break;
+ case E_BLOCK_ACTIVATOR_RAIL: break;
+ case E_BLOCK_POWERED_RAIL: HandlePoweredRailPhysics(InsideMeta); break;
+ case E_BLOCK_DETECTOR_RAIL:
+ {
+ HandleDetectorRailPhysics(InsideMeta, a_Dt);
+ WasDetectorRail = true;
+ break;
+ }
+ default: VERIFY(!"Unhandled rail type despite checking if block was rail!"); break;
+ }
+
+ 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
+ }
+ else
+ {
+ // Not on rail, default physics
+ 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();
+}
-static const double MAX_SPEED = 8;
-static const double MAX_SPEED_NEGATIVE = (0 - MAX_SPEED);
-void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk)
+void cMinecart::HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
{
-
- super::HandlePhysics(a_Dt, a_Chunk); // Main physics handling
-
/*
NOTE: Please bear in mind that taking away from negatives make them even more negative,
adding to negatives make them positive, etc.
*/
- // Get block meta below the cart
- int RelPosX = (int)floor(GetPosX()) - a_Chunk.GetPosX() * cChunkDef::Width;
- int RelPosZ = (int)floor(GetPosZ()) - a_Chunk.GetPosZ() * cChunkDef::Width;
- NIBBLETYPE BelowMeta = a_Chunk.GetMeta(RelPosX, (int)floor(GetPosY() - 1), RelPosZ);
- double SpeedX = GetSpeedX(), SpeedY = GetSpeedY(), SpeedZ = GetSpeedZ(); // Get current speed
-
- switch (BelowMeta)
+ switch (a_RailMeta)
{
case E_META_RAIL_ZM_ZP: // NORTHSOUTH
{
- SetRotation(270);
- SpeedY = 0; // Don't move vertically as on ground
- SpeedX = 0; // Correct diagonal movement from curved rails
+ SetYaw(270);
+ SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0); // Don't move vertically as on ground
+ SetSpeedX(0); // Correct diagonal movement from curved rails
+
+ // Execute both the entity and block collision checks
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
- if (SpeedZ != 0) // Don't do anything if cart is stationary
+ if (GetSpeedZ() != 0) // Don't do anything if cart is stationary
{
- if (SpeedZ > 0)
+ if (GetSpeedZ() > 0)
{
// Going SOUTH, slow down
- SpeedZ = SpeedZ - 0.1;
+ AddSpeedZ(-0.1);
}
else
{
// Going NORTH, slow down
- SpeedZ = SpeedZ + 0.1;
+ AddSpeedZ(0.1);
}
}
break;
}
-
case E_META_RAIL_XM_XP: // EASTWEST
{
- SetRotation(180);
- SpeedY = 0;
- SpeedZ = 0;
+ SetYaw(180);
+ SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
+ SetSpeedZ(0);
+
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
- if (SpeedX != 0)
+ if (GetSpeedX() != 0)
{
- if (SpeedX > 0)
+ if (GetSpeedX() > 0)
{
- SpeedX = SpeedX - 0.1;
+ AddSpeedX(-0.1);
}
else
{
- SpeedX = SpeedX + 0.1;
+ AddSpeedX(0.1);
}
}
break;
}
-
case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH
{
- SetRotation(270);
- SetPosY(floor(GetPosY()) + 0.2); // It seems it doesn't work without levitation :/
- SpeedX = 0;
+ SetYaw(270);
+ SetSpeedX(0);
- if (SpeedZ >= 0)
+ if (GetSpeedZ() >= 0)
{
// SpeedZ POSITIVE, going SOUTH
- if (SpeedZ <= MAX_SPEED) // Speed limit
+ if (GetSpeedZ() <= MAX_SPEED) // Speed limit
{
- SpeedZ = SpeedZ + 0.5; // Speed up
- SpeedY = (0 - SpeedZ); // Downward movement is negative (0 minus positive numbers is negative)
- }
- else
- {
- SpeedZ = MAX_SPEED; // Enforce speed limit
- SpeedY = (0 - SpeedZ);
+ AddSpeedZ(0.5); // Speed up
+ SetSpeedY(-GetSpeedZ()); // Downward movement is negative (0 minus positive numbers is negative)
}
}
else
{
// SpeedZ NEGATIVE, going NORTH
- SpeedZ = SpeedZ + 0.4; // Slow down
- SpeedY = (0 - SpeedZ); // Upward movement is positive (0 minus negative number is positive number)
+ AddSpeedZ(1); // Slow down
+ SetSpeedY(-GetSpeedZ()); // Upward movement is positive (0 minus negative number is positive number)
}
break;
}
-
case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH
{
- SetRotation(270);
- SetPosY(floor(GetPosY()) + 0.2);
- SpeedX = 0;
+ SetYaw(270);
+ SetSpeedX(0);
- if (SpeedZ > 0)
+ if (GetSpeedZ() > 0)
{
// SpeedZ POSITIVE, going SOUTH
- SpeedZ = SpeedZ - 0.4; // Slow down
- SpeedY = SpeedZ; // Upward movement positive
+ AddSpeedZ(-1); // Slow down
+ SetSpeedY(GetSpeedZ()); // Upward movement positive
}
else
{
- if (SpeedZ >= MAX_SPEED_NEGATIVE) // Speed limit
+ if (GetSpeedZ() >= MAX_SPEED_NEGATIVE) // Speed limit
{
// SpeedZ NEGATIVE, going NORTH
- SpeedZ = SpeedZ - 0.5; // Speed up
- SpeedY = SpeedZ; // Downward movement negative
+ AddSpeedZ(-0.5); // Speed up
+ SetSpeedY(GetSpeedZ()); // Downward movement negative
}
- else
+ }
+ break;
+ }
+ case E_META_RAIL_ASCEND_XM: // ASCEND EAST
+ {
+ SetYaw(180);
+ SetSpeedZ(0);
+
+ if (GetSpeedX() >= 0)
+ {
+ if (GetSpeedX() <= MAX_SPEED)
{
- SpeedZ = MAX_SPEED_NEGATIVE; // Enforce speed limit
- SpeedY = SpeedZ;
+ AddSpeedX(0.5);
+ SetSpeedY(-GetSpeedX());
}
}
+ else
+ {
+ AddSpeedX(1);
+ SetSpeedY(-GetSpeedX());
+ }
break;
}
+ case E_META_RAIL_ASCEND_XP: // ASCEND WEST
+ {
+ SetYaw(180);
+ SetSpeedZ(0);
- case E_META_RAIL_ASCEND_XM: // ASCEND EAST
+ if (GetSpeedX() > 0)
+ {
+ AddSpeedX(-1);
+ SetSpeedY(GetSpeedX());
+ }
+ else
+ {
+ if (GetSpeedX() >= MAX_SPEED_NEGATIVE)
+ {
+ AddSpeedX(-0.5);
+ SetSpeedY(GetSpeedX());
+ }
+ }
+ break;
+ }
+ case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST
+ {
+ SetYaw(315); // Set correct rotation server side
+ SetPosY(floor(GetPosY()) + 0.55); // Levitate dat cart
+ SetSpeedY(0);
+
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
+
+ // SnapToRail handles turning
+
+ break;
+ }
+ case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST
+ {
+ SetYaw(225);
+ SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
+
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
+
+ break;
+ }
+ case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST
+ {
+ SetYaw(135);
+ SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
+
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
+
+ break;
+ }
+ case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST
+ {
+ SetYaw(45);
+ SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
+
+ TestBlockCollision(a_RailMeta);
+ TestEntityCollision(a_RailMeta);
+
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unhandled rail meta!"); // Dun dun DUN!
+ break;
+ }
+ }
+}
+
+
+
+
+void cMinecart::HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta)
+{
+ // Initialise to 'slow down' values
+ int AccelDecelSpeed = -2;
+ int AccelDecelNegSpeed = 2;
+
+ if ((a_RailMeta & 0x8) == 0x8)
+ {
+ // Rail powered - set variables to 'speed up' values
+ AccelDecelSpeed = 1;
+ AccelDecelNegSpeed = -1;
+ }
+
+ switch (a_RailMeta & 0x07)
+ {
+ case E_META_RAIL_ZM_ZP: // NORTHSOUTH
{
- SetRotation(180);
- SetPosY(floor(GetPosY()) + 0.2);
- SpeedZ = 0;
+ SetYaw(270);
+ SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
+ SetSpeedX(0);
- if (SpeedX >= 0)
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
+
+ if (GetSpeedZ() != 0)
{
- if (SpeedX <= MAX_SPEED)
+ if (GetSpeedZ() > 0)
{
- SpeedX = SpeedX + 0.5;
- SpeedY = (0 - SpeedX);
+ AddSpeedZ(AccelDecelSpeed);
}
else
{
- SpeedX = MAX_SPEED;
- SpeedY = (0 - SpeedX);
+ AddSpeedZ(AccelDecelNegSpeed);
}
}
- else
+ break;
+ }
+ case E_META_RAIL_XM_XP: // EASTWEST
+ {
+ SetYaw(180);
+ SetPosY(floor(GetPosY()) + 0.55);
+ SetSpeedY(0);
+ SetSpeedZ(0);
+
+ bool BlckCol = TestBlockCollision(a_RailMeta), EntCol = TestEntityCollision(a_RailMeta);
+ if (EntCol || BlckCol) return;
+
+ if (GetSpeedX() != 0)
{
- SpeedX = SpeedX + 0.4;
- SpeedY = (0 - SpeedX);
+ if (GetSpeedX() > 0)
+ {
+ AddSpeedX(AccelDecelSpeed);
+ }
+ else
+ {
+ AddSpeedX(AccelDecelNegSpeed);
+ }
}
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
{
- SetRotation(180);
- SetPosY(floor(GetPosY()) + 0.2);
- SpeedZ = 0;
+ SetYaw(180);
+ SetSpeedZ(0);
- if (SpeedX > 0)
+ if (GetSpeedX() > 0)
{
- SpeedX = SpeedX - 0.4;
- SpeedY = SpeedX;
+ AddSpeedX(AccelDecelSpeed);
+ SetSpeedY(GetSpeedX());
}
else
{
- if (SpeedX >= MAX_SPEED_NEGATIVE)
+ if (GetSpeedX() >= MAX_SPEED_NEGATIVE)
{
- SpeedX = SpeedX - 0.5;
- SpeedY = SpeedX;
+ AddSpeedX(AccelDecelNegSpeed);
+ SetSpeedY(GetSpeedX());
}
- else
+ }
+ break;
+ }
+ case E_META_RAIL_ASCEND_ZM: // ASCEND NORTH
+ {
+ SetYaw(270);
+ SetSpeedX(0);
+
+ if (GetSpeedZ() >= 0)
+ {
+ if (GetSpeedZ() <= MAX_SPEED)
{
- SpeedX = MAX_SPEED_NEGATIVE;
- SpeedY = SpeedX;
+ AddSpeedZ(AccelDecelSpeed);
+ SetSpeedY(-GetSpeedZ());
}
}
+ else
+ {
+ AddSpeedZ(AccelDecelNegSpeed);
+ SetSpeedY(-GetSpeedZ());
+ }
break;
}
-
- case E_META_RAIL_CURVED_ZM_XM: // Ends pointing NORTH and WEST
+ case E_META_RAIL_ASCEND_ZP: // ASCEND SOUTH
{
- SetRotation(315); // Set correct rotation server side
- SetPosY(floor(GetPosY()) + 0.2); // Levitate dat cart
+ SetYaw(270);
+ SetSpeedX(0);
- if (SpeedZ > 0) // Cart moving south
+ if (GetSpeedZ() > 0)
{
- SpeedX = (0 - SpeedZ); // Diagonally move southwest (which will make cart hit a southwest rail)
+ AddSpeedZ(AccelDecelSpeed);
+ SetSpeedY(GetSpeedZ());
}
- else if (SpeedX > 0) // Cart moving east
+ else
{
- SpeedZ = (0 - SpeedX); // Diagonally move northeast
+ if (GetSpeedZ() >= MAX_SPEED_NEGATIVE)
+ {
+ AddSpeedZ(AccelDecelNegSpeed);
+ SetSpeedY(GetSpeedZ());
+ }
}
break;
}
+ default: ASSERT(!"Unhandled powered rail metadata!"); break;
+ }
+}
+
+
+
+
+
+void cMinecart::HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
+{
+ m_World->SetBlockMeta(m_DetectorRailPosition, a_RailMeta | 0x08);
+
+ // No special handling
+ HandleRailPhysics(a_RailMeta & 0x07, a_Dt);
+}
+
- case E_META_RAIL_CURVED_ZM_XP: // Curved NORTH EAST
- {
- SetRotation(225);
- SetPosY(floor(GetPosY()) + 0.2);
- if (SpeedZ > 0)
+
+void cMinecart::HandleActivatorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt)
+{
+ HandleRailPhysics(a_RailMeta & 0x07, a_Dt);
+}
+
+
+
+
+
+void cMinecart::SnapToRail(NIBBLETYPE a_RailMeta)
+{
+ switch (a_RailMeta)
+ {
+ case E_META_RAIL_ASCEND_XM:
+ case E_META_RAIL_ASCEND_XP:
+ case E_META_RAIL_XM_XP:
+ {
+ SetSpeedZ(0);
+ SetPosZ(floor(GetPosZ()) + 0.5);
+ break;
+ }
+ case E_META_RAIL_ASCEND_ZM:
+ case E_META_RAIL_ASCEND_ZP:
+ case E_META_RAIL_ZM_ZP:
+ {
+ SetSpeedX(0);
+ 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)
{
- SpeedX = SpeedZ;
+ if (GetSpeedZ() > 0)
+ {
+ SetSpeedX(-GetSpeedZ() * 0.7);
+ }
+
+ SetSpeedZ(0);
+ SetPosZ(floor(GetPosZ()) + 0.5);
}
- else if (SpeedX < 0)
+ else if (GetPosX() > floor(GetPosX()) + 0.5)
{
- SpeedZ = SpeedX;
+ 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);
+ }
- case E_META_RAIL_CURVED_ZP_XM: // Curved SOUTH WEST
+ 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:
{
- SetRotation(135);
- SetPosY(floor(GetPosY()) + 0.2);
+ 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);
+ }
- if (SpeedZ < 0)
+ SetSpeedX(0);
+ SetPosX(floor(GetPosX()) + 0.5);
+ }
+ SetSpeedY(0);
+ break;
+ }
+ case E_META_RAIL_CURVED_ZP_XP:
+ {
+ if (GetPosZ() < floor(GetPosZ()) + 0.5)
{
- SpeedX = SpeedZ;
+ if (GetSpeedZ() < 0)
+ {
+ SetSpeedX(-GetSpeedZ() * 0.7);
+ }
+
+ SetSpeedZ(0);
+ SetPosZ(floor(GetPosZ()) + 0.5);
}
- else if (SpeedX > 0)
+ else if (GetPosX() < floor(GetPosX()) + 0.5)
{
- SpeedZ = SpeedX;
+ if (GetSpeedX() < 0)
+ {
+ SetSpeedZ(-GetSpeedX() * 0.7);
+ }
+
+ SetSpeedX(0);
+ SetPosX(floor(GetPosX()) + 0.5);
}
+ SetSpeedY(0);
break;
}
+ default: break;
+ }
+}
+
+
+
- case E_META_RAIL_CURVED_ZP_XP: // Curved SOUTH EAST
- {
- SetRotation(45);
- SetPosY(floor(GetPosY()) + 0.2);
- if (SpeedZ < 0)
+bool cMinecart::TestBlockCollision(NIBBLETYPE a_RailMeta)
+{
+ switch (a_RailMeta)
+ {
+ case E_META_RAIL_ZM_ZP:
+ {
+ if (GetSpeedZ() > 0)
{
- SpeedX = (0 - SpeedZ);
+ BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)ceil(GetPosZ()));
+ if (!IsBlockRail(Block) && g_BlockIsSolid[Block])
+ {
+ // We could try to detect a block in front based purely on coordinates, but xoft made a bounding box system - why not use? :P
+ cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()), (int)floor(GetPosY()), (int)ceil(GetPosZ())), 0.5, 1);
+ cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight());
+
+ if (bbBlock.DoesIntersect(bbMinecart))
+ {
+ SetSpeed(0, 0, 0);
+ SetPosZ(floor(GetPosZ()) + 0.4);
+ return true;
+ }
+ }
}
- else if (SpeedX < 0)
+ else if (GetSpeedZ() < 0)
{
- SpeedZ = (0 - SpeedX);
+ BLOCKTYPE Block = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) - 1);
+ if (!IsBlockRail(Block) && g_BlockIsSolid[Block])
+ {
+ cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) - 1), 0.5, 1);
+ cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ() - 1), GetWidth() / 2, GetHeight());
+
+ if (bbBlock.DoesIntersect(bbMinecart))
+ {
+ SetSpeed(0, 0, 0);
+ SetPosZ(floor(GetPosZ()) + 0.65);
+ return true;
+ }
+ }
}
break;
}
-
- default:
+ case E_META_RAIL_XM_XP:
{
- ASSERT(!"Unhandled rail meta!"); // Dun dun DUN!
+ if (GetSpeedX() > 0)
+ {
+ BLOCKTYPE Block = m_World->GetBlock((int)ceil(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()));
+ if (!IsBlockRail(Block) && g_BlockIsSolid[Block])
+ {
+ cBoundingBox bbBlock(Vector3d((int)ceil(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ())), 0.5, 1);
+ cBoundingBox bbMinecart(Vector3d(GetPosX(), floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight());
+
+ if (bbBlock.DoesIntersect(bbMinecart))
+ {
+ SetSpeed(0, 0, 0);
+ SetPosX(floor(GetPosX()) + 0.4);
+ return true;
+ }
+ }
+ }
+ 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])
+ {
+ cBoundingBox bbBlock(Vector3d((int)floor(GetPosX()) - 1, (int)floor(GetPosY()), (int)floor(GetPosZ())), 0.5, 1);
+ cBoundingBox bbMinecart(Vector3d(GetPosX() - 1, floor(GetPosY()), GetPosZ()), GetWidth() / 2, GetHeight());
+
+ if (bbBlock.DoesIntersect(bbMinecart))
+ {
+ SetSpeed(0, 0, 0);
+ SetPosX(floor(GetPosX()) + 0.65);
+ return true;
+ }
+ }
+ }
+ break;
+ }
+ 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:
+ {
+ BLOCKTYPE BlockXM = m_World->GetBlock((int)floor(GetPosX()) - 1, (int)floor(GetPosY()), (int)floor(GetPosZ()));
+ BLOCKTYPE BlockXP = m_World->GetBlock((int)floor(GetPosX()) + 1, (int)floor(GetPosY()), (int)floor(GetPosZ()));
+ BLOCKTYPE BlockZM = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) + 1);
+ BLOCKTYPE BlockZP = m_World->GetBlock((int)floor(GetPosX()), (int)floor(GetPosY()), (int)floor(GetPosZ()) + 1);
+ if (
+ (!IsBlockRail(BlockXM) && g_BlockIsSolid[BlockXM]) ||
+ (!IsBlockRail(BlockXP) && g_BlockIsSolid[BlockXP]) ||
+ (!IsBlockRail(BlockZM) && g_BlockIsSolid[BlockZM]) ||
+ (!IsBlockRail(BlockZP) && g_BlockIsSolid[BlockZP])
+ )
+ {
+ SetSpeed(0, 0, 0);
+ SetPosition((int)floor(GetPosX()) + 0.5, GetPosY(), (int)floor(GetPosZ()) + 0.5);
+ return true;
+ }
break;
}
+ default: break;
}
+ return false;
+}
- // Set speed to speed variables
- SetSpeedX(SpeedX);
- SetSpeedY(SpeedY);
- SetSpeedZ(SpeedZ);
- // Broadcast position to client
- BroadcastMovementUpdate();
+
+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;
}
@@ -358,6 +904,14 @@ void cMinecart::HandleRailPhysics(float a_Dt, cChunk & a_Chunk)
void cMinecart::DoTakeDamage(TakeDamageInfo & TDI)
{
+ if ((TDI.Attacker != NULL) && TDI.Attacker->IsPlayer() && ((cPlayer *)TDI.Attacker)->IsGameModeCreative())
+ {
+ Destroy();
+ TDI.FinalDamage = GetMaxHealth(); // Instant hit for creative
+ super::DoTakeDamage(TDI);
+ return; // No drops for creative
+ }
+
m_LastDamage = TDI.FinalDamage;
super::DoTakeDamage(TDI);
@@ -365,7 +919,7 @@ void cMinecart::DoTakeDamage(TakeDamageInfo & TDI)
if (GetHealth() <= 0)
{
- Destroy(true);
+ Destroy();
cItems Drops;
switch (m_Payload)
@@ -410,11 +964,25 @@ void cMinecart::DoTakeDamage(TakeDamageInfo & TDI)
+void cMinecart::Destroyed()
+{
+ if (m_bIsOnDetectorRail)
+ {
+ m_World->SetBlock(m_DetectorRailPosition.x, m_DetectorRailPosition.y, m_DetectorRailPosition.z, E_BLOCK_DETECTOR_RAIL, m_World->GetBlockMeta(m_DetectorRailPosition) & 0x07);
+ }
+}
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cEmptyMinecart:
+// cRideableMinecart:
-cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) :
- super(mpNone, a_X, a_Y, a_Z)
+cRideableMinecart::cRideableMinecart(double a_X, double a_Y, double a_Z, const cItem & a_Content, int a_Height) :
+ super(mpNone, a_X, a_Y, a_Z),
+ m_Content(a_Content),
+ m_Height(a_Height)
{
}
@@ -422,7 +990,7 @@ cEmptyMinecart::cEmptyMinecart(double a_X, double a_Y, double a_Z) :
-void cEmptyMinecart::OnRightClicked(cPlayer & a_Player)
+void cRideableMinecart::OnRightClicked(cPlayer & a_Player)
{
if (m_Attachee != NULL)
{
@@ -489,7 +1057,8 @@ void cMinecartWithChest::OnRightClicked(cPlayer & a_Player)
cMinecartWithFurnace::cMinecartWithFurnace(double a_X, double a_Y, double a_Z) :
super(mpFurnace, a_X, a_Y, a_Z),
- m_IsFueled(false)
+ m_IsFueled(false),
+ m_FueledTimeLeft(-1)
{
}
@@ -505,8 +1074,12 @@ void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player)
{
a_Player.GetInventory().RemoveOneEquippedItem();
}
-
+ if (!m_IsFueled) // We don't want to change the direction by right clicking it.
+ {
+ AddSpeed(a_Player.GetLookVector().x, 0, a_Player.GetLookVector().z);
+ }
m_IsFueled = true;
+ m_FueledTimeLeft = m_FueledTimeLeft + 600; // The minecart will be active 600 more ticks.
m_World->BroadcastEntityMetadata(*this);
}
}
@@ -515,6 +1088,32 @@ void cMinecartWithFurnace::OnRightClicked(cPlayer & a_Player)
+void cMinecartWithFurnace::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ if (m_IsFueled)
+ {
+ m_FueledTimeLeft--;
+ if (m_FueledTimeLeft < 0)
+ {
+ m_IsFueled = false;
+ m_World->BroadcastEntityMetadata(*this);
+ return;
+ }
+
+ if (GetSpeed().Length() > 6)
+ {
+ return;
+ }
+ AddSpeed(GetSpeed() / 4);
+ }
+}
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cMinecartWithTNT:
diff --git a/src/Entities/Minecart.h b/src/Entities/Minecart.h
index b1b48be4e..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
{
@@ -51,17 +37,44 @@ public:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override;
virtual void DoTakeDamage(TakeDamageInfo & TDI) override;
+ virtual void Destroyed() override;
int LastDamage(void) const { return m_LastDamage; }
- void HandleRailPhysics(float a_Dt, cChunk & a_Chunk);
ePayload GetPayload(void) const { return m_Payload; }
protected:
ePayload m_Payload;
+ int m_LastDamage;
+ Vector3i m_DetectorRailPosition;
+ bool m_bIsOnDetectorRail;
cMinecart(ePayload a_Payload, double a_X, double a_Y, double a_Z);
- int m_LastDamage;
+ /** Handles physics on normal rails
+ For each tick, slow down on flat rails, speed up or slow down on ascending/descending rails (depending on direction), and turn on curved rails
+ */
+ void HandleRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt);
+
+ /** Handles powered rail physics
+ Each tick, speed up or slow down cart, depending on metadata of rail (powered or not)
+ */
+ void HandlePoweredRailPhysics(NIBBLETYPE a_RailMeta);
+
+ /** Handles detector rail activation
+ Activates detector rails when a minecart is on them. Calls HandleRailPhysics() for physics simulations
+ */
+ void HandleDetectorRailPhysics(NIBBLETYPE a_RailMeta, float a_Dt);
+
+ /** 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 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);
} ;
@@ -69,18 +82,24 @@ protected:
-class cEmptyMinecart :
+class cRideableMinecart :
public cMinecart
{
typedef cMinecart super;
public:
- CLASS_PROTODEF(cEmptyMinecart);
+ CLASS_PROTODEF(cRideableMinecart);
- cEmptyMinecart(double a_X, double a_Y, double a_Z);
+ cRideableMinecart(double a_X, double a_Y, double a_Z, const cItem & a_Content, int a_Height);
+ const cItem & GetContent(void) const {return m_Content;}
+ int GetBlockHeight(void) const {return m_Height;}
// cEntity overrides:
virtual void OnRightClicked(cPlayer & a_Player) override;
+protected:
+
+ cItem m_Content;
+ int m_Height;
} ;
@@ -130,10 +149,18 @@ public:
// cEntity overrides:
virtual void OnRightClicked(cPlayer & a_Player) override;
- bool IsFueled (void) const { return m_IsFueled; }
+ virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
+
+ // Set functions.
+ void SetIsFueled(bool a_IsFueled, int a_FueledTimeLeft = -1) {m_IsFueled = a_IsFueled; m_FueledTimeLeft = a_FueledTimeLeft;}
+
+ // Get functions.
+ int GetFueledTimeLeft(void) const {return m_FueledTimeLeft; }
+ bool IsFueled (void) const {return m_IsFueled;}
private:
+ int m_FueledTimeLeft;
bool m_IsFueled;
} ;
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 67d5a47ef..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)
@@ -74,6 +69,7 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
, m_IsChargingBow(false)
, m_BowCharge(0)
, m_FloaterID(-1)
+ , m_Team(NULL)
{
LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d",
a_PlayerName.c_str(), a_Client->GetIPString().c_str(),
@@ -107,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);
}
@@ -196,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)
{
@@ -434,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;
@@ -459,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 */);
}
@@ -786,10 +788,24 @@ 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;
}
}
+
+ if ((a_TDI.Attacker != NULL) && (a_TDI.Attacker->IsPlayer()))
+ {
+ cPlayer* Attacker = (cPlayer*) a_TDI.Attacker;
+
+ if ((m_Team != NULL) && (m_Team == Attacker->m_Team))
+ {
+ if (!m_Team->AllowsFriendlyFire())
+ {
+ // Friendly fire is disabled
+ return;
+ }
+ }
+ }
super::DoTakeDamage(a_TDI);
@@ -836,6 +852,25 @@ void cPlayer::KilledBy(cEntity * a_Killer)
GetWorld()->BroadcastChat(Printf("%s[DEATH] %s%s was killed by a %s", cChatColor::Red.c_str(), cChatColor::White.c_str(), GetName().c_str(), KillerClass.c_str()));
}
+
+ class cIncrementCounterCB
+ : public cObjectiveCallback
+ {
+ AString m_Name;
+ public:
+ cIncrementCounterCB(const AString & a_Name) : m_Name(a_Name) {}
+
+ virtual bool Item(cObjective * a_Objective) override
+ {
+ a_Objective->AddScore(m_Name, 1);
+ return true;
+ }
+ } IncrementCounter (GetName());
+
+ cScoreboard & Scoreboard = m_World->GetScoreBoard();
+
+ // Update scoreboard objectives
+ Scoreboard.ForEachObjectiveWith(cObjective::E_TYPE_DEATH_COUNT, IncrementCounter);
}
@@ -908,8 +943,52 @@ bool cPlayer::IsGameModeSurvival(void) const
bool cPlayer::IsGameModeAdventure(void) const
{
- return (m_GameMode == gmCreative) || // Either the player is explicitly in Adventure
- ((m_GameMode == gmNotSet) && m_World->IsGameModeCreative()); // or they inherit from the world and the world is Adventure
+ return (m_GameMode == gmAdventure) || // Either the player is explicitly in Adventure
+ ((m_GameMode == gmNotSet) && m_World->IsGameModeAdventure()); // or they inherit from the world and the world is Adventure
+}
+
+
+
+
+
+void cPlayer::SetTeam(cTeam * a_Team)
+{
+ if (m_Team == a_Team)
+ {
+ return;
+ }
+
+ if (m_Team)
+ {
+ m_Team->RemovePlayer(GetName());
+ }
+
+ m_Team = a_Team;
+
+ if (m_Team)
+ {
+ m_Team->AddPlayer(GetName());
+ }
+}
+
+
+
+
+
+cTeam * cPlayer::UpdateTeam(void)
+{
+ if (m_World == NULL)
+ {
+ SetTeam(NULL);
+ }
+ else
+ {
+ cScoreboard & Scoreboard = m_World->GetScoreBoard();
+
+ SetTeam(Scoreboard.QueryPlayerTeam(GetName()));
+ }
+
+ return m_Team;
}
@@ -1342,56 +1421,70 @@ 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())
- {
- if (GetInventory().RemoveOneEquippedItem())
- {
- DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again
- 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(-GetRotation(), GetPitch(), vZ, vX, vY);
+ 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
}
@@ -1523,7 +1616,7 @@ bool cPlayer::LoadFromDisk()
Json::Value & JSON_PlayerRotation = root["rotation"];
if (JSON_PlayerRotation.size() == 3)
{
- SetRotation ((float)JSON_PlayerRotation[(unsigned int)0].asDouble());
+ SetYaw ((float)JSON_PlayerRotation[(unsigned int)0].asDouble());
SetPitch ((float)JSON_PlayerRotation[(unsigned int)1].asDouble());
SetRoll ((float)JSON_PlayerRotation[(unsigned int)2].asDouble());
}
@@ -1538,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"]);
@@ -1586,7 +1664,7 @@ bool cPlayer::SaveToDisk()
JSON_PlayerPosition.append(Json::Value(GetPosZ()));
Json::Value JSON_PlayerRotation;
- JSON_PlayerRotation.append(Json::Value(GetRotation()));
+ JSON_PlayerRotation.append(Json::Value(GetYaw()));
JSON_PlayerRotation.append(Json::Value(GetPitch()));
JSON_PlayerRotation.append(Json::Value(GetRoll()));
@@ -1675,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
@@ -1884,3 +1883,33 @@ void cPlayer::ApplyFoodExhaustionFromMovement()
+
+void cPlayer::Detach()
+{
+ super::Detach();
+ int PosX = (int)floor(GetPosX());
+ int PosY = (int)floor(GetPosY());
+ int PosZ = (int)floor(GetPosZ());
+
+ // Search for a position within an area to teleport player after detachment
+ // Position must be solid land, and occupied by a nonsolid block
+ // If nothing found, player remains where they are
+ for (int x = PosX - 2; x <= (PosX + 2); ++x)
+ {
+ for (int y = PosY; y <= (PosY + 3); ++y)
+ {
+ for (int z = PosZ - 2; z <= (PosZ + 2); ++z)
+ {
+ if (!g_BlockIsSolid[m_World->GetBlock(x, y, z)] && g_BlockIsSolid[m_World->GetBlock(x, y - 1, z)])
+ {
+ TeleportToCoords(x, y, z);
+ return;
+ }
+ }
+ }
+ }
+}
+
+
+
+
diff --git a/src/Entities/Player.h b/src/Entities/Player.h
index 66f1c07a7..50f7560d6 100644
--- a/src/Entities/Player.h
+++ b/src/Entities/Player.h
@@ -13,6 +13,7 @@
class cGroup;
class cWindow;
class cClientHandle;
+class cTeam;
@@ -30,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
@@ -45,7 +44,7 @@ public:
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
- virtual void HandlePhysics(float a_Dt, cChunk & a_Chunk) override { };
+ virtual void HandlePhysics(float a_Dt, cChunk &) override { UNUSED(a_Dt); };
/// Returns the curently equipped weapon; empty item if none
virtual cItem GetEquippedWeapon(void) const override { return m_Inventory.GetEquippedItem(); }
@@ -114,7 +113,7 @@ public:
double GetEyeHeight(void) const; // tolua_export
Vector3d GetEyePosition(void) const; // tolua_export
inline bool IsOnGround(void) const {return m_bTouchGround; } // tolua_export
- inline const double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc.
+ inline double GetStance(void) const { return GetPosY() + 1.62; } // tolua_export // TODO: Proper stance when crouching etc.
inline cInventory & GetInventory(void) { return m_Inventory; } // tolua_export
inline const cInventory & GetInventory(void) const { return m_Inventory; }
@@ -153,6 +152,15 @@ public:
AString GetIP(void) const { return m_IP; } // tolua_export
+ /// Returns the associated team, NULL if none
+ cTeam * GetTeam(void) { return m_Team; } // tolua_export
+
+ /// Sets the player team, NULL if none
+ void SetTeam(cTeam * a_Team);
+
+ /// Forces the player to query the scoreboard for his team
+ cTeam * UpdateTeam(void);
+
// tolua_end
void SetIP(const AString & a_IP);
@@ -214,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);
@@ -224,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); }
@@ -336,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
@@ -350,6 +357,8 @@ public:
virtual bool IsCrouched (void) const { return m_IsCrouched; }
virtual bool IsSprinting(void) const { return m_IsSprinting; }
virtual bool IsRclking (void) const { return IsEating(); }
+
+ virtual void Detach(void);
protected:
typedef std::map< std::string, bool > PermissionMap;
@@ -370,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:
@@ -412,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;
@@ -454,6 +457,8 @@ protected:
int m_FloaterID;
+ cTeam* m_Team;
+
void ResolvePermissions(void);
@@ -461,7 +466,7 @@ protected:
virtual void Destroyed(void);
- /// Filters out damage for creative mode
+ /// Filters out damage for creative mode/friendly fire
virtual void DoTakeDamage(TakeDamageInfo & TDI) override;
/// Called in each tick to handle food-related processing
@@ -469,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/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp
index 9e5069ba6..bffa790a3 100644
--- a/src/Entities/ProjectileEntity.cpp
+++ b/src/Entities/ProjectileEntity.cpp
@@ -206,7 +206,7 @@ cProjectileEntity::cProjectileEntity(eKind a_Kind, cEntity * a_Creator, const Ve
m_IsInGround(false)
{
SetSpeed(a_Speed);
- SetRotationFromSpeed();
+ SetYawFromSpeed();
SetPitchFromSpeed();
}
@@ -350,7 +350,7 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
NewSpeed.y += m_Gravity / 20;
NewSpeed *= TracerCallback.GetSlowdownCoeff();
SetSpeed(NewSpeed);
- SetRotationFromSpeed();
+ SetYawFromSpeed();
SetPitchFromSpeed();
// DEBUG:
@@ -358,7 +358,7 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
m_UniqueID,
GetPosX(), GetPosY(), GetPosZ(),
GetSpeedX(), GetSpeedY(), GetSpeedZ(),
- GetRotation(), GetPitch()
+ GetYaw(), GetPitch()
);
}
@@ -369,7 +369,7 @@ void cProjectileEntity::HandlePhysics(float a_Dt, cChunk & a_Chunk)
void cProjectileEntity::SpawnOn(cClientHandle & a_Client)
{
// Default spawning - use the projectile kind to spawn an object:
- a_Client.SendSpawnObject(*this, m_ProjectileKind, 12, ANGLE_TO_PROTO(GetRotation()), ANGLE_TO_PROTO(GetPitch()));
+ a_Client.SendSpawnObject(*this, m_ProjectileKind, 12, ANGLE_TO_PROTO(GetYaw()), ANGLE_TO_PROTO(GetPitch()));
a_Client.SendEntityMetadata(*this);
}
@@ -402,11 +402,11 @@ cArrowEntity::cArrowEntity(cEntity * a_Creator, double a_X, double a_Y, double a
{
SetSpeed(a_Speed);
SetMass(0.1);
- SetRotationFromSpeed();
+ SetYawFromSpeed();
SetPitchFromSpeed();
LOGD("Created arrow %d with speed {%.02f, %.02f, %.02f} and rot {%.02f, %.02f}",
m_UniqueID, GetSpeedX(), GetSpeedY(), GetSpeedZ(),
- GetRotation(), GetPitch()
+ GetYaw(), GetPitch()
);
}
@@ -679,7 +679,8 @@ super(pkExpBottle, a_Creator, a_X, a_Y, a_Z, 0.25, 0.25)
void cExpBottleEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
{
- // TODO: Spawn experience orbs
+ // Spawn an experience orb with a reward between 3 and 11.
+ m_World->SpawnExperienceOrb(GetPosX(), GetPosY(), GetPosZ(), 3 + m_World->GetTickRandomNumber(8));
Destroy();
}
@@ -710,8 +711,6 @@ void cFireworkEntity::OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace)
SetSpeed(0, 0, 0);
SetPosition(GetPosX(), GetPosY() - 0.5, GetPosZ());
- std::cout << a_HitPos.x << " " << a_HitPos.y << " " << a_HitPos.z << std::endl;
-
m_IsInGround = true;
BroadcastMovementUpdate();
diff --git a/src/Entities/ProjectileEntity.h b/src/Entities/ProjectileEntity.h
index 959e81ae5..4721409ee 100644
--- a/src/Entities/ProjectileEntity.h
+++ b/src/Entities/ProjectileEntity.h
@@ -52,7 +52,11 @@ public:
virtual void OnHitSolidBlock(const Vector3d & a_HitPos, char a_HitFace);
/// Called by the physics blocktracer when the entity hits another entity
- virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos) {}
+ virtual void OnHitEntity(cEntity & a_EntityHit, const Vector3d & a_HitPos)
+ {
+ UNUSED(a_EntityHit);
+ UNUSED(a_HitPos);
+ }
/// Called by Chunk when the projectile is eligible for player collection
virtual void CollectedBy(cPlayer * a_Dest);
diff --git a/src/ForEachChunkProvider.h b/src/ForEachChunkProvider.h
new file mode 100644
index 000000000..70cd2196a
--- /dev/null
+++ b/src/ForEachChunkProvider.h
@@ -0,0 +1,14 @@
+
+#pragma once
+
+class cChunkDataCallback;
+
+class cBlockArea;
+
+class cForEachChunkProvider
+{
+public:
+ virtual bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) = 0;
+
+ virtual bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) = 0;
+};
diff --git a/src/Generating/CMakeLists.txt b/src/Generating/CMakeLists.txt
index e1ba299fc..793f48890 100644
--- a/src/Generating/CMakeLists.txt
+++ b/src/Generating/CMakeLists.txt
@@ -9,3 +9,5 @@ file(GLOB SOURCE
)
add_library(Generating ${SOURCE})
+
+target_link_libraries(Generating OSSupport iniFile)
diff --git a/src/Generating/Caves.cpp b/src/Generating/Caves.cpp
index df45bb4c2..2571e6b77 100644
--- a/src/Generating/Caves.cpp
+++ b/src/Generating/Caves.cpp
@@ -285,7 +285,7 @@ bool cCaveTunnel::RefineDefPoints(const cCaveDefPoints & a_Src, cCaveDefPoints &
void cCaveTunnel::Smooth(void)
{
cCaveDefPoints Pts;
- while (true)
+ for (;;)
{
if (!RefineDefPoints(m_Points, Pts))
{
@@ -331,7 +331,7 @@ void cCaveTunnel::FinishLinear(void)
int yd = dy - dx / 2;
int zd = dz - dx / 2;
- while (true)
+ for (;;)
{
m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
@@ -363,7 +363,7 @@ void cCaveTunnel::FinishLinear(void)
int xd = dx - dy / 2;
int zd = dz - dy / 2;
- while (true)
+ for (;;)
{
m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
@@ -397,7 +397,7 @@ void cCaveTunnel::FinishLinear(void)
int xd = dx - dz / 2;
int yd = dy - dz / 2;
- while (true)
+ for (;;)
{
m_Points.push_back(cCaveDefPoint(PrevX, PrevY, PrevZ, R));
@@ -509,6 +509,7 @@ void cCaveTunnel::ProcessChunk(
case E_BLOCK_GRAVEL:
case E_BLOCK_SAND:
case E_BLOCK_SANDSTONE:
+ case E_BLOCK_SOULSAND:
case E_BLOCK_NETHERRACK:
case E_BLOCK_COAL_ORE:
case E_BLOCK_IRON_ORE:
diff --git a/src/Generating/ChunkGenerator.cpp b/src/Generating/ChunkGenerator.cpp
index 126a896af..ef38f1399 100644
--- a/src/Generating/ChunkGenerator.cpp
+++ b/src/Generating/ChunkGenerator.cpp
@@ -2,13 +2,11 @@
#include "Globals.h"
#include "ChunkGenerator.h"
-#include "../World.h"
#include "inifile/iniFile.h"
-#include "../Root.h"
-#include "../Bindings/PluginManager.h"
#include "ChunkDesc.h"
#include "ComposableGenerator.h"
#include "Noise3DGenerator.h"
+#include "../MersenneTwister.h"
@@ -29,8 +27,9 @@ const unsigned int QUEUE_SKIP_LIMIT = 500;
cChunkGenerator::cChunkGenerator(void) :
super("cChunkGenerator"),
- m_World(NULL),
- m_Generator(NULL)
+ m_Generator(NULL),
+ m_PluginInterface(NULL),
+ m_ChunkSink(NULL)
{
}
@@ -47,10 +46,12 @@ cChunkGenerator::~cChunkGenerator()
-bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile)
+bool cChunkGenerator::Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile)
{
+ m_PluginInterface = &a_PluginInterface;
+ m_ChunkSink = &a_ChunkSink;
+
MTRand rnd;
- m_World = a_World;
m_Seed = a_IniFile.GetValueSetI("Seed", "Seed", rnd.randInt());
AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable");
@@ -73,7 +74,7 @@ bool cChunkGenerator::Start(cWorld * a_World, cIniFile & a_IniFile)
return false;
}
- m_Generator->Initialize(a_World, a_IniFile);
+ m_Generator->Initialize(a_IniFile);
return super::Start();
}
@@ -200,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))
{
@@ -220,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);
@@ -237,14 +245,14 @@ void cChunkGenerator::Execute(void)
}
// Hack for regenerating chunks: if Y != 0, the chunk is considered invalid, even if it has its data set
- if ((coords.m_ChunkY == 0) && m_World->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
+ if ((coords.m_ChunkY == 0) && m_ChunkSink->IsChunkValid(coords.m_ChunkX, coords.m_ChunkZ))
{
LOGD("Chunk [%d, %d] already generated, skipping generation", coords.m_ChunkX, coords.m_ChunkZ);
// Already generated, ignore request
continue;
}
- if (SkipEnabled && !m_World->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ))
+ if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(coords.m_ChunkX, coords.m_ChunkZ))
{
LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", coords.m_ChunkX, coords.m_ChunkZ);
continue;
@@ -253,9 +261,6 @@ void cChunkGenerator::Execute(void)
LOGD("Generating chunk [%d, %d, %d]", coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
DoGenerate(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
- // Save the chunk right after generating, so that we don't have to generate it again on next run
- m_World->GetStorage().QueueSaveChunk(coords.m_ChunkX, coords.m_ChunkY, coords.m_ChunkZ);
-
NumChunksGenerated++;
} // while (!bStop)
}
@@ -265,27 +270,20 @@ void cChunkGenerator::Execute(void)
void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
+ ASSERT(m_PluginInterface != NULL);
+ ASSERT(m_ChunkSink != NULL);
+
cChunkDesc ChunkDesc(a_ChunkX, a_ChunkZ);
- cRoot::Get()->GetPluginManager()->CallHookChunkGenerating(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc);
+ m_PluginInterface->CallHookChunkGenerating(ChunkDesc);
m_Generator->DoGenerate(a_ChunkX, a_ChunkZ, ChunkDesc);
- cRoot::Get()->GetPluginManager()->CallHookChunkGenerated(m_World, a_ChunkX, a_ChunkZ, &ChunkDesc);
+ m_PluginInterface->CallHookChunkGenerated(ChunkDesc);
#ifdef _DEBUG
// Verify that the generator has produced valid data:
ChunkDesc.VerifyHeightmap();
#endif
- cChunkDef::BlockNibbles BlockMetas;
- ChunkDesc.CompressBlockMetas(BlockMetas);
-
- m_World->SetChunkData(
- a_ChunkX, a_ChunkZ,
- ChunkDesc.GetBlockTypes(), BlockMetas,
- NULL, NULL, // We don't have lighting, chunk will be lighted when needed
- &ChunkDesc.GetHeightMap(), &ChunkDesc.GetBiomeMap(),
- ChunkDesc.GetEntities(), ChunkDesc.GetBlockEntities(),
- true
- );
+ m_ChunkSink->OnChunkGenerated(ChunkDesc);
}
@@ -304,9 +302,8 @@ cChunkGenerator::cGenerator::cGenerator(cChunkGenerator & a_ChunkGenerator) :
-void cChunkGenerator::cGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
+void cChunkGenerator::cGenerator::Initialize(cIniFile & a_IniFile)
{
- m_World = a_World;
UNUSED(a_IniFile);
}
@@ -319,7 +316,7 @@ EMCSBiome cChunkGenerator::cGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
cChunkDef::BiomeMap Biomes;
int Y = 0;
int ChunkX, ChunkZ;
- cWorld::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, Y, ChunkZ);
+ cChunkDef::AbsoluteToRelative(a_BlockX, Y, a_BlockZ, ChunkX, ChunkZ);
GenerateBiomes(ChunkX, ChunkZ, Biomes);
return cChunkDef::GetBiome(Biomes, a_BlockX, a_BlockZ);
}
diff --git a/src/Generating/ChunkGenerator.h b/src/Generating/ChunkGenerator.h
index 2d3bb8082..9b2d9eb3c 100644
--- a/src/Generating/ChunkGenerator.h
+++ b/src/Generating/ChunkGenerator.h
@@ -26,7 +26,6 @@ If the generator queue is overloaded, the generator skips chunks with no clients
// fwd:
-class cWorld;
class cIniFile;
class cChunkDesc;
@@ -40,7 +39,7 @@ class cChunkGenerator :
typedef cIsThread super;
public:
- /// The interface that a class has to implement to become a generator
+ /** The interface that a class has to implement to become a generator */
class cGenerator
{
public:
@@ -48,7 +47,7 @@ public:
virtual ~cGenerator() {} ; // Force a virtual destructor
/// Called to initialize the generator on server startup.
- virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile);
+ virtual void Initialize(cIniFile & a_IniFile);
/// Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading.
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0;
@@ -61,14 +60,59 @@ public:
protected:
cChunkGenerator & m_ChunkGenerator;
- cWorld * m_World;
+ } ;
+
+
+ /** The interface through which the plugins are called for their OnChunkGenerating / OnChunkGenerated hooks. */
+ class cPluginInterface
+ {
+ public:
+ // Force a virtual destructor
+ virtual ~cPluginInterface() {}
+
+ /** Called when the chunk is about to be generated.
+ The generator may be partly or fully overriden by the implementation
+ */
+ virtual void CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) = 0;
+
+ /** Called after the chunk is generated, before it is handed to the chunk sink.
+ a_ChunkDesc contains the generated chunk data. Implementation may modify this data.
+ */
+ virtual void CallHookChunkGenerated(cChunkDesc & a_ChunkDesc) = 0;
+ } ;
+
+
+ /** The interface through which the generated chunks are handed to the cWorld or whoever created us. */
+ class cChunkSink
+ {
+ public:
+ // Force a virtual destructor
+ virtual ~cChunkSink() {}
+
+ /** Called after the chunk has been generated
+ The interface may store the chunk, send it over network, whatever.
+ The chunk is not expected to be modified, but the generator will survive if the implementation
+ changes the data within. All changes are ignored, though.
+ */
+ virtual void OnChunkGenerated(cChunkDesc & a_ChunkDesc) = 0;
+
+ /** Called just before the chunk generation is started,
+ to verify that it hasn't been generated in the meantime.
+ If this callback returns true, the chunk is not generated.
+ */
+ virtual bool IsChunkValid(int a_ChunkX, int a_ChunkZ) = 0;
+
+ /** Called when the generator is overloaded to skip chunks that are no longer needed.
+ If this callback returns false, the chunk is not generated.
+ */
+ virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) = 0;
} ;
cChunkGenerator (void);
~cChunkGenerator();
- bool Start(cWorld * a_World, cIniFile & a_IniFile);
+ bool Start(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile);
void Stop(void);
/// Queues the chunk for generation; removes duplicate requests
@@ -91,8 +135,6 @@ public:
private:
- cWorld * m_World;
-
int m_Seed;
cCriticalSection m_CS;
@@ -101,6 +143,13 @@ private:
cEvent m_evtRemoved; ///< Set when an item is removed from the queue
cGenerator * m_Generator; ///< The actual generator engine used to generate chunks
+
+ /** The plugin interface that may modify the generated chunks */
+ cPluginInterface * m_PluginInterface;
+
+ /** The destination where the generated chunks are sent */
+ cChunkSink * m_ChunkSink;
+
// cIsThread override:
virtual void Execute(void) override;
diff --git a/src/Generating/CompoGen.cpp b/src/Generating/CompoGen.cpp
index f929ddc2f..60356fe46 100644
--- a/src/Generating/CompoGen.cpp
+++ b/src/Generating/CompoGen.cpp
@@ -589,7 +589,22 @@ void cCompoGenNether::ComposeTerrain(cChunkDesc & a_ChunkDesc)
for (int y = 0; y < SEGMENT_HEIGHT; y++)
{
int Val = Lo + (Hi - Lo) * y / SEGMENT_HEIGHT;
- a_ChunkDesc.SetBlockType(x, y + Segment, z, (Val < m_Threshold) ? E_BLOCK_NETHERRACK : E_BLOCK_AIR);
+ BLOCKTYPE Block = E_BLOCK_AIR;
+ if (Val < m_Threshold) // Don't calculate if the block should be Netherrack or Soulsand when it's already decided that it's air.
+ {
+ NOISE_DATATYPE NoiseX = ((NOISE_DATATYPE)(BaseX + x)) / 8;
+ NOISE_DATATYPE NoiseY = ((NOISE_DATATYPE)(BaseZ + z)) / 8;
+ NOISE_DATATYPE CompBlock = m_Noise1.CubicNoise3D(NoiseX, (float) (y + Segment) / 2, NoiseY);
+ if (CompBlock < -0.5)
+ {
+ Block = E_BLOCK_SOULSAND;
+ }
+ else
+ {
+ Block = E_BLOCK_NETHERRACK;
+ }
+ }
+ a_ChunkDesc.SetBlockType(x, y + Segment, z, Block);
}
}
diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp
index c86568c95..cfa7e9c6f 100644
--- a/src/Generating/ComposableGenerator.cpp
+++ b/src/Generating/ComposableGenerator.cpp
@@ -28,11 +28,88 @@
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cTerrainCompositionGen:
+cTerrainCompositionGen * cTerrainCompositionGen::CreateCompositionGen(cIniFile & a_IniFile, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed)
+{
+ AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
+ if (CompoGenName.empty())
+ {
+ LOGWARN("[Generator] CompositionGen value not set in world.ini, using \"Biomal\".");
+ CompoGenName = "Biomal";
+ a_IniFile.SetValue("Generator", "CompositionGen", CompoGenName);
+ }
+
+ cTerrainCompositionGen * res = NULL;
+ if (NoCaseCompare(CompoGenName, "sameblock") == 0)
+ {
+ res = new cCompoGenSameBlock;
+ }
+ else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
+ {
+ res = new cCompoGenDebugBiomes;
+ }
+ else if (NoCaseCompare(CompoGenName, "classic") == 0)
+ {
+ res = new cCompoGenClassic;
+ }
+ else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
+ {
+ res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
+ }
+ else if (NoCaseCompare(CompoGenName, "end") == 0)
+ {
+ res = new cEndGen(a_Seed);
+ }
+ else if (NoCaseCompare(CompoGenName, "nether") == 0)
+ {
+ res = new cCompoGenNether(a_Seed);
+ }
+ else if (NoCaseCompare(CompoGenName, "Noise3D") == 0)
+ {
+ res = new cNoise3DComposable(a_Seed);
+ }
+ else if (NoCaseCompare(CompoGenName, "biomal") == 0)
+ {
+ res = new cCompoGenBiomal(a_Seed);
+ /*
+ // Performance-testing:
+ LOGINFO("Measuring performance of cCompoGenBiomal...");
+ clock_t BeginTick = clock();
+ for (int x = 0; x < 500; x++)
+ {
+ cChunkDesc Desc(200 + x * 8, 200 + x * 8);
+ a_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap());
+ a_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap());
+ res->ComposeTerrain(Desc);
+ }
+ clock_t Duration = clock() - BeginTick;
+ LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
+ //*/
+ }
+ else
+ {
+ LOGWARN("Unknown CompositionGen \"%s\", using \"Biomal\" instead.", CompoGenName.c_str());
+ a_IniFile.DeleteValue("Generator", "CompositionGen");
+ a_IniFile.SetValue("Generator", "CompositionGen", "Biomal");
+ return CreateCompositionGen(a_IniFile, a_BiomeGen, a_HeightGen, a_Seed);
+ }
+ ASSERT(res != NULL);
+
+ // Read the settings from the ini file:
+ res->InitializeCompoGen(a_IniFile);
+
+ return res;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cComposableGenerator:
+
cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator),
m_BiomeGen(NULL),
@@ -80,9 +157,9 @@ cComposableGenerator::~cComposableGenerator()
-void cComposableGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
+void cComposableGenerator::Initialize(cIniFile & a_IniFile)
{
- super::Initialize(a_World, a_IniFile);
+ super::Initialize(a_IniFile);
InitBiomeGen(a_IniFile);
InitHeightGen(a_IniFile);
@@ -173,60 +250,8 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
{
- AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", "");
- if (HeightGenName.empty())
- {
- LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\".");
- HeightGenName = "Biomal";
- }
-
- int Seed = m_ChunkGenerator.GetSeed();
bool CacheOffByDefault = false;
- if (NoCaseCompare(HeightGenName, "flat") == 0)
- {
- m_HeightGen = new cHeiGenFlat;
- CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
- }
- else if (NoCaseCompare(HeightGenName, "classic") == 0)
- {
- m_HeightGen = new cHeiGenClassic(Seed);
- }
- else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
- {
- m_HeightGen = new cDistortedHeightmap(Seed, *m_BiomeGen);
- }
- else if (NoCaseCompare(HeightGenName, "End") == 0)
- {
- m_HeightGen = new cEndGen(Seed);
- }
- else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
- {
- m_HeightGen = new cNoise3DComposable(Seed);
- }
- else // "biomal" or <not found>
- {
- if (NoCaseCompare(HeightGenName, "biomal") != 0)
- {
- LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
- }
- m_HeightGen = new cHeiGenBiomal(Seed, *m_BiomeGen);
-
- /*
- // Performance-testing:
- LOGINFO("Measuring performance of cHeiGenBiomal...");
- clock_t BeginTick = clock();
- for (int x = 0; x < 500; x++)
- {
- cChunkDef::HeightMap Heights;
- m_HeightGen->GenHeightMap(x * 5, x * 5, Heights);
- }
- clock_t Duration = clock() - BeginTick;
- LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
- //*/
- }
-
- // Read the settings:
- m_HeightGen->InitializeHeightGen(a_IniFile);
+ m_HeightGen = cTerrainHeightGen::CreateHeightGen(a_IniFile, *m_BiomeGen, m_ChunkGenerator.GetSeed(), CacheOffByDefault);
// Add a cache, if requested:
int CacheSize = a_IniFile.GetValueSetI("Generator", "HeightGenCacheSize", CacheOffByDefault ? 0 : 64);
@@ -251,67 +276,7 @@ void cComposableGenerator::InitHeightGen(cIniFile & a_IniFile)
void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile)
{
- int Seed = m_ChunkGenerator.GetSeed();
- AString CompoGenName = a_IniFile.GetValueSet("Generator", "CompositionGen", "");
- if (CompoGenName.empty())
- {
- LOGWARN("[Generator] CompositionGen value not set in world.ini, using \"Biomal\".");
- CompoGenName = "Biomal";
- }
- if (NoCaseCompare(CompoGenName, "sameblock") == 0)
- {
- m_CompositionGen = new cCompoGenSameBlock;
- }
- else if (NoCaseCompare(CompoGenName, "debugbiomes") == 0)
- {
- m_CompositionGen = new cCompoGenDebugBiomes;
- }
- else if (NoCaseCompare(CompoGenName, "classic") == 0)
- {
- m_CompositionGen = new cCompoGenClassic;
- }
- else if (NoCaseCompare(CompoGenName, "DistortedHeightmap") == 0)
- {
- m_CompositionGen = new cDistortedHeightmap(Seed, *m_BiomeGen);
- }
- else if (NoCaseCompare(CompoGenName, "end") == 0)
- {
- m_CompositionGen = new cEndGen(Seed);
- }
- else if (NoCaseCompare(CompoGenName, "nether") == 0)
- {
- m_CompositionGen = new cCompoGenNether(Seed);
- }
- else if (NoCaseCompare(CompoGenName, "Noise3D") == 0)
- {
- m_CompositionGen = new cNoise3DComposable(m_ChunkGenerator.GetSeed());
- }
- else
- {
- if (NoCaseCompare(CompoGenName, "biomal") != 0)
- {
- LOGWARN("Unknown CompositionGen \"%s\", using \"biomal\" instead.", CompoGenName.c_str());
- }
- m_CompositionGen = new cCompoGenBiomal(Seed);
-
- /*
- // Performance-testing:
- LOGINFO("Measuring performance of cCompoGenBiomal...");
- clock_t BeginTick = clock();
- for (int x = 0; x < 500; x++)
- {
- cChunkDesc Desc(200 + x * 8, 200 + x * 8);
- m_BiomeGen->GenBiomes(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetBiomeMap());
- m_HeightGen->GenHeightMap(Desc.GetChunkX(), Desc.GetChunkZ(), Desc.GetHeightMap());
- m_CompositionGen->ComposeTerrain(Desc);
- }
- clock_t Duration = clock() - BeginTick;
- LOGINFO("CompositionGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
- //*/
- }
-
- // Read the settings from the ini file:
- m_CompositionGen->InitializeCompoGen(a_IniFile);
+ m_CompositionGen = cTerrainCompositionGen::CreateCompositionGen(a_IniFile, *m_BiomeGen, *m_HeightGen, m_ChunkGenerator.GetSeed());
int CompoGenCacheSize = a_IniFile.GetValueSetI("Generator", "CompositionGenCacheSize", 64);
if (CompoGenCacheSize > 1)
@@ -402,15 +367,16 @@ void cComposableGenerator::InitStructureGens(cIniFile & a_IniFile)
void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
{
int Seed = m_ChunkGenerator.GetSeed();
- AString Structures = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator");
+ eDimension Dimension = StringToDimension(a_IniFile.GetValue("General", "Dimension", "Overworld"));
- AStringVector Str = StringSplitAndTrim(Structures, ",");
+ AString Finishers = a_IniFile.GetValueSet("Generator", "Finishers", "SprinkleFoliage,Ice,Snow,Lilypads,BottomLava,DeadBushes,PreSimulator");
+ AStringVector Str = StringSplitAndTrim(Finishers, ",");
for (AStringVector::const_iterator itr = Str.begin(); itr != Str.end(); ++itr)
{
// Finishers, alpha-sorted:
if (NoCaseCompare(*itr, "BottomLava") == 0)
{
- int DefaultBottomLavaLevel = (m_World->GetDimension() == dimNether) ? 30 : 10;
+ int DefaultBottomLavaLevel = (Dimension == dimNether) ? 30 : 10;
int BottomLavaLevel = a_IniFile.GetValueSetI("Generator", "BottomLavaLevel", DefaultBottomLavaLevel);
m_FinishGens.push_back(new cFinishGenBottomLava(BottomLavaLevel));
}
@@ -424,12 +390,16 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(*itr, "LavaSprings") == 0)
{
- m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, *m_World));
+ m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, Dimension));
}
else if (NoCaseCompare(*itr, "Lilypads") == 0)
{
m_FinishGens.push_back(new cFinishGenSingleBiomeSingleTopBlock(Seed, E_BLOCK_LILY_PAD, biSwampland, 4, E_BLOCK_WATER, E_BLOCK_STATIONARY_WATER));
}
+ else if (NoCaseCompare(*itr, "NetherClumpFoliage") == 0)
+ {
+ m_FinishGens.push_back(new cFinishGenNetherClumpFoliage(Seed));
+ }
else if (NoCaseCompare(*itr, "PreSimulator") == 0)
{
m_FinishGens.push_back(new cFinishGenPreSimulator);
@@ -444,7 +414,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(*itr, "WaterSprings") == 0)
{
- m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, *m_World));
+ m_FinishGens.push_back(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, Dimension));
}
} // for itr - Str[]
}
diff --git a/src/Generating/ComposableGenerator.h b/src/Generating/ComposableGenerator.h
index 732f64303..29add0636 100644
--- a/src/Generating/ComposableGenerator.h
+++ b/src/Generating/ComposableGenerator.h
@@ -50,7 +50,7 @@ public:
virtual void InitializeBiomeGen(cIniFile & a_IniFile) {}
/// Creates the correct BiomeGen descendant based on the ini file settings and the seed provided.
- /// a_CacheOffByDefault gets set to whether the cache should be enabled by default
+ /// a_CacheOffByDefault gets set to whether the cache should be disabled by default
/// Used in BiomeVisualiser, too.
/// Implemented in BioGen.cpp!
static cBiomeGen * CreateBiomeGen(cIniFile & a_IniFile, int a_Seed, bool & a_CacheOffByDefault);
@@ -77,6 +77,13 @@ public:
/// Reads parameters from the ini file, prepares generator for use.
virtual void InitializeHeightGen(cIniFile & a_IniFile) {}
+
+ /** Creates the correct TerrainHeightGen descendant based on the ini file settings and the seed provided.
+ a_BiomeGen is the underlying biome generator, some height generators may depend on it to generate more biomes
+ a_CacheOffByDefault gets set to whether the cache should be disabled by default
+ Implemented in HeiGen.cpp!
+ */
+ static cTerrainHeightGen * CreateHeightGen(cIniFile & a_IniFile, cBiomeGen & a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault);
} ;
@@ -97,6 +104,12 @@ public:
/// Reads parameters from the ini file, prepares generator for use.
virtual void InitializeCompoGen(cIniFile & a_IniFile) {}
+
+ /** Creates the correct TerrainCompositionGen descendant based on the ini file settings and the seed provided.
+ a_BiomeGen is the underlying biome generator, some composition generators may depend on it to generate more biomes
+ a_HeightGen is the underlying height generator, some composition generators may depend on it providing additional values
+ */
+ static cTerrainCompositionGen * CreateCompositionGen(cIniFile & a_IniFile, cBiomeGen & a_BiomeGen, cTerrainHeightGen & a_HeightGen, int a_Seed);
} ;
@@ -149,7 +162,7 @@ public:
cComposableGenerator(cChunkGenerator & a_ChunkGenerator);
virtual ~cComposableGenerator();
- virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
+ virtual void Initialize(cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
diff --git a/src/Generating/DistortedHeightmap.cpp b/src/Generating/DistortedHeightmap.cpp
index 15e352e30..eb9fe92ba 100644
--- a/src/Generating/DistortedHeightmap.cpp
+++ b/src/Generating/DistortedHeightmap.cpp
@@ -774,10 +774,11 @@ void cDistortedHeightmap::ComposeColumn(cChunkDesc & a_ChunkDesc, int a_RelX, in
return;
}
default:
+ {
ASSERT(!"Unhandled biome");
return;
+ }
} // switch (Biome)
- ASSERT(!"Unexpected fallthrough");
}
diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp
index 866551e8a..02045f76a 100644
--- a/src/Generating/FinishGen.cpp
+++ b/src/Generating/FinishGen.cpp
@@ -13,6 +13,7 @@
#include "../Noise.h"
#include "../BlockID.h"
#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway()
+#include "../Simulator/FireSimulator.h"
#include "../World.h"
@@ -40,6 +41,125 @@ static inline bool IsWater(BLOCKTYPE a_BlockType)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFinishGenNetherClumpFoliage:
+
+void cFinishGenNetherClumpFoliage::GenFinish(cChunkDesc & a_ChunkDesc)
+{
+ double ChunkX = a_ChunkDesc.GetChunkX() + 0.1; // We can't devide through 0 so lets add 0.1 to all the chunk coordinates.
+ double ChunkZ = a_ChunkDesc.GetChunkZ() + 0.1;
+
+ NOISE_DATATYPE Val1 = m_Noise.CubicNoise2D((float) (ChunkX * ChunkZ * 0.01f), (float) (ChunkZ / ChunkX * 0.01f));
+ NOISE_DATATYPE Val2 = m_Noise.CubicNoise2D((float) (ChunkX / ChunkZ / 0.01f), (float) (ChunkZ * ChunkX / 0.01f));
+
+ if (Val1 < 0)
+ {
+ Val1 = -Val1;
+ }
+
+ if (Val2 < 0)
+ {
+ Val2 = -Val2;
+ }
+
+ int PosX, PosZ;
+ // Calculate PosX
+ if (Val1 <= 1)
+ {
+ PosX = (int) floor(Val1 * 16);
+ }
+ else
+ {
+ PosX = (int) floor(16 / Val1);
+ }
+
+ // Calculate PosZ
+ if (Val2 <= 1)
+ {
+ PosZ = (int) floor(Val2 * 16);
+ }
+ else
+ {
+ PosZ = (int) floor(16 / Val2);
+ }
+
+ for (int y = 1; y < cChunkDef::Height; y++)
+ {
+ if (a_ChunkDesc.GetBlockType(PosX, y, PosZ) != E_BLOCK_AIR)
+ {
+ continue;
+ }
+ if (!g_BlockIsSolid[a_ChunkDesc.GetBlockType(PosX, y - 1, PosZ)]) // Only place on solid blocks
+ {
+ continue;
+ }
+
+ NOISE_DATATYPE BlockType = m_Noise.CubicNoise1D((float) (ChunkX * ChunkZ) / (y * 0.1f));
+ if (BlockType < -0.7)
+ {
+ TryPlaceClump(a_ChunkDesc, PosX, y, PosZ, E_BLOCK_BROWN_MUSHROOM);
+ }
+ else if (BlockType < 0)
+ {
+ TryPlaceClump(a_ChunkDesc, PosX, y, PosZ, E_BLOCK_RED_MUSHROOM);
+ }
+ else if (BlockType < 0.7)
+ {
+ TryPlaceClump(a_ChunkDesc, PosX, y, PosZ, E_BLOCK_FIRE);
+ }
+ }
+}
+
+
+
+
+
+void cFinishGenNetherClumpFoliage::TryPlaceClump(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Block)
+{
+ bool IsFireBlock = a_Block == E_BLOCK_FIRE;
+
+ for (int x = a_RelX - 4; x < a_RelX + 4; x++)
+ {
+ float xx = (float) a_ChunkDesc.GetChunkX() * cChunkDef::Width + x;
+ for (int z = a_RelZ - 4; z < a_RelZ + 4; z++)
+ {
+ float zz = (float) a_ChunkDesc.GetChunkZ() * cChunkDef::Width + z;
+ for (int y = a_RelY - 2; y < a_RelY + 2; y++)
+ {
+ if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) // Don't replace non air blocks.
+ {
+ continue;
+ }
+
+ BLOCKTYPE BlockBelow = a_ChunkDesc.GetBlockType(x, y - 1, z);
+ if (!g_BlockIsSolid[BlockBelow]) // Only place on solid blocks
+ {
+ continue;
+ }
+
+ if (IsFireBlock) // don't place fire on non-forever burning blocks.
+ {
+ if (!cFireSimulator::DoesBurnForever(BlockBelow))
+ {
+ continue;
+ }
+ }
+
+
+ NOISE_DATATYPE Val = m_Noise.CubicNoise2D(xx, zz);
+ if (Val < -0.70)
+ {
+ a_ChunkDesc.SetBlockType(x, y, z, a_Block);
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFinishGenSprinkleFoliage:
bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ)
@@ -520,7 +640,7 @@ void cFinishGenPreSimulator::StationarizeFluid(
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cFinishGenFluidSprings:
-cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World) :
+cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension) :
m_Noise(a_Seed + a_Fluid * 100), // Need to take fluid into account, otherwise water and lava springs generate next to each other
m_HeightDistribution(255),
m_Fluid(a_Fluid)
@@ -528,8 +648,8 @@ cFinishGenFluidSprings::cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cI
bool IsWater = (a_Fluid == E_BLOCK_WATER);
AString SectionName = IsWater ? "WaterSprings" : "LavaSprings";
AString DefaultHeightDistribution;
- int DefaultChance;
- switch (a_World.GetDimension())
+ int DefaultChance = 0;
+ switch (a_Dimension)
{
case dimNether:
{
diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h
index ed7df5909..2e5732929 100644
--- a/src/Generating/FinishGen.h
+++ b/src/Generating/FinishGen.h
@@ -47,6 +47,28 @@ protected:
+class cFinishGenNetherClumpFoliage :
+ public cFinishGen
+{
+public:
+ cFinishGenNetherClumpFoliage(int a_Seed) :
+ m_Noise(a_Seed),
+ m_Seed(a_Seed)
+ {
+ }
+
+protected:
+ cNoise m_Noise;
+ int m_Seed;
+
+ void TryPlaceClump(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_Block);
+ virtual void GenFinish(cChunkDesc & a_ChunkDesc) override;
+} ;
+
+
+
+
+
class cFinishGenSprinkleFoliage :
public cFinishGen
{
@@ -117,6 +139,7 @@ public:
{
}
+ int GetLevel(void) const { return m_Level; }
protected:
int m_Level;
@@ -164,7 +187,7 @@ class cFinishGenFluidSprings :
public cFinishGen
{
public:
- cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, const cWorld & a_World);
+ cFinishGenFluidSprings(int a_Seed, BLOCKTYPE a_Fluid, cIniFile & a_IniFile, eDimension a_Dimension);
protected:
diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp
index 2bf641089..10710b4a1 100644
--- a/src/Generating/HeiGen.cpp
+++ b/src/Generating/HeiGen.cpp
@@ -7,6 +7,9 @@
#include "HeiGen.h"
#include "../LinearUpscale.h"
#include "inifile/iniFile.h"
+#include "DistortedHeightmap.h"
+#include "EndGen.h"
+#include "Noise3DGenerator.h"
@@ -14,6 +17,77 @@
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cTerrainHeightGen:
+
+cTerrainHeightGen * cTerrainHeightGen::CreateHeightGen(cIniFile &a_IniFile, cBiomeGen & a_BiomeGen, int a_Seed, bool & a_CacheOffByDefault)
+{
+ AString HeightGenName = a_IniFile.GetValueSet("Generator", "HeightGen", "");
+ if (HeightGenName.empty())
+ {
+ LOGWARN("[Generator] HeightGen value not set in world.ini, using \"Biomal\".");
+ HeightGenName = "Biomal";
+ }
+
+ a_CacheOffByDefault = false;
+ cTerrainHeightGen * res = NULL;
+ if (NoCaseCompare(HeightGenName, "flat") == 0)
+ {
+ res = new cHeiGenFlat;
+ a_CacheOffByDefault = true; // We're generating faster than a cache would retrieve data
+ }
+ else if (NoCaseCompare(HeightGenName, "classic") == 0)
+ {
+ res = new cHeiGenClassic(a_Seed);
+ }
+ else if (NoCaseCompare(HeightGenName, "DistortedHeightmap") == 0)
+ {
+ res = new cDistortedHeightmap(a_Seed, a_BiomeGen);
+ }
+ else if (NoCaseCompare(HeightGenName, "End") == 0)
+ {
+ res = new cEndGen(a_Seed);
+ }
+ else if (NoCaseCompare(HeightGenName, "Noise3D") == 0)
+ {
+ res = new cNoise3DComposable(a_Seed);
+ }
+ else if (NoCaseCompare(HeightGenName, "biomal") == 0)
+ {
+ res = new cHeiGenBiomal(a_Seed, a_BiomeGen);
+
+ /*
+ // Performance-testing:
+ LOGINFO("Measuring performance of cHeiGenBiomal...");
+ clock_t BeginTick = clock();
+ for (int x = 0; x < 500; x++)
+ {
+ cChunkDef::HeightMap Heights;
+ res->GenHeightMap(x * 5, x * 5, Heights);
+ }
+ clock_t Duration = clock() - BeginTick;
+ LOGINFO("HeightGen for 500 chunks took %d ticks (%.02f sec)", Duration, (double)Duration / CLOCKS_PER_SEC);
+ //*/
+ }
+ else
+ {
+ // No match found, force-set the default and retry
+ LOGWARN("Unknown HeightGen \"%s\", using \"Biomal\" instead.", HeightGenName.c_str());
+ a_IniFile.DeleteValue("Generator", "HeightGen");
+ a_IniFile.SetValue("Generator", "HeightGen", "Biomal");
+ return CreateHeightGen(a_IniFile, a_BiomeGen, a_Seed, a_CacheOffByDefault);
+ }
+
+ // Read the settings:
+ res->InitializeHeightGen(a_IniFile);
+
+ return res;
+}
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cHeiGenFlat:
void cHeiGenFlat::GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap)
diff --git a/src/Generating/Noise3DGenerator.cpp b/src/Generating/Noise3DGenerator.cpp
index 2511bb656..afa40c647 100644
--- a/src/Generating/Noise3DGenerator.cpp
+++ b/src/Generating/Noise3DGenerator.cpp
@@ -150,10 +150,8 @@ cNoise3DGenerator::~cNoise3DGenerator()
-void cNoise3DGenerator::Initialize(cWorld * a_World, cIniFile & a_IniFile)
+void cNoise3DGenerator::Initialize(cIniFile & a_IniFile)
{
- m_World = a_World;
-
// Params:
m_SeaLevel = a_IniFile.GetValueSetI("Generator", "Noise3DSeaLevel", 62);
m_HeightAmplification = (NOISE_DATATYPE)a_IniFile.GetValueSetF("Generator", "Noise3DHeightAmplification", 0);
diff --git a/src/Generating/Noise3DGenerator.h b/src/Generating/Noise3DGenerator.h
index 0d211cddc..42f61a854 100644
--- a/src/Generating/Noise3DGenerator.h
+++ b/src/Generating/Noise3DGenerator.h
@@ -24,7 +24,7 @@ public:
cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator);
virtual ~cNoise3DGenerator();
- virtual void Initialize(cWorld * a_World, cIniFile & a_IniFile) override;
+ virtual void Initialize(cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
diff --git a/src/Generating/Ravines.cpp b/src/Generating/Ravines.cpp
index 6413b963b..cfda47e32 100644
--- a/src/Generating/Ravines.cpp
+++ b/src/Generating/Ravines.cpp
@@ -381,7 +381,7 @@ void cStructGenRavines::cRavine::FinishLinear(void)
int R = itr->m_Radius;
int T = itr->m_Top;
int B = itr->m_Bottom;
- while (true)
+ for (;;)
{
m_Points.push_back(cRavDefPoint(PrevX, PrevZ, R, T, B));
if ((PrevX == x1) && (PrevZ == z1))
diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp
index fbed57cb6..7e8a3c75f 100644
--- a/src/Generating/Trees.cpp
+++ b/src/Generating/Trees.cpp
@@ -216,7 +216,14 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No
GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
break;
}
-
+
+ case biBirchForestM:
+ case biBirchForestHillsM:
+ {
+ GetTallBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks);
+ break;
+ }
+
case biRoofedForest:
case biColdTaiga:
case biColdTaigaHills:
@@ -237,8 +244,6 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No
case biIcePlainsSpikes:
case biJungleM:
case biJungleEdgeM:
- case biBirchForestM:
- case biBirchForestHillsM:
case biRoofedForestM:
case biColdTaigaM:
case biMegaSpruceTaiga:
@@ -377,6 +382,44 @@ void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Nois
+void GetTallBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
+{
+ int Height = 9 + (a_Noise.IntNoise3DInt(a_BlockX + 64 * a_Seq, a_BlockY, a_BlockZ) % 3);
+
+ // Prealloc, so that we don't realloc too often later:
+ a_LogBlocks.reserve(Height);
+ a_OtherBlocks.reserve(80);
+
+ // The entire trunk, out of logs:
+ for (int i = Height - 1; i >= 0; --i)
+ {
+ a_LogBlocks.push_back(sSetBlock(a_BlockX, a_BlockY + i, a_BlockZ, E_BLOCK_LOG, E_META_LOG_BIRCH));
+ }
+ int h = a_BlockY + Height;
+
+ // Top layer - just the Plus:
+ PushCoordBlocks(a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ a_OtherBlocks.push_back(sSetBlock(a_BlockX, h, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH)); // There's no log at this layer
+ h--;
+
+ // Second layer - log, Plus and maybe Corners:
+ PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x5fffffff, a_OtherBlocks, 1, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ h--;
+
+ // Third and fourth layers - BigO2 and maybe 2*Corners:
+ for (int Row = 0; Row < 2; Row++)
+ {
+ PushCoordBlocks (a_BlockX, h, a_BlockZ, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ PushCornerBlocks(a_BlockX, h, a_BlockZ, a_Seq, a_Noise, 0x3fffffff + Row * 0x10000000, a_OtherBlocks, 2, E_BLOCK_LEAVES, E_META_LEAVES_BIRCH);
+ h--;
+ } // for Row - 2*
+}
+
+
+
+
+
void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks)
{
// Half chance for a spruce, half for a pine:
diff --git a/src/Generating/Trees.h b/src/Generating/Trees.h
index f5148ad6f..514158eb7 100644
--- a/src/Generating/Trees.h
+++ b/src/Generating/Trees.h
@@ -63,6 +63,9 @@ void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
/// Generates an image of a random birch tree
void GetBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
+/// Generates an image of a random large birch tree
+void GetTallBirchTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks,sSetBlockVector & a_OtherBlocks);
+
/// Generates an image of a random conifer tree
void GetConiferTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
diff --git a/src/Globals.h b/src/Globals.h
index 58badf4dd..7e53da80e 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -14,7 +14,24 @@
#pragma warning(disable:4481)
// Disable some warnings that we don't care about:
- #pragma warning(disable:4100)
+ #pragma warning(disable:4100) // Unreferenced formal parameter
+
+ // Useful warnings from warning level 4:
+ #pragma warning(3 : 4127) // Conditional expression is constant
+ #pragma warning(3 : 4189) // Local variable is initialized but not referenced
+ #pragma warning(3 : 4245) // Conversion from 'type1' to 'type2', signed/unsigned mismatch
+ #pragma warning(3 : 4310) // Cast truncates constant value
+ #pragma warning(3 : 4389) // Signed/unsigned mismatch
+ #pragma warning(3 : 4505) // Unreferenced local function has been removed
+ #pragma warning(3 : 4701) // Potentially unitialized local variable used
+ #pragma warning(3 : 4702) // Unreachable code
+ #pragma warning(3 : 4706) // Assignment within conditional expression
+
+ // Disabling this warning, because we know what we're doing when we're doing this:
+ #pragma warning(disable: 4355) // 'this' used in initializer list
+
+ // 2014_01_06 xoft: Disabled this warning because MSVC is stupid and reports it in obviously wrong places
+ // #pragma warning(3 : 4244) // Conversion from 'type1' to 'type2', possible loss of data
#define OBSOLETE __declspec(deprecated)
@@ -74,6 +91,9 @@ typedef unsigned long long UInt64;
typedef unsigned int UInt32;
typedef unsigned short UInt16;
+typedef unsigned char Byte;
+
+
@@ -192,7 +212,7 @@ typedef unsigned short UInt16;
#ifdef _DEBUG
#define ASSERT( x ) ( !!(x) || ( LOGERROR("Assertion failed: %s, file %s, line %i", #x, __FILE__, __LINE__ ), assert(0), 0 ) )
#else
- #define ASSERT(x) ((void)0)
+ #define ASSERT(x) ((void)(x))
#endif
// Pretty much the same as ASSERT() but stays in Release builds
@@ -216,6 +236,7 @@ public:
// Common headers (part 2, with macros):
#include "ChunkDef.h"
+#include "BiomeDef.h"
#include "BlockID.h"
#include "Entities/Effects.h"
diff --git a/src/HTTPServer/HTTPConnection.cpp b/src/HTTPServer/HTTPConnection.cpp
index 68afdfc11..78b7ce4d9 100644
--- a/src/HTTPServer/HTTPConnection.cpp
+++ b/src/HTTPServer/HTTPConnection.cpp
@@ -26,7 +26,7 @@ cHTTPConnection::cHTTPConnection(cHTTPServer & a_HTTPServer) :
cHTTPConnection::~cHTTPConnection()
{
- // LOGD("HTTP: Del connection at %p", this);
+ delete m_CurrentRequest;
}
@@ -57,7 +57,7 @@ void cHTTPConnection::SendNeedAuth(const AString & a_Realm)
void cHTTPConnection::Send(const cHTTPResponse & a_Response)
{
- ASSERT(m_State = wcsRecvIdle);
+ ASSERT(m_State == wcsRecvIdle);
a_Response.AppendToData(m_OutgoingData);
m_State = wcsSendingResp;
m_HTTPServer.NotifyConnectionWrite(*this);
@@ -205,6 +205,12 @@ void cHTTPConnection::DataReceived(const char * a_Data, int a_Size)
{
m_State = wcsRecvIdle;
m_HTTPServer.RequestFinished(*this, *m_CurrentRequest);
+ if (!m_CurrentRequest->DoesAllowKeepAlive())
+ {
+ m_State = wcsInvalid;
+ m_HTTPServer.CloseConnection(*this);
+ return;
+ }
delete m_CurrentRequest;
m_CurrentRequest = NULL;
}
diff --git a/src/HTTPServer/HTTPConnection.h b/src/HTTPServer/HTTPConnection.h
index 00941f2ae..5b8103554 100644
--- a/src/HTTPServer/HTTPConnection.h
+++ b/src/HTTPServer/HTTPConnection.h
@@ -41,49 +41,52 @@ public:
cHTTPConnection(cHTTPServer & a_HTTPServer);
virtual ~cHTTPConnection();
- /// Sends HTTP status code together with a_Reason (used for HTTP errors)
+ /** Sends HTTP status code together with a_Reason (used for HTTP errors) */
void SendStatusAndReason(int a_StatusCode, const AString & a_Reason);
- /// Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm
+ /** Sends the "401 unauthorized" reply together with instructions on authorizing, using the specified realm */
void SendNeedAuth(const AString & a_Realm);
- /// Sends the headers contained in a_Response
+ /** Sends the headers contained in a_Response */
void Send(const cHTTPResponse & a_Response);
- /// Sends the data as the response (may be called multiple times)
+ /** Sends the data as the response (may be called multiple times) */
void Send(const void * a_Data, int a_Size);
- /// Sends the data as the response (may be called multiple times)
+ /** Sends the data as the response (may be called multiple times) */
void Send(const AString & a_Data) { Send(a_Data.data(), a_Data.size()); }
- /// Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive)
+ /** Indicates that the current response is finished, gets ready for receiving another request (HTTP 1.1 keepalive) */
void FinishResponse(void);
- /// Resets the connection for a new request. Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd"
+ /** Resets the internal connection state for a new request.
+ Depending on the state, this will send an "InternalServerError" status or a "ResponseEnd" */
void AwaitNextRequest(void);
- /// Terminates the connection; finishes any request being currently processed
+ /** Terminates the connection; finishes any request being currently processed */
void Terminate(void);
protected:
typedef std::map<AString, AString> cNameValueMap;
- /// The parent webserver that is to be notified of events on this connection
+ /** The parent webserver that is to be notified of events on this connection */
cHTTPServer & m_HTTPServer;
- /// All the incoming data until the entire request header is parsed
+ /** All the incoming data until the entire request header is parsed */
AString m_IncomingHeaderData;
- /// Status in which the request currently is
+ /** Status in which the request currently is */
eState m_State;
- /// Data that is queued for sending, once the socket becomes writable
+ /** Data that is queued for sending, once the socket becomes writable */
AString m_OutgoingData;
- /// The request being currently received (valid only between having parsed the headers and finishing receiving the body)
+ /** The request being currently received
+ Valid only between having parsed the headers and finishing receiving the body. */
cHTTPRequest * m_CurrentRequest;
- /// Number of bytes that remain to read for the complete body of the message to be received. Valid only in wcsRecvBody
+ /** Number of bytes that remain to read for the complete body of the message to be received.
+ Valid only in wcsRecvBody */
int m_CurrentRequestBodyRemaining;
diff --git a/src/HTTPServer/HTTPFormParser.cpp b/src/HTTPServer/HTTPFormParser.cpp
index 01c68881a..e661ea6f9 100644
--- a/src/HTTPServer/HTTPFormParser.cpp
+++ b/src/HTTPServer/HTTPFormParser.cpp
@@ -133,7 +133,6 @@ bool cHTTPFormParser::HasFormData(const cHTTPRequest & a_Request)
(a_Request.GetURL().find('?') != AString::npos)
)
);
- return false;
}
diff --git a/src/HTTPServer/HTTPMessage.cpp b/src/HTTPServer/HTTPMessage.cpp
index ab23866e6..98627eb8e 100644
--- a/src/HTTPServer/HTTPMessage.cpp
+++ b/src/HTTPServer/HTTPMessage.cpp
@@ -72,7 +72,8 @@ cHTTPRequest::cHTTPRequest(void) :
m_EnvelopeParser(*this),
m_IsValid(true),
m_UserData(NULL),
- m_HasAuth(false)
+ m_HasAuth(false),
+ m_AllowKeepAlive(false)
{
}
@@ -236,6 +237,10 @@ void cHTTPRequest::OnHeaderLine(const AString & a_Key, const AString & a_Value)
m_HasAuth = true;
}
}
+ if ((a_Key == "Connection") && (NoCaseCompare(a_Value, "keep-alive") == 0))
+ {
+ m_AllowKeepAlive = true;
+ }
AddHeader(a_Key, a_Value);
}
diff --git a/src/HTTPServer/HTTPMessage.h b/src/HTTPServer/HTTPMessage.h
index 2a4c2879e..ab3338db7 100644
--- a/src/HTTPServer/HTTPMessage.h
+++ b/src/HTTPServer/HTTPMessage.h
@@ -35,7 +35,7 @@ public:
// Force a virtual destructor in all descendants
virtual ~cHTTPMessage() {};
- /// Adds a header into the internal map of headers. Recognizes special headers: Content-Type and Content-Length
+ /** Adds a header into the internal map of headers. Recognizes special headers: Content-Type and Content-Length */
void AddHeader(const AString & a_Key, const AString & a_Value);
void SetContentType (const AString & a_ContentType) { m_ContentType = a_ContentType; }
@@ -51,10 +51,10 @@ protected:
cNameValueMap m_Headers;
- /// Type of the content; parsed by AddHeader(), set directly by SetContentLength()
+ /** Type of the content; parsed by AddHeader(), set directly by SetContentLength() */
AString m_ContentType;
- /// Length of the content that is to be received. -1 when the object is created, parsed by AddHeader() or set directly by SetContentLength()
+ /** Length of the content that is to be received. -1 when the object is created, parsed by AddHeader() or set directly by SetContentLength() */
int m_ContentLength;
} ;
@@ -76,64 +76,71 @@ public:
*/
int ParseHeaders(const char * a_Data, int a_Size);
- /// Returns true if the request did contain a Content-Length header
+ /** Returns true if the request did contain a Content-Length header */
bool HasReceivedContentLength(void) const { return (m_ContentLength >= 0); }
- /// Returns the method used in the request
+ /** Returns the method used in the request */
const AString & GetMethod(void) const { return m_Method; }
- /// Returns the URL used in the request
+ /** Returns the URL used in the request */
const AString & GetURL(void) const { return m_URL; }
- /// Returns the URL used in the request, without any parameters
+ /** Returns the URL used in the request, without any parameters */
AString GetBareURL(void) const;
- /// Sets the UserData pointer that is stored within this request. The request doesn't touch this data (doesn't delete it)!
+ /** Sets the UserData pointer that is stored within this request.
+ The request doesn't touch this data (doesn't delete it)! */
void SetUserData(void * a_UserData) { m_UserData = a_UserData; }
- /// Retrieves the UserData pointer that has been stored within this request.
+ /** Retrieves the UserData pointer that has been stored within this request. */
void * GetUserData(void) const { return m_UserData; }
- /// Returns true if more data is expected for the request headers
+ /** Returns true if more data is expected for the request headers */
bool IsInHeaders(void) const { return m_EnvelopeParser.IsInHeaders(); }
- /// Returns true if the request did present auth data that was understood by the parser
+ /** Returns true if the request did present auth data that was understood by the parser */
bool HasAuth(void) const { return m_HasAuth; }
- /// Returns the username that the request presented. Only valid if HasAuth() is true
+ /** Returns the username that the request presented. Only valid if HasAuth() is true */
const AString & GetAuthUsername(void) const { return m_AuthUsername; }
- /// Returns the password that the request presented. Only valid if HasAuth() is true
+ /** Returns the password that the request presented. Only valid if HasAuth() is true */
const AString & GetAuthPassword(void) const { return m_AuthPassword; }
+ bool DoesAllowKeepAlive(void) const { return m_AllowKeepAlive; }
+
protected:
- /// Parser for the envelope data
+ /** Parser for the envelope data */
cEnvelopeParser m_EnvelopeParser;
- /// True if the data received so far is parsed successfully. When false, all further parsing is skipped
+ /** True if the data received so far is parsed successfully. When false, all further parsing is skipped */
bool m_IsValid;
- /// Bufferred incoming data, while parsing for the request line
+ /** Bufferred incoming data, while parsing for the request line */
AString m_IncomingHeaderData;
- /// Method of the request (GET / PUT / POST / ...)
+ /** Method of the request (GET / PUT / POST / ...) */
AString m_Method;
- /// Full URL of the request
+ /** Full URL of the request */
AString m_URL;
- /// Data that the HTTPServer callbacks are allowed to store.
+ /** Data that the HTTPServer callbacks are allowed to store. */
void * m_UserData;
- /// Set to true if the request contains auth data that was understood by the parser
+ /** Set to true if the request contains auth data that was understood by the parser */
bool m_HasAuth;
- /// The username used for auth
+ /** The username used for auth */
AString m_AuthUsername;
- /// The password used for auth
+ /** The password used for auth */
AString m_AuthPassword;
+ /** Set to true if the request indicated that it supports keepalives.
+ If false, the server will close the connection once the request is finished */
+ bool m_AllowKeepAlive;
+
/** Parses the incoming data for the first line (RequestLine)
Returns the number of bytes consumed, or -1 for an error
diff --git a/src/HTTPServer/MultipartParser.cpp b/src/HTTPServer/MultipartParser.cpp
index b49f6ec07..14c2c00fa 100644
--- a/src/HTTPServer/MultipartParser.cpp
+++ b/src/HTTPServer/MultipartParser.cpp
@@ -156,7 +156,7 @@ void cMultipartParser::Parse(const char * a_Data, int a_Size)
// Append to buffer, then parse it:
m_IncomingData.append(a_Data, a_Size);
- while (true)
+ for (;;)
{
if (m_EnvelopeParser.IsInHeaders())
{
diff --git a/src/HTTPServer/NameValueParser.cpp b/src/HTTPServer/NameValueParser.cpp
index fd56f6b24..9ea8594ae 100644
--- a/src/HTTPServer/NameValueParser.cpp
+++ b/src/HTTPServer/NameValueParser.cpp
@@ -253,7 +253,6 @@ void cNameValueParser::Parse(const char * a_Data, int a_Size)
m_State = psValueRaw;
break;
}
- i++;
} // while (i < a_Size)
break;
} // case psEqual
diff --git a/src/Inventory.cpp b/src/Inventory.cpp
index a9b4ab9c5..0e1cedc85 100644
--- a/src/Inventory.cpp
+++ b/src/Inventory.cpp
@@ -57,6 +57,8 @@ int cInventory::HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlo
int cInventory::HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots)
{
+
+ UNUSED(a_ConsiderEmptySlots);
if ((a_BeginSlotNum < 0) || (a_BeginSlotNum >= invNumSlots))
{
LOGWARNING("%s: Bad BeginSlotNum, got %d, there are %d slots; correcting to 0.", __FUNCTION__, a_BeginSlotNum, invNumSlots - 1);
@@ -81,7 +83,7 @@ int cInventory::HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int
{
NumLeft -= MaxStack;
}
- else if (Slot.IsStackableWith(a_ItemStack))
+ else if (Slot.IsEqual(a_ItemStack))
{
NumLeft -= MaxStack - Slot.m_ItemCount;
}
@@ -96,8 +98,6 @@ int cInventory::HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int
-
-
int cInventory::AddItem(const cItem & a_Item, bool a_AllowNewStacks, bool a_tryToFillEquippedFirst)
{
cItem ToAdd(a_Item);
diff --git a/src/Item.cpp b/src/Item.cpp
index 196a260ef..9170006b6 100644
--- a/src/Item.cpp
+++ b/src/Item.cpp
@@ -91,28 +91,6 @@ bool cItem::DamageItem(short a_Amount)
-bool cItem::IsStackableWith(const cItem & a_OtherStack) const
-{
- if (a_OtherStack.m_ItemType != m_ItemType)
- {
- return false;
- }
- if (a_OtherStack.m_ItemDamage != m_ItemDamage)
- {
- return false;
- }
- if (a_OtherStack.m_Enchantments != m_Enchantments)
- {
- return false;
- }
-
- return true;
-}
-
-
-
-
-
bool cItem::IsFullStack(void) const
{
return (m_ItemCount >= ItemHandler(m_ItemType)->GetMaxStackSize());
@@ -153,6 +131,14 @@ void cItem::GetJson(Json::Value & a_OutValue) const
{
a_OutValue["ench"] = Enchantments;
}
+ if (!IsCustomNameEmpty())
+ {
+ a_OutValue["Name"] = m_CustomName;
+ }
+ if (!IsLoreEmpty())
+ {
+ a_OutValue["Lore"] = m_Lore;
+ }
}
}
@@ -169,6 +155,8 @@ void cItem::FromJson(const Json::Value & a_Value)
m_ItemDamage = (short)a_Value.get("Health", -1 ).asInt();
m_Enchantments.Clear();
m_Enchantments.AddFromString(a_Value.get("ench", "").asString());
+ m_CustomName = a_Value.get("Name", "").asString();
+ m_Lore = a_Value.get("Lore", "").asString();
}
}
@@ -246,7 +234,7 @@ void cItems::Delete(int a_Idx)
-void cItems::Set(int a_Idx, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage)
+void cItems::Set(int a_Idx, short a_ItemType, char a_ItemCount, short a_ItemDamage)
{
if ((a_Idx < 0) || (a_Idx >= (int)size()))
{
diff --git a/src/Item.h b/src/Item.h
index c60d0542c..727965112 100644
--- a/src/Item.h
+++ b/src/Item.h
@@ -36,7 +36,9 @@ public:
cItem(void) :
m_ItemType(E_ITEM_EMPTY),
m_ItemCount(0),
- m_ItemDamage(0)
+ m_ItemDamage(0),
+ m_CustomName(""),
+ m_Lore("")
{
}
@@ -46,12 +48,16 @@ public:
short a_ItemType,
char a_ItemCount = 1,
short a_ItemDamage = 0,
- const AString & a_Enchantments = ""
+ const AString & a_Enchantments = "",
+ const AString & a_CustomName = "",
+ const AString & a_Lore = ""
) :
m_ItemType (a_ItemType),
m_ItemCount (a_ItemCount),
m_ItemDamage (a_ItemDamage),
- m_Enchantments(a_Enchantments)
+ m_Enchantments(a_Enchantments),
+ m_CustomName (a_CustomName),
+ m_Lore (a_Lore)
{
if (!IsValidItem(m_ItemType))
{
@@ -69,7 +75,9 @@ public:
m_ItemType (a_CopyFrom.m_ItemType),
m_ItemCount (a_CopyFrom.m_ItemCount),
m_ItemDamage (a_CopyFrom.m_ItemDamage),
- m_Enchantments(a_CopyFrom.m_Enchantments)
+ m_Enchantments(a_CopyFrom.m_Enchantments),
+ m_CustomName (a_CopyFrom.m_CustomName),
+ m_Lore (a_CopyFrom.m_Lore)
{
}
@@ -80,6 +88,8 @@ public:
m_ItemCount = 0;
m_ItemDamage = 0;
m_Enchantments.Clear();
+ m_CustomName = "";
+ m_Lore = "";
}
@@ -96,13 +106,16 @@ public:
return ((m_ItemType <= 0) || (m_ItemCount <= 0));
}
-
+ /* Returns true if this itemstack can stack with the specified stack (types match, enchantments etc.) ItemCounts are ignored!
+ */
bool IsEqual(const cItem & a_Item) const
{
return (
IsSameType(a_Item) &&
(m_ItemDamage == a_Item.m_ItemDamage) &&
- (m_Enchantments == a_Item.m_Enchantments)
+ (m_Enchantments == a_Item.m_Enchantments) &&
+ (m_CustomName == a_Item.m_CustomName) &&
+ (m_Lore == a_Item.m_Lore)
);
}
@@ -111,7 +124,16 @@ public:
{
return (m_ItemType == a_Item.m_ItemType) || (IsEmpty() && a_Item.IsEmpty());
}
-
+
+
+ bool IsBothNameAndLoreEmpty(void) const
+ {
+ return (m_CustomName.empty() && m_Lore.empty());
+ }
+
+
+ bool IsCustomNameEmpty(void) const { return (m_CustomName.empty()); }
+ bool IsLoreEmpty(void) const { return (m_Lore.empty()); }
/// Returns a copy of this item with m_ItemCount set to 1. Useful to preserve enchantments etc. on stacked items
cItem CopyOne(void) const;
@@ -127,9 +149,6 @@ public:
inline bool IsDamageable(void) const { return (GetMaxDamage() > 0); }
- /// Returns true if this itemstack can stack with the specified stack (types match, enchantments etc.) ItemCounts are ignored!
- bool IsStackableWith(const cItem & a_OtherStack) const;
-
/// Returns true if the item is stacked up to its maximum stacking.
bool IsFullStack(void) const;
@@ -155,6 +174,8 @@ public:
short m_ItemType;
char m_ItemCount;
short m_ItemDamage;
+ AString m_CustomName;
+ AString m_Lore;
cEnchantments m_Enchantments;
};
// tolua_end
@@ -181,9 +202,9 @@ public:
void Delete(int a_Idx);
void Clear (void) {clear(); }
int Size (void) {return size(); }
- void Set (int a_Idx, ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage);
+ void Set (int a_Idx, short a_ItemType, char a_ItemCount, short a_ItemDamage);
- void Add (ENUM_ITEM_ID a_ItemType, char a_ItemCount, short a_ItemDamage)
+ void Add (short a_ItemType, char a_ItemCount, short a_ItemDamage)
{
push_back(cItem(a_ItemType, a_ItemCount, a_ItemDamage));
}
diff --git a/src/ItemGrid.cpp b/src/ItemGrid.cpp
index d2e6b1c69..e8b58695f 100644
--- a/src/ItemGrid.cpp
+++ b/src/ItemGrid.cpp
@@ -226,7 +226,7 @@ int cItemGrid::HowManyCanFit(const cItem & a_ItemStack, bool a_AllowNewStacks)
NumLeft -= MaxStack;
}
}
- else if (m_Slots[i].IsStackableWith(a_ItemStack))
+ else if (m_Slots[i].IsEqual(a_ItemStack))
{
NumLeft -= MaxStack - m_Slots[i].m_ItemCount;
}
@@ -275,7 +275,7 @@ int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_Priorit
(a_PrioritarySlot != -1) &&
(
m_Slots[a_PrioritarySlot].IsEmpty() ||
- m_Slots[a_PrioritarySlot].IsStackableWith(a_ItemStack)
+ m_Slots[a_PrioritarySlot].IsEqual(a_ItemStack)
)
)
{
@@ -285,7 +285,7 @@ int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks, int a_Priorit
// Scan existing stacks:
for (int i = m_NumSlots - 1; i >= 0; i--)
{
- if (m_Slots[i].IsStackableWith(a_ItemStack))
+ if (m_Slots[i].IsEqual(a_ItemStack))
{
NumLeft -= AddItemToSlot(a_ItemStack, i, NumLeft, MaxStack);
}
@@ -438,7 +438,7 @@ int cItemGrid::HowManyItems(const cItem & a_Item)
int res = 0;
for (int i = 0; i < m_NumSlots; i++)
{
- if (m_Slots[i].IsStackableWith(a_Item))
+ if (m_Slots[i].IsEqual(a_Item))
{
res += m_Slots[i].m_ItemCount;
}
diff --git a/src/Items/ItemBed.h b/src/Items/ItemBed.h
index ab4182eea..9b7c8bff8 100644
--- a/src/Items/ItemBed.h
+++ b/src/Items/ItemBed.h
@@ -37,7 +37,7 @@ public:
return false;
}
- a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetRotation());
+ a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetYaw());
// Check if there is empty space for the foot section:
Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta);
diff --git a/src/Items/ItemComparator.h b/src/Items/ItemComparator.h
index 3fbb7603d..3a5d1d200 100644
--- a/src/Items/ItemComparator.h
+++ b/src/Items/ItemComparator.h
@@ -30,7 +30,7 @@ public:
) override
{
a_BlockType = E_BLOCK_INACTIVE_COMPARATOR;
- a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation());
+ a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetYaw());
return true;
}
} ;
diff --git a/src/Items/ItemFishingRod.h b/src/Items/ItemFishingRod.h
index 941ce3b71..b2eaee63a 100644
--- a/src/Items/ItemFishingRod.h
+++ b/src/Items/ItemFishingRod.h
@@ -9,9 +9,11 @@
#pragma once
+#include "../Bindings/PluginManager.h"
#include "../Entities/Floater.h"
#include "../Entities/Entity.h"
#include "../Item.h"
+#include "../Root.h"
@@ -210,10 +212,14 @@ public:
}
}
-
+ if (cRoot::Get()->GetPluginManager()->CallHookPlayerFishing(*a_Player, Drops))
+ {
+ return true;
+ }
Vector3d FloaterPos = FloaterInfo.GetPos();
Vector3d FlyDirection = a_Player->GetEyePosition() - FloaterPos;
a_World->SpawnItemPickups(Drops, FloaterPos.x, FloaterPos.y, FloaterPos.z, FlyDirection.x, FlyDirection.y + 1, FlyDirection.z);
+ cRoot::Get()->GetPluginManager()->CallHookPlayerFished(*a_Player, Drops);
}
}
else
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index e39bb054b..db0ffc9db 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -25,7 +25,13 @@ public:
virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir);
/// Called when the client sends the SHOOT status in the lclk packet
- virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace) {}
+ virtual void OnItemShoot(cPlayer *, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace)
+ {
+ UNUSED(a_BlockX);
+ UNUSED(a_BlockY);
+ UNUSED(a_BlockZ);
+ UNUSED(a_BlockFace);
+ }
/// Called while the player diggs a block using this item
virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace);
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/Items/ItemMinecart.h b/src/Items/ItemMinecart.h
index f8eb31a49..4071f8c60 100644
--- a/src/Items/ItemMinecart.h
+++ b/src/Items/ItemMinecart.h
@@ -60,7 +60,7 @@ public:
cMinecart * Minecart = NULL;
switch (m_ItemType)
{
- case E_ITEM_MINECART: Minecart = new cEmptyMinecart (x, y, z); break;
+ case E_ITEM_MINECART: Minecart = new cRideableMinecart (x, y, z, cItem(), 1); break;
case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (x, y, z); break;
case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace (x, y, z); break;
case E_ITEM_MINECART_WITH_TNT: Minecart = new cMinecartWithTNT (x, y, z); break;
diff --git a/src/Items/ItemRedstoneDust.h b/src/Items/ItemRedstoneDust.h
index 38bf00ba6..de90c8075 100644
--- a/src/Items/ItemRedstoneDust.h
+++ b/src/Items/ItemRedstoneDust.h
@@ -27,7 +27,7 @@ public:
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
- if (!g_BlockIsTorchPlaceable[a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)]) // Some solid blocks, such as cocoa beans, are not suitable for dust
+ if (!g_BlockFullyOccupiesVoxel[a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)]) // Some solid blocks, such as cocoa beans, are not suitable for dust
{
return false;
}
diff --git a/src/Items/ItemRedstoneRepeater.h b/src/Items/ItemRedstoneRepeater.h
index f69f24eb8..e71c8e672 100644
--- a/src/Items/ItemRedstoneRepeater.h
+++ b/src/Items/ItemRedstoneRepeater.h
@@ -30,7 +30,7 @@ public:
) override
{
a_BlockType = E_BLOCK_REDSTONE_REPEATER_OFF;
- a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetRotation());
+ a_BlockMeta = cBlockRedstoneRepeaterHandler::RepeaterRotationToMetaData(a_Player->GetYaw());
return true;
}
} ;
diff --git a/src/Items/ItemSeeds.h b/src/Items/ItemSeeds.h
index 8ca86663f..67f0d38bd 100644
--- a/src/Items/ItemSeeds.h
+++ b/src/Items/ItemSeeds.h
@@ -56,7 +56,6 @@ public:
case E_ITEM_SEEDS: a_BlockType = E_BLOCK_CROPS; return true;
default: a_BlockType = E_BLOCK_AIR; return true;
}
- return false;
}
} ;
diff --git a/src/Items/ItemSign.h b/src/Items/ItemSign.h
index 5ccd79e29..8c134ab83 100644
--- a/src/Items/ItemSign.h
+++ b/src/Items/ItemSign.h
@@ -34,7 +34,7 @@ public:
{
if (a_BlockFace == BLOCK_FACE_TOP)
{
- a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetRotation());
+ a_BlockMeta = cBlockSignHandler::RotationToMetaData(a_Player->GetYaw());
a_BlockType = E_BLOCK_SIGN_POST;
}
else
diff --git a/src/LeakFinder.cpp b/src/LeakFinder.cpp
index 0f84adb2b..42a5afe56 100644
--- a/src/LeakFinder.cpp
+++ b/src/LeakFinder.cpp
@@ -95,15 +95,11 @@
*
**********************************************************************/
-#include <windows.h>
-#include <objidl.h> // Needed if compiled with "WIN32_LEAN_AND_MEAN"
+#include "Globals.h"
+
#include <tchar.h>
+#include <objidl.h> // Needed if compiled with "WIN32_LEAN_AND_MEAN"
#include <crtdbg.h>
-#include <stdio.h>
-
-#include <string>
-#include <vector>
-
#include "LeakFinder.h"
@@ -463,11 +459,11 @@ public:
pHashEntry->nDataSize = nDataSize;
pHashEntry->Next = NULL;
#ifdef _M_IX86
- pHashEntry->pCallstackOffset = (LPVOID) min(context.Ebp, context.Esp);
+ pHashEntry->pCallstackOffset = (LPVOID) std::min(context.Ebp, context.Esp);
#elif _M_X64
- pHashEntry->pCallstackOffset = (LPVOID) min(context.Rdi, context.Rsp);
+ pHashEntry->pCallstackOffset = (LPVOID) std::min(context.Rdi, context.Rsp);
#elif _M_IA64
- pHashEntry->pCallstackOffset = (LPVOID) min(context.IntSp, context.RsBSP);
+ pHashEntry->pCallstackOffset = (LPVOID) std::min(context.IntSp, context.RsBSP);
#else
#error "Platform not supported!"
#endif
@@ -490,7 +486,7 @@ public:
if (pHashEntry->nMaxStackSize > 0)
{
SIZE_T len = ((SIZE_T) pHashEntry->pStackBaseAddr + pHashEntry->nMaxStackSize) - (SIZE_T)pHashEntry->pCallstackOffset;
- bytesToRead = min(len, MAX_CALLSTACK_LEN_BUF);
+ bytesToRead = std::min(len, (SIZE_T)MAX_CALLSTACK_LEN_BUF);
}
// Now read the callstack:
if (ReadProcessMemory(GetCurrentProcess(), (LPCVOID) pHashEntry->pCallstackOffset, &(pHashEntry->pcCallstackAddr), bytesToRead, &(pHashEntry->nCallstackLen)) == 0)
@@ -866,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");
@@ -900,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/LightingThread.cpp b/src/LightingThread.cpp
index d7e60e458..a823c08cc 100644
--- a/src/LightingThread.cpp
+++ b/src/LightingThread.cpp
@@ -189,7 +189,7 @@ void cLightingThread::ChunkReady(int a_ChunkX, int a_ChunkZ)
{
if (
(itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1) &&
- (itr->x - a_ChunkX >= -1) && (itr->x - a_ChunkX <= 1)
+ (itr->z - a_ChunkZ >= -1) && (itr->z - a_ChunkZ <= 1)
)
{
// It is a neighbor
@@ -216,7 +216,7 @@ void cLightingThread::ChunkReady(int a_ChunkX, int a_ChunkZ)
void cLightingThread::Execute(void)
{
- while (true)
+ for (;;)
{
{
cCSLock Lock(m_CS);
@@ -495,6 +495,7 @@ void cLightingThread::CalcLightStep(
int & a_NumSeedsOut, unsigned char * a_IsSeedOut, unsigned int * a_SeedIdxOut
)
{
+ UNUSED(a_IsSeedIn);
int NumSeedsOut = 0;
for (int i = 0; i < a_NumSeedsIn; i++)
{
diff --git a/src/LineBlockTracer.cpp b/src/LineBlockTracer.cpp
index 9fcbca915..da1c7f2fd 100644
--- a/src/LineBlockTracer.cpp
+++ b/src/LineBlockTracer.cpp
@@ -196,8 +196,7 @@ bool cLineBlockTracer::Item(cChunk * a_Chunk)
ASSERT((m_CurrentY >= 0) && (m_CurrentY < cChunkDef::Height)); // This should be provided by FixStartAboveWorld() / FixStartBelowWorld()
// This is the actual line tracing loop.
- bool Finished = false;
- while (true)
+ for (;;)
{
// Report the current block through the callbacks:
if (a_Chunk == NULL)
diff --git a/src/Log.cpp b/src/Log.cpp
index a0de4531b..2d6be0f59 100644
--- a/src/Log.cpp
+++ b/src/Log.cpp
@@ -147,11 +147,11 @@ void cLog::Log(const char * a_Format, va_list argList)
-void cLog::Log(const char* a_Format, ...)
+void cLog::Log(const char * a_Format, ...)
{
va_list argList;
va_start(argList, a_Format);
- Log( a_Format, argList );
+ Log(a_Format, argList);
va_end(argList);
}
@@ -159,9 +159,9 @@ void cLog::Log(const char* a_Format, ...)
-void cLog::SimpleLog(const char* a_String)
+void cLog::SimpleLog(const char * a_String)
{
- Log("%s", a_String );
+ Log("%s", a_String);
}
diff --git a/src/Log.h b/src/Log.h
index d00022c6f..cba248dae 100644
--- a/src/Log.h
+++ b/src/Log.h
@@ -14,11 +14,11 @@ private:
public:
cLog(const AString & a_FileName);
~cLog();
- void Log(const char* a_Format, va_list argList );
- void Log(const char* a_Format, ...);
+ void Log(const char * a_Format, va_list argList);
+ void Log(const char * a_Format, ...);
// tolua_begin
- void SimpleLog(const char* a_String);
- void OpenLog( const char* a_FileName );
+ void SimpleLog(const char * a_String);
+ void OpenLog(const char * a_FileName);
void CloseLog();
void ClearLog();
static cLog* GetInstance();
diff --git a/src/MCServer.vcproj.user b/src/MCServer.vcproj.user
new file mode 100644
index 000000000..b17909f71
--- /dev/null
+++ b/src/MCServer.vcproj.user
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<VisualStudioUserFile
+ ProjectType="Visual C++"
+ Version="9,00"
+ ShowAllFiles="false"
+ >
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ >
+ <DebugSettings
+ Command="$(TargetPath)"
+ WorkingDirectory="..\MCServer"
+ CommandArguments=""
+ Attach="false"
+ DebuggerType="3"
+ RemoteCommand=""
+ HttpUrl=""
+ PDBPath=""
+ SQLDebugging=""
+ Environment=""
+ EnvironmentMerge="true"
+ DebuggerFlavor="0"
+ MPIRunCommand=""
+ MPIRunArguments=""
+ MPIRunWorkingDirectory=""
+ ApplicationCommand=""
+ ApplicationArguments=""
+ ShimCommand=""
+ MPIAcceptMode=""
+ MPIAcceptFilter=""
+ />
+ </Configuration>
+ <Configuration
+ Name="DebugProfile|Win32"
+ >
+ <DebugSettings
+ Command="$(TargetPath)"
+ WorkingDirectory="..\MCServer"
+ CommandArguments=""
+ Attach="false"
+ DebuggerType="3"
+ RemoteCommand=""
+ HttpUrl=""
+ PDBPath=""
+ SQLDebugging=""
+ Environment=""
+ EnvironmentMerge="true"
+ DebuggerFlavor="0"
+ MPIRunCommand=""
+ MPIRunArguments=""
+ MPIRunWorkingDirectory=""
+ ApplicationCommand=""
+ ApplicationArguments=""
+ ShimCommand=""
+ MPIAcceptMode=""
+ MPIAcceptFilter=""
+ />
+ </Configuration>
+ <Configuration
+ Name="MinSizeRel|Win32"
+ >
+ <DebugSettings
+ Command="$(TargetPath)"
+ WorkingDirectory="..\MCServer"
+ CommandArguments=""
+ Attach="false"
+ DebuggerType="3"
+ RemoteCommand=""
+ HttpUrl=""
+ PDBPath=""
+ SQLDebugging=""
+ Environment=""
+ EnvironmentMerge="true"
+ DebuggerFlavor="0"
+ MPIRunCommand=""
+ MPIRunArguments=""
+ MPIRunWorkingDirectory=""
+ ApplicationCommand=""
+ ApplicationArguments=""
+ ShimCommand=""
+ MPIAcceptMode=""
+ MPIAcceptFilter=""
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ >
+ <DebugSettings
+ Command="$(TargetPath)"
+ WorkingDirectory="..\MCServer"
+ CommandArguments=""
+ Attach="false"
+ DebuggerType="3"
+ RemoteCommand=""
+ HttpUrl=""
+ PDBPath=""
+ SQLDebugging=""
+ Environment=""
+ EnvironmentMerge="true"
+ DebuggerFlavor="0"
+ MPIRunCommand=""
+ MPIRunArguments=""
+ MPIRunWorkingDirectory=""
+ ApplicationCommand=""
+ ApplicationArguments=""
+ ShimCommand=""
+ MPIAcceptMode=""
+ MPIAcceptFilter=""
+ />
+ </Configuration>
+ <Configuration
+ Name="ReleaseProfile|Win32"
+ >
+ <DebugSettings
+ Command="$(TargetPath)"
+ WorkingDirectory="..\MCServer"
+ CommandArguments=""
+ Attach="false"
+ DebuggerType="3"
+ Remote="1"
+ RemoteMachine="ASAGA"
+ RemoteCommand=""
+ HttpUrl=""
+ PDBPath=""
+ SQLDebugging=""
+ Environment=""
+ EnvironmentMerge="true"
+ DebuggerFlavor="0"
+ MPIRunCommand=""
+ MPIRunArguments=""
+ MPIRunWorkingDirectory=""
+ ApplicationCommand=""
+ ApplicationArguments=""
+ ShimCommand=""
+ MPIAcceptMode=""
+ MPIAcceptFilter=""
+ />
+ </Configuration>
+ <Configuration
+ Name="RelWithDebInfo|Win32"
+ >
+ <DebugSettings
+ Command="$(TargetPath)"
+ WorkingDirectory="..\MCServer"
+ CommandArguments=""
+ Attach="false"
+ DebuggerType="3"
+ RemoteCommand=""
+ HttpUrl=""
+ PDBPath=""
+ SQLDebugging=""
+ Environment=""
+ EnvironmentMerge="true"
+ DebuggerFlavor="0"
+ MPIRunCommand=""
+ MPIRunArguments=""
+ MPIRunWorkingDirectory=""
+ ApplicationCommand=""
+ ApplicationArguments=""
+ ShimCommand=""
+ MPIAcceptMode=""
+ MPIAcceptFilter=""
+ />
+ </Configuration>
+ </Configurations>
+</VisualStudioUserFile>
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/Chicken.cpp b/src/Mobs/Chicken.cpp
index 087fd088a..fab92ce49 100644
--- a/src/Mobs/Chicken.cpp
+++ b/src/Mobs/Chicken.cpp
@@ -9,7 +9,6 @@
-
cChicken::cChicken(void) :
super("Chicken", mtChicken, "mob.chicken.hurt", "mob.chicken.hurt", 0.3, 0.4),
m_EggDropTimer(0)
diff --git a/src/Mobs/Chicken.h b/src/Mobs/Chicken.h
index 979c4d8a0..a4c1d6b9e 100644
--- a/src/Mobs/Chicken.h
+++ b/src/Mobs/Chicken.h
@@ -19,8 +19,9 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual void Tick(float a_Dt, cChunk & a_Chunk) override;
-private:
+ virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_SEEDS); }
+private:
int m_EggDropTimer;
} ;
diff --git a/src/Mobs/Cow.cpp b/src/Mobs/Cow.cpp
index 9eb74dac2..d8e905217 100644
--- a/src/Mobs/Cow.cpp
+++ b/src/Mobs/Cow.cpp
@@ -41,5 +41,3 @@ void cCow::OnRightClicked(cPlayer & a_Player)
}
}
-
-
diff --git a/src/Mobs/Cow.h b/src/Mobs/Cow.h
index 0391d4a31..973171ab5 100644
--- a/src/Mobs/Cow.h
+++ b/src/Mobs/Cow.h
@@ -19,6 +19,9 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
+
+ virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); }
+
} ;
diff --git a/src/Mobs/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 76df76633..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);
- SetRotation( 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/Mooshroom.cpp b/src/Mobs/Mooshroom.cpp
index 940e2db44..88101cd83 100644
--- a/src/Mobs/Mooshroom.cpp
+++ b/src/Mobs/Mooshroom.cpp
@@ -6,7 +6,6 @@
-
// TODO: Milk Cow
@@ -30,4 +29,3 @@ void cMooshroom::GetDrops(cItems & a_Drops, cEntity * a_Killer)
-
diff --git a/src/Mobs/Mooshroom.h b/src/Mobs/Mooshroom.h
index 73f6348b6..c94301098 100644
--- a/src/Mobs/Mooshroom.h
+++ b/src/Mobs/Mooshroom.h
@@ -18,6 +18,8 @@ public:
CLASS_PROTODEF(cMooshroom);
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
+
+ virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); }
} ;
diff --git a/src/Mobs/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..904cd63cc 100644
--- a/src/Mobs/PassiveMonster.cpp
+++ b/src/Mobs/PassiveMonster.cpp
@@ -2,9 +2,8 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "PassiveMonster.h"
-#include "../MersenneTwister.h"
#include "../World.h"
-
+#include "../Entities/Player.h"
@@ -36,19 +35,22 @@ 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)
+ CheckEventLostPlayer();
+ }
+ cItem FollowedItem = GetFollowedItem();
+ if (FollowedItem.IsEmpty())
+ {
+ return;
+ }
+ cPlayer * a_Closest_Player = m_World->FindClosestPlayer(GetPosition(), (float)m_SightDistance);
+ if (a_Closest_Player != NULL)
+ {
+ if (a_Closest_Player->GetEquippedItem().IsEqual(FollowedItem))
{
- if (m_EMState == ESCAPING)
- {
- CheckEventLostPlayer();
- }
+ Vector3d PlayerPos = a_Closest_Player->GetPosition();
+ MoveToPosition(PlayerPos);
}
}
}
diff --git a/src/Mobs/PassiveMonster.h b/src/Mobs/PassiveMonster.h
index 14a6be6b1..0b3c155da 100644
--- a/src/Mobs/PassiveMonster.h
+++ b/src/Mobs/PassiveMonster.h
@@ -19,6 +19,9 @@ public:
/// When hit by someone, run away
virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
+ /** Returns the item that the animal of this class follows when a player holds it in hand
+ Return an empty item not to follow (default). */
+ virtual const cItem GetFollowedItem(void) const { return cItem(); }
} ;
diff --git a/src/Mobs/Pig.cpp b/src/Mobs/Pig.cpp
index 0871a38a9..d8f3dda37 100644
--- a/src/Mobs/Pig.cpp
+++ b/src/Mobs/Pig.cpp
@@ -73,5 +73,3 @@ void cPig::OnRightClicked(cPlayer & a_Player)
-
-
diff --git a/src/Mobs/Pig.h b/src/Mobs/Pig.h
index 4fd0d8db8..d434324c1 100644
--- a/src/Mobs/Pig.h
+++ b/src/Mobs/Pig.h
@@ -19,6 +19,9 @@ public:
virtual void GetDrops(cItems & a_Drops, cEntity * a_Killer = NULL) override;
virtual void OnRightClicked(cPlayer & a_Player) override;
+
+ virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_CARROT); }
+
bool IsSaddled(void) const { return m_bIsSaddled; }
private:
diff --git a/src/Mobs/Sheep.cpp b/src/Mobs/Sheep.cpp
index bda4ccff8..4761103e5 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,40 @@ 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..402e8e61c 100644
--- a/src/Mobs/Sheep.h
+++ b/src/Mobs/Sheep.h
@@ -19,6 +19,10 @@ 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;
+
+ virtual const cItem GetFollowedItem(void) const override { return cItem(E_ITEM_WHEAT); }
+
bool IsSheared(void) const { return m_IsSheared; }
int GetFurColor(void) const { return m_WoolColor; }
@@ -26,6 +30,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..44ec14295 100644
--- a/src/Mobs/Villager.cpp
+++ b/src/Mobs/Villager.cpp
@@ -3,6 +3,8 @@
#include "Villager.h"
#include "../World.h"
+#include "../BlockArea.h"
+#include "../Blocks/BlockHandler.h"
@@ -10,7 +12,9 @@
cVillager::cVillager(eVillagerType VillagerType) :
super("Villager", mtVillager, "", "", 0.6, 1.8),
- m_Type(VillagerType)
+ m_Type(VillagerType),
+ m_VillagerAction(false),
+ m_ActionCountDown(-1)
{
}
@@ -21,7 +25,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)
{
@@ -33,3 +37,153 @@ void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
+
+void cVillager::Tick(float a_Dt, cChunk & a_Chunk)
+{
+ super::Tick(a_Dt, a_Chunk);
+
+ if (m_ActionCountDown > -1)
+ {
+ m_ActionCountDown--;
+ if (m_ActionCountDown == 0)
+ {
+ switch (m_Type)
+ {
+ case vtFarmer:
+ {
+ HandleFarmerPlaceCrops();
+ }
+ }
+ }
+ return;
+ }
+
+ if (m_VillagerAction)
+ {
+ switch (m_Type)
+ {
+ case vtFarmer:
+ {
+ HandleFarmerTryHarvestCrops();
+ }
+ }
+ m_VillagerAction = false;
+ return;
+ }
+
+ // Don't always try to do a special action. Each tick has 1% to do a special action.
+ if (m_World->GetTickRandomNumber(99) != 0)
+ {
+ return;
+ }
+
+ switch (m_Type)
+ {
+ case vtFarmer:
+ {
+ HandleFarmerPrepareFarmCrops();
+ }
+ }
+}
+
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Farmer functions.
+void cVillager::HandleFarmerPrepareFarmCrops()
+{
+ if (!m_World->VillagersShouldHarvestCrops())
+ {
+ return;
+ }
+
+ cBlockArea Surrounding;
+ /// Read a 11x7x11 area.
+ Surrounding.Read(
+ m_World,
+ (int) GetPosX() - 5,
+ (int) GetPosX() + 5,
+ (int) GetPosY() - 3,
+ (int) GetPosY() + 3,
+ (int) GetPosZ() - 5,
+ (int) GetPosZ() + 5
+ );
+
+ for (int I = 0; I < 5; I++)
+ {
+ for (int Y = 0; Y < 6; Y++)
+ {
+ // Pick random coordinates and check for crops.
+ int X = m_World->GetTickRandomNumber(11);
+ int Z = m_World->GetTickRandomNumber(11);
+
+ // A villager can't farm this.
+ if (!IsBlockFarmable(Surrounding.GetRelBlockType(X, Y, Z)))
+ {
+ continue;
+ }
+ if (Surrounding.GetRelBlockMeta(X, Y, Z) != 0x7)
+ {
+ continue;
+ }
+
+ m_VillagerAction = true;
+ m_CropsPos = Vector3i((int) GetPosX() + X - 5, (int) GetPosY() + Y - 3, (int) GetPosZ() + Z - 5);
+ MoveToPosition(Vector3f((float) (m_CropsPos.x + 0.5), (float) m_CropsPos.y, (float) (m_CropsPos.z + 0.5)));
+ return;
+ } // for Y loop.
+ } // Repeat the procces 5 times.
+}
+
+
+
+
+
+void cVillager::HandleFarmerTryHarvestCrops()
+{
+ // Harvest the crops if the villager isn't moving and if the crops are closer then 2 blocks.
+ if (!m_bMovingToDestination && (GetPosition() - m_CropsPos).Length() < 2)
+ {
+ // Check if the blocks didn't change while the villager was walking to the coordinates.
+ BLOCKTYPE CropBlock = m_World->GetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z);
+ if (IsBlockFarmable(CropBlock) && m_World->GetBlockMeta(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z) == 0x7)
+ {
+ cBlockHandler * Handler = cBlockHandler::GetBlockHandler(CropBlock);
+ Handler->DropBlock(m_World, this, m_CropsPos.x, m_CropsPos.y, m_CropsPos.z);
+ m_World->SetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z, E_BLOCK_AIR, 0);
+ m_ActionCountDown = 20;
+ }
+ }
+}
+
+
+
+
+void cVillager::HandleFarmerPlaceCrops()
+{
+ // Check if there is still farmland at the spot where the crops were.
+ if (m_World->GetBlock(m_CropsPos.x, m_CropsPos.y - 1, m_CropsPos.z) == E_BLOCK_FARMLAND)
+ {
+ m_World->SetBlock(m_CropsPos.x, m_CropsPos.y, m_CropsPos.z, E_BLOCK_CROPS, 0);
+ }
+}
+
+
+
+
+
+bool cVillager::IsBlockFarmable(BLOCKTYPE a_BlockType)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_CROPS:
+ case E_BLOCK_POTATOES:
+ case E_BLOCK_CARROTS:
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
diff --git a/src/Mobs/Villager.h b/src/Mobs/Villager.h
index 4cd9aaa8e..b99ae876f 100644
--- a/src/Mobs/Villager.h
+++ b/src/Mobs/Villager.h
@@ -29,12 +29,36 @@ public:
CLASS_PROTODEF(cVillager);
+ // Override functions
virtual void DoTakeDamage(TakeDamageInfo & a_TDI) override;
- int GetVilType(void) const { return m_Type; }
+ virtual void Tick (float a_Dt, cChunk & a_Chunk) override;
+
+ // cVillager functions
+ /** return true if the given blocktype are: crops, potatoes or carrots.*/
+ bool IsBlockFarmable(BLOCKTYPE a_BlockType);
+
+ //////////////////////////////////////////////////////////////////
+ // Farmer functions
+ /** It searches in a 11x7x11 area for crops. If it found some it will navigate to them.*/
+ void HandleFarmerPrepareFarmCrops();
+
+ /** Looks if the farmer has reached it's destination, and if it's still crops and the destination is closer then 2 blocks it will harvest them.*/
+ void HandleFarmerTryHarvestCrops();
+
+ /** Replaces the crops he harvested.*/
+ void HandleFarmerPlaceCrops();
+
+ // Get and set functions.
+ int GetVilType(void) const { return m_Type; }
+ Vector3i GetCropsPos(void) const { return m_CropsPos; }
+ bool DoesHaveActionActivated(void) const { return m_VillagerAction; }
private:
+ int m_ActionCountDown;
int m_Type;
+ bool m_VillagerAction;
+ Vector3i m_CropsPos;
} ;
diff --git a/src/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 55454a4b5..af50eda5d 100644
--- a/src/OSSupport/BlockingTCPLink.cpp
+++ b/src/OSSupport/BlockingTCPLink.cpp
@@ -2,18 +2,7 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "BlockingTCPLink.h"
-
-
-
-
-
-#ifdef _WIN32
- #define MSG_NOSIGNAL (0)
-#endif
-#ifdef __MACH__
- #define MSG_NOSIGNAL (0)
-#endif
-
+#include "Errors.h"
@@ -86,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/CriticalSection.h b/src/OSSupport/CriticalSection.h
index 1bfe81439..73a71f5e1 100644
--- a/src/OSSupport/CriticalSection.h
+++ b/src/OSSupport/CriticalSection.h
@@ -14,9 +14,14 @@ public:
void Lock(void);
void Unlock(void);
+ // IsLocked/IsLockedByCurrentThread are only used in ASSERT statements, but because of the changes with ASSERT they must always be defined
+ // The fake versions (in Release) will not effect the program in any way
#ifdef _DEBUG
bool IsLocked(void);
bool IsLockedByCurrentThread(void);
+ #else
+ bool IsLocked(void) { return false; }
+ bool IsLockedByCurrentThread(void) { return false; }
#endif // _DEBUG
private:
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/Queue.h b/src/OSSupport/Queue.h
new file mode 100644
index 000000000..6c3d58295
--- /dev/null
+++ b/src/OSSupport/Queue.h
@@ -0,0 +1,178 @@
+
+// Queue.h
+
+// Implements the cQueue class representing a thread safe queue
+
+#pragma once
+
+/*
+Items can be added multiple times to a queue, there are two functions for
+adding, EnqueueItem() and EnqueueItemIfNotPresent(). The first one always
+enqueues the specified item, the second one checks if the item is already
+present and only queues it if it isn't.
+
+Usage:
+To create a queue of type T, instantiate a cQueue<T> object. You can also
+modify the behavior of the queue when deleting items and when adding items
+that are already in the queue by providing a second parameter, a class that
+implements the functions Delete() and Combine(). An example is given in
+cQueueFuncs and is used as the default behavior.
+*/
+
+/// This empty struct allows for the callback functions to be inlined
+template<class T>
+struct cQueueFuncs
+{
+public:
+
+ /// Called when an Item is deleted from the queue without being returned
+ static void Delete(T) {};
+
+ /// Called when an Item is inserted with EnqueueItemIfNotPresent and there is another equal value already inserted
+ static void Combine(T & a_existing, const T & a_new) {};
+};
+
+
+
+
+
+template <class ItemType, class Funcs = cQueueFuncs<ItemType> >
+class cQueue
+{
+ // The actual storage type for the queue
+ typedef typename std::list<ItemType> QueueType;
+
+ // Make iterator an alias for the QueueType's iterator
+ typedef typename QueueType::iterator iterator;
+
+public:
+ cQueue() {}
+ ~cQueue() {}
+
+
+ /// Enqueues an item to the queue, may block if other threads are accessing the queue.
+ void EnqueueItem(ItemType a_Item)
+ {
+ cCSLock Lock(m_CS);
+ m_Contents.push_back(a_Item);
+ m_evtAdded.Set();
+ }
+
+
+ /// Enqueues an item in the queue if not already present (as determined by operator ==). Blocks other threads from accessing the queue.
+ void EnqueueItemIfNotPresent(ItemType a_Item)
+ {
+ cCSLock Lock(m_CS);
+
+ for (iterator itr = m_Contents.begin(); itr != m_Contents.end(); ++itr)
+ {
+ if ((*itr) == a_Item)
+ {
+ Funcs::Combine(*itr, a_Item);
+ return;
+ }
+ }
+ m_Contents.push_back(a_Item);
+ m_evtAdded.Set();
+ }
+
+
+ /// Dequeues an item from the queue if any are present.
+ /// Returns true if successful. Value of item is undefined if dequeuing was unsuccessful.
+ bool TryDequeueItem(ItemType & item)
+ {
+ cCSLock Lock(m_CS);
+ if (m_Contents.size() == 0)
+ {
+ return false;
+ }
+ item = m_Contents.front();
+ m_Contents.pop_front();
+ m_evtRemoved.Set();
+ return true;
+ }
+
+
+ /// Dequeues an item from the queue, blocking until an item is available.
+ ItemType DequeueItem(void)
+ {
+ cCSLock Lock(m_CS);
+ while (m_Contents.size() == 0)
+ {
+ cCSUnlock Unlock(Lock);
+ m_evtAdded.Wait();
+ }
+ ItemType item = m_Contents.front();
+ m_Contents.pop_front();
+ m_evtRemoved.Set();
+ return item;
+ }
+
+
+ /// Blocks until the queue is empty.
+ void BlockTillEmpty(void)
+ {
+ cCSLock Lock(m_CS);
+ while (!m_Contents.empty())
+ {
+ cCSUnlock Unlock(Lock);
+ m_evtRemoved.Wait();
+ }
+ }
+
+
+ /// Removes all Items from the Queue, calling Delete on each of them.
+ void Clear(void)
+ {
+ cCSLock Lock(m_CS);
+ while (!m_Contents.empty())
+ {
+ Funcs::Delete(m_Contents.front());
+ m_Contents.pop_front();
+ }
+ }
+
+
+ /// Returns the size at time of being called.
+ /// Do not use to determine whether to call DequeueItem(), use TryDequeueItem() instead
+ size_t Size(void)
+ {
+ cCSLock Lock(m_CS);
+ return m_Contents.size();
+ }
+
+
+ /// Removes the item from the queue. If there are multiple such items, only the first one is removed.
+ /// Returns true if the item has been removed, false if no such item found.
+ bool Remove(ItemType a_Item)
+ {
+ cCSLock Lock(m_CS);
+ for (iterator itr = m_Contents.begin(); itr != m_Contents.end(); ++itr)
+ {
+ if ((*itr) == a_Item)
+ {
+ m_Contents.erase(itr);
+ m_evtRemoved.Set();
+ return true;
+ }
+ }
+ return false;
+ }
+
+private:
+ /// The contents of the queue
+ QueueType m_Contents;
+
+ /// Mutex that protects access to the queue contents
+ cCriticalSection m_CS;
+
+ /// Event that is signalled when an item is added
+ cEvent m_evtAdded;
+
+ /// Event that is signalled when an item is removed (both dequeued or erased)
+ cEvent m_evtRemoved;
+};
+
+
+
+
diff --git a/src/OSSupport/Socket.cpp b/src/OSSupport/Socket.cpp
index f25f800c2..6afaceedf 100644
--- a/src/OSSupport/Socket.cpp
+++ b/src/OSSupport/Socket.cpp
@@ -6,7 +6,8 @@
#ifndef _WIN32
#include <netdb.h>
#include <unistd.h>
- #include <arpa/inet.h> //inet_ntoa()
+ #include <arpa/inet.h> // inet_ntoa()
+ #include <sys/ioctl.h> // ioctl()
#else
#define socklen_t int
#endif
@@ -72,10 +73,6 @@ void cSocket::CloseSocket()
#else // _WIN32
- if (shutdown(m_Socket, SHUT_RDWR) != 0)//SD_BOTH);
- {
- LOGWARN("Error on shutting down socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str());
- }
if (close(m_Socket) != 0)
{
LOGWARN("Error closing socket %d (%s): %s", m_Socket, m_IPString.c_str(), GetLastErrorString().c_str());
@@ -91,52 +88,19 @@ void cSocket::CloseSocket()
-AString cSocket::GetErrorString( int a_ErrNo )
+void cSocket::ShutdownReadWrite(void)
{
- 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
+ int res = shutdown(m_Socket, SD_BOTH);
+ #else
+ int res = shutdown(m_Socket, SHUT_RDWR);
+ #endif
+ if (res != 0)
{
- Printf(Out, "Error %d while getting error string for error #%d!", errno, a_ErrNo);
- return Out;
+ LOGWARN("%s: Error shutting down socket %d (%s): %d (%s)",
+ __FUNCTION__, m_Socket, m_IPString.c_str(), this->GetLastError(), GetLastErrorString().c_str()
+ );
}
-
- #endif // else _WIN32
}
@@ -357,7 +321,7 @@ bool cSocket::ConnectIPv4(const AString & a_HostNameOrAddr, unsigned short a_Por
-int cSocket::Receive(char* a_Buffer, unsigned int a_Length, unsigned int a_Flags)
+int cSocket::Receive(char * a_Buffer, unsigned int a_Length, unsigned int a_Flags)
{
return recv(m_Socket, a_Buffer, a_Length, a_Flags);
}
@@ -368,7 +332,7 @@ int cSocket::Receive(char* a_Buffer, unsigned int a_Length, unsigned int a_Flags
int cSocket::Send(const char * a_Buffer, unsigned int a_Length)
{
- return send(m_Socket, a_Buffer, a_Length, 0);
+ return send(m_Socket, a_Buffer, a_Length, MSG_NOSIGNAL);
}
@@ -391,3 +355,25 @@ unsigned short cSocket::GetPort(void) const
+
+void cSocket::SetNonBlocking(void)
+{
+ #ifdef _WIN32
+ u_long NonBlocking = 1;
+ int res = ioctlsocket(m_Socket, FIONBIO, &NonBlocking);
+ #else
+ int NonBlocking = 1;
+ int res = ioctl(m_Socket, FIONBIO, (char *)&NonBlocking);
+ #endif
+ if (res != 0)
+ {
+ LOGERROR("Cannot set socket to non-blocking. This would make the server deadlock later on, aborting.\nErr: %d, %d, %s",
+ res, GetLastError(), GetLastErrorString().c_str()
+ );
+ abort();
+ }
+}
+
+
+
+
diff --git a/src/OSSupport/Socket.h b/src/OSSupport/Socket.h
index 81bfd28fc..bdc2babf5 100644
--- a/src/OSSupport/Socket.h
+++ b/src/OSSupport/Socket.h
@@ -5,6 +5,18 @@
+// Windows and MacOSX don't have the MSG_NOSIGNAL flag
+#if ( \
+ defined(_WIN32) || \
+ (defined(__APPLE__) && defined(__MACH__)) \
+)
+ #define MSG_NOSIGNAL (0)
+#endif
+
+
+#include "Errors.h"
+
+
class cSocket
{
public:
@@ -12,6 +24,12 @@ public:
{
IPv4 = AF_INET,
IPv6 = AF_INET6,
+
+ #ifdef _WIN32
+ ErrWouldBlock = WSAEWOULDBLOCK,
+ #else
+ ErrWouldBlock = EWOULDBLOCK,
+ #endif
} ;
#ifdef _WIN32
@@ -27,7 +45,11 @@ public:
bool IsValid(void) const { return IsValidSocket(m_Socket); }
void CloseSocket(void);
-
+
+ /** Notifies the socket that we don't expect any more reads nor writes on it.
+ Most TCPIP implementations use this to send the FIN flag in a packet */
+ void ShutdownReadWrite(void);
+
operator xSocket(void) const;
xSocket GetSocket(void) const;
@@ -41,11 +63,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
@@ -95,8 +116,11 @@ public:
unsigned short GetPort(void) const; // Returns 0 on failure
const AString & GetIPString(void) const { return m_IPString; }
+
+ /** Sets the socket into non-blocking mode */
+ void SetNonBlocking(void);
private:
xSocket m_Socket;
AString m_IPString;
-}; \ No newline at end of file
+};
diff --git a/src/OSSupport/SocketThreads.cpp b/src/OSSupport/SocketThreads.cpp
index 3e505616c..3e2631733 100644
--- a/src/OSSupport/SocketThreads.cpp
+++ b/src/OSSupport/SocketThreads.cpp
@@ -7,6 +7,7 @@
#include "Globals.h"
#include "SocketThreads.h"
+#include "Errors.h"
@@ -71,29 +72,6 @@ bool cSocketThreads::AddClient(const cSocket & a_Socket, cCallback * a_Client)
-/*
-void cSocketThreads::RemoveClient(const cSocket * a_Socket)
-{
- // Remove the socket (and associated client) from processing
-
- cCSLock Lock(m_CS);
- for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
- {
- if ((*itr)->RemoveSocket(a_Socket))
- {
- return;
- }
- } // for itr - m_Threads[]
-
- // Cannot assert here, this may actually happen legally, since cClientHandle has to clean up the socket and it may have already closed in the meantime
- // ASSERT(!"Removing an unknown socket");
-}
-*/
-
-
-
-
-
void cSocketThreads::RemoveClient(const cCallback * a_Client)
{
// Remove the associated socket and the client from processing
@@ -155,47 +133,6 @@ void cSocketThreads::Write(const cCallback * a_Client, const AString & a_Data)
-/// Stops reading from the socket - when this call returns, no more calls to the callbacks are made
-void cSocketThreads::StopReading(const cCallback * a_Client)
-{
- cCSLock Lock(m_CS);
- for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
- {
- if ((*itr)->StopReading(a_Client))
- {
- return;
- }
- } // for itr - m_Threads[]
-
- // Cannot assert, this normally happens if the socket is closed before the client deinitializes
- // ASSERT(!"Stopping reading on an unknown client");
-}
-
-
-
-
-
-/// Queues the socket for closing, as soon as its outgoing data is sent
-void cSocketThreads::QueueClose(const cCallback * a_Client)
-{
- LOGD("QueueClose(client %p)", a_Client);
-
- cCSLock Lock(m_CS);
- for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
- {
- if ((*itr)->QueueClose(a_Client))
- {
- return;
- }
- } // for itr - m_Threads[]
-
- ASSERT(!"Queueing close of an unknown client");
-}
-
-
-
-
-
////////////////////////////////////////////////////////////////////////////////
// cSocketThreads::cSocketThread:
@@ -233,13 +170,14 @@ cSocketThreads::cSocketThread::~cSocketThread()
void cSocketThreads::cSocketThread::AddClient(const cSocket & a_Socket, cCallback * a_Client)
{
+ ASSERT(m_Parent->m_CS.IsLockedByCurrentThread());
ASSERT(m_NumSlots < MAX_SLOTS); // Use HasEmptySlot() to check before adding
m_Slots[m_NumSlots].m_Client = a_Client;
m_Slots[m_NumSlots].m_Socket = a_Socket;
+ m_Slots[m_NumSlots].m_Socket.SetNonBlocking();
m_Slots[m_NumSlots].m_Outgoing.clear();
- m_Slots[m_NumSlots].m_ShouldClose = false;
- m_Slots[m_NumSlots].m_ShouldCallClient = true;
+ m_Slots[m_NumSlots].m_State = sSlot::ssNormal;
m_NumSlots++;
// Notify the thread of the change:
@@ -253,7 +191,7 @@ void cSocketThreads::cSocketThread::AddClient(const cSocket & a_Socket, cCallbac
bool cSocketThreads::cSocketThread::RemoveClient(const cCallback * a_Client)
{
- // Returns true if removed, false if not found
+ ASSERT(m_Parent->m_CS.IsLockedByCurrentThread());
if (m_NumSlots == 0)
{
@@ -267,36 +205,31 @@ bool cSocketThreads::cSocketThread::RemoveClient(const cCallback * a_Client)
continue;
}
- // Found, remove it:
- m_Slots[i] = m_Slots[--m_NumSlots];
-
- // Notify the thread of the change:
- ASSERT(m_ControlSocket2.IsValid());
- m_ControlSocket2.Send("r", 1);
- return true;
- } // for i - m_Slots[]
-
- // Not found
- return false;
-}
-
-
-
-
-
-bool cSocketThreads::cSocketThread::RemoveSocket(const cSocket * a_Socket)
-{
- // Returns true if removed, false if not found
-
- for (int i = m_NumSlots - 1; i >= 0 ; --i)
- {
- if (m_Slots[i].m_Socket != *a_Socket)
+ // Found the slot:
+ if (m_Slots[i].m_State == sSlot::ssRemoteClosed)
{
- continue;
+ // The remote has already closed the socket, remove the slot altogether:
+ m_Slots[i] = m_Slots[--m_NumSlots];
+ }
+ else
+ {
+ // Query and queue the last batch of outgoing data:
+ AString Data;
+ m_Slots[i].m_Client->GetOutgoingData(Data);
+ m_Slots[i].m_Outgoing.append(Data);
+ if (m_Slots[i].m_Outgoing.empty())
+ {
+ // No more outgoing data, shut the socket down immediately:
+ m_Slots[i].m_Socket.ShutdownReadWrite();
+ m_Slots[i].m_State = sSlot::ssShuttingDown;
+ }
+ else
+ {
+ // More data to send, shut down reading and wait for the rest to get sent:
+ m_Slots[i].m_State = sSlot::ssWritingRestOut;
+ }
+ m_Slots[i].m_Client = NULL;
}
-
- // Found, remove it:
- m_Slots[i] = m_Slots[--m_NumSlots];
// Notify the thread of the change:
ASSERT(m_ControlSocket2.IsValid());
@@ -314,6 +247,8 @@ bool cSocketThreads::cSocketThread::RemoveSocket(const cSocket * a_Socket)
bool cSocketThreads::cSocketThread::HasClient(const cCallback * a_Client) const
{
+ ASSERT(m_Parent->m_CS.IsLockedByCurrentThread());
+
for (int i = m_NumSlots - 1; i >= 0; --i)
{
if (m_Slots[i].m_Client == a_Client)
@@ -346,6 +281,8 @@ bool cSocketThreads::cSocketThread::HasSocket(const cSocket * a_Socket) const
bool cSocketThreads::cSocketThread::NotifyWrite(const cCallback * a_Client)
{
+ ASSERT(m_Parent->m_CS.IsLockedByCurrentThread());
+
if (HasClient(a_Client))
{
// Notify the thread that there's another packet in the queue:
@@ -362,7 +299,7 @@ bool cSocketThreads::cSocketThread::NotifyWrite(const cCallback * a_Client)
bool cSocketThreads::cSocketThread::Write(const cCallback * a_Client, const AString & a_Data)
{
- // Returns true if socket handled by this thread
+ ASSERT(m_Parent->m_CS.IsLockedByCurrentThread());
for (int i = m_NumSlots - 1; i >= 0; --i)
{
if (m_Slots[i].m_Client == a_Client)
@@ -383,47 +320,6 @@ bool cSocketThreads::cSocketThread::Write(const cCallback * a_Client, const AStr
-bool cSocketThreads::cSocketThread::StopReading (const cCallback * a_Client)
-{
- // Returns true if client handled by this thread
- for (int i = m_NumSlots - 1; i >= 0; --i)
- {
- if (m_Slots[i].m_Client == a_Client)
- {
- m_Slots[i].m_ShouldCallClient = false;
- return true;
- }
- } // for i - m_Slots[]
- return false;
-}
-
-
-
-
-
-bool cSocketThreads::cSocketThread::QueueClose(const cCallback * a_Client)
-{
- // Returns true if socket handled by this thread
- for (int i = m_NumSlots - 1; i >= 0; --i)
- {
- if (m_Slots[i].m_Client == a_Client)
- {
- m_Slots[i].m_ShouldClose = true;
-
- // Notify the thread that there's a close queued (in case its conditions are already met):
- ASSERT(m_ControlSocket2.IsValid());
- m_ControlSocket2.Send("c", 1);
-
- return true;
- }
- } // for i - m_Slots[]
- return false;
-}
-
-
-
-
-
bool cSocketThreads::cSocketThread::Start(void)
{
// Create the control socket listener
@@ -493,35 +389,29 @@ void cSocketThreads::cSocketThread::Execute(void)
// The main thread loop:
while (!m_ShouldTerminate)
{
- // Put all sockets into the Read set:
+ // Read outgoing data from the clients:
+ QueueOutgoingData();
+
+ // Put sockets into the sets
fd_set fdRead;
+ fd_set fdWrite;
cSocket::xSocket Highest = m_ControlSocket1.GetSocket();
-
- PrepareSet(&fdRead, Highest);
+ PrepareSets(&fdRead, &fdWrite, Highest);
// Wait for the sockets:
- if (select(Highest + 1, &fdRead, NULL, NULL, NULL) == -1)
- {
- LOG("select(R) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str());
- continue;
- }
-
- ReadFromSockets(&fdRead);
-
- // Test sockets for writing:
- fd_set fdWrite;
- Highest = m_ControlSocket1.GetSocket();
- PrepareSet(&fdWrite, Highest);
timeval Timeout;
- Timeout.tv_sec = 0;
+ Timeout.tv_sec = 5;
Timeout.tv_usec = 0;
- if (select(Highest + 1, NULL, &fdWrite, NULL, &Timeout) == -1)
+ if (select(Highest + 1, &fdRead, &fdWrite, NULL, &Timeout) == -1)
{
- LOG("select(W) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str());
+ LOG("select() call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str());
continue;
}
+ // Perform the IO:
+ ReadFromSockets(&fdRead);
WriteToSockets(&fdWrite);
+ CleanUpShutSockets();
} // while (!mShouldTerminate)
}
@@ -529,10 +419,11 @@ void cSocketThreads::cSocketThread::Execute(void)
-void cSocketThreads::cSocketThread::PrepareSet(fd_set * a_Set, cSocket::xSocket & a_Highest)
+void cSocketThreads::cSocketThread::PrepareSets(fd_set * a_Read, fd_set * a_Write, cSocket::xSocket & a_Highest)
{
- FD_ZERO(a_Set);
- FD_SET(m_ControlSocket1.GetSocket(), a_Set);
+ FD_ZERO(a_Read);
+ FD_ZERO(a_Write);
+ FD_SET(m_ControlSocket1.GetSocket(), a_Read);
cCSLock Lock(m_Parent->m_CS);
for (int i = m_NumSlots - 1; i >= 0; --i)
@@ -541,12 +432,22 @@ void cSocketThreads::cSocketThread::PrepareSet(fd_set * a_Set, cSocket::xSocket
{
continue;
}
+ if (m_Slots[i].m_State == sSlot::ssRemoteClosed)
+ {
+ // This socket won't provide nor consume any data anymore, don't put it in the Set
+ continue;
+ }
cSocket::xSocket s = m_Slots[i].m_Socket.GetSocket();
- FD_SET(s, a_Set);
+ FD_SET(s, a_Read);
if (s > a_Highest)
{
a_Highest = s;
}
+ if (!m_Slots[i].m_Outgoing.empty())
+ {
+ // There's outgoing data for the socket, put it in the Write set
+ FD_SET(s, a_Write);
+ }
} // for i - m_Slots[]
}
@@ -576,29 +477,45 @@ void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read)
}
char Buffer[1024];
int Received = m_Slots[i].m_Socket.Receive(Buffer, ARRAYCOUNT(Buffer), 0);
- if (Received == 0)
- {
- // The socket has been closed by the remote party, close our socket and let it be removed after we process all reading
- m_Slots[i].m_Socket.CloseSocket();
- if (m_Slots[i].m_ShouldCallClient)
- {
- m_Slots[i].m_Client->SocketClosed();
- }
- }
- else if (Received > 0)
+ if (Received <= 0)
{
- if (m_Slots[i].m_ShouldCallClient)
+ if (cSocket::GetLastError() != cSocket::ErrWouldBlock)
{
- m_Slots[i].m_Client->DataReceived(Buffer, Received);
+ // The socket has been closed by the remote party
+ switch (m_Slots[i].m_State)
+ {
+ case sSlot::ssNormal:
+ {
+ // Notify the callback that the remote has closed the socket; keep the slot
+ m_Slots[i].m_Client->SocketClosed();
+ m_Slots[i].m_State = sSlot::ssRemoteClosed;
+ break;
+ }
+ case sSlot::ssWritingRestOut:
+ case sSlot::ssShuttingDown:
+ case sSlot::ssShuttingDown2:
+ {
+ // Force-close the socket and remove the slot:
+ m_Slots[i].m_Socket.CloseSocket();
+ m_Slots[i] = m_Slots[--m_NumSlots];
+ break;
+ }
+ default:
+ {
+ LOG("%s: Unexpected socket state: %d (%s)",
+ __FUNCTION__, m_Slots[i].m_Socket.GetSocket(), m_Slots[i].m_Socket.GetIPString().c_str()
+ );
+ ASSERT(!"Unexpected socket state");
+ break;
+ }
+ } // switch (m_Slots[i].m_State)
}
}
else
{
- // The socket has encountered an error, close it and let it be removed after we process all reading
- m_Slots[i].m_Socket.CloseSocket();
- if (m_Slots[i].m_ShouldCallClient)
+ if (m_Slots[i].m_Client != NULL)
{
- m_Slots[i].m_Client->SocketClosed();
+ m_Slots[i].m_Client->DataReceived(Buffer, Received);
}
}
} // for i - m_Slots[]
@@ -622,41 +539,42 @@ void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write)
if (m_Slots[i].m_Outgoing.empty())
{
// Request another chunk of outgoing data:
- if (m_Slots[i].m_ShouldCallClient)
+ if (m_Slots[i].m_Client != NULL)
{
- m_Slots[i].m_Client->GetOutgoingData(m_Slots[i].m_Outgoing);
+ AString Data;
+ m_Slots[i].m_Client->GetOutgoingData(Data);
+ m_Slots[i].m_Outgoing.append(Data);
}
if (m_Slots[i].m_Outgoing.empty())
{
- // Nothing ready
- if (m_Slots[i].m_ShouldClose)
+ // No outgoing data is ready
+ if (m_Slots[i].m_State == sSlot::ssWritingRestOut)
{
- // Socket was queued for closing and there's no more data to send, close it now:
-
- // DEBUG
- LOGD("Socket was queued for closing, closing now. Slot %d, client %p, socket %d", i, m_Slots[i].m_Client, m_Slots[i].m_Socket.GetSocket());
-
- m_Slots[i].m_Socket.CloseSocket();
- // The slot must be freed actively by the client, using RemoveClient()
+ m_Slots[i].m_State = sSlot::ssShuttingDown;
+ m_Slots[i].m_Socket.ShutdownReadWrite();
}
continue;
}
} // if (outgoing data is empty)
- int Sent = m_Slots[i].m_Socket.Send(m_Slots[i].m_Outgoing.data(), m_Slots[i].m_Outgoing.size());
- if (Sent < 0)
+ if (!SendDataThroughSocket(m_Slots[i].m_Socket, m_Slots[i].m_Outgoing))
{
int Err = cSocket::GetLastError();
- LOGWARNING("Error %d while writing to client \"%s\", disconnecting. \"%s\"", Err, m_Slots[i].m_Socket.GetIPString().c_str(), 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_ShouldCallClient)
+ if (m_Slots[i].m_Client != NULL)
{
m_Slots[i].m_Client->SocketClosed();
}
return;
}
- m_Slots[i].m_Outgoing.erase(0, Sent);
+ if (m_Slots[i].m_Outgoing.empty() && (m_Slots[i].m_State == sSlot::ssWritingRestOut))
+ {
+ m_Slots[i].m_State = sSlot::ssShuttingDown;
+ m_Slots[i].m_Socket.ShutdownReadWrite();
+ }
+
// _X: If there's data left, it means the client is not reading fast enough, the server would unnecessarily spin in the main loop with zero actions taken; so signalling is disabled
// This means that if there's data left, it will be sent only when there's incoming data or someone queues another packet (for any socket handled by this thread)
/*
@@ -673,3 +591,93 @@ void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write)
+
+bool cSocketThreads::cSocketThread::SendDataThroughSocket(cSocket & a_Socket, AString & a_Data)
+{
+ // Send data in smaller chunks, so that the OS send buffers aren't overflown easily
+ while (!a_Data.empty())
+ {
+ size_t NumToSend = std::min(a_Data.size(), (size_t)1024);
+ int Sent = a_Socket.Send(a_Data.data(), NumToSend);
+ if (Sent < 0)
+ {
+ int Err = cSocket::GetLastError();
+ if (Err == cSocket::ErrWouldBlock)
+ {
+ // The OS send buffer is full, leave the outgoing data for the next time
+ return true;
+ }
+ // An error has occured
+ return false;
+ }
+ if (Sent == 0)
+ {
+ a_Socket.CloseSocket();
+ return true;
+ }
+ a_Data.erase(0, Sent);
+ }
+ return true;
+}
+
+
+
+
+
+void cSocketThreads::cSocketThread::CleanUpShutSockets(void)
+{
+ cCSLock Lock(m_Parent->m_CS);
+ for (int i = m_NumSlots - 1; i >= 0; i--)
+ {
+ switch (m_Slots[i].m_State)
+ {
+ case sSlot::ssShuttingDown2:
+ {
+ // The socket has reached the shutdown timeout, close it and clear its slot:
+ m_Slots[i].m_Socket.CloseSocket();
+ m_Slots[i] = m_Slots[--m_NumSlots];
+ break;
+ }
+ case sSlot::ssShuttingDown:
+ {
+ // The socket has been shut down for a single thread loop, let it loop once more before closing:
+ m_Slots[i].m_State = sSlot::ssShuttingDown2;
+ break;
+ }
+ default: break;
+ }
+ } // for i - m_Slots[]
+}
+
+
+
+
+void cSocketThreads::cSocketThread::QueueOutgoingData(void)
+{
+ cCSLock Lock(m_Parent->m_CS);
+ for (int i = 0; i < m_NumSlots; i++)
+ {
+ if (m_Slots[i].m_Client != NULL)
+ {
+ AString Data;
+ m_Slots[i].m_Client->GetOutgoingData(Data);
+ m_Slots[i].m_Outgoing.append(Data);
+ }
+ if (m_Slots[i].m_Outgoing.empty())
+ {
+ // No outgoing data is ready
+ if (m_Slots[i].m_State == sSlot::ssWritingRestOut)
+ {
+ // The socket doesn't want to be kept alive anymore, and doesn't have any remaining data to send.
+ // Shut it down and then close it after a timeout, or when the other side agrees
+ m_Slots[i].m_State = sSlot::ssShuttingDown;
+ m_Slots[i].m_Socket.ShutdownReadWrite();
+ }
+ continue;
+ }
+ }
+}
+
+
+
+
diff --git a/src/OSSupport/SocketThreads.h b/src/OSSupport/SocketThreads.h
index ecbac3aeb..fcd2ce11f 100644
--- a/src/OSSupport/SocketThreads.h
+++ b/src/OSSupport/SocketThreads.h
@@ -7,19 +7,20 @@
/*
Additional details:
-When a client is terminating a connection:
-- they call the StopReading() method to disable callbacks for the incoming data
-- they call the Write() method to queue any outstanding outgoing data
-- they call the QueueClose() method to queue the socket to close after outgoing data has been sent.
-When a socket slot is marked as having no callback, it is kept alive until its outgoing data queue is empty and its m_ShouldClose flag is set.
-This means that the socket can be written to several times before finally closing it via QueueClose()
+When a client wants to terminate the connection, they call the RemoveClient() function. This calls the
+callback one last time to read all the available outgoing data, putting it in the slot's m_OutgoingData
+buffer. Then it marks the slot as having no callback. The socket is kept alive until its outgoing data
+queue is empty, then shutdown is called on it and finally the socket is closed after a timeout.
+If at any time within this the remote end closes the socket, then the socket is closed directly.
+As soon as the socket is closed, the slot is finally removed from the SocketThread.
+The graph in $/docs/SocketThreads States.gv shows the state-machine transitions of the slot.
*/
-/// How many clients should one thread handle? (must be less than FD_SETSIZE for your platform)
+/** How many clients should one thread handle? (must be less than FD_SETSIZE for your platform) */
#define MAX_SLOTS 63
@@ -27,8 +28,6 @@ This means that the socket can be written to several times before finally closin
#pragma once
-#ifndef CSOCKETTHREADS_H_INCLUDED
-#define CSOCKETTHREADS_H_INCLUDED
#include "Socket.h"
#include "IsThread.h"
@@ -61,13 +60,17 @@ public:
class cCallback
{
public:
- /// Called when data is received from the remote party
+ // Force a virtual destructor in all subclasses:
+ virtual ~cCallback() {}
+
+ /** Called when data is received from the remote party */
virtual void DataReceived(const char * a_Data, int a_Size) = 0;
- /// Called when data can be sent to remote party; the function is supposed to append outgoing data to a_Data
+ /** Called when data can be sent to remote party
+ The function is supposed to *set* outgoing data to a_Data (overwrite) */
virtual void GetOutgoingData(AString & a_Data) = 0;
- /// Called when the socket has been closed for any reason
+ /** Called when the socket has been closed for any reason */
virtual void SocketClosed(void) = 0;
} ;
@@ -75,24 +78,21 @@ public:
cSocketThreads(void);
~cSocketThreads();
- /// Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client; returns true if successful
+ /** Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client; returns true if successful */
bool AddClient(const cSocket & a_Socket, cCallback * a_Client);
- /// Remove the associated socket and the client from processing. The socket is left to send its data and is removed only after all its m_OutgoingData is sent
+ /** Remove the associated socket and the client from processing.
+ The socket is left to send its last outgoing data and is removed only after all its m_Outgoing is sent
+ and after the socket is properly shutdown (unless the remote disconnects before that)
+ */
void RemoveClient(const cCallback * a_Client);
- /// Notify the thread responsible for a_Client that the client has something to write
+ /** Notify the thread responsible for a_Client that the client has something to write */
void NotifyWrite(const cCallback * a_Client);
- /// Puts a_Data into outgoing data queue for a_Client
+ /** Puts a_Data into outgoing data queue for a_Client */
void Write(const cCallback * a_Client, const AString & a_Data);
- /// Stops reading from the client - when this call returns, no more calls to the callbacks are made
- void StopReading(const cCallback * a_Client);
-
- /// Queues the client for closing, as soon as its outgoing data is sent
- void QueueClose(const cCallback * a_Client);
-
private:
class cSocketThread :
@@ -111,13 +111,10 @@ private:
void AddClient (const cSocket & a_Socket, cCallback * a_Client); // Takes ownership of the socket
bool RemoveClient(const cCallback * a_Client); // Returns true if removed, false if not found
- bool RemoveSocket(const cSocket * a_Socket); // Returns true if removed, false if not found
bool HasClient (const cCallback * a_Client) const;
bool HasSocket (const cSocket * a_Socket) const;
bool NotifyWrite (const cCallback * a_Client); // Returns true if client handled by this thread
bool Write (const cCallback * a_Client, const AString & a_Data); // Returns true if client handled by this thread
- bool StopReading (const cCallback * a_Client); // Returns true if client handled by this thread
- bool QueueClose (const cCallback * a_Client); // Returns true if client handled by this thread
bool Start(void); // Hide the cIsThread's Start method, we need to provide our own startup to create the control socket
@@ -131,24 +128,56 @@ private:
cSocket m_ControlSocket1;
cSocket m_ControlSocket2;
- // Socket-client-packetqueues triplets.
+ // Socket-client-dataqueues-state quadruplets.
// Manipulation with these assumes that the parent's m_CS is locked
struct sSlot
{
- cSocket m_Socket; // The socket is primarily owned by this
+ /** The socket is primarily owned by this object */
+ cSocket m_Socket;
+
+ /** The callback to call for events. May be NULL */
cCallback * m_Client;
- AString m_Outgoing; // If sending writes only partial data, the rest is stored here for another send
- bool m_ShouldClose; // If true, the socket is to be closed after sending all outgoing data
- bool m_ShouldCallClient; // If true, the client callbacks are called. Set to false in StopReading()
+
+ /** If sending writes only partial data, the rest is stored here for another send.
+ Also used when the slot is being removed to store the last batch of outgoing data. */
+ AString m_Outgoing;
+
+ enum eState
+ {
+ ssNormal, ///< Normal read / write operations
+ ssWritingRestOut, ///< The client callback was removed, continue to send outgoing data
+ ssShuttingDown, ///< The last outgoing data has been sent, the socket has called shutdown()
+ ssShuttingDown2, ///< The shutdown has been done at least 1 thread loop ago (timeout detection)
+ ssRemoteClosed, ///< The remote end has closed the connection (and we still have a client callback)
+ } m_State;
} ;
+
sSlot m_Slots[MAX_SLOTS];
int m_NumSlots; // Number of slots actually used
virtual void Execute(void) override;
- void PrepareSet (fd_set * a_Set, cSocket::xSocket & a_Highest); // Puts all sockets into the set, along with m_ControlSocket1
- void ReadFromSockets(fd_set * a_Read); // Reads from sockets indicated in a_Read
- void WriteToSockets (fd_set * a_Write); // Writes to sockets indicated in a_Write
+ /** Prepares the Read and Write socket sets for select()
+ Puts all sockets into the read set, along with m_ControlSocket1.
+ Only sockets that have outgoing data queued on them are put in the write set.*/
+ void PrepareSets(fd_set * a_ReadSet, fd_set * a_WriteSet, cSocket::xSocket & a_Highest);
+
+ /** Reads from sockets indicated in a_Read */
+ void ReadFromSockets(fd_set * a_Read);
+
+ /** Writes to sockets indicated in a_Write */
+ void WriteToSockets (fd_set * a_Write);
+
+ /** Sends data through the specified socket, trying to fill the OS send buffer in chunks.
+ Returns true if there was no error while sending, false if an error has occured.
+ Modifies a_Data to contain only the unsent data. */
+ bool SendDataThroughSocket(cSocket & a_Socket, AString & a_Data);
+
+ /** Removes those slots in ssShuttingDown2 state, sets those with ssShuttingDown state to ssShuttingDown2 */
+ void CleanUpShutSockets(void);
+
+ /** Calls each client's callback to retrieve outgoing data for that client. */
+ void QueueOutgoingData(void);
} ;
typedef std::list<cSocketThread *> cSocketThreadList;
@@ -161,9 +190,3 @@ private:
-
-#endif // CSOCKETTHREADS_H_INCLUDED
-
-
-
-
diff --git a/src/OSSupport/Timer.cpp b/src/OSSupport/Timer.cpp
index ed16f9e3a..fd838dd0d 100644
--- a/src/OSSupport/Timer.cpp
+++ b/src/OSSupport/Timer.cpp
@@ -28,7 +28,7 @@ long long cTimer::GetNowTime(void)
#else
struct timeval now;
gettimeofday(&now, NULL);
- return (long long)(now.tv_sec * 1000 + now.tv_usec / 1000);
+ return (long long)now.tv_sec * 1000 + (long long)now.tv_usec / 1000;
#endif
}
diff --git a/src/Piston.cpp b/src/Piston.cpp
index b15e7d95e..75eeb5e98 100644
--- a/src/Piston.cpp
+++ b/src/Piston.cpp
@@ -238,6 +238,7 @@ bool cPiston::CanPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
bool cPiston::CanBreakPush(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
+ UNUSED(a_BlockMeta);
return g_BlockPistonBreakable[a_BlockType];
}
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index fdbffb3e9..791082537 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -12,6 +12,7 @@
#include "../Defines.h"
#include "../Endianness.h"
+#include "../Scoreboard.h"
@@ -87,10 +88,14 @@ public:
virtual void SendPlayerMoveLook (void) = 0;
virtual void SendPlayerPosition (void) = 0;
virtual void SendPlayerSpawn (const cPlayer & a_Player) = 0;
+ virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) = 0;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) = 0;
virtual void SendRespawn (void) = 0;
virtual void SendExperience (void) = 0;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) = 0;
+ virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) = 0;
+ virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) = 0;
+ virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) = 0;
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) = 0; // a_Src coords are Block * 8
virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) = 0;
virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) = 0;
@@ -102,6 +107,7 @@ public:
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) = 0;
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) = 0;
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) = 0;
+ virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) = 0;
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) = 0;
virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) = 0;
virtual void SendWeather (eWeather a_Weather) = 0;
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
index e49dd43ff..323a13992 100644
--- a/src/Protocol/Protocol125.cpp
+++ b/src/Protocol/Protocol125.cpp
@@ -354,8 +354,8 @@ void cProtocol125::SendEntityLook(const cEntity & a_Entity)
cCSLock Lock(m_CSPacket);
WriteByte(PACKET_ENT_LOOK);
WriteInt (a_Entity.GetUniqueID());
- WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256));
- WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256));
+ WriteByte((char)((a_Entity.GetYaw() / 360.f) * 256));
+ WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256));
Flush();
}
@@ -423,8 +423,8 @@ void cProtocol125::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX,
WriteByte(a_RelX);
WriteByte(a_RelY);
WriteByte(a_RelZ);
- WriteByte((char)((a_Entity.GetRotation() / 360.f) * 256));
- WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256));
+ WriteByte((char)((a_Entity.GetYaw() / 360.f) * 256));
+ WriteByte((char)((a_Entity.GetPitch() / 360.f) * 256));
Flush();
}
@@ -664,7 +664,7 @@ void cProtocol125::SendPlayerMoveLook(void)
WriteDouble(Player->GetStance() + 0.03); // Add a small amount so that the player doesn't start inside a block
WriteDouble(Player->GetPosY() + 0.03); // Add a small amount so that the player doesn't start inside a block
WriteDouble(Player->GetPosZ());
- WriteFloat ((float)(Player->GetRotation()));
+ WriteFloat ((float)(Player->GetYaw()));
WriteFloat ((float)(Player->GetPitch()));
WriteBool (Player->IsOnGround());
Flush();
@@ -694,8 +694,8 @@ void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player)
WriteInt ((int)(a_Player.GetPosX() * 32));
WriteInt ((int)(a_Player.GetPosY() * 32));
WriteInt ((int)(a_Player.GetPosZ() * 32));
- WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256));
- WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256));
+ WriteByte ((char)((a_Player.GetYaw() / 360.f) * 256));
+ WriteByte ((char)((a_Player.GetPitch() / 360.f) * 256));
WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
Flush();
}
@@ -704,6 +704,20 @@ void cProtocol125::SendPlayerSpawn(const cPlayer & a_Player)
+void cProtocol125::SendPluginMessage(const AString & a_Channel, const AString & a_Message)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_PLUGIN_MESSAGE);
+ WriteString(a_Channel);
+ WriteShort((short)a_Message.size());
+ SendData(a_Message.data(), a_Message.size());
+ Flush();
+}
+
+
+
+
+
void cProtocol125::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
{
cCSLock Lock(m_CSPacket);
@@ -850,7 +864,7 @@ void cProtocol125::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp
WriteInt ((int)(a_Vehicle.GetPosY() * 32));
WriteInt ((int)(a_Vehicle.GetPosZ() * 32));
WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256));
- WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256));
+ WriteByte ((Byte)((a_Vehicle.GetYaw() / 360.f) * 256));
WriteInt (a_VehicleSubType);
if (a_VehicleSubType != 0)
{
@@ -883,7 +897,7 @@ void cProtocol125::SendTeleportEntity(const cEntity & a_Entity)
WriteInt ((int)(floor(a_Entity.GetPosX() * 32)));
WriteInt ((int)(floor(a_Entity.GetPosY() * 32)));
WriteInt ((int)(floor(a_Entity.GetPosZ() * 32)));
- WriteByte ((char)((a_Entity.GetRotation() / 360.f) * 256));
+ WriteByte ((char)((a_Entity.GetYaw() / 360.f) * 256));
WriteByte ((char)((a_Entity.GetPitch() / 360.f) * 256));
Flush();
}
diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h
index 0b32137d8..cd15ab518 100644
--- a/src/Protocol/Protocol125.h
+++ b/src/Protocol/Protocol125.h
@@ -63,10 +63,14 @@ public:
virtual void SendPlayerMoveLook (void) override;
virtual void SendPlayerPosition (void) override;
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
virtual void SendRespawn (void) override;
virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
+ virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override {} // This protocol doesn't support such message
+ virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override {} // This protocol doesn't support such message
+ virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override {} // This protocol doesn't support such message
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
@@ -78,11 +82,12 @@ public:
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override {};
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
virtual void SendWeather (eWeather a_Weather) override;
- virtual void SendWholeInventory (const cWindow & a_Window) override;
- virtual void SendWindowClose (const cWindow & a_Window) override;
+ virtual void SendWholeInventory (const cWindow & a_Window) override;
+ virtual void SendWindowClose (const cWindow & a_Window) override;
virtual void SendWindowOpen (const cWindow & a_Window) override;
virtual void SendWindowProperty (const cWindow & a_Window, short a_Property, short a_Value) override;
diff --git a/src/Protocol/Protocol132.cpp b/src/Protocol/Protocol132.cpp
index 346607b79..f5fd95c7e 100644
--- a/src/Protocol/Protocol132.cpp
+++ b/src/Protocol/Protocol132.cpp
@@ -5,7 +5,6 @@
#include "Globals.h"
#include "ChunkDataSerializer.h"
-#include "cryptopp/randpool.h"
#include "Protocol132.h"
#include "../Root.h"
#include "../Server.h"
@@ -17,8 +16,22 @@
#include "../UI/Window.h"
#include "../Entities/Pickup.h"
#include "../WorldStorage/FastNBT.h"
+#include "../WorldStorage/EnchantmentSerializer.h"
#include "../StringCompression.h"
+#ifdef _MSC_VER
+ #pragma warning(push)
+ #pragma warning(disable:4127)
+ #pragma warning(disable:4244)
+ #pragma warning(disable:4231)
+ #pragma warning(disable:4189)
+ #pragma warning(disable:4702)
+#endif
+
+#ifdef _MSC_VER
+ #pragma warning(pop)
+#endif
+
@@ -37,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...
@@ -81,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:
@@ -185,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;
@@ -356,8 +283,8 @@ void cProtocol132::SendPlayerSpawn(const cPlayer & a_Player)
WriteInt ((int)(a_Player.GetPosX() * 32));
WriteInt ((int)(a_Player.GetPosY() * 32));
WriteInt ((int)(a_Player.GetPosZ() * 32));
- WriteByte ((char)((a_Player.GetRot().x / 360.f) * 256));
- WriteByte ((char)((a_Player.GetRot().y / 360.f) * 256));
+ WriteByte ((char)((a_Player.GetYaw() / 360.f) * 256));
+ WriteByte ((char)((a_Player.GetPitch() / 360.f) * 256));
WriteShort (HeldItem.IsEmpty() ? 0 : HeldItem.m_ItemType);
// Player metadata: just use a default metadata value, since the client doesn't like starting without any metadata:
WriteByte (0); // Index 0, byte (flags)
@@ -410,8 +337,8 @@ void cProtocol132::SendSpawnMob(const cMonster & a_Mob)
WriteInt (a_Mob.GetUniqueID());
WriteByte (a_Mob.GetMobType());
WriteVectorI((Vector3i)(a_Mob.GetPosition() * 32));
- WriteByte ((Byte)((a_Mob.GetRotation() / 360.f) * 256));
- WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256));
+ WriteByte ((Byte)((a_Mob.GetYaw() / 360.f) * 256));
+ WriteByte ((Byte)((a_Mob.GetPitch() / 360.f) * 256));
WriteByte ((Byte)((a_Mob.GetHeadYaw() / 360.f) * 256));
WriteShort ((short)(a_Mob.GetSpeedX() * 400));
WriteShort ((short)(a_Mob.GetSpeedY() * 400));
@@ -570,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;
@@ -584,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);
@@ -702,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;
@@ -752,7 +677,7 @@ void cProtocol132::WriteItem(const cItem & a_Item)
// Send the enchantments:
cFastNBTWriter Writer;
const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
- a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName);
+ EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, Writer, TagName);
Writer.Finish();
AString Compressed;
CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
@@ -838,7 +763,7 @@ int cProtocol132::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
)
)
{
- a_Item.m_Enchantments.ParseFromNBT(NBT, tag);
+ EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, NBT, tag);
}
}
@@ -866,10 +791,10 @@ void cProtocol132::SendCompass(const cWorld & a_World)
void cProtocol132::SendEncryptionKeyRequest(void)
{
cCSLock Lock(m_CSPacket);
- WriteByte((char)0xfd);
+ 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();
@@ -882,19 +807,17 @@ 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));
- byte DecryptedNonce[MAX_ENC_LEN];
- DecodingResult res = rsaDecryptor.Decrypt(rng, (const byte *)a_EncNonce.data(), a_EncNonce.size(), DecryptedNonce);
- if (!res.isValidCoding || (res.messageLength != 4))
+ cRSAPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey();
+
+ Int32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)];
+ 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");
return;
}
- if (ntohl(*((int *)DecryptedNonce)) != (unsigned)(uintptr_t)this)
+ if (ntohl(DecryptedNonce[0]) != (unsigned)(uintptr_t)this)
{
LOGD("Bad nonce value");
m_Client->Kick("Hacked client");
@@ -902,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");
@@ -914,12 +837,18 @@ void cProtocol132::HandleEncryptionKeyResponse(const AString & a_EncKey, const A
{
// Send encryption key response:
cCSLock Lock(m_CSPacket);
- WriteByte((char)0xfc);
+ WriteByte(0xfc);
WriteShort(0);
WriteShort(0);
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;
}
@@ -928,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 f76272b8d..89f4636f5 100644
--- a/src/Protocol/Protocol132.h
+++ b/src/Protocol/Protocol132.h
@@ -10,8 +10,21 @@
#pragma once
#include "Protocol125.h"
-#include "cryptopp/modes.h"
-#include "cryptopp/aes.h"
+
+#ifdef _MSC_VER
+ #pragma warning(push)
+ #pragma warning(disable:4127)
+ #pragma warning(disable:4189)
+ #pragma warning(disable:4231)
+ #pragma warning(disable:4244)
+ #pragma warning(disable:4702)
+#endif
+
+#ifdef _MSC_VER
+ #pragma warning(pop)
+#endif
+
+#include "../Crypto.h"
@@ -65,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:
@@ -94,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 28122034c..f82e6de45 100644
--- a/src/Protocol/Protocol14x.cpp
+++ b/src/Protocol/Protocol14x.cpp
@@ -16,7 +16,6 @@ Implements the 1.4.x protocol classes representing these protocols:
#include "../Root.h"
#include "../Server.h"
#include "../ClientHandle.h"
-#include "cryptopp/randpool.h"
#include "../Item.h"
#include "ChunkDataSerializer.h"
#include "../Entities/Player.h"
@@ -25,8 +24,18 @@ Implements the 1.4.x protocol classes representing these protocols:
#include "../Entities/Pickup.h"
#include "../Entities/FallingBlock.h"
+#ifdef _MSC_VER
+ #pragma warning(push)
+ #pragma warning(disable:4127)
+ #pragma warning(disable:4244)
+ #pragma warning(disable:4231)
+ #pragma warning(disable:4189)
+ #pragma warning(disable:4702)
+#endif
-
+#ifdef _MSC_VER
+ #pragma warning(pop)
+#endif
#define HANDLE_PACKET_READ(Proc, Type, Var) \
@@ -239,7 +248,7 @@ void cProtocol146::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTyp
WriteInt ((int)(a_Vehicle.GetPosY() * 32));
WriteInt ((int)(a_Vehicle.GetPosZ() * 32));
WriteByte ((Byte)((a_Vehicle.GetPitch() / 360.f) * 256));
- WriteByte ((Byte)((a_Vehicle.GetRotation() / 360.f) * 256));
+ WriteByte ((Byte)((a_Vehicle.GetYaw() / 360.f) * 256));
WriteInt (a_VehicleSubType);
if (a_VehicleSubType != 0)
{
diff --git a/src/Protocol/Protocol15x.cpp b/src/Protocol/Protocol15x.cpp
index 7e2aa9490..264a596b9 100644
--- a/src/Protocol/Protocol15x.cpp
+++ b/src/Protocol/Protocol15x.cpp
@@ -35,8 +35,11 @@ Implements the 1.5.x protocol classes:
enum
{
- PACKET_WINDOW_OPEN = 0x64,
- PACKET_PARTICLE_EFFECT = 0x3F,
+ PACKET_WINDOW_OPEN = 0x64,
+ PACKET_PARTICLE_EFFECT = 0x3F,
+ PACKET_SCOREBOARD_OBJECTIVE = 0xCE,
+ PACKET_SCORE_UPDATE = 0xCF,
+ PACKET_DISPLAY_OBJECTIVE = 0xD0
} ;
@@ -97,6 +100,53 @@ void cProtocol150::SendParticleEffect(const AString & a_ParticleName, float a_Sr
+void cProtocol150::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_SCOREBOARD_OBJECTIVE);
+ WriteString(a_Name);
+ WriteString(a_DisplayName);
+ WriteByte(a_Mode);
+ Flush();
+}
+
+
+
+
+
+void cProtocol150::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_SCORE_UPDATE);
+ WriteString(a_Player);
+ WriteByte(a_Mode);
+
+ if (a_Mode != 1)
+ {
+ WriteString(a_Objective);
+ WriteInt((int) a_Score);
+ }
+
+ Flush();
+}
+
+
+
+
+
+void cProtocol150::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display)
+{
+ cCSLock Lock(m_CSPacket);
+ WriteByte(PACKET_DISPLAY_OBJECTIVE);
+ WriteByte((int) a_Display);
+ WriteString(a_Objective);
+ Flush();
+}
+
+
+
+
+
int cProtocol150::ParseWindowClick(void)
{
HANDLE_PACKET_READ(ReadChar, char, WindowID);
@@ -112,7 +162,7 @@ int cProtocol150::ParseWindowClick(void)
}
// Convert Button, Mode, SlotNum and HeldItem into eClickAction:
- eClickAction Action;
+ eClickAction Action = caUnknown;
switch ((Mode << 8) | Button)
{
case 0x0000: Action = (SlotNum != -999) ? caLeftClick : caLeftClickOutside; break;
diff --git a/src/Protocol/Protocol15x.h b/src/Protocol/Protocol15x.h
index 0074b3a83..0d171a67c 100644
--- a/src/Protocol/Protocol15x.h
+++ b/src/Protocol/Protocol15x.h
@@ -30,6 +30,9 @@ public:
virtual void SendWindowOpen (const cWindow & a_Window) override;
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
+ virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
+ virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override;
+ virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
virtual int ParseWindowClick(void);
} ;
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index bbbd5e973..04bade867 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -1,4 +1,3 @@
-
// Protocol17x.cpp
/*
@@ -16,6 +15,7 @@ Implements the 1.7.x protocol classes:
#include "../Server.h"
#include "../World.h"
#include "../WorldStorage/FastNBT.h"
+#include "../WorldStorage/EnchantmentSerializer.h"
#include "../StringCompression.h"
#include "../Entities/ExpOrb.h"
#include "../Entities/Minecart.h"
@@ -24,6 +24,7 @@ Implements the 1.7.x protocol classes:
#include "../Entities/Player.h"
#include "../Mobs/IncludeAllMonsters.h"
#include "../UI/Window.h"
+#include "../BlockEntities/CommandBlockEntity.h"
@@ -52,6 +53,22 @@ Implements the 1.7.x protocol classes:
+const int MAX_ENC_LEN = 512; // Maximum size of the encrypted message; should be 128, but who knows...
+
+
+
+
+
+// fwd: main.cpp:
+extern bool g_ShouldLogCommIn, g_ShouldLogCommOut;
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cProtocol172:
+
cProtocol172::cProtocol172(cClientHandle * a_Client, const AString & a_ServerAddress, UInt16 a_ServerPort, UInt32 a_State) :
super(a_Client),
m_ServerAddress(a_ServerAddress),
@@ -62,6 +79,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_ShouldLogCommIn || g_ShouldLogCommOut)
+ {
+ cFile::CreateFolder("CommLogs");
+ AString FileName = Printf("CommLogs/%x__%s.log", (unsigned)time(NULL), a_Client->GetIPString().c_str());
+ m_CommLogFile.Open(FileName, cFile::fmWrite);
+ }
}
@@ -72,11 +96,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;
@@ -121,8 +145,8 @@ void cProtocol172::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, cha
void cProtocol172::SendBlockBreakAnim(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage)
{
- cPacketizer Pkt(*this, 0x24); // Block Break Animation packet
- Pkt.WriteInt(a_EntityID);
+ cPacketizer Pkt(*this, 0x25); // Block Break Animation packet
+ Pkt.WriteVarInt(a_EntityID);
Pkt.WriteInt(a_BlockX);
Pkt.WriteInt(a_BlockY);
Pkt.WriteInt(a_BlockZ);
@@ -216,8 +240,23 @@ void cProtocol172::SendDestroyEntity(const cEntity & a_Entity)
void cProtocol172::SendDisconnect(const AString & a_Reason)
{
- cPacketizer Pkt(*this, 0x40);
- Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str()));
+ switch (m_State)
+ {
+ case 2:
+ {
+ // During login:
+ cPacketizer Pkt(*this, 0);
+ Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str()));
+ break;
+ }
+ case 3:
+ {
+ // In-game:
+ cPacketizer Pkt(*this, 0x40);
+ Pkt.WriteString(Printf("{\"text\":\"%s\"}", EscapeString(a_Reason).c_str()));
+ break;
+ }
+ }
}
@@ -628,6 +667,18 @@ void cProtocol172::SendPlayerSpawn(const cPlayer & a_Player)
+void cProtocol172::SendPluginMessage(const AString & a_Channel, const AString & a_Message)
+{
+ cPacketizer Pkt(*this, 0x3f);
+ Pkt.WriteString(a_Channel);
+ Pkt.WriteShort((short)a_Message.size());
+ Pkt.WriteBuf(a_Message.data(), a_Message.size());
+}
+
+
+
+
+
void cProtocol172::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
{
cPacketizer Pkt(*this, 0x1E);
@@ -678,6 +729,46 @@ void cProtocol172::SendExperienceOrb(const cExpOrb & a_ExpOrb)
+void cProtocol172::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
+{
+ cPacketizer Pkt(*this, 0x3B);
+ Pkt.WriteString(a_Name);
+ Pkt.WriteString(a_DisplayName);
+ Pkt.WriteByte(a_Mode);
+}
+
+
+
+
+
+void cProtocol172::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
+{
+ cPacketizer Pkt(*this, 0x3C);
+ Pkt.WriteString(a_Player);
+ Pkt.WriteByte(a_Mode);
+
+ if (a_Mode != 1)
+ {
+ Pkt.WriteString(a_Objective);
+ Pkt.WriteInt((int) a_Score);
+ }
+}
+
+
+
+
+
+void cProtocol172::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display)
+{
+ cPacketizer Pkt(*this, 0x3D);
+ Pkt.WriteByte((int) a_Display);
+ Pkt.WriteString(a_Objective);
+}
+
+
+
+
+
void cProtocol172::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) // a_Src coords are Block * 8
{
cPacketizer Pkt(*this, 0x29); // Sound Effect packet
@@ -865,6 +956,28 @@ void cProtocol172::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+void cProtocol172::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
+{
+ cPacketizer Pkt(*this, 0x35); // Update tile entity packet
+ Pkt.WriteInt(a_BlockEntity.GetPosX());
+ Pkt.WriteShort(a_BlockEntity.GetPosY());
+ Pkt.WriteInt(a_BlockEntity.GetPosZ());
+
+ Byte Action = 0;
+ switch (a_BlockEntity.GetBlockType())
+ {
+ case E_BLOCK_MOB_SPAWNER: Action = 1; break; // Update mob spawner spinny mob thing
+ case E_BLOCK_COMMAND_BLOCK: Action = 2; break; // Update command block text
+ default: ASSERT(!"Unhandled or unimplemented BlockEntity update request!"); break;
+ }
+ Pkt.WriteByte(Action);
+
+ Pkt.WriteBlockEntity(a_BlockEntity);
+}
+
+
+
+
void cProtocol172::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
{
@@ -872,10 +985,11 @@ void cProtocol172::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, cons
Pkt.WriteInt(a_BlockX);
Pkt.WriteShort((short)a_BlockY);
Pkt.WriteInt(a_BlockZ);
- Pkt.WriteString(a_Line1);
- Pkt.WriteString(a_Line2);
- Pkt.WriteString(a_Line3);
- Pkt.WriteString(a_Line4);
+ // Need to send only up to 15 chars, otherwise the client crashes (#598)
+ Pkt.WriteString(a_Line1.substr(0, 15));
+ Pkt.WriteString(a_Line2.substr(0, 15));
+ Pkt.WriteString(a_Line3.substr(0, 15));
+ Pkt.WriteString(a_Line4.substr(0, 15));
}
@@ -975,6 +1089,31 @@ 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_ShouldLogCommIn)
+ {
+ 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);
+ ASSERT(m_ReceivedData.GetReadableSpace() == OldReadableSpace);
+ AString Hex;
+ CreateHexDump(Hex, AllData.data(), AllData.size(), 16);
+ m_CommLogFile.Printf("Incoming data, %d (0x%x) unparsed bytes 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()
+ );
+ m_CommLogFile.Flush();
+ }
+
if (!m_ReceivedData.Write(a_Data, a_Size))
{
// Too much data in the incoming queue, report to caller:
@@ -983,19 +1122,20 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
}
// Handle all complete packets:
- while (true)
+ for (;;)
{
UInt32 PacketLen;
- int PacketStart = m_ReceivedData.GetDataStart();
if (!m_ReceivedData.ReadVarInt(PacketLen))
{
// Not enough data
- return;
+ m_ReceivedData.ResetRead();
+ break;
}
if (!m_ReceivedData.CanReadBytes(PacketLen))
{
// The full packet hasn't been received yet
- return;
+ m_ReceivedData.ResetRead();
+ break;
}
cByteBuffer bb(PacketLen + 1);
VERIFY(m_ReceivedData.ReadToByteBuffer(bb, (int)PacketLen));
@@ -1008,24 +1148,93 @@ void cProtocol172::AddReceivedData(const char * a_Data, int a_Size)
if (!bb.ReadVarInt(PacketType))
{
// Not enough data
- return;
+ break;
}
- HandlePacket(bb, PacketType);
+ // Log the packet info into the comm log file:
+ if (g_ShouldLogCommIn)
+ {
+ 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:
+ LOGWARNING("Unhandled packet: type 0x%x, state %d, length %u", PacketType, m_State, PacketLen);
+
+ #ifdef _DEBUG
+ // Dump the packet contents into the log:
+ bb.ResetRead();
+ AString Packet;
+ bb.ReadAll(Packet);
+ Packet.resize(Packet.size() - 1); // Drop the final NUL pushed there for over-read detection
+ AString Out;
+ CreateHexDump(Out, Packet.data(), (int)Packet.size(), 24);
+ LOGD("Packet contents:\n%s", Out.c_str());
+ #endif // _DEBUG
+
+ // Put a message in the comm log:
+ if (g_ShouldLogCommIn)
+ {
+ m_CommLogFile.Printf("^^^^^^ Unhandled packet ^^^^^^\n\n\n");
+ }
+
+ return;
+ }
if (bb.GetReadableSpace() != 1)
{
// Read more or less than packet length, report as error
+ 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_ShouldLogCommIn)
+ {
+ 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);
}
- } // while (true)
+ } // for(ever)
+
+ // Log any leftover bytes into the logfile:
+ if (g_ShouldLogCommIn && (m_ReceivedData.GetReadableSpace() > 0))
+ {
+ AString AllData;
+ int OldReadableSpace = m_ReceivedData.GetReadableSpace();
+ m_ReceivedData.ReadAll(AllData);
+ m_ReceivedData.ResetRead();
+ m_ReceivedData.SkipRead(m_ReceivedData.GetReadableSpace() - OldReadableSpace);
+ ASSERT(m_ReceivedData.GetReadableSpace() == OldReadableSpace);
+ AString Hex;
+ CreateHexDump(Hex, AllData.data(), AllData.size(), 16);
+ m_CommLogFile.Printf("There are %d (0x%x) bytes of non-parse-able data left in the buffer:\n%s",
+ m_ReceivedData.GetReadableSpace(), m_ReceivedData.GetReadableSpace(), Hex.c_str()
+ );
+ m_CommLogFile.Flush();
+ }
}
-void cProtocol172::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
+bool cProtocol172::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
{
switch (m_State)
{
@@ -1034,8 +1243,8 @@ void cProtocol172::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
// Status
switch (a_PacketType)
{
- case 0x00: HandlePacketStatusRequest(a_ByteBuffer); return;
- case 0x01: HandlePacketStatusPing (a_ByteBuffer); return;
+ case 0x00: HandlePacketStatusRequest(a_ByteBuffer); return true;
+ case 0x01: HandlePacketStatusPing (a_ByteBuffer); return true;
}
break;
}
@@ -1045,8 +1254,8 @@ void cProtocol172::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
// Login
switch (a_PacketType)
{
- case 0x00: HandlePacketLoginStart (a_ByteBuffer); return;
- case 0x01: HandlePacketLoginEncryptionResponse(a_ByteBuffer); return;
+ case 0x00: HandlePacketLoginStart (a_ByteBuffer); return true;
+ case 0x01: HandlePacketLoginEncryptionResponse(a_ByteBuffer); return true;
}
break;
}
@@ -1056,36 +1265,54 @@ void cProtocol172::HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType)
// Game
switch (a_PacketType)
{
- case 0x00: HandlePacketKeepAlive (a_ByteBuffer); return;
- case 0x01: HandlePacketChatMessage (a_ByteBuffer); return;
- case 0x02: HandlePacketUseEntity (a_ByteBuffer); return;
- case 0x03: HandlePacketPlayer (a_ByteBuffer); return;
- case 0x04: HandlePacketPlayerPos (a_ByteBuffer); return;
- case 0x05: HandlePacketPlayerLook (a_ByteBuffer); return;
- case 0x06: HandlePacketPlayerPosLook (a_ByteBuffer); return;
- case 0x07: HandlePacketBlockDig (a_ByteBuffer); return;
- case 0x08: HandlePacketBlockPlace (a_ByteBuffer); return;
- case 0x09: HandlePacketSlotSelect (a_ByteBuffer); return;
- case 0x0a: HandlePacketAnimation (a_ByteBuffer); return;
- case 0x0b: HandlePacketEntityAction (a_ByteBuffer); return;
- case 0x0c: HandlePacketSteerVehicle (a_ByteBuffer); return;
- case 0x0d: HandlePacketWindowClose (a_ByteBuffer); return;
- case 0x0e: HandlePacketWindowClick (a_ByteBuffer); return;
+ case 0x00: HandlePacketKeepAlive (a_ByteBuffer); return true;
+ case 0x01: HandlePacketChatMessage (a_ByteBuffer); return true;
+ case 0x02: HandlePacketUseEntity (a_ByteBuffer); return true;
+ case 0x03: HandlePacketPlayer (a_ByteBuffer); return true;
+ case 0x04: HandlePacketPlayerPos (a_ByteBuffer); return true;
+ case 0x05: HandlePacketPlayerLook (a_ByteBuffer); return true;
+ case 0x06: HandlePacketPlayerPosLook (a_ByteBuffer); return true;
+ case 0x07: HandlePacketBlockDig (a_ByteBuffer); return true;
+ case 0x08: HandlePacketBlockPlace (a_ByteBuffer); return true;
+ case 0x09: HandlePacketSlotSelect (a_ByteBuffer); return true;
+ case 0x0a: HandlePacketAnimation (a_ByteBuffer); return true;
+ case 0x0b: HandlePacketEntityAction (a_ByteBuffer); return true;
+ case 0x0c: HandlePacketSteerVehicle (a_ByteBuffer); return true;
+ case 0x0d: HandlePacketWindowClose (a_ByteBuffer); return true;
+ case 0x0e: HandlePacketWindowClick (a_ByteBuffer); return true;
case 0x0f: // Confirm transaction - not used in MCS
- case 0x10: HandlePacketCreativeInventoryAction(a_ByteBuffer); return;
- case 0x12: HandlePacketUpdateSign (a_ByteBuffer); return;
- case 0x13: HandlePacketPlayerAbilities (a_ByteBuffer); return;
- case 0x14: HandlePacketTabComplete (a_ByteBuffer); return;
- case 0x15: HandlePacketClientSettings (a_ByteBuffer); return;
- case 0x16: HandlePacketClientStatus (a_ByteBuffer); return;
- case 0x17: HandlePacketPluginMessage (a_ByteBuffer); return;
+ case 0x10: HandlePacketCreativeInventoryAction(a_ByteBuffer); return true;
+ case 0x12: HandlePacketUpdateSign (a_ByteBuffer); return true;
+ case 0x13: HandlePacketPlayerAbilities (a_ByteBuffer); return true;
+ case 0x14: HandlePacketTabComplete (a_ByteBuffer); return true;
+ case 0x15: HandlePacketClientSettings (a_ByteBuffer); return true;
+ case 0x16: HandlePacketClientStatus (a_ByteBuffer); return true;
+ case 0x17: HandlePacketPluginMessage (a_ByteBuffer); return true;
}
break;
}
+ default:
+ {
+ // Received a packet in an unknown state, report:
+ LOGWARNING("Received a packet in an unknown protocol state %d. Ignoring further packets.", m_State);
+
+ // Cannot kick the client - we don't know this state and thus the packet number for the kick packet
+
+ // Switch to a state when all further packets are silently ignored:
+ m_State = 255;
+ return false;
+ }
+ case 255:
+ {
+ // This is the state used for "not processing packets anymore" when we receive a bad packet from a client.
+ // Do not output anything (the caller will do that for us), just return failure
+ return false;
+ }
} // switch (m_State)
- // Unknown packet type, report to the client:
+ // Unknown packet type, report to the ClientHandle:
m_Client->PacketUnknown(a_PacketType);
+ return false;
}
@@ -1113,9 +1340,12 @@ void cProtocol172::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
cRoot::Get()->GetServer()->GetMaxPlayers(),
cRoot::Get()->GetServer()->GetNumPlayers()
);
- AppendPrintf(Response, "\"description\":{\"text\":\"%s\"}",
+ AppendPrintf(Response, "\"description\":{\"text\":\"%s\"},",
cRoot::Get()->GetServer()->GetDescription().c_str()
);
+ AppendPrintf(Response, "\"favicon\":\"data:image/png;base64,%s\"",
+ cRoot::Get()->GetServer()->GetFaviconData().c_str()
+ );
Response.append("}");
cPacketizer Pkt(*this, 0x00); // Response packet
@@ -1128,7 +1358,64 @@ void cProtocol172::HandlePacketStatusRequest(cByteBuffer & a_ByteBuffer)
void cProtocol172::HandlePacketLoginEncryptionResponse(cByteBuffer & a_ByteBuffer)
{
- // TODO: Add protocol encryption
+ short EncKeyLength, EncNonceLength;
+ a_ByteBuffer.ReadBEShort(EncKeyLength);
+ AString EncKey;
+ if (!a_ByteBuffer.ReadString(EncKey, EncKeyLength))
+ {
+ return;
+ }
+ a_ByteBuffer.ReadBEShort(EncNonceLength);
+ AString EncNonce;
+ if (!a_ByteBuffer.ReadString(EncNonce, EncNonceLength))
+ {
+ return;
+ }
+ if ((EncKeyLength > MAX_ENC_LEN) || (EncNonceLength > MAX_ENC_LEN))
+ {
+ LOGD("Too long encryption");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Decrypt EncNonce using privkey
+ cRSAPrivateKey & rsaDecryptor = cRoot::Get()->GetServer()->GetPrivateKey();
+ Int32 DecryptedNonce[MAX_ENC_LEN / sizeof(Int32)];
+ int res = rsaDecryptor.Decrypt((const Byte *)EncNonce.data(), EncNonce.size(), (Byte *)DecryptedNonce, sizeof(DecryptedNonce));
+ if (res != 4)
+ {
+ LOGD("Bad nonce length: got %d, exp %d", res, 4);
+ m_Client->Kick("Hacked client");
+ return;
+ }
+ if (ntohl(DecryptedNonce[0]) != (unsigned)(uintptr_t)this)
+ {
+ LOGD("Bad nonce value");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ // Decrypt the symmetric encryption key using privkey:
+ Byte DecryptedKey[MAX_ENC_LEN];
+ res = rsaDecryptor.Decrypt((const Byte *)EncKey.data(), EncKey.size(), DecryptedKey, sizeof(DecryptedKey));
+ if (res != 16)
+ {
+ LOGD("Bad key length");
+ m_Client->Kick("Hacked client");
+ return;
+ }
+
+ StartEncryption(DecryptedKey);
+
+ // Send login success:
+ {
+ cPacketizer Pkt(*this, 0x02); // Login success packet
+ Pkt.WriteString(Printf("%d", m_Client->GetUniqueID())); // TODO: proper UUID
+ Pkt.WriteString(m_Client->GetUsername());
+ }
+
+ m_State = 3; // State = Game
+ m_Client->HandleLogin(4, m_Client->GetUsername());
}
@@ -1140,7 +1427,25 @@ void cProtocol172::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer)
AString Username;
a_ByteBuffer.ReadVarUTF8String(Username);
- // TODO: Protocol encryption should be set up here if not localhost / auth
+ if (!m_Client->HandleHandshake(Username))
+ {
+ // The client is not welcome here, they have been sent a Kick packet already
+ return;
+ }
+
+ // If auth is required, then send the encryption request:
+ if (cRoot::Get()->GetServer()->ShouldAuthenticate())
+ {
+ cPacketizer Pkt(*this, 0x01);
+ Pkt.WriteString(cRoot::Get()->GetServer()->GetServerID());
+ const AString & PubKeyDer = cRoot::Get()->GetServer()->GetPublicKeyDER();
+ Pkt.WriteShort(PubKeyDer.size());
+ Pkt.WriteBuf(PubKeyDer.data(), PubKeyDer.size());
+ Pkt.WriteShort(4);
+ Pkt.WriteInt((int)(intptr_t)this); // Using 'this' as the cryptographic nonce, so that we don't have to generate one each time :)
+ m_Client->SetUsername(Username);
+ return;
+ }
// Send login success:
{
@@ -1374,7 +1679,7 @@ void cProtocol172::HandlePacketPluginMessage(cByteBuffer & a_ByteBuffer)
HANDLE_READ(a_ByteBuffer, ReadBEShort, short, Length);
AString Data;
a_ByteBuffer.ReadString(Data, Length);
- // TODO: m_Client->HandlePluginMessage(Channel, Data);
+ m_Client->HandlePluginMessage(Channel, Data);
}
@@ -1528,11 +1833,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;
@@ -1612,7 +1917,7 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
return;
}
- // Load enchantments from the NBT:
+ // Load enchantments and custom display names from the NBT data:
for (int tag = NBT.GetFirstChild(NBT.GetRoot()); tag >= 0; tag = NBT.GetNextSibling(tag))
{
if (
@@ -1623,7 +1928,28 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
)
)
{
- a_Item.m_Enchantments.ParseFromNBT(NBT, tag);
+ EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, NBT, tag);
+ }
+ else if ((NBT.GetType(tag) == TAG_Compound) && (NBT.GetName(tag) == "display")) // Custom name and lore tag
+ {
+ for (int displaytag = NBT.GetFirstChild(tag); displaytag >= 0; displaytag = NBT.GetNextSibling(displaytag))
+ {
+ if ((NBT.GetType(displaytag) == TAG_String) && (NBT.GetName(displaytag) == "Name")) // Custon name tag
+ {
+ a_Item.m_CustomName = NBT.GetString(displaytag);
+ }
+ else if ((NBT.GetType(displaytag) == TAG_List) && (NBT.GetName(displaytag) == "Lore")) // Lore tag
+ {
+ AString Lore;
+
+ for (int loretag = NBT.GetFirstChild(displaytag); loretag >= 0; loretag = NBT.GetNextSibling(loretag)) // Loop through array of strings
+ {
+ AppendPrintf(Lore, "%s`", NBT.GetString(loretag).c_str()); // Append the lore with a newline, used internally by MCS to display a new line in the client; don't forget to c_str ;)
+ }
+
+ a_Item.m_Lore = Lore;
+ }
+ }
}
}
}
@@ -1632,6 +1958,27 @@ void cProtocol172::ParseItemMetadata(cItem & a_Item, const AString & a_Metadata)
+void cProtocol172::StartEncryption(const Byte * a_Key)
+{
+ m_Encryptor.Init(a_Key, a_Key);
+ m_Decryptor.Init(a_Key, a_Key);
+ m_IsEncrypted = true;
+
+ // Prepare the m_AuthServerID:
+ cSHA1Checksum Checksum;
+ const AString & ServerID = cRoot::Get()->GetServer()->GetServerID();
+ Checksum.Update((const Byte *)ServerID.c_str(), ServerID.length());
+ Checksum.Update(a_Key, 16);
+ Checksum.Update((const Byte *)cRoot::Get()->GetServer()->GetPublicKeyDER().data(), cRoot::Get()->GetServer()->GetPublicKeyDER().size());
+ Byte Digest[20];
+ Checksum.Finalize(Digest);
+ cSHA1Checksum::DigestToJava(Digest, m_AuthServerID);
+}
+
+
+
+
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cProtocol172::cPacketizer:
@@ -1650,6 +1997,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_ShouldLogCommOut)
+ {
+ 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()
+ );
+ }
}
@@ -1676,16 +2034,45 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item)
WriteByte (a_Item.m_ItemCount);
WriteShort(a_Item.m_ItemDamage);
- if (a_Item.m_Enchantments.IsEmpty())
+ if (a_Item.m_Enchantments.IsEmpty() && a_Item.IsBothNameAndLoreEmpty())
{
WriteShort(-1);
return;
}
- // Send the enchantments:
+ // Send the enchantments and custom names:
cFastNBTWriter Writer;
- const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
- a_Item.m_Enchantments.WriteToNBTCompound(Writer, TagName);
+ if (!a_Item.m_Enchantments.IsEmpty())
+ {
+ const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
+ EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments,Writer, TagName);
+ }
+ if (!a_Item.IsBothNameAndLoreEmpty())
+ {
+ Writer.BeginCompound("display");
+ if (!a_Item.IsCustomNameEmpty())
+ {
+ Writer.AddString("Name", a_Item.m_CustomName.c_str());
+ }
+ if (!a_Item.IsLoreEmpty())
+ {
+ Writer.BeginList("Lore", TAG_String);
+
+ AStringVector Decls = StringSplit(a_Item.m_Lore, "`");
+ for (AStringVector::const_iterator itr = Decls.begin(), end = Decls.end(); itr != end; ++itr)
+ {
+ if (itr->empty())
+ {
+ // The decl is empty (two `s), ignore
+ continue;
+ }
+ Writer.AddString("", itr->c_str());
+ }
+
+ Writer.EndList();
+ }
+ Writer.EndCompound();
+ }
Writer.Finish();
AString Compressed;
CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
@@ -1696,6 +2083,51 @@ void cProtocol172::cPacketizer::WriteItem(const cItem & a_Item)
+void cProtocol172::cPacketizer::WriteBlockEntity(const cBlockEntity & a_BlockEntity)
+{
+ cFastNBTWriter Writer;
+
+ switch (a_BlockEntity.GetBlockType())
+ {
+ case E_BLOCK_COMMAND_BLOCK:
+ {
+ cCommandBlockEntity & CommandBlockEntity = (cCommandBlockEntity &)a_BlockEntity;
+
+ Writer.AddByte("TrackOutput", 1); // Neither I nor the MC wiki has any idea about this
+ Writer.AddInt("SuccessCount", CommandBlockEntity.GetResult());
+ Writer.AddInt("x", CommandBlockEntity.GetPosX());
+ Writer.AddInt("y", CommandBlockEntity.GetPosY());
+ Writer.AddInt("z", CommandBlockEntity.GetPosZ());
+ Writer.AddString("Command", CommandBlockEntity.GetCommand().c_str());
+ // You can set custom names for windows in Vanilla
+ // For a command block, this would be the 'name' prepended to anything it outputs into global chat
+ // MCS doesn't have this, so just leave it @ '@'. (geddit?)
+ Writer.AddString("CustomName", "@");
+ Writer.AddString("id", "Control"); // "Tile Entity ID" - MC wiki; vanilla server always seems to send this though
+
+ if (!CommandBlockEntity.GetLastOutput().empty())
+ {
+ AString Output;
+ Printf(Output, "{\"text\":\"%s\"}", CommandBlockEntity.GetLastOutput().c_str());
+
+ Writer.AddString("LastOutput", Output.c_str());
+ }
+ break;
+ }
+ default: break;
+ }
+
+ Writer.Finish();
+
+ AString Compressed;
+ CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
+ WriteShort(Compressed.size());
+ WriteBuf(Compressed.data(), Compressed.size());
+}
+
+
+
+
void cProtocol172::cPacketizer::WriteByteAngle(double a_Angle)
{
@@ -1767,8 +2199,23 @@ void cProtocol172::cPacketizer::WriteEntityMetadata(const cEntity & a_Entity)
WriteInt(1); // Shaking direction, doesn't seem to affect anything
WriteByte(0x73);
WriteFloat((float)(((const cMinecart &)a_Entity).LastDamage() + 10)); // Damage taken / shake effect multiplyer
-
- if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace)
+
+ if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpNone)
+ {
+ cRideableMinecart & RideableMinecart = ((cRideableMinecart &)a_Entity);
+ if (!RideableMinecart.GetContent().IsEmpty())
+ {
+ WriteByte(0x54);
+ int Content = RideableMinecart.GetContent().m_ItemType;
+ Content |= RideableMinecart.GetContent().m_ItemDamage << 8;
+ WriteInt(Content);
+ WriteByte(0x55);
+ WriteInt(RideableMinecart.GetBlockHeight());
+ WriteByte(0x56);
+ WriteByte(1);
+ }
+ }
+ else if (((cMinecart &)a_Entity).GetPayload() == cMinecart::mpFurnace)
{
WriteByte(0x10);
WriteByte(((const cMinecartWithFurnace &)a_Entity).IsFueled() ? 1 : 0);
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index cc0eda1e7..6a75e41c8 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -16,17 +16,30 @@ Declares the 1.7.x protocol classes:
#include "Protocol.h"
#include "../ByteBuffer.h"
-#include "cryptopp/modes.h"
-#include "cryptopp/aes.h"
+
+#ifdef _MSC_VER
+ #pragma warning(push)
+ #pragma warning(disable:4127)
+ #pragma warning(disable:4244)
+ #pragma warning(disable:4231)
+ #pragma warning(disable:4189)
+ #pragma warning(disable:4702)
+#endif
+
+#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:
@@ -72,11 +85,15 @@ public:
virtual void SendPlayerMoveLook (void) override;
virtual void SendPlayerPosition (void) override;
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
virtual void SendRespawn (void) override;
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override; // a_Src coords are Block * 8
virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
+ virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
+ virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override;
+ virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
virtual void SendSpawnMob (const cMonster & a_Mob) override;
@@ -87,6 +104,7 @@ public:
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
virtual void SendWeather (eWeather a_Weather) override;
@@ -174,6 +192,7 @@ protected:
void WriteEntityMetadata(const cEntity & a_Entity); // Writes the metadata for the specified entity, not including the terminating 0x7f
void WriteMobMetadata(const cMonster & a_Mob); // Writes the mob-specific metadata for the specified mob
void WriteEntityProperties(const cEntity & a_Entity); // Writes the entity properties for the specified entity, including the Count field
+ void WriteBlockEntity(const cBlockEntity & a_BlockEntity);
protected:
cProtocol172 & m_Protocol;
@@ -200,15 +219,21 @@ 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
void AddReceivedData(const char * a_Data, int a_Size);
- /// Reads and handles the packet. The packet length and type have already been read.
- void HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType);
+ /** Reads and handles the packet. The packet length and type have already been read.
+ Returns true if the packet was understood, false if it was an unknown packet
+ */
+ bool HandlePacket(cByteBuffer & a_ByteBuffer, UInt32 a_PacketType);
// Packet handlers while in the Status state (m_State == 1):
void HandlePacketStatusPing (cByteBuffer & a_ByteBuffer);
@@ -256,6 +281,8 @@ protected:
/// Parses item metadata as read by ReadItem(), into the item enchantments.
void ParseItemMetadata(cItem & a_Item, const AString & a_Metadata);
+
+ void StartEncryption(const Byte * a_Key);
} ;
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index 1cae4a750..32409c2aa 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -198,7 +198,7 @@ void cProtocolRecognizer::SendDisconnect(const AString & a_Reason)
else
{
// This is used when the client sends a server-ping, respond with the default packet:
- WriteByte ((char)0xff); // PACKET_DISCONNECT
+ WriteByte (0xff); // PACKET_DISCONNECT
WriteString(a_Reason);
}
}
@@ -476,6 +476,16 @@ void cProtocolRecognizer::SendPlayerSpawn(const cPlayer & a_Player)
+void cProtocolRecognizer::SendPluginMessage(const AString & a_Channel, const AString & a_Message)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendPluginMessage(a_Channel, a_Message);
+}
+
+
+
+
+
void cProtocolRecognizer::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
{
ASSERT(m_Protocol != NULL);
@@ -516,6 +526,36 @@ void cProtocolRecognizer::SendExperienceOrb(const cExpOrb & a_ExpOrb)
+void cProtocolRecognizer::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendScoreboardObjective(a_Name, a_DisplayName, a_Mode);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendScoreUpdate(a_Objective, a_Player, a_Score, a_Mode);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendDisplayObjective(a_Objective, a_Display);
+}
+
+
+
+
+
void cProtocolRecognizer::SendSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch)
{
ASSERT(m_Protocol != NULL);
@@ -626,6 +666,16 @@ void cProtocolRecognizer::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
+void cProtocolRecognizer::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendUpdateBlockEntity(a_BlockEntity);
+}
+
+
+
+
+
void cProtocolRecognizer::SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4)
{
ASSERT(m_Protocol != NULL);
@@ -787,7 +837,7 @@ bool cProtocolRecognizer::TryRecognizeLengthlessProtocol(void)
}
switch (ch)
{
- case PROTO_VERSION_1_3_2:
+ case PROTO_VERSION_1_3_2:
{
m_Protocol = new cProtocol132(m_Client);
return true;
@@ -915,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/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index fbcf59f3b..f58c66d10 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -18,7 +18,7 @@
// Adjust these if a new protocol is added or an old one is removed:
-#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2"
+#define MCS_CLIENT_VERSIONS "1.2.4, 1.2.5, 1.3.1, 1.3.2, 1.4.2, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.5, 1.5.1, 1.5.2, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.7.2, 1.7.4"
#define MCS_PROTOCOL_VERSIONS "29, 39, 47, 49, 51, 60, 61, 73, 74, 77, 78, 4"
@@ -98,10 +98,14 @@ public:
virtual void SendPlayerMoveLook (void) override;
virtual void SendPlayerPosition (void) override;
virtual void SendPlayerSpawn (const cPlayer & a_Player) override;
+ virtual void SendPluginMessage (const AString & a_Channel, const AString & a_Message) override;
virtual void SendRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID) override;
virtual void SendRespawn (void) override;
virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
+ virtual void SendScoreboardObjective (const AString & a_Name, const AString & a_DisplayName, Byte a_Mode) override;
+ virtual void SendScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode) override;
+ virtual void SendDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display) override;
virtual void SendSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch) override;
virtual void SendSoundParticleEffect (int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data) override;
virtual void SendSpawnFallingBlock (const cFallingBlock & a_FallingBlock) override;
@@ -113,6 +117,7 @@ public:
virtual void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ) override;
virtual void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay) override;
virtual void SendUnloadChunk (int a_ChunkX, int a_ChunkZ) override;
+ virtual void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity) override;
virtual void SendUpdateSign (int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4) override;
virtual void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ ) override;
virtual void SendWeather (eWeather a_Weather) override;
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/VC2008/MCServer.rc b/src/Resources/MCServer.rc
index e0fbbea5d..e0fbbea5d 100644
--- a/VC2008/MCServer.rc
+++ b/src/Resources/MCServer.rc
diff --git a/VC2008/icon.ico b/src/Resources/icon.ico
index 4024523a1..4024523a1 100644
--- a/VC2008/icon.ico
+++ b/src/Resources/icon.ico
Binary files differ
diff --git a/VC2008/icon_128.png b/src/Resources/icon_128.png
index 87d939a04..87d939a04 100644
--- a/VC2008/icon_128.png
+++ b/src/Resources/icon_128.png
Binary files differ
diff --git a/VC2008/icon_256.png b/src/Resources/icon_256.png
index 9a77a490f..9a77a490f 100644
--- a/VC2008/icon_256.png
+++ b/src/Resources/icon_256.png
Binary files differ
diff --git a/VC2008/resource_MCServer.h b/src/Resources/resource_MCServer.h
index 42f6c4eaf..42f6c4eaf 100644
--- a/VC2008/resource_MCServer.h
+++ b/src/Resources/resource_MCServer.h
diff --git a/src/Root.cpp b/src/Root.cpp
index 798f965be..883bfe76e 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -200,7 +200,7 @@ void cRoot::Start(void)
long long finishmseconds = Time.GetNowTime();
finishmseconds -= mseconds;
- LOG("Startup complete, took %i ms!", finishmseconds);
+ LOG("Startup complete, took %lld ms!", finishmseconds);
#ifdef _WIN32
EnableMenuItem(hmenu, SC_CLOSE, MF_ENABLED); // Re-enable close button
#endif
@@ -596,10 +596,10 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac
public:
cCallback (const AString & a_PlayerName, cPlayerListCallback & a_Callback) :
- m_Callback(a_Callback),
m_BestRating(0),
m_NameLength(a_PlayerName.length()),
m_PlayerName(a_PlayerName),
+ m_Callback(a_Callback),
m_BestMatch(NULL),
m_NumMatches(0)
{}
@@ -701,9 +701,9 @@ int cRoot::GetPhysicalRAMUsage(void)
{
AString Line;
std::getline(StatFile, Line);
- if (strncmp(Line.c_str(), "VmRSS:", 7) == 0)
+ if (strncmp(Line.c_str(), "VmRSS:", 6) == 0)
{
- int res = atoi(Line.c_str() + 8);
+ int res = atoi(Line.c_str() + 7);
return (res == 0) ? -1 : res; // If parsing failed, return -1
}
}
diff --git a/src/Scoreboard.cpp b/src/Scoreboard.cpp
new file mode 100644
index 000000000..b2edd613b
--- /dev/null
+++ b/src/Scoreboard.cpp
@@ -0,0 +1,510 @@
+
+// Scoreboard.cpp
+
+// Implementation of a scoreboard that keeps track of specified objectives
+
+#include "Globals.h"
+
+#include "Scoreboard.h"
+#include "World.h"
+#include "ClientHandle.h"
+
+
+
+
+
+AString cObjective::TypeToString(eType a_Type)
+{
+ switch (a_Type)
+ {
+ case E_TYPE_DUMMY: return "dummy";
+ case E_TYPE_DEATH_COUNT: return "deathCount";
+ case E_TYPE_PLAYER_KILL_COUNT: return "playerKillCount";
+ case E_TYPE_TOTAL_KILL_COUNT: return "totalKillCount";
+ case E_TYPE_HEALTH: return "health";
+ case E_TYPE_ACHIEVEMENT: return "achievement";
+ case E_TYPE_STAT: return "stat";
+ case E_TYPE_STAT_ITEM_CRAFT: return "stat.craftItem";
+ case E_TYPE_STAT_ITEM_USE: return "stat.useItem";
+ case E_TYPE_STAT_ITEM_BREAK: return "stat.breakItem";
+ case E_TYPE_STAT_BLOCK_MINE: return "stat.mineBlock";
+ case E_TYPE_STAT_ENTITY_KILL: return "stat.killEntity";
+ case E_TYPE_STAT_ENTITY_KILLED_BY: return "stat.entityKilledBy";
+
+ default: return "";
+ }
+}
+
+
+
+
+
+cObjective::eType cObjective::StringToType(const AString & a_Name)
+{
+ static struct {
+ eType m_Type;
+ const char * m_String;
+ } TypeMap [] =
+ {
+ {E_TYPE_DUMMY, "dummy"},
+ {E_TYPE_DEATH_COUNT, "deathCount"},
+ {E_TYPE_PLAYER_KILL_COUNT, "playerKillCount"},
+ {E_TYPE_TOTAL_KILL_COUNT, "totalKillCount"},
+ {E_TYPE_HEALTH, "health"},
+ {E_TYPE_ACHIEVEMENT, "achievement"},
+ {E_TYPE_STAT, "stat"},
+ {E_TYPE_STAT_ITEM_CRAFT, "stat.craftItem"},
+ {E_TYPE_STAT_ITEM_USE, "stat.useItem"},
+ {E_TYPE_STAT_ITEM_BREAK, "stat.breakItem"},
+ {E_TYPE_STAT_BLOCK_MINE, "stat.mineBlock"},
+ {E_TYPE_STAT_ENTITY_KILL, "stat.killEntity"},
+ {E_TYPE_STAT_ENTITY_KILLED_BY, "stat.entityKilledBy"}
+ };
+ for (size_t i = 0; i < ARRAYCOUNT(TypeMap); i++)
+ {
+ if (NoCaseCompare(TypeMap[i].m_String, a_Name) == 0)
+ {
+ return TypeMap[i].m_Type;
+ }
+ } // for i - TypeMap[]
+ return E_TYPE_DUMMY;
+}
+
+
+
+
+
+cObjective::cObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type, cWorld * a_World)
+ : m_DisplayName(a_DisplayName)
+ , m_Name(a_Name)
+ , m_Type(a_Type)
+ , m_World(a_World)
+{
+}
+
+
+
+
+
+void cObjective::Reset(void)
+{
+ for (cScoreMap::iterator it = m_Scores.begin(); it != m_Scores.end(); ++it)
+ {
+ m_World->BroadcastScoreUpdate(m_Name, it->first, 0, 1);
+ }
+
+ m_Scores.clear();
+}
+
+
+
+
+
+cObjective::Score cObjective::GetScore(const AString & a_Name) const
+{
+ cScoreMap::const_iterator it = m_Scores.find(a_Name);
+
+ if (it == m_Scores.end())
+ {
+ return 0;
+ }
+ else
+ {
+ return it->second;
+ }
+}
+
+
+
+
+
+void cObjective::SetScore(const AString & a_Name, cObjective::Score a_Score)
+{
+ m_Scores[a_Name] = a_Score;
+
+ m_World->BroadcastScoreUpdate(m_Name, a_Name, a_Score, 0);
+}
+
+
+
+
+
+void cObjective::ResetScore(const AString & a_Name)
+{
+ m_Scores.erase(a_Name);
+
+ m_World->BroadcastScoreUpdate(m_Name, a_Name, 0, 1);
+}
+
+
+
+
+
+cObjective::Score cObjective::AddScore(const AString & a_Name, cObjective::Score a_Delta)
+{
+ // TODO 2014-01-19 xdot: Potential optimization - Reuse iterator
+ Score NewScore = m_Scores[a_Name] + a_Delta;
+
+ SetScore(a_Name, NewScore);
+
+ return NewScore;
+}
+
+
+
+
+
+cObjective::Score cObjective::SubScore(const AString & a_Name, cObjective::Score a_Delta)
+{
+ // TODO 2014-01-19 xdot: Potential optimization - Reuse iterator
+ Score NewScore = m_Scores[a_Name] - a_Delta;
+
+ SetScore(a_Name, NewScore);
+
+ return NewScore;
+}
+
+
+
+
+
+void cObjective::SetDisplayName(const AString & a_Name)
+{
+ m_DisplayName = a_Name;
+
+ m_World->BroadcastScoreboardObjective(m_Name, m_DisplayName, 2);
+}
+
+
+
+
+
+void cObjective::SendTo(cClientHandle & a_Client)
+{
+ a_Client.SendScoreboardObjective(m_Name, m_DisplayName, 0);
+
+ for (cScoreMap::const_iterator it = m_Scores.begin(); it != m_Scores.end(); ++it)
+ {
+ a_Client.SendScoreUpdate(m_Name, it->first, it->second, 0);
+ }
+}
+
+
+
+
+
+cTeam::cTeam(const AString & a_Name, const AString & a_DisplayName,
+ const AString & a_Prefix, const AString & a_Suffix)
+ : m_AllowsFriendlyFire(true)
+ , m_CanSeeFriendlyInvisible(false)
+ , m_Name(a_Name)
+ , m_DisplayName(a_DisplayName)
+ , m_Prefix(a_Prefix)
+ , m_Suffix(a_Suffix)
+{}
+
+
+
+
+
+bool cTeam::AddPlayer(const AString & a_Name)
+{
+ return m_Players.insert(a_Name).second;
+}
+
+
+
+
+
+bool cTeam::RemovePlayer(const AString & a_Name)
+{
+ return m_Players.erase(a_Name) > 0;
+}
+
+
+
+
+
+bool cTeam::HasPlayer(const AString & a_Name) const
+{
+ cPlayerNameSet::const_iterator it = m_Players.find(a_Name);
+
+ return it != m_Players.end();
+}
+
+
+
+
+
+void cTeam::Reset(void)
+{
+ // TODO 2014-01-22 xdot: Inform online players
+
+ m_Players.clear();
+}
+
+
+
+
+unsigned int cTeam::GetNumPlayers(void) const
+{
+ return m_Players.size();
+}
+
+
+
+
+
+cScoreboard::cScoreboard(cWorld * a_World) : m_World(a_World)
+{
+ for (int i = 0; i < (int) E_DISPLAY_SLOT_COUNT; ++i)
+ {
+ m_Display[i] = NULL;
+ }
+}
+
+
+
+
+
+cObjective* cScoreboard::RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type)
+{
+ cObjective Objective(a_Name, a_DisplayName, a_Type, m_World);
+
+ std::pair<cObjectiveMap::iterator, bool> Status = m_Objectives.insert(cNamedObjective(a_Name, Objective));
+
+ if (Status.second)
+ {
+ ASSERT(m_World != NULL);
+ m_World->BroadcastScoreboardObjective(a_Name, a_DisplayName, 0);
+
+ return &Status.first->second;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+
+
+
+bool cScoreboard::RemoveObjective(const AString & a_Name)
+{
+ cCSLock Lock(m_CSObjectives);
+
+ cObjectiveMap::iterator it = m_Objectives.find(a_Name);
+
+ if (it == m_Objectives.end())
+ {
+ return false;
+ }
+
+ m_Objectives.erase(it);
+
+ ASSERT(m_World != NULL);
+ m_World->BroadcastScoreboardObjective(it->second.GetName(), it->second.GetDisplayName(), 1);
+
+ return true;
+}
+
+
+
+
+
+cObjective * cScoreboard::GetObjective(const AString & a_Name)
+{
+ cCSLock Lock(m_CSObjectives);
+
+ cObjectiveMap::iterator it = m_Objectives.find(a_Name);
+
+ if (it == m_Objectives.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return &it->second;
+ }
+}
+
+
+
+
+
+cTeam * cScoreboard::RegisterTeam(
+ const AString & a_Name, const AString & a_DisplayName,
+ const AString & a_Prefix, const AString & a_Suffix
+)
+{
+ cTeam Team(a_Name, a_DisplayName, a_Prefix, a_Suffix);
+
+ std::pair<cTeamMap::iterator, bool> Status = m_Teams.insert(cNamedTeam(a_Name, Team));
+
+ return Status.second ? &Status.first->second : NULL;
+}
+
+
+
+
+
+bool cScoreboard::RemoveTeam(const AString & a_Name)
+{
+ cCSLock Lock(m_CSTeams);
+
+ cTeamMap::iterator it = m_Teams.find(a_Name);
+
+ if (it == m_Teams.end())
+ {
+ return false;
+ }
+
+ m_Teams.erase(it);
+
+ return true;
+}
+
+
+
+
+
+cTeam * cScoreboard::GetTeam(const AString & a_Name)
+{
+ cCSLock Lock(m_CSTeams);
+
+ cTeamMap::iterator it = m_Teams.find(a_Name);
+
+ if (it == m_Teams.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return &it->second;
+ }
+}
+
+
+
+
+
+cTeam * cScoreboard::QueryPlayerTeam(const AString & a_Name)
+{
+ cCSLock Lock(m_CSTeams);
+
+ for (cTeamMap::iterator it = m_Teams.begin(); it != m_Teams.end(); ++it)
+ {
+ if (it->second.HasPlayer(a_Name))
+ {
+ return &it->second;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+
+
+void cScoreboard::SetDisplay(const AString & a_Objective, eDisplaySlot a_Slot)
+{
+ ASSERT(a_Slot < E_DISPLAY_SLOT_COUNT);
+
+ cObjective * Objective = GetObjective(a_Objective);
+
+ SetDisplay(Objective, a_Slot);
+}
+
+
+
+
+
+void cScoreboard::SetDisplay(cObjective * a_Objective, eDisplaySlot a_Slot)
+{
+ m_Display[a_Slot] = a_Objective;
+
+ ASSERT(m_World != NULL);
+ m_World->BroadcastDisplayObjective(a_Objective ? a_Objective->GetName() : "", a_Slot);
+}
+
+
+
+
+
+cObjective * cScoreboard::GetObjectiveIn(eDisplaySlot a_Slot)
+{
+ ASSERT(a_Slot < E_DISPLAY_SLOT_COUNT);
+
+ return m_Display[a_Slot];
+}
+
+
+
+
+
+void cScoreboard::ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback)
+{
+ cCSLock Lock(m_CSObjectives);
+
+ for (cObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it)
+ {
+ if (it->second.GetType() == a_Type)
+ {
+ // Call callback
+ if (a_Callback.Item(&it->second))
+ {
+ return;
+ }
+ }
+ }
+}
+
+
+
+
+
+void cScoreboard::SendTo(cClientHandle & a_Client)
+{
+ cCSLock Lock(m_CSObjectives);
+
+ for (cObjectiveMap::iterator it = m_Objectives.begin(); it != m_Objectives.end(); ++it)
+ {
+ it->second.SendTo(a_Client);
+ }
+
+ for (int i = 0; i < (int) E_DISPLAY_SLOT_COUNT; ++i)
+ {
+ // Avoid race conditions
+ cObjective * Objective = m_Display[i];
+
+ if (Objective)
+ {
+ a_Client.SendDisplayObjective(Objective->GetName(), (eDisplaySlot) i);
+ }
+ }
+}
+
+
+
+
+
+unsigned int cScoreboard::GetNumObjectives(void) const
+{
+ return m_Objectives.size();
+}
+
+
+
+
+
+unsigned int cScoreboard::GetNumTeams(void) const
+{
+ return m_Teams.size();
+}
+
+
+
+
+
diff --git a/src/Scoreboard.h b/src/Scoreboard.h
new file mode 100644
index 000000000..f64ba2bce
--- /dev/null
+++ b/src/Scoreboard.h
@@ -0,0 +1,276 @@
+
+// Scoreboard.h
+
+// Implementation of a scoreboard that keeps track of specified objectives
+
+
+
+
+
+#pragma once
+
+
+
+
+
+class cObjective;
+class cWorld;
+
+typedef cItemCallback<cObjective> cObjectiveCallback;
+
+
+
+
+
+// tolua_begin
+class cObjective
+{
+public:
+
+ typedef int Score;
+
+ enum eType
+ {
+ E_TYPE_DUMMY,
+
+ E_TYPE_DEATH_COUNT,
+ E_TYPE_PLAYER_KILL_COUNT,
+ E_TYPE_TOTAL_KILL_COUNT,
+ E_TYPE_HEALTH,
+
+ E_TYPE_ACHIEVEMENT,
+
+ E_TYPE_STAT,
+ E_TYPE_STAT_ITEM_CRAFT,
+ E_TYPE_STAT_ITEM_USE,
+ E_TYPE_STAT_ITEM_BREAK,
+
+ E_TYPE_STAT_BLOCK_MINE,
+ E_TYPE_STAT_ENTITY_KILL,
+ E_TYPE_STAT_ENTITY_KILLED_BY
+ };
+
+ // tolua_end
+
+ static AString TypeToString(eType a_Type);
+
+ static eType StringToType(const AString & a_Name);
+
+public:
+
+ cObjective(const AString & a_Name, const AString & a_DisplayName, eType a_Type, cWorld * a_World);
+
+ // tolua_begin
+
+ eType GetType(void) const { return m_Type; }
+
+ const AString & GetName(void) const { return m_Name; }
+ const AString & GetDisplayName(void) const { return m_DisplayName; }
+
+ /// Resets the objective
+ void Reset(void);
+
+ /// Returns the score of the specified player
+ Score GetScore(const AString & a_Name) const;
+
+ /// Sets the score of the specified player
+ void SetScore(const AString & a_Name, Score a_Score);
+
+ /// Resets the score of the specified player
+ void ResetScore(const AString & a_Name);
+
+ /// Adds a_Delta and returns the new score
+ Score AddScore(const AString & a_Name, Score a_Delta);
+
+ /// Subtracts a_Delta and returns the new score
+ Score SubScore(const AString & a_Name, Score a_Delta);
+
+ void SetDisplayName(const AString & a_Name);
+
+ // tolua_end
+
+ /// Send this objective to the specified client
+ void SendTo(cClientHandle & a_Client);
+
+private:
+
+ typedef std::pair<AString, Score> cTrackedPlayer;
+
+ typedef std::map<AString, Score> cScoreMap;
+
+ cScoreMap m_Scores;
+
+ AString m_DisplayName;
+ AString m_Name;
+
+ eType m_Type;
+
+ cWorld * m_World;
+
+ friend class cScoreboardSerializer;
+
+};
+
+
+
+
+
+// tolua_begin
+class cTeam
+{
+public:
+
+ // tolua_end
+
+ cTeam(
+ const AString & a_Name, const AString & a_DisplayName,
+ const AString & a_Prefix, const AString & a_Suffix
+ );
+
+ /// Adds a new player to the team
+ bool AddPlayer(const AString & a_Name);
+
+ /// Removes a player from the team
+ bool RemovePlayer(const AString & a_Name);
+
+ /// Returns whether the specified player is in this team
+ bool HasPlayer(const AString & a_Name) const;
+
+ /// Removes all registered players
+ void Reset(void);
+
+ // tolua_begin
+
+ /// Returns the number of registered players
+ unsigned int GetNumPlayers(void) const;
+
+ bool AllowsFriendlyFire(void) const { return m_AllowsFriendlyFire; }
+ bool CanSeeFriendlyInvisible(void) const { return m_CanSeeFriendlyInvisible; }
+
+ const AString & GetDisplayName(void) const { return m_DisplayName; }
+ const AString & GetName(void) const { return m_DisplayName; }
+
+ const AString & GetPrefix(void) const { return m_Prefix; }
+ const AString & GetSuffix(void) const { return m_Suffix; }
+
+ void SetFriendlyFire(bool a_Flag) { m_AllowsFriendlyFire = a_Flag; }
+ void SetCanSeeFriendlyInvisible(bool a_Flag) { m_CanSeeFriendlyInvisible = a_Flag; }
+
+ void SetDisplayName(const AString & a_Name);
+
+ void SetPrefix(const AString & a_Prefix) { m_Prefix = a_Prefix; }
+ void SetSuffix(const AString & a_Suffix) { m_Suffix = a_Suffix; }
+
+ // tolua_end
+
+private:
+
+ typedef std::set<AString> cPlayerNameSet;
+
+ bool m_AllowsFriendlyFire;
+ bool m_CanSeeFriendlyInvisible;
+
+ AString m_DisplayName;
+ AString m_Name;
+
+ AString m_Prefix;
+ AString m_Suffix;
+
+ cPlayerNameSet m_Players;
+
+ friend class cScoreboardSerializer;
+
+};
+
+
+
+
+
+// tolua_begin
+class cScoreboard
+{
+public:
+
+ enum eDisplaySlot
+ {
+ E_DISPLAY_SLOT_LIST = 0,
+ E_DISPLAY_SLOT_SIDEBAR,
+ E_DISPLAY_SLOT_NAME,
+
+ E_DISPLAY_SLOT_COUNT
+ };
+
+ // tolua_end
+
+
+public:
+
+ cScoreboard(cWorld * a_World);
+
+ // tolua_begin
+
+ /// Registers a new scoreboard objective, returns the cObjective instance, NULL on name collision
+ cObjective * RegisterObjective(const AString & a_Name, const AString & a_DisplayName, cObjective::eType a_Type);
+
+ /// Removes a registered objective, returns true if operation was successful
+ bool RemoveObjective(const AString & a_Name);
+
+ /// Retrieves the objective with the specified name, NULL if not found
+ cObjective * GetObjective(const AString & a_Name);
+
+ /// Registers a new team, returns the cTeam instance, NULL on name collision
+ cTeam * RegisterTeam(const AString & a_Name, const AString & a_DisplayName, const AString & a_Prefix, const AString & a_Suffix);
+
+ /// Removes a registered team, returns true if operation was successful
+ bool RemoveTeam(const AString & a_Name);
+
+ /// Retrieves the team with the specified name, NULL if not found
+ cTeam * GetTeam(const AString & a_Name);
+
+ cTeam * QueryPlayerTeam(const AString & a_Name); // WARNING: O(n logn)
+
+ void SetDisplay(const AString & a_Objective, eDisplaySlot a_Slot);
+
+ void SetDisplay(cObjective * a_Objective, eDisplaySlot a_Slot);
+
+ cObjective * GetObjectiveIn(eDisplaySlot a_Slot);
+
+ /// Execute callback for each objective with the specified type
+ void ForEachObjectiveWith(cObjective::eType a_Type, cObjectiveCallback& a_Callback);
+
+ unsigned int GetNumObjectives(void) const;
+
+ unsigned int GetNumTeams(void) const;
+
+ // tolua_end
+
+ /// Send this scoreboard to the specified client
+ void SendTo(cClientHandle & a_Client);
+
+
+private:
+
+ typedef std::pair<AString, cObjective> cNamedObjective;
+ typedef std::pair<AString, cTeam> cNamedTeam;
+
+ typedef std::map<AString, cObjective> cObjectiveMap;
+ typedef std::map<AString, cTeam> cTeamMap;
+
+ // TODO 2014-01-19 xdot: Potential optimization - Sort objectives based on type
+ cCriticalSection m_CSObjectives;
+ cObjectiveMap m_Objectives;
+
+ cCriticalSection m_CSTeams;
+ cTeamMap m_Teams;
+
+ cWorld * m_World;
+
+ cObjective* m_Display[E_DISPLAY_SLOT_COUNT];
+
+ friend class cScoreboardSerializer;
+
+} ;
+
+
+
+
diff --git a/src/Server.cpp b/src/Server.cpp
index a93be9a5b..ba2b46d55 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -118,7 +118,7 @@ cServer::cServer(void) :
void cServer::ClientDestroying(const cClientHandle * a_Client)
{
- m_SocketThreads.StopReading(a_Client);
+ m_SocketThreads.RemoveClient(a_Client);
}
@@ -143,15 +143,6 @@ void cServer::WriteToClient(const cClientHandle * a_Client, const AString & a_Da
-void cServer::QueueClientClose(const cClientHandle * a_Client)
-{
- m_SocketThreads.QueueClose(a_Client);
-}
-
-
-
-
-
void cServer::RemoveClient(const cClientHandle * a_Client)
{
m_SocketThreads.RemoveClient(a_Client);
@@ -173,6 +164,7 @@ void cServer::ClientMovedToWorld(const cClientHandle * a_Client)
void cServer::PlayerCreated(const cPlayer * a_Player)
{
+ UNUSED(a_Player);
// To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread
cCSLock Lock(m_CSPlayerCountDiff);
m_PlayerCountDiff += 1;
@@ -184,6 +176,7 @@ void cServer::PlayerCreated(const cPlayer * a_Player)
void cServer::PlayerDestroying(const cPlayer * a_Player)
{
+ UNUSED(a_Player);
// To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread
cCSLock Lock(m_CSPlayerCountDiff);
m_PlayerCountDiff -= 1;
@@ -201,6 +194,8 @@ bool cServer::InitServer(cIniFile & a_SettingsIni)
m_PlayerCount = 0;
m_PlayerCountDiff = 0;
+ 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)
{
LOGERROR("ERROR: Trying to initialize server while server is already running!");
@@ -242,7 +237,8 @@ bool cServer::InitServer(cIniFile & a_SettingsIni)
m_bIsConnected = true;
m_ServerID = "-";
- if (a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true))
+ m_ShouldAuthenticate = a_SettingsIni.GetValueSetB("Authentication", "Authenticate", true);
+ if (m_ShouldAuthenticate)
{
MTRand mtrand1;
unsigned int r1 = (mtrand1.randInt() % 1147483647) + 1000000000;
@@ -289,17 +285,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();
}
@@ -455,7 +443,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
{
return;
}
-
+
// Special handling: "stop" and "restart" are built in
if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0))
{
@@ -491,13 +479,13 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
if (split[0].compare("killmem") == 0)
{
- while (true)
+ for (;;)
{
new char[100 * 1024 * 1024]; // Allocate and leak 100 MiB in a loop -> fill memory and kill MCS
}
}
#endif
-
+
if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output))
{
a_Output.Finished();
@@ -514,6 +502,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
{
+ UNUSED(a_Split);
typedef std::pair<AString, AString> AStringPair;
typedef std::vector<AStringPair> AStringPairs;
@@ -525,6 +514,8 @@ void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback &
virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override
{
+ UNUSED(a_Plugin);
+ UNUSED(a_Permission);
if (!a_HelpString.empty())
{
m_Commands.push_back(AStringPair(a_Command, a_HelpString));
diff --git a/src/Server.h b/src/Server.h
index 0d93469a5..b5280c59d 100644
--- a/src/Server.h
+++ b/src/Server.h
@@ -11,10 +11,24 @@
#include "OSSupport/SocketThreads.h"
#include "OSSupport/ListenThread.h"
-#include "cryptopp/rsa.h"
-#include "cryptopp/randpool.h"
+
#include "RCONServer.h"
+#ifdef _MSC_VER
+ #pragma warning(push)
+ #pragma warning(disable:4127)
+ #pragma warning(disable:4244)
+ #pragma warning(disable:4231)
+ #pragma warning(disable:4189)
+ #pragma warning(disable:4702)
+#endif
+
+#include "Crypto.h"
+
+#ifdef _MSC_VER
+ #pragma warning(pop)
+#endif
+
@@ -35,6 +49,8 @@ class cServer // tolua_export
: public cListenThread::cCallback
{ // tolua_export
public: // tolua_export
+
+ virtual ~cServer() {}
bool InitServer(cIniFile & a_SettingsIni);
// tolua_begin
@@ -55,13 +71,13 @@ public: // tolua_export
bool Command(cClientHandle & a_Client, AString & a_Cmd);
- /// Executes the console command, sends output through the specified callback
+ /** Executes the console command, sends output through the specified callback */
void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
- /// Lists all available console commands and their helpstrings
+ /** Lists all available console commands and their helpstrings */
void PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
- /// Binds the built-in console commands with the plugin manager
+ /** Binds the built-in console commands with the plugin manager */
static void BindBuiltInConsoleCommands(void);
void Shutdown(void);
@@ -71,33 +87,38 @@ public: // tolua_export
const AString & GetServerID(void) const { return m_ServerID; } // tolua_export
- void ClientDestroying(const cClientHandle * a_Client); // Called by cClientHandle::Destroy(); stop m_SocketThreads from calling back into a_Client
+ /** Called by cClientHandle's destructor; stop m_SocketThreads from calling back into a_Client */
+ void ClientDestroying(const cClientHandle * a_Client);
- void NotifyClientWrite(const cClientHandle * a_Client); // Notifies m_SocketThreads that client has something to be written
+ /** Notifies m_SocketThreads that client has something to be written */
+ void NotifyClientWrite(const cClientHandle * a_Client);
void WriteToClient(const cClientHandle * a_Client, const AString & a_Data); // Queues outgoing data for the client through m_SocketThreads
- void QueueClientClose(const cClientHandle * a_Client); // Queues the clienthandle to close when all its outgoing data is sent
-
void RemoveClient(const cClientHandle * a_Client); // Removes the clienthandle from m_SocketThreads
- /// Don't tick a_Client anymore, it will be ticked from its cPlayer instead
+ /** Don't tick a_Client anymore, it will be ticked from its cPlayer instead */
void ClientMovedToWorld(const cClientHandle * a_Client);
- /// Notifies the server that a player was created; the server uses this to adjust the number of players
+ /** Notifies the server that a player was created; the server uses this to adjust the number of players */
void PlayerCreated(const cPlayer * a_Player);
- /// Notifies the server that a player is being destroyed; the server uses this to adjust the number of players
+ /** Notifies the server that a player is being destroyed; the server uses this to adjust the number of players */
void PlayerDestroying(const cPlayer * a_Player);
+
+ /** Returns base64 encoded favicon data (obtained from favicon.png) */
+ const AString & GetFaviconData(void) const { return m_FaviconData; }
+
+ cRSAPrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
+ const AString & GetPublicKeyDER(void) const { return m_PublicKeyDER; }
- CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; }
- CryptoPP::RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; }
+ bool ShouldAuthenticate(void) const { return m_ShouldAuthenticate; }
private:
friend class cRoot; // so cRoot can create and destroy cServer
- /// When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap)
+ /** When NotifyClientWrite() is called, it is queued for this thread to process (to avoid deadlocks between cSocketThreads, cClientHandle and cChunkMap) */
class cNotifyWriteThread :
public cIsThread
{
@@ -121,7 +142,7 @@ private:
void NotifyClientWrite(const cClientHandle * a_Client);
} ;
- /// The server tick thread takes care of the players who aren't yet spawned in a world
+ /** The server tick thread takes care of the players who aren't yet spawned in a world */
class cTickThread :
public cIsThread
{
@@ -160,30 +181,38 @@ 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;
AString m_Description;
+ AString m_FaviconData;
int m_MaxPlayers;
bool m_bIsHardcore;
cTickThread m_TickThread;
cEvent m_RestartEvent;
- /// The server ID used for client authentication
+ /** The server ID used for client authentication */
AString m_ServerID;
+ /** If true, players will be online-authenticated agains Mojang servers.
+ This setting is the same as the "online-mode" setting in Vanilla. */
+ bool m_ShouldAuthenticate;
+
cServer(void);
- /// Loads, or generates, if missing, RSA keys for protocol encryption
+ /** Loads, or generates, if missing, RSA keys for protocol encryption */
void PrepareKeys(void);
bool Tick(float a_Dt);
- /// Ticks the clients in m_Clients, manages the list in respect to removing clients
+ /** Ticks the clients in m_Clients, manages the list in respect to removing clients */
void TickClients(float a_Dt);
// cListenThread::cCallback overrides:
diff --git a/src/Simulator/FireSimulator.h b/src/Simulator/FireSimulator.h
index 66c31b440..9ccc3ef4f 100644
--- a/src/Simulator/FireSimulator.h
+++ b/src/Simulator/FireSimulator.h
@@ -22,7 +22,7 @@ public:
cFireSimulator(cWorld & a_World, cIniFile & a_IniFile);
~cFireSimulator();
- virtual void Simulate(float a_Dt) override {} // not used
+ virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used
virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override;
diff --git a/src/Simulator/NoopFluidSimulator.h b/src/Simulator/NoopFluidSimulator.h
index 8f894433f..9113aec3c 100644
--- a/src/Simulator/NoopFluidSimulator.h
+++ b/src/Simulator/NoopFluidSimulator.h
@@ -27,8 +27,14 @@ public:
}
// cSimulator overrides:
- virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override {}
- virtual void Simulate(float a_Dt) override {}
+ virtual void AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk) override
+ {
+ UNUSED(a_BlockX);
+ UNUSED(a_BlockY);
+ UNUSED(a_BlockZ);
+ UNUSED(a_Chunk);
+ }
+ virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);}
} ;
diff --git a/src/Simulator/RedstoneSimulator.cpp b/src/Simulator/RedstoneSimulator.cpp
index f65908729..e361cbf49 100644
--- a/src/Simulator/RedstoneSimulator.cpp
+++ b/src/Simulator/RedstoneSimulator.cpp
@@ -4,6 +4,7 @@
#include "RedstoneSimulator.h"
#include "../BlockEntities/DropSpenserEntity.h"
#include "../BlockEntities/NoteEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
#include "../Entities/TNTEntity.h"
#include "../Blocks/BlockTorch.h"
#include "../Blocks/BlockDoor.h"
@@ -44,83 +45,46 @@ void cRedstoneSimulator::AddBlock(int a_BlockX, int a_BlockY, int a_BlockZ, cChu
int RelX = a_BlockX - a_Chunk->GetPosX() * cChunkDef::Width;
int RelZ = a_BlockZ - a_Chunk->GetPosZ() * cChunkDef::Width;
+
+ BLOCKTYPE Block;
+ NIBBLETYPE Meta;
+ a_Chunk->GetBlockTypeMeta(RelX, a_BlockY, RelZ, Block, Meta);
- if (!IsAllowedBlock(a_Chunk->GetBlock(RelX, a_BlockY, RelZ)))
- {
- return;
- }
-
- // Check for duplicates:
- cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
- for (cRedstoneSimulatorChunkData::const_iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr)
- {
- if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ))
- {
- return;
- }
- }
-
- ChunkData.push_back(cCoordWithInt(RelX, a_BlockY, RelZ));
-}
-
-
-
-
-
-void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
-{
- cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
- if (ChunkData.empty())
- {
- return;
- }
-
- int BaseX = a_Chunk->GetPosX() * cChunkDef::Width;
- int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width;
+ // Every time a block is changed (AddBlock called), we want to go through all lists and check to see if the coordiantes stored within are still valid
+ // Checking only when a block is changed, as opposed to every tick, also improves performance
- // Check to see if PoweredBlocks have invalid items (source is air or unpowered)
- for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end();)
+ for (PoweredBlocksList::iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr)
{
- int RelX = itr->a_SourcePos.x - a_ChunkX * cChunkDef::Width;
- int RelZ = itr->a_SourcePos.z - a_ChunkZ * cChunkDef::Width;
- int DestRelX = itr->a_BlockPos.x - a_ChunkX * cChunkDef::Width;
- int DestRelZ = itr->a_BlockPos.z - a_ChunkZ * cChunkDef::Width;
-
- BLOCKTYPE SourceBlockType;
- NIBBLETYPE SourceBlockMeta;
- BLOCKTYPE DestBlockType;
- if (
- !a_Chunk->UnboundedRelGetBlock(RelX, itr->a_SourcePos.y, RelZ, SourceBlockType, SourceBlockMeta) ||
- !a_Chunk->UnboundedRelGetBlockType(DestRelX, itr->a_BlockPos.y, DestRelZ, DestBlockType)
- )
+ if (!itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
continue;
}
- if (SourceBlockType != itr->a_SourceBlock)
+ if (!IsPotentialSource(Block))
{
- LOGD("cRedstoneSimulator: Erased block %s from powered blocks list due to present/past block type mismatch", ItemToFullString(itr->a_SourceBlock).c_str());
- itr = m_PoweredBlocks.erase(itr);
+ LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list as it no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
+ m_PoweredBlocks.erase(itr);
+ break;
}
else if (
// Changeable sources
- ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (SourceBlockMeta == 0)) ||
- ((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(SourceBlockMeta)) ||
- ((SourceBlockType == E_BLOCK_DETECTOR_RAIL) && (SourceBlockMeta & 0x08) == 0x08) ||
- (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(SourceBlockMeta))) ||
- (((SourceBlockType == E_BLOCK_STONE_PRESSURE_PLATE) || (SourceBlockType == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (SourceBlockMeta == 0))
+ ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) ||
+ ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) ||
+ ((Block == E_BLOCK_DETECTOR_RAIL) && (Meta & 0x08) == 0) ||
+ (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta))) ||
+ (((Block == E_BLOCK_STONE_PRESSURE_PLATE) || (Block == E_BLOCK_WOODEN_PRESSURE_PLATE)) && (Meta == 0))
)
{
- LOGD("cRedstoneSimulator: Erased block %s from powered blocks list due to present/past metadata mismatch", ItemToFullString(itr->a_SourceBlock).c_str());
- itr = m_PoweredBlocks.erase(itr);
+ LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
+ m_PoweredBlocks.erase(itr);
+ break;
}
- else if (SourceBlockType == E_BLOCK_DAYLIGHT_SENSOR)
+ else if (Block == E_BLOCK_DAYLIGHT_SENSOR)
{
if (!a_Chunk->IsLightValid())
{
- m_World.QueueLightChunk(a_ChunkX, a_ChunkZ);
- ++itr;
- continue;
+ m_World.QueueLightChunk(a_Chunk->GetPosX(), a_Chunk->GetPosZ());
+ break;
}
else
{
@@ -130,151 +94,151 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c
if (a_Chunk->GetTimeAlteredLight(SkyLight) <= 8) // Could use SkyLight - m_World.GetSkyDarkness();
{
LOGD("cRedstoneSimulator: Erased daylight sensor from powered blocks list due to insufficient light level");
- itr = m_PoweredBlocks.erase(itr);
- }
- else
- {
- ++itr;
- continue;
+ m_PoweredBlocks.erase(itr);
+ break;
}
}
}
- else if ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (DestBlockType == E_BLOCK_REDSTONE_WIRE))
+ }
+
+ for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr)
+ {
+ if (itr->a_SourcePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
- // It is simply not allowed that a wire powers another wire, presuming that data here is sane and a dest and source are beside each other
- LOGD("cRedstoneSimulator: Erased redstone wire from powered blocks list because its source was also wire");
- itr = m_PoweredBlocks.erase(itr);
+ if (!IsPotentialSource(Block))
+ {
+ LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer connected to a source", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
+ m_LinkedPoweredBlocks.erase(itr);
+ break;
+ }
+ else if (
+ // Things that can send power through a block but which depends on meta
+ ((Block == E_BLOCK_REDSTONE_WIRE) && (Meta == 0)) ||
+ ((Block == E_BLOCK_LEVER) && !IsLeverOn(Meta)) ||
+ (((Block == E_BLOCK_STONE_BUTTON) || (Block == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(Meta)))
+ )
+ {
+ LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list due to present/past metadata mismatch", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
+ m_LinkedPoweredBlocks.erase(itr);
+ break;
+ }
}
- else
+ else if (itr->a_MiddlePos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
- ++itr;
+ if (!IsViableMiddleBlock(Block))
+ {
+ LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from linked powered blocks list as it is no longer powered through a valid middle block", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
+ m_LinkedPoweredBlocks.erase(itr);
+ break;
+ }
}
}
- // Check to see if LinkedPoweredBlocks have invalid items: source, block powered through, or power destination block has changed
- for (LinkedBlocksList::iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end();)
+ for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end(); ++itr)
{
- int RelX = itr->a_SourcePos.x - a_ChunkX * cChunkDef::Width;
- int RelZ = itr->a_SourcePos.z - a_ChunkZ * cChunkDef::Width;
- int MidRelX = itr->a_MiddlePos.x - a_ChunkX * cChunkDef::Width;
- int MidRelZ = itr->a_MiddlePos.z - a_ChunkZ * cChunkDef::Width;
-
- BLOCKTYPE SourceBlockType;
- NIBBLETYPE SourceBlockMeta;
- BLOCKTYPE MiddleBlockType;
- if (
- !a_Chunk->UnboundedRelGetBlock(RelX, itr->a_SourcePos.y, RelZ, SourceBlockType, SourceBlockMeta) ||
- !a_Chunk->UnboundedRelGetBlockType(MidRelX, itr->a_MiddlePos.y, MidRelZ, MiddleBlockType)
- )
+ if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
continue;
}
- if (SourceBlockType != itr->a_SourceBlock)
- {
- LOGD("cRedstoneSimulator: Erased block %s from linked powered blocks list due to present/past block type mismatch", ItemToFullString(itr->a_SourceBlock).c_str());
- itr = m_LinkedPoweredBlocks.erase(itr);
- }
- else if (MiddleBlockType != itr->a_MiddleBlock)
+ if (!IsAllowedBlock(Block))
{
- LOGD("cRedstoneSimulator: Erased block %s from linked powered blocks list due to present/past middle block mismatch", ItemToFullString(itr->a_SourceBlock).c_str());
- itr = m_LinkedPoweredBlocks.erase(itr);
- }
- else if (
- // Things that can send power through a block but which depends on meta
- ((SourceBlockType == E_BLOCK_REDSTONE_WIRE) && (SourceBlockMeta == 0)) ||
- ((SourceBlockType == E_BLOCK_LEVER) && !IsLeverOn(SourceBlockMeta)) ||
- (((SourceBlockType == E_BLOCK_STONE_BUTTON) || (SourceBlockType == E_BLOCK_WOODEN_BUTTON)) && (!IsButtonOn(SourceBlockMeta)))
- )
- {
- LOGD("cRedstoneSimulator: Erased block %s from linked powered blocks list due to present/past metadata mismatch", ItemToFullString(itr->a_SourceBlock).c_str());
- itr = m_LinkedPoweredBlocks.erase(itr);
- }
- else
- {
- ++itr;
+ LOGD("cRedstoneSimulator: Erased block @ {%i, %i, %i} from toggleable simulated list as it is no longer redstone", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z);
+ m_SimulatedPlayerToggleableBlocks.erase(itr);
+ break;
}
}
- for (SimulatedPlayerToggleableList::iterator itr = m_SimulatedPlayerToggleableBlocks.begin(); itr != m_SimulatedPlayerToggleableBlocks.end();)
+ for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end(); ++itr)
{
- int RelX = itr->a_BlockPos.x - a_ChunkX * cChunkDef::Width;
- int RelZ = itr->a_BlockPos.z - a_ChunkZ * cChunkDef::Width;
-
- BLOCKTYPE SourceBlockType;
- if (!a_Chunk->UnboundedRelGetBlockType(RelX, itr->a_BlockPos.y, RelZ, SourceBlockType))
+ if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
continue;
}
- else if (!IsAllowedBlock(SourceBlockType))
+
+ if ((Block != E_BLOCK_REDSTONE_REPEATER_ON) && (Block != E_BLOCK_REDSTONE_REPEATER_OFF))
{
- LOGD("cRedstoneSimulator: Erased block %s from toggleable simulated list as block is no longer redstone", ItemToFullString(SourceBlockType).c_str());
- itr = m_SimulatedPlayerToggleableBlocks.erase(itr);
+ m_RepeatersDelayList.erase(itr);
+ break;
}
- else
+ }
+
+ cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
+ for (cRedstoneSimulatorChunkData::iterator itr = ChunkData.begin(); itr != ChunkData.end(); ++itr)
+ {
+ if ((itr->x == RelX) && (itr->y == a_BlockY) && (itr->z == RelZ)) // We are at an entry matching the current (changed) block
{
- ++itr;
+ if (!IsAllowedBlock(Block))
+ {
+ ChunkData.erase(itr); // The new blocktype is not redstone; it must be removed from this list
+ }
+ else
+ {
+ itr->Data = Block; // Update block information
+ }
+ return;
}
}
- for (RepeatersDelayList::iterator itr = m_RepeatersDelayList.begin(); itr != m_RepeatersDelayList.end();)
+ if (!IsAllowedBlock(Block))
{
- int RelX = itr->a_BlockPos.x - a_ChunkX * cChunkDef::Width;
- int RelZ = itr->a_BlockPos.z - a_ChunkZ * cChunkDef::Width;
+ return;
+ }
+
+ ChunkData.push_back(cCoordWithBlock(RelX, a_BlockY, RelZ, Block));
+}
- BLOCKTYPE SourceBlockType;
- if (!a_Chunk->UnboundedRelGetBlockType(RelX, itr->a_BlockPos.y, RelZ, SourceBlockType))
- {
- continue;
- }
- if ((SourceBlockType != E_BLOCK_REDSTONE_REPEATER_ON) && (SourceBlockType != E_BLOCK_REDSTONE_REPEATER_OFF))
- {
- itr = m_RepeatersDelayList.erase(itr);
- continue;
- }
- ++itr;
- }
- for (cRedstoneSimulatorChunkData::iterator dataitr = ChunkData.begin(), end = ChunkData.end(); dataitr != end;)
+
+void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk)
+{
+ // We still attempt to simulate all blocks in the chunk every tick, because of outside influence that needs to be taken into account
+ // For example, repeaters need to be ticked, pressure plates checked for entities, daylight sensor checked for light changes, etc.
+ // The easiest way to make this more efficient is probably just to reduce code within the handlers that put too much strain on server, like getting or setting blocks
+ // A marking dirty system might be a TODO for later on, perhaps
+
+ cRedstoneSimulatorChunkData & ChunkData = a_Chunk->GetRedstoneSimulatorData();
+ if (ChunkData.empty())
{
- BLOCKTYPE BlockType = a_Chunk->GetBlock(dataitr->x, dataitr->y, dataitr->z);
- if (!IsAllowedBlock(BlockType))
- {
- dataitr = ChunkData.erase(dataitr);
- continue;
- }
+ return;
+ }
- // PoweredBlock and LinkedPoweredBlock list was fine, now to the actual handling
+ int BaseX = a_Chunk->GetPosX() * cChunkDef::Width;
+ int BaseZ = a_Chunk->GetPosZ() * cChunkDef::Width;
+
+ for (cRedstoneSimulatorChunkData::const_iterator dataitr = ChunkData.begin(); dataitr != ChunkData.end(); ++dataitr)
+ {
int a_X = BaseX + dataitr->x;
int a_Z = BaseZ + dataitr->z;
- switch (BlockType)
+ switch (dataitr->Data)
{
case E_BLOCK_BLOCK_OF_REDSTONE: HandleRedstoneBlock(a_X, dataitr->y, a_Z); break;
case E_BLOCK_LEVER: HandleRedstoneLever(a_X, dataitr->y, a_Z); break;
- case E_BLOCK_TNT: HandleTNT(a_X, dataitr->y, a_Z); break;
+ case E_BLOCK_TNT: HandleTNT(a_X, dataitr->y, a_Z); break;
case E_BLOCK_TRAPDOOR: HandleTrapdoor(a_X, dataitr->y, a_Z); break;
case E_BLOCK_REDSTONE_WIRE: HandleRedstoneWire(a_X, dataitr->y, a_Z); break;
case E_BLOCK_NOTE_BLOCK: HandleNoteBlock(a_X, dataitr->y, a_Z); break;
case E_BLOCK_DAYLIGHT_SENSOR: HandleDaylightSensor(a_X, dataitr->y, a_Z); break;
+ case E_BLOCK_COMMAND_BLOCK: HandleCommandBlock(a_X, dataitr->y, a_Z); break;
case E_BLOCK_REDSTONE_TORCH_OFF:
case E_BLOCK_REDSTONE_TORCH_ON:
{
- HandleRedstoneTorch(a_X, dataitr->y, a_Z, BlockType);
+ HandleRedstoneTorch(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
case E_BLOCK_STONE_BUTTON:
case E_BLOCK_WOODEN_BUTTON:
{
- HandleRedstoneButton(a_X, dataitr->y, a_Z, BlockType);
+ HandleRedstoneButton(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
case E_BLOCK_REDSTONE_REPEATER_OFF:
case E_BLOCK_REDSTONE_REPEATER_ON:
{
- HandleRedstoneRepeater(a_X, dataitr->y, a_Z, BlockType);
+ HandleRedstoneRepeater(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
case E_BLOCK_PISTON:
@@ -286,7 +250,7 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c
case E_BLOCK_REDSTONE_LAMP_OFF:
case E_BLOCK_REDSTONE_LAMP_ON:
{
- HandleRedstoneLamp(a_X, dataitr->y, a_Z, BlockType);
+ HandleRedstoneLamp(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
case E_BLOCK_DISPENSER:
@@ -305,18 +269,16 @@ void cRedstoneSimulator::SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, c
case E_BLOCK_DETECTOR_RAIL:
case E_BLOCK_POWERED_RAIL:
{
- HandleRail(a_X, dataitr->y, a_Z, BlockType);
+ HandleRail(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
case E_BLOCK_WOODEN_PRESSURE_PLATE:
case E_BLOCK_STONE_PRESSURE_PLATE:
{
- HandlePressurePlate(a_X, dataitr->y, a_Z, BlockType);
+ HandlePressurePlate(a_X, dataitr->y, a_Z, dataitr->Data);
break;
}
}
-
- ++dataitr;
}
}
@@ -487,7 +449,7 @@ void cRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_Bl
};
// Check to see if directly beside a power source
- if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ))
+ if (IsWirePowered(a_BlockX, a_BlockY, a_BlockZ))
{
m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 15); // Maximum power
}
@@ -545,7 +507,7 @@ void cRedstoneSimulator::HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_Bl
// transferring power to other wires around.
// However, self not directly powered anymore, so source must have been removed,
// therefore, self must be set to meta zero
- m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0);
+ m_World.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_REDSTONE_WIRE, 0); // SetMeta & WakeUpSims doesn't seem to work here, so SetBlock
return; // No need to process block power sets because self not powered
}
else
@@ -693,7 +655,7 @@ void cRedstoneSimulator::HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int
// Apparently, incrementing ticks only works reliably here, and not in SimChunk;
// With a world with lots of redstone, the repeaters simply do not delay
// I am confounded to say why. Perhaps optimisation failure.
- LOGD("Incremented a repeater @ %i %i %i | Elapsed ticks: %i | Target delay: %i", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks);
+ LOGD("Incremented a repeater @ {%i %i %i} | Elapsed ticks: %i | Target delay: %i", itr->a_BlockPos.x, itr->a_BlockPos.y, itr->a_BlockPos.z, itr->a_ElapsedTicks, itr->a_DelayTicks);
itr->a_ElapsedTicks++;
}
}
@@ -781,50 +743,45 @@ void cRedstoneSimulator::HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ)
void cRedstoneSimulator::HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ)
{
- if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x08) == 0x08)
+ if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ))
{
- // Block position is located at top half of door
- // Is Y - 1 both within world boundaries, a door block, and the bottom half of a door?
- // The bottom half stores the open/closed information
- if (
- (a_BlockY - 1 >= 0) &&
- ((m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_WOODEN_DOOR) || (m_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_IRON_DOOR)) &&
- (m_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ & 0x08) == 0)
- )
+ if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, true))
{
- if ((m_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ) & 0x04) == 0) // Closed door?
- {
- if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Powered? If so, toggle open
- {
- cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ);
- }
- }
- else // Opened door
- {
- if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Unpowered? Close if so
- {
- cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ);
- }
- }
+ cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ);
+ SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, true);
}
}
else
{
- if ((m_World.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ) & 0x04) == 0) // Closed door?
+ if (!AreCoordsSimulated(a_BlockX, a_BlockY, a_BlockZ, false))
{
- if (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Powered? If so, toggle open
- {
- cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ);
- }
+ cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ);
+ SetPlayerToggleableBlockAsSimulated(a_BlockX, a_BlockY, a_BlockZ, false);
}
- else // Opened door
+ }
+}
+
+
+
+
+
+void cRedstoneSimulator::HandleCommandBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ class cSetPowerToCommandBlock :
+ public cCommandBlockCallback
+ {
+ bool m_IsPowered;
+ public:
+ cSetPowerToCommandBlock(bool a_IsPowered) : m_IsPowered(a_IsPowered) {}
+
+ virtual bool Item(cCommandBlockEntity * a_CommandBlock) override
{
- if (!AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ)) // Unpowered? Close if so
- {
- cBlockDoorHandler::ChangeDoor(&m_World, a_BlockX, a_BlockY, a_BlockZ);
- }
+ a_CommandBlock->SetRedstonePower(m_IsPowered);
+ return false;
}
- }
+ } CmdBlockSP (AreCoordsPowered(a_BlockX, a_BlockY, a_BlockZ));
+
+ m_World.DoWithCommandBlockAt(a_BlockX, a_BlockY, a_BlockZ, CmdBlockSP);
}
@@ -970,6 +927,7 @@ void cRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_BlockY, int a_B
else
{
m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x0);
+ m_World.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ);
}
break;
}
@@ -1032,6 +990,7 @@ void cRedstoneSimulator::HandlePressurePlate(int a_BlockX, int a_BlockY, int a_B
else
{
m_World.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0x0);
+ m_World.WakeUpSimulators(a_BlockX, a_BlockY, a_BlockZ);
}
break;
}
@@ -1188,6 +1147,33 @@ bool cRedstoneSimulator::IsPistonPowered(int a_BlockX, int a_BlockY, int a_Block
+bool cRedstoneSimulator::IsWirePowered(int a_BlockX, int a_BlockY, int a_BlockZ)
+{
+ for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr)
+ {
+ if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; }
+
+ if (m_World.GetBlock(itr->a_SourcePos) != E_BLOCK_REDSTONE_WIRE)
+ {
+ return true;
+ }
+ }
+
+ for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr)
+ {
+ if (!itr->a_BlockPos.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ))) { continue; }
+
+ if (m_World.GetBlock(itr->a_SourcePos) != E_BLOCK_REDSTONE_WIRE)
+ {
+ return true;
+ }
+ }
+ return false; // Source was in front of the piston's front face
+}
+
+
+
+
bool cRedstoneSimulator::AreCoordsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool IsCurrentStatePowered)
{
@@ -1333,11 +1319,6 @@ void cRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, int a_Block
// Don't set air, fixes some bugs (wires powering themselves)
return;
}
- if ((Block == E_BLOCK_REDSTONE_WIRE) && (a_SourceBlock == E_BLOCK_REDSTONE_WIRE))
- {
- // Wires cannot power themselves normally, instead, the wire handler will manually set meta
- return;
- }
for (PoweredBlocksList::const_iterator itr = m_PoweredBlocks.begin(); itr != m_PoweredBlocks.end(); ++itr) // Check powered list
{
@@ -1354,7 +1335,6 @@ void cRedstoneSimulator::SetBlockPowered(int a_BlockX, int a_BlockY, int a_Block
sPoweredBlocks RC;
RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ);
- RC.a_SourceBlock = a_SourceBlock;
m_PoweredBlocks.push_back(RC);
}
@@ -1379,11 +1359,6 @@ void cRedstoneSimulator::SetBlockLinkedPowered(
{
return;
}
- if ((a_SourceBlock == E_BLOCK_REDSTONE_WIRE) && (DestBlock == E_BLOCK_REDSTONE_WIRE))
- {
- // Wires cannot power another wire through a block
- return;
- }
for (LinkedBlocksList::const_iterator itr = m_LinkedPoweredBlocks.begin(); itr != m_LinkedPoweredBlocks.end(); ++itr) // Check linked powered list
{
@@ -1402,8 +1377,6 @@ void cRedstoneSimulator::SetBlockLinkedPowered(
RC.a_BlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
RC.a_MiddlePos = Vector3i(a_MiddleX, a_MiddleY, a_MiddleZ);
RC.a_SourcePos = Vector3i(a_SourceX, a_SourceY, a_SourceZ);
- RC.a_SourceBlock = a_SourceBlock;
- RC.a_MiddleBlock = a_MiddleBlock;
m_LinkedPoweredBlocks.push_back(RC);
}
diff --git a/src/Simulator/RedstoneSimulator.h b/src/Simulator/RedstoneSimulator.h
index 60c86a3c5..bb2efeb8a 100644
--- a/src/Simulator/RedstoneSimulator.h
+++ b/src/Simulator/RedstoneSimulator.h
@@ -4,7 +4,7 @@
#include "Simulator.h"
/// Per-chunk data for the simulator, specified individual chunks to simulate; 'Data' is not used
-typedef cCoordWithIntList cRedstoneSimulatorChunkData;
+typedef cCoordWithBlockVector cRedstoneSimulatorChunkData;
@@ -19,7 +19,7 @@ public:
cRedstoneSimulator(cWorld & a_World);
~cRedstoneSimulator();
- virtual void Simulate(float a_Dt) override {}; // Not used in this simulator
+ virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used
virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
virtual bool IsAllowedBlock( BLOCKTYPE a_BlockType ) override { return IsRedstone(a_BlockType); }
@@ -39,7 +39,6 @@ private:
{
Vector3i a_BlockPos; // Position of powered block
Vector3i a_SourcePos; // Position of source powering the block at a_BlockPos
- BLOCKTYPE a_SourceBlock; // The source block type (for pistons pushing away sources and replacing with non source etc.)
};
struct sLinkedPoweredBlocks // Define structure of the indirectly powered blocks list (i.e. repeaters powering through a block to the block at the other side)
@@ -47,8 +46,6 @@ private:
Vector3i a_BlockPos;
Vector3i a_MiddlePos;
Vector3i a_SourcePos;
- BLOCKTYPE a_SourceBlock;
- BLOCKTYPE a_MiddleBlock;
};
struct sSimulatedPlayerToggleableList
@@ -81,107 +78,97 @@ private:
// In addition to being non-performant, it would stop the player from actually breaking said device
/* ====== SOURCES ====== */
- /// <summary>Handles the redstone torch</summary>
+ /** Handles the redstone torch */
void HandleRedstoneTorch(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState);
- /// <summary>Handles the redstone block</summary>
+ /** Handles the redstone block */
void HandleRedstoneBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Handles levers</summary>
+ /** Handles levers */
void HandleRedstoneLever(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Handles buttons</summary>
+ /** Handles buttons */
void HandleRedstoneButton(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
- /// <summary>Handles daylight sensors</summary>
+ /** Handles daylight sensors */
void HandleDaylightSensor(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Handles pressure plates</summary>
+ /** Handles pressure plates */
void HandlePressurePlate(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType);
/* ==================== */
/* ====== CARRIERS ====== */
- /// <summary>Handles redstone wire</summary>
+ /** Handles redstone wire */
void HandleRedstoneWire(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Handles repeaters</summary>
+ /** Handles repeaters */
void HandleRedstoneRepeater(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState);
/* ====================== */
/* ====== DEVICES ====== */
- /// <summary>Handles pistons</summary>
+ /** Handles pistons */
void HandlePiston(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Handles dispensers and droppers</summary>
+ /** Handles dispensers and droppers */
void HandleDropSpenser(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Handles TNT (exploding)</summary>
+ /** Handles TNT (exploding) */
void HandleTNT(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Handles redstone lamps</summary>
+ /** Handles redstone lamps */
void HandleRedstoneLamp(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyState);
- /// <summary>Handles doords</summary>
+ /** Handles doords */
void HandleDoor(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Handles activator, detector, and powered rails</summary>
+ /** Handles command blocks */
+ void HandleCommandBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
+ /** Handles activator, detector, and powered rails */
void HandleRail(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_MyType);
- /// <summary>Handles trapdoors</summary>
+ /** Handles trapdoors */
void HandleTrapdoor(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Handles noteblocks</summary>
+ /** Handles noteblocks */
void HandleNoteBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
/* ===================== */
/* ====== Helper functions ====== */
- /// <summary>Marks a block as powered</summary>
+ /** Marks a block as powered */
void SetBlockPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock);
- /// <summary>Marks a block as being powered through another block</summary>
+ /** Marks a block as being powered through another block */
void SetBlockLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, int a_MiddleX, int a_MiddleY, int a_MiddleZ, int a_SourceX, int a_SourceY, int a_SourceZ, BLOCKTYPE a_SourceBlock, BLOCKTYPE a_MiddeBlock);
- /// <summary>Marks a block as simulated, who should not be simulated further unless their power state changes, to accomodate a player manually toggling the block without triggering the simulator toggling it back</summary>
+ /** Marks a block as simulated, who should not be simulated further unless their power state changes, to accomodate a player manually toggling the block without triggering the simulator toggling it back */
void SetPlayerToggleableBlockAsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool WasLastStatePowered);
- /// <summary>Marks the second block in a direction as linked powered</summary>
+ /** Marks the second block in a direction as linked powered */
void SetDirectionLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Direction, BLOCKTYPE a_SourceBlock);
- /// <summary>Marks all blocks immediately surrounding a coordinate as powered</summary>
+ /** Marks all blocks immediately surrounding a coordinate as powered */
void SetAllDirsAsPowered(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_SourceBlock);
- /// <summary>Queues a repeater to be powered or unpowered</summary>
+ /** Queues a repeater to be powered or unpowered */
void QueueRepeaterPowerChange(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta, short a_ElapsedTicks, bool ShouldPowerOn);
- /// <summary>Returns if a coordinate is powered or linked powered</summary>
+ /** Returns if a coordinate is powered or linked powered */
bool AreCoordsPowered(int a_BlockX, int a_BlockY, int a_BlockZ) { return AreCoordsDirectlyPowered(a_BlockX, a_BlockY, a_BlockZ) || AreCoordsLinkedPowered(a_BlockX, a_BlockY, a_BlockZ); }
- /// <summary>Returns if a coordinate is in the directly powered blocks list</summary>
+ /** Returns if a coordinate is in the directly powered blocks list */
bool AreCoordsDirectlyPowered(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Returns if a coordinate is in the indirectly powered blocks list</summary>
+ /** Returns if a coordinate is in the indirectly powered blocks list */
bool AreCoordsLinkedPowered(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Returns if a coordinate was marked as simulated (for blocks toggleable by players)</summary>
+ /** Returns if a coordinate was marked as simulated (for blocks toggleable by players) */
bool AreCoordsSimulated(int a_BlockX, int a_BlockY, int a_BlockZ, bool IsCurrentStatePowered);
- /// <summary>Returns if a repeater is powered</summary>
+ /** Returns if a repeater is powered */
bool IsRepeaterPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta);
- /// <summary>Returns if a piston is powered</summary>
+ /** Returns if a piston is powered */
bool IsPistonPowered(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_Meta);
+ /** Returns if a wire is powered
+ The only diffence between this and a normal AreCoordsPowered is that this function checks for a wire powering another wire
+ */
+ bool IsWirePowered(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// <summary>Returns if lever metadata marks it as emitting power</summary>
+
+ /** Returns if lever metadata marks it as emitting power */
bool IsLeverOn(NIBBLETYPE a_BlockMeta);
- /// <summary>Returns if button metadata marks it as emitting power</summary>
+ /** Returns if button metadata marks it as emitting power */
bool IsButtonOn(NIBBLETYPE a_BlockMeta);
/* ============================== */
/* ====== Misc Functions ====== */
- /// <summary>Returns if a block is viable to be the MiddleBlock of a SetLinkedPowered operation</summary>
- inline static bool IsViableMiddleBlock(BLOCKTYPE Block)
- {
- if (!g_BlockIsSolid[Block]) { return false; }
+ /** Returns if a block is viable to be the MiddleBlock of a SetLinkedPowered operation */
+ inline static bool IsViableMiddleBlock(BLOCKTYPE Block) { return g_BlockFullyOccupiesVoxel[Block]; }
- switch (Block)
- {
- // Add SOLID but not viable middle blocks here
- case E_BLOCK_PISTON:
- case E_BLOCK_PISTON_EXTENSION:
- case E_BLOCK_STICKY_PISTON:
- case E_BLOCK_REDSTONE_REPEATER_ON:
- case E_BLOCK_REDSTONE_REPEATER_OFF:
- case E_BLOCK_DAYLIGHT_SENSOR:
- {
- return false;
- }
- default: return true;
- }
- }
-
- /// <summary>Returns if a block is a mechanism (something that accepts power and does something)</summary>
+ /** Returns if a block is a mechanism (something that accepts power and does something) */
inline static bool IsMechanism(BLOCKTYPE Block)
{
switch (Block)
{
case E_BLOCK_ACTIVATOR_RAIL:
+ case E_BLOCK_COMMAND_BLOCK:
case E_BLOCK_PISTON:
case E_BLOCK_STICKY_PISTON:
case E_BLOCK_DISPENSER:
@@ -205,16 +192,16 @@ private:
}
}
- /// <summary>Returns if a block has the potential to output power</summary>
+ /** Returns if a block has the potential to output power */
inline static bool IsPotentialSource(BLOCKTYPE Block)
{
switch (Block)
{
+ case E_BLOCK_DETECTOR_RAIL:
case E_BLOCK_DAYLIGHT_SENSOR:
case E_BLOCK_WOODEN_BUTTON:
case E_BLOCK_STONE_BUTTON:
case E_BLOCK_REDSTONE_WIRE:
- case E_BLOCK_REDSTONE_TORCH_OFF:
case E_BLOCK_REDSTONE_TORCH_ON:
case E_BLOCK_LEVER:
case E_BLOCK_REDSTONE_REPEATER_ON:
@@ -227,7 +214,7 @@ private:
}
}
- /// <summary>Returns if a block is any sort of redstone device</summary>
+ /** Returns if a block is any sort of redstone device */
inline static bool IsRedstone(BLOCKTYPE Block)
{
switch (Block)
@@ -236,6 +223,7 @@ private:
case E_BLOCK_ACTIVATOR_RAIL:
case E_BLOCK_ACTIVE_COMPARATOR:
case E_BLOCK_BLOCK_OF_REDSTONE:
+ case E_BLOCK_COMMAND_BLOCK:
case E_BLOCK_DETECTOR_RAIL:
case E_BLOCK_DISPENSER:
case E_BLOCK_DAYLIGHT_SENSOR:
@@ -248,6 +236,7 @@ private:
case E_BLOCK_LEVER:
case E_BLOCK_LIGHT_WEIGHTED_PRESSURE_PLATE:
case E_BLOCK_NOTE_BLOCK:
+ case E_BLOCK_POWERED_RAIL:
case E_BLOCK_REDSTONE_LAMP_OFF:
case E_BLOCK_REDSTONE_LAMP_ON:
case E_BLOCK_REDSTONE_REPEATER_OFF:
@@ -271,4 +260,4 @@ private:
default: return false;
}
}
-}; \ No newline at end of file
+};
diff --git a/src/Simulator/SandSimulator.h b/src/Simulator/SandSimulator.h
index 6e9ea15ac..6e64aa425 100644
--- a/src/Simulator/SandSimulator.h
+++ b/src/Simulator/SandSimulator.h
@@ -15,7 +15,7 @@ public:
cSandSimulator(cWorld & a_World, cIniFile & a_IniFile);
// cSimulator overrides:
- virtual void Simulate(float a_Dt) override {} // Unused in this simulator
+ virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used
virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
virtual bool IsAllowedBlock(BLOCKTYPE a_BlockType) override;
diff --git a/src/Simulator/Simulator.h b/src/Simulator/Simulator.h
index 5cd0e8657..a25b7f1b6 100644
--- a/src/Simulator/Simulator.h
+++ b/src/Simulator/Simulator.h
@@ -25,7 +25,14 @@ public:
virtual void Simulate(float a_Dt) = 0;
/// Called in each tick for each chunk, a_Dt is the time passed since the last tick, in msec; direct access to chunk data available
- virtual void SimulateChunk(float a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) {};
+ virtual void SimulateChunk(float a_Dt, int a_ChunkX,
+ int a_ChunkZ, cChunk * a_Chunk)
+ {
+ UNUSED(a_Dt);
+ UNUSED(a_ChunkX);
+ UNUSED(a_ChunkZ);
+ UNUSED(a_Chunk);
+ };
/// Called when a block changes
virtual void WakeUp(int a_BlockX, int a_BlockY, int a_BlockZ, cChunk * a_Chunk);
diff --git a/src/StackWalker.cpp b/src/StackWalker.cpp
index bf18b9fc9..b4f8ed8c7 100644
--- a/src/StackWalker.cpp
+++ b/src/StackWalker.cpp
@@ -73,10 +73,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
**********************************************************************/
-#include <windows.h>
+
+#include "Globals.h"
+
#include <tchar.h>
-#include <stdio.h>
-#include <stdlib.h>
#pragma comment(lib, "version.lib") // for "VerQueryValue"
#pragma warning(disable:4826)
diff --git a/src/StringCompression.cpp b/src/StringCompression.cpp
index 36946b282..5b9a3bb0a 100644
--- a/src/StringCompression.cpp
+++ b/src/StringCompression.cpp
@@ -11,7 +11,7 @@
/// Compresses a_Data into a_Compressed; returns Z_XXX error constants same as zlib's compress2()
-int CompressString(const char * a_Data, int a_Length, AString & a_Compressed)
+int CompressString(const char * a_Data, int a_Length, AString & a_Compressed, int a_Factor)
{
uLongf CompressedSize = compressBound(a_Length);
@@ -19,7 +19,7 @@ int CompressString(const char * a_Data, int a_Length, AString & a_Compressed)
// It saves us one allocation and one memcpy of the entire compressed data
// It may not work on some STL implementations! (Confirmed working on MSVC 2008 & 2010)
a_Compressed.resize(CompressedSize);
- int errorcode = compress2( (Bytef*)a_Compressed.data(), &CompressedSize, (const Bytef*)a_Data, a_Length, Z_DEFAULT_COMPRESSION);
+ int errorcode = compress2( (Bytef*)a_Compressed.data(), &CompressedSize, (const Bytef*)a_Data, a_Length, a_Factor);
if (errorcode != Z_OK)
{
return errorcode;
@@ -74,7 +74,7 @@ int CompressStringGZIP(const char * a_Data, int a_Length, AString & a_Compressed
return res;
}
- while (true)
+ for (;;)
{
res = deflate(&strm, Z_FINISH);
switch (res)
@@ -137,7 +137,7 @@ extern int UncompressStringGZIP(const char * a_Data, int a_Length, AString & a_U
return res;
}
- while (true)
+ for (;;)
{
res = inflate(&strm, Z_FINISH);
switch (res)
diff --git a/src/StringCompression.h b/src/StringCompression.h
index 459e8f568..3f4e12d2d 100644
--- a/src/StringCompression.h
+++ b/src/StringCompression.h
@@ -10,7 +10,7 @@
/// Compresses a_Data into a_Compressed using ZLIB; returns Z_XXX error constants same as zlib's compress2()
-extern int CompressString(const char * a_Data, int a_Length, AString & a_Compressed);
+extern int CompressString(const char * a_Data, int a_Length, AString & a_Compressed, int a_Factor);
/// Uncompresses a_Data into a_Uncompressed; returns Z_XXX error constants same as zlib's decompress()
extern int UncompressString(const char * a_Data, int a_Length, AString & a_Uncompressed, int a_UncompressedSize);
diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp
index f7aeeed26..3fe75d611 100644
--- a/src/StringUtils.cpp
+++ b/src/StringUtils.cpp
@@ -18,27 +18,40 @@
-AString & AppendVPrintf(AString & str, const char *format, va_list args)
+AString & AppendVPrintf(AString & str, const char * format, va_list args)
{
ASSERT(format != NULL);
char buffer[2048];
size_t len;
+ #ifdef va_copy
+ va_list argsCopy;
+ va_copy(argsCopy, args);
+ #else
+ #define argsCopy args
+ #endif
#ifdef _MSC_VER
// MS CRT provides secure printf that doesn't behave like in the C99 standard
- if ((len = _vsnprintf_s(buffer, ARRAYCOUNT(buffer), _TRUNCATE, format, args)) != -1)
+ if ((len = _vsnprintf_s(buffer, ARRAYCOUNT(buffer), _TRUNCATE, format, argsCopy)) != -1)
#else // _MSC_VER
- if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, args)) < ARRAYCOUNT(buffer))
+ if ((len = vsnprintf(buffer, ARRAYCOUNT(buffer), format, argsCopy)) < ARRAYCOUNT(buffer))
#endif // else _MSC_VER
{
// The result did fit into the static buffer
+ #ifdef va_copy
+ va_end(argsCopy);
+ #endif
str.append(buffer, len);
return str;
}
+ #ifdef va_copy
+ va_end(argsCopy);
+ #endif
- // The result did not fit into the static buffer
+ // The result did not fit into the static buffer, use a dynamic buffer:
#ifdef _MSC_VER
// for MS CRT, we need to calculate the result length
+ // MS doesn't have va_copy() and does nod need it at all
len = _vscprintf(format, args);
if (len == -1)
{
@@ -47,15 +60,19 @@ AString & AppendVPrintf(AString & str, const char *format, va_list args)
#endif // _MSC_VER
// Allocate a buffer and printf into it:
- str.resize(len + 1);
- // HACK: we're accessing AString's internal buffer in a way that is NOT guaranteed to always work. But it works on all STL implementations tested.
- // I can't think of any other way that is safe, doesn't allocate twice as much space as needed and doesn't use C++11 features like the move constructor
+ #ifdef va_copy
+ va_copy(argsCopy, args);
+ #endif
+ std::vector<char> Buffer(len + 1);
#ifdef _MSC_VER
- vsprintf_s((char *)str.data(), len + 1, format, args);
+ vsprintf_s((char *)&(Buffer.front()), Buffer.size(), format, argsCopy);
#else // _MSC_VER
- vsnprintf((char *)str.data(), len + 1, format, args);
+ vsnprintf((char *)&(Buffer.front()), Buffer.size(), format, argsCopy);
#endif // else _MSC_VER
- str.resize(len);
+ str.append(&(Buffer.front()), Buffer.size() - 1);
+ #ifdef va_copy
+ va_end(argsCopy);
+ #endif
return str;
}
@@ -91,7 +108,7 @@ AString Printf(const char * format, ...)
-AString & AppendPrintf(AString &str, const char *format, ...)
+AString & AppendPrintf(AString &str, const char * format, ...)
{
va_list args;
va_start(args, format);
@@ -612,7 +629,7 @@ AString StripColorCodes(const AString & a_Message)
{
AString res(a_Message);
size_t idx = 0;
- while (true)
+ for (;;)
{
idx = res.find("\xc2\xa7", idx);
if (idx == AString::npos)
@@ -759,7 +776,88 @@ AString Base64Decode(const AString & a_Base64String)
}
}
res.resize(o >> 3);
- return res;}
+ return res;
+}
+
+
+
+
+
+AString Base64Encode(const AString & a_Input)
+{
+ static const char BASE64[64] = {
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
+ };
+
+ AString output;
+ output.resize(((a_Input.size() + 2) / 3) * 4);
+
+ size_t output_index = 0;
+ size_t size_full24 = (a_Input.size() / 3) * 3;
+
+ for (size_t i = 0; i < size_full24; i += 3)
+ {
+ output[output_index++] = BASE64[(unsigned char)a_Input[i] >> 2];
+ output[output_index++] = BASE64[((unsigned char)a_Input[i] << 4 | (unsigned char)a_Input[i + 1] >> 4) & 63];
+ output[output_index++] = BASE64[((unsigned char)a_Input[i + 1] << 2 | (unsigned char)a_Input[i + 2] >> 6) & 63];
+ output[output_index++] = BASE64[(unsigned char)a_Input[i + 2] & 63];
+ }
+
+ if (size_full24 < a_Input.size())
+ {
+ output[output_index++] = BASE64[(unsigned char)a_Input[size_full24] >> 2];
+ if (size_full24 + 1 == a_Input.size())
+ {
+ output[output_index++] = BASE64[((unsigned char)a_Input[size_full24] << 4) & 63];
+ output[output_index++] = '=';
+ }
+ else
+ {
+ output[output_index++] = BASE64[((unsigned char)a_Input[size_full24] << 4 | (unsigned char)a_Input[size_full24 + 1] >> 4) & 63];
+ output[output_index++] = BASE64[((unsigned char)a_Input[size_full24 + 1] << 2) & 63];
+ }
+
+ output[output_index++] = '=';
+ }
+ ASSERT(output_index == output.size());
+
+ return output;
+}
+
+
+
+
+
+short GetBEShort(const char * a_Mem)
+{
+ const Byte * Bytes = (const Byte *)a_Mem;
+ return (Bytes[0] << 8) | Bytes[1];
+}
+
+
+
+
+
+int GetBEInt(const char * a_Mem)
+{
+ const Byte * Bytes = (const Byte *)a_Mem;
+ return (Bytes[0] << 24) | (Bytes[1] << 16) | (Bytes[2] << 8) | Bytes[3];
+}
+
+
+
+
+
+void SetBEInt(char * a_Mem, Int32 a_Value)
+{
+ a_Mem[0] = a_Value >> 24;
+ a_Mem[1] = (a_Value >> 16) & 0xff;
+ a_Mem[2] = (a_Value >> 8) & 0xff;
+ a_Mem[3] = a_Value & 0xff;
+}
diff --git a/src/StringUtils.h b/src/StringUtils.h
index 3917cc4ec..dfbfc2a75 100644
--- a/src/StringUtils.h
+++ b/src/StringUtils.h
@@ -21,7 +21,7 @@ typedef std::list<AString> AStringList;
-/// Add the formated string to the existing data in the string
+/** Add the formated string to the existing data in the string */
extern AString & AppendVPrintf(AString & str, const char * format, va_list args);
/// Output the formatted text into the string
@@ -81,6 +81,18 @@ extern AString ReplaceAllCharOccurrences(const AString & a_String, char a_From,
/// Decodes a Base64-encoded string into the raw data
extern AString Base64Decode(const AString & a_Base64String);
+/// Encodes a string into Base64
+extern AString Base64Encode(const AString & a_Input);
+
+/// Reads two bytes from the specified memory location and interprets them as BigEndian short
+extern short GetBEShort(const char * a_Mem);
+
+/// Reads four bytes from the specified memory location and interprets them as BigEndian int
+extern int GetBEInt(const char * a_Mem);
+
+/// Writes four bytes to the specified memory location so that they interpret as BigEndian int
+extern void SetBEInt(char * a_Mem, Int32 a_Value);
+
// If you have any other string helper functions, declare them here
diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp
index 61e432665..bfcad3d92 100644
--- a/src/UI/SlotArea.cpp
+++ b/src/UI/SlotArea.cpp
@@ -85,10 +85,10 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickA
{
if (DraggingItem.m_ItemType <= 0) // Empty-handed?
{
+ DraggingItem = Slot.CopyOne(); // Obtain copy of slot to preserve lore, enchantments, etc.
+
DraggingItem.m_ItemCount = (char)(((float)Slot.m_ItemCount) / 2.f + 0.5f);
Slot.m_ItemCount -= DraggingItem.m_ItemCount;
- DraggingItem.m_ItemType = Slot.m_ItemType;
- DraggingItem.m_ItemDamage = Slot.m_ItemDamage;
if (Slot.m_ItemCount <= 0)
{
@@ -101,9 +101,12 @@ void cSlotArea::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickA
cItemHandler * Handler = ItemHandler(Slot.m_ItemType);
if ((DraggingItem.m_ItemCount > 0) && (Slot.m_ItemCount < Handler->GetMaxStackSize()))
{
- Slot.m_ItemType = DraggingItem.m_ItemType;
- Slot.m_ItemCount++;
- Slot.m_ItemDamage = DraggingItem.m_ItemDamage;
+ char OldSlotCount = Slot.m_ItemCount;
+
+ Slot = DraggingItem.CopyOne(); // See above
+ OldSlotCount++;
+ Slot.m_ItemCount = OldSlotCount;
+
DraggingItem.m_ItemCount--;
}
if (DraggingItem.m_ItemCount <= 0)
@@ -226,7 +229,7 @@ void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_
for (int i = 0; i < m_NumSlots; i++)
{
const cItem * Slot = GetSlot(i, a_Player);
- if (!Slot->IsStackableWith(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
+ if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots))
{
// Different items
continue;
@@ -265,7 +268,7 @@ bool cSlotArea::CollectItemsToHand(cItem & a_Dragging, cPlayer & a_Player, bool
for (int i = 0; i < NumSlots; i++)
{
const cItem & SlotItem = *GetSlot(i, a_Player);
- if (!SlotItem.IsStackableWith(a_Dragging))
+ if (!SlotItem.IsEqual(a_Dragging))
{
continue;
}
@@ -491,7 +494,7 @@ void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player)
return;
}
cItem * PlayerSlots = GetPlayerSlots(a_Player) + 1;
- do
+ for (;;)
{
// Try distributing the result. If it fails, bail out:
cItem ResultCopy(Result);
@@ -517,7 +520,7 @@ void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player)
// The recipe has changed, bail out
return;
}
- } while (true);
+ }
}
@@ -908,7 +911,7 @@ void cSlotAreaTemporary::TossItems(cPlayer & a_Player, int a_Begin, int a_End)
} // for i - itr->second[]
double vX = 0, vY = 0, vZ = 0;
- EulerToVector(-a_Player.GetRotation(), a_Player.GetPitch(), vZ, vX, vY);
+ EulerToVector(-a_Player.GetYaw(), a_Player.GetPitch(), vZ, vX, vY);
vY = -vY * 2 + 1.f;
a_Player.GetWorld()->SpawnItemPickups(Drops, a_Player.GetPosX(), a_Player.GetPosY() + 1.6f, a_Player.GetPosZ(), vX * 3, vY * 3, vZ * 3, true); // 'true' because player created
}
diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp
index ee75921d1..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();
@@ -642,7 +665,7 @@ int cWindow::DistributeItemToSlots(cPlayer & a_Player, const cItem & a_Item, int
Area->SetSlot(LocalSlotNum, a_Player, ToStore);
NumDistributed += ToStore.m_ItemCount;
}
- else if (AtSlot.IsStackableWith(a_Item))
+ else if (AtSlot.IsEqual(a_Item))
{
// Occupied, add and cap at MaxStack:
int CanStore = std::min(a_NumToEachSlot, (int)MaxStack - AtSlot.m_ItemCount);
diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp
index a1f0842aa..e6a5a01b3 100644
--- a/src/WebAdmin.cpp
+++ b/src/WebAdmin.cpp
@@ -305,6 +305,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPConnection & a_Connection, cHTTPReque
void cWebAdmin::HandleRootRequest(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
{
+ UNUSED(a_Request);
static const char LoginForm[] = \
"<h1>MCServer WebAdmin</h1>" \
"<center>" \
@@ -450,6 +451,7 @@ AString cWebAdmin::GetBaseURL(const AStringVector & a_URLSplit)
void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_Request)
{
+ UNUSED(a_Connection);
const AString & URL = a_Request.GetURL();
if (
(strncmp(URL.c_str(), "/webadmin", 9) == 0) ||
@@ -473,6 +475,7 @@ void cWebAdmin::OnRequestBegun(cHTTPConnection & a_Connection, cHTTPRequest & a_
void cWebAdmin::OnRequestBody(cHTTPConnection & a_Connection, cHTTPRequest & a_Request, const char * a_Data, int a_Size)
{
+ UNUSED(a_Connection);
cRequestData * Data = (cRequestData *)(a_Request.GetUserData());
if (Data == NULL)
{
diff --git a/src/WebAdmin.h b/src/WebAdmin.h
index 0907e7bc3..3eb807640 100644
--- a/src/WebAdmin.h
+++ b/src/WebAdmin.h
@@ -105,7 +105,7 @@ public:
cWebAdmin(void);
- ~cWebAdmin();
+ virtual ~cWebAdmin();
/// Initializes the object. Returns true if successfully initialized and ready to start
bool Init(void);
@@ -169,9 +169,16 @@ protected:
virtual void OnBody(const char * a_Data, int a_Size) override;
// cHTTPFormParser::cCallbacks overrides. Files are ignored:
- virtual void OnFileStart(cHTTPFormParser & a_Parser, const AString & a_FileName) override {}
- virtual void OnFileData(cHTTPFormParser & a_Parser, const char * a_Data, int a_Size) override {}
- virtual void OnFileEnd(cHTTPFormParser & a_Parser) override {}
+ virtual void OnFileStart(cHTTPFormParser &, const AString & a_FileName) override
+ {
+ UNUSED(a_FileName);
+ }
+ virtual void OnFileData(cHTTPFormParser &, const char * a_Data, int a_Size) override
+ {
+ UNUSED(a_Data);
+ UNUSED(a_Size);
+ }
+ virtual void OnFileEnd(cHTTPFormParser &) override {}
} ;
diff --git a/src/World.cpp b/src/World.cpp
index 28c46c27e..88bbf5f8a 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -10,15 +10,20 @@
#include "Root.h"
#include "inifile/iniFile.h"
#include "ChunkMap.h"
+#include "Generating/ChunkDesc.h"
#include "OSSupport/Timer.h"
+#include "WorldStorage/ScoreboardSerializer.h"
// Entities (except mobs):
#include "Entities/ExpOrb.h"
#include "Entities/FallingBlock.h"
+#include "Entities/Minecart.h"
#include "Entities/Pickup.h"
#include "Entities/Player.h"
#include "Entities/TNTEntity.h"
+#include "BlockEntities/CommandBlockEntity.h"
+
// Simulators:
#include "Simulator/SimulatorManager.h"
#include "Simulator/FloodyFluidSimulator.h"
@@ -229,6 +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),
@@ -238,11 +248,17 @@ cWorld::cWorld(const AString & a_WorldName) :
m_SkyDarkness(0),
m_Weather(eWeather_Sunny),
m_WeatherInterval(24000), // Guaranteed 1 day of sunshine at server start :)
- m_TickThread(*this)
+ m_GeneratorCallbacks(*this),
+ m_TickThread(*this),
+ m_Scoreboard(this)
{
LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str());
cFile::CreateFolder(FILE_IO_PREFIX + m_WorldName);
+
+ // Load the scoreboard
+ cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard);
+ Serializer.Load();
}
@@ -262,6 +278,10 @@ cWorld::~cWorld()
m_Storage.WaitForFinish();
+ // Unload the scoreboard
+ cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard);
+ Serializer.Save();
+
delete m_ChunkMap;
}
@@ -367,10 +387,13 @@ void cWorld::InitializeSpawn(void)
cWorldLoadProgress Progress(this);
// Wait for the loader to finish loading
- m_Storage.WaitForQueuesEmpty();
+ m_Storage.WaitForLoadQueueEmpty();
// Wait for the generator to finish generating
m_Generator.WaitForQueueEmpty();
+
+ // Wait for the loader to finish saving
+ m_Storage.WaitForSaveQueueEmpty();
Progress.Stop();
}
@@ -506,6 +529,7 @@ void cWorld::Start(void)
}
m_StorageSchema = IniFile.GetValueSet ("Storage", "Schema", m_StorageSchema);
+ 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);
@@ -522,6 +546,8 @@ 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_VillagersShouldHarvestCrops = IniFile.GetValueSetB("Monsters", "VillagersShouldHarvestCrops", true);
m_GameMode = (eGameMode)IniFile.GetValueSetI("GameMode", "GameMode", m_GameMode);
@@ -579,8 +605,8 @@ void cWorld::Start(void)
m_SimulatorManager->RegisterSimulator(m_RedstoneSimulator, 1);
m_Lighting.Start(this);
- m_Storage.Start(this, m_StorageSchema);
- m_Generator.Start(this, IniFile);
+ m_Storage.Start(this, m_StorageSchema, m_StorageCompressionFactor );
+ m_Generator.Start(m_GeneratorCallbacks, m_GeneratorCallbacks, IniFile);
m_ChunkSender.Start(this);
m_TickThread.Start();
@@ -685,6 +711,7 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec)
TickClients(a_Dt);
TickQueuedBlocks();
TickQueuedTasks();
+ TickScheduledTasks();
GetSimulatorManager()->Simulate(a_Dt);
@@ -723,6 +750,7 @@ void cWorld::Tick(float a_Dt, int a_LastTickDurationMSec)
void cWorld::TickWeather(float a_Dt)
{
+ UNUSED(a_Dt);
// There are no weather changes anywhere but in the Overworld:
if (GetDimension() != dimOverworld)
{
@@ -794,7 +822,7 @@ void cWorld::TickMobs(float a_Dt)
cMonster::mfAmbient,
cMonster::mfWater,
} ;
- for (int i = 0; i < ARRAYCOUNT(AllFamilies); i++)
+ for (size_t i = 0; i < ARRAYCOUNT(AllFamilies); i++)
{
cMonster::eFamily Family = AllFamilies[i];
int SpawnDelay = cMonster::GetSpawnDelay(Family);
@@ -859,6 +887,30 @@ void cWorld::TickQueuedTasks(void)
+void cWorld::TickScheduledTasks(void)
+{
+ // Make a copy of the tasks to avoid deadlocks on accessing m_Tasks
+ cScheduledTasks Tasks;
+ {
+ cCSLock Lock(m_CSScheduledTasks);
+ while (!m_ScheduledTasks.empty() && (m_ScheduledTasks.front()->m_TargetTick < m_WorldAge))
+ {
+ Tasks.push_back(m_ScheduledTasks.front());
+ m_ScheduledTasks.pop_front();
+ }
+ }
+
+ // Execute and delete each task:
+ for (cScheduledTasks::iterator itr = Tasks.begin(), end = Tasks.end(); itr != end; ++itr)
+ {
+ (*itr)->m_Task->Run(*this);
+ delete *itr;
+ } // for itr - m_Tasks[]
+}
+
+
+
+
void cWorld::TickClients(float a_Dt)
{
cClientHandleList RemoveClients;
@@ -1114,6 +1166,15 @@ bool cWorld::DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBl
+bool cWorld::DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback)
+{
+ return m_ChunkMap->DoWithCommandBlockAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback);
+}
+
+
+
+
+
bool cWorld::GetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4)
{
return m_ChunkMap->GetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4);
@@ -1641,8 +1702,32 @@ int cWorld::SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward)
+int cWorld::SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType, const cItem & a_Content, int a_BlockHeight)
+{
+ cMinecart * Minecart;
+ switch (a_MinecartType)
+ {
+ case E_ITEM_MINECART: Minecart = new cRideableMinecart (a_X, a_Y, a_Z, a_Content, a_BlockHeight); break;
+ case E_ITEM_CHEST_MINECART: Minecart = new cMinecartWithChest (a_X, a_Y, a_Z); break;
+ case E_ITEM_FURNACE_MINECART: Minecart = new cMinecartWithFurnace (a_X, a_Y, a_Z); break;
+ case E_ITEM_MINECART_WITH_TNT: Minecart = new cMinecartWithTNT (a_X, a_Y, a_Z); break;
+ case E_ITEM_MINECART_WITH_HOPPER: Minecart = new cMinecartWithHopper (a_X, a_Y, a_Z); break;
+ default:
+ {
+ return -1;
+ }
+ } // switch (a_MinecartType)
+ Minecart->Initialize(this);
+ return Minecart->GetUniqueID();
+}
+
+
+
+
+
void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff)
{
+ UNUSED(a_InitialVelocityCoeff);
cTNTEntity * TNT = new cTNTEntity(a_X, a_Y, a_Z, a_FuseTimeInSec);
TNT->Initialize(this);
// TODO: Add a bit of speed in horiz and vert axes, based on the a_InitialVelocityCoeff
@@ -1914,6 +1999,60 @@ void cWorld::BroadcastRemoveEntityEffect(const cEntity & a_Entity, int a_EffectI
+void cWorld::BroadcastScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ cClientHandle * ch = (*itr)->GetClientHandle();
+ if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ {
+ continue;
+ }
+ ch->SendScoreboardObjective(a_Name, a_DisplayName, a_Mode);
+ }
+}
+
+
+
+
+
+void cWorld::BroadcastScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ cClientHandle * ch = (*itr)->GetClientHandle();
+ if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ {
+ continue;
+ }
+ ch->SendScoreUpdate(a_Objective, a_Player, a_Score, a_Mode);
+ }
+}
+
+
+
+
+
+void cWorld::BroadcastDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display)
+{
+ cCSLock Lock(m_CSPlayers);
+ for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
+ {
+ cClientHandle * ch = (*itr)->GetClientHandle();
+ if ((ch == NULL) || !ch->IsLoggedIn() || ch->IsDestroyed())
+ {
+ continue;
+ }
+ ch->SendDisplayObjective(a_Objective, a_Display);
+ }
+}
+
+
+
+
+
void cWorld::BroadcastSoundEffect(const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude)
{
m_ChunkMap->BroadcastSoundEffect(a_SoundName, a_SrcX, a_SrcY, a_SrcZ, a_Volume, a_Pitch, a_Exclude);
@@ -2285,7 +2424,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);
@@ -2300,7 +2439,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;
@@ -2480,6 +2624,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);
@@ -2568,6 +2734,26 @@ void cWorld::QueueTask(cTask * a_Task)
+void cWorld::ScheduleTask(int a_DelayTicks, cTask * a_Task)
+{
+ Int64 TargetTick = a_DelayTicks + m_WorldAge;
+
+ // Insert the task into the list of scheduled tasks, ordered by its target tick
+ cCSLock Lock(m_CSScheduledTasks);
+ for (cScheduledTasks::iterator itr = m_ScheduledTasks.begin(), end = m_ScheduledTasks.end(); itr != end; ++itr)
+ {
+ if ((*itr)->m_TargetTick >= TargetTick)
+ {
+ m_ScheduledTasks.insert(itr, new cScheduledTask(TargetTick, a_Task));
+ return;
+ }
+ }
+ m_ScheduledTasks.push_back(new cScheduledTask(TargetTick, a_Task));
+}
+
+
+
+
void cWorld::AddEntity(cEntity * a_Entity)
{
m_ChunkMap->AddEntity(a_Entity);
@@ -2694,9 +2880,6 @@ int cWorld::SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eTyp
{
Monster->SetPosition(a_PosX, a_PosY, a_PosZ);
}
-
- // Because it's logical that ALL mob spawns need spawn effects, not just spawners
- BroadcastSoundParticleEffect(2004, (int)a_PosX, (int)a_PosY, (int)a_PosZ, 0);
return SpawnMobFinalize(Monster);
}
@@ -2838,3 +3021,77 @@ void cWorld::cTaskSaveAllChunks::Run(cWorld & a_World)
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cWorld::cChunkGeneratorCallbacks:
+
+cWorld::cChunkGeneratorCallbacks::cChunkGeneratorCallbacks(cWorld & a_World) :
+ m_World(&a_World)
+{
+}
+
+
+
+
+
+void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc)
+{
+ cChunkDef::BlockNibbles BlockMetas;
+ a_ChunkDesc.CompressBlockMetas(BlockMetas);
+
+ m_World->SetChunkData(
+ a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(),
+ a_ChunkDesc.GetBlockTypes(), BlockMetas,
+ NULL, NULL, // We don't have lighting, chunk will be lighted when needed
+ &a_ChunkDesc.GetHeightMap(), &a_ChunkDesc.GetBiomeMap(),
+ a_ChunkDesc.GetEntities(), a_ChunkDesc.GetBlockEntities(),
+ true
+ );
+
+ // Save the chunk right after generating, so that we don't have to generate it again on next run
+ m_World->GetStorage().QueueSaveChunk(a_ChunkDesc.GetChunkX(), 0, a_ChunkDesc.GetChunkZ());
+}
+
+
+
+
+
+bool cWorld::cChunkGeneratorCallbacks::IsChunkValid(int a_ChunkX, int a_ChunkZ)
+{
+ return m_World->IsChunkValid(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+bool cWorld::cChunkGeneratorCallbacks::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ)
+{
+ return m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ);
+}
+
+
+
+
+
+void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerating(cChunkDesc & a_ChunkDesc)
+{
+ cPluginManager::Get()->CallHookChunkGenerating(
+ m_World, a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), &a_ChunkDesc
+ );
+}
+
+
+
+
+
+void cWorld::cChunkGeneratorCallbacks::CallHookChunkGenerated (cChunkDesc & a_ChunkDesc)
+{
+ cPluginManager::Get()->CallHookChunkGenerated(
+ m_World, a_ChunkDesc.GetChunkX(), a_ChunkDesc.GetChunkZ(), &a_ChunkDesc
+ );
+}
+
+
+
+
+
diff --git a/src/World.h b/src/World.h
index c067252d9..4d5659ee6 100644
--- a/src/World.h
+++ b/src/World.h
@@ -22,6 +22,8 @@
#include "Item.h"
#include "Mobs/Monster.h"
#include "Entities/ProjectileEntity.h"
+#include "ForEachChunkProvider.h"
+#include "Scoreboard.h"
@@ -46,12 +48,13 @@ class cMobCensus;
typedef std::list< cPlayer * > cPlayerList;
-typedef cItemCallback<cPlayer> cPlayerListCallback;
-typedef cItemCallback<cEntity> cEntityCallback;
-typedef cItemCallback<cChestEntity> cChestCallback;
-typedef cItemCallback<cDispenserEntity> cDispenserCallback;
-typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
-typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
+typedef cItemCallback<cPlayer> cPlayerListCallback;
+typedef cItemCallback<cEntity> cEntityCallback;
+typedef cItemCallback<cChestEntity> cChestCallback;
+typedef cItemCallback<cDispenserEntity> cDispenserCallback;
+typedef cItemCallback<cFurnaceEntity> cFurnaceCallback;
+typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
+typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
@@ -59,13 +62,13 @@ typedef cItemCallback<cNoteEntity> cNoteBlockCallback;
// tolua_begin
-class cWorld
+class cWorld : public cForEachChunkProvider
{
public:
// tolua_end
- /// A simple RAII locker for the chunkmap - locks the chunkmap in its constructor, unlocks it in the destructor
+ /** A simple RAII locker for the chunkmap - locks the chunkmap in its constructor, unlocks it in the destructor */
class cLock :
public cCSLock
{
@@ -73,15 +76,18 @@ public:
public:
cLock(cWorld & a_World);
} ;
+
- /// A common ancestor for all tasks queued onto the tick thread
+ /** A common ancestor for all tasks queued onto the tick thread */
class cTask
{
public:
+ virtual ~cTask(){};
virtual void Run(cWorld & a_World) = 0;
} ;
typedef std::vector<cTask *> cTasks;
+
class cTaskSaveAllChunks :
public cTask
@@ -115,16 +121,16 @@ public:
BroadcastTimeUpdate();
}
- /// Returns the current game mode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable
+ /** Returns the current game mode. Partly OBSOLETE, you should use IsGameModeXXX() functions wherever applicable */
eGameMode GetGameMode(void) const { return m_GameMode; }
- /// Returns true if the world is in Creative mode
+ /** Returns true if the world is in Creative mode */
bool IsGameModeCreative(void) const { return (m_GameMode == gmCreative); }
- /// Returns true if the world is in Survival mode
+ /** Returns true if the world is in Survival mode */
bool IsGameModeSurvival(void) const { return (m_GameMode == gmSurvival); }
- /// Returns true if the world is in Adventure mode
+ /** Returns true if the world is in Adventure mode */
bool IsGameModeAdventure(void) const { return (m_GameMode == gmAdventure); }
bool IsPVPEnabled(void) const { return m_bEnabledPVP; }
@@ -132,20 +138,22 @@ public:
bool ShouldLavaSpawnFire(void) const { return m_ShouldLavaSpawnFire; }
+ bool VillagersShouldHarvestCrops(void) const { return m_VillagersShouldHarvestCrops; }
+
eDimension GetDimension(void) const { return m_Dimension; }
- /// Returns the world height at the specified coords; waits for the chunk to get loaded / generated
+ /** Returns the world height at the specified coords; waits for the chunk to get loaded / generated */
int GetHeight(int a_BlockX, int a_BlockZ);
// tolua_end
- /// Retrieves the world height at the specified coords; returns false if chunk not loaded / generated
+ /** Retrieves the world height at the specified coords; returns false if chunk not loaded / generated */
bool TryGetHeight(int a_BlockX, int a_BlockZ, int & a_Height); // Exported in ManualBindings.cpp
// Broadcast respective packets to all clients of the chunk where the event is taking place
// (Please keep these alpha-sorted)
void BroadcastAttachEntity (const cEntity & a_Entity, const cEntity * a_Vehicle);
- void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL);
+ void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = NULL); // tolua_export
void BroadcastBlockBreakAnimation(int a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = NULL);
void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); ///< If there is a block entity at the specified coods, sends it to all clients except a_Exclude
void BroadcastChat (const AString & a_Message, const cClientHandle * a_Exclude = NULL); // tolua_export
@@ -165,6 +173,9 @@ public:
void BroadcastParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount, cClientHandle * a_Exclude = NULL);
void BroadcastPlayerListItem (const cPlayer & a_Player, bool a_IsOnline, const cClientHandle * a_Exclude = NULL);
void BroadcastRemoveEntityEffect (const cEntity & a_Entity, int a_EffectID, const cClientHandle * a_Exclude = NULL);
+ void BroadcastScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode);
+ void BroadcastScoreUpdate (const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode);
+ void BroadcastDisplayObjective (const AString & a_Objective, cScoreboard::eDisplaySlot a_Display);
void BroadcastSoundEffect (const AString & a_SoundName, int a_SrcX, int a_SrcY, int a_SrcZ, float a_Volume, float a_Pitch, const cClientHandle * a_Exclude = NULL); // tolua_export a_Src coords are Block * 8
void BroadcastSoundParticleEffect(int a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data, const cClientHandle * a_Exclude = NULL); // tolua_export
void BroadcastSpawnEntity (cEntity & a_Entity, const cClientHandle * a_Exclude = NULL);
@@ -174,7 +185,7 @@ public:
void BroadcastUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ );
void BroadcastWeather (eWeather a_Weather, const cClientHandle * a_Exclude = NULL);
- /// If there is a block entity at the specified coords, sends it to the client specified
+ /** If there is a block entity at the specified coords, sends it to the client specified */
void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client);
void MarkChunkDirty (int a_ChunkX, int a_ChunkZ);
@@ -208,7 +219,7 @@ public:
bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback);
- /// Gets the chunk's blocks, only the block types
+ /** Gets the chunk's blocks, only the block types */
bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes);
bool IsChunkValid (int a_ChunkX, int a_ChunkZ) const;
@@ -221,89 +232,92 @@ public:
void AddPlayer( cPlayer* a_Player );
void RemovePlayer( cPlayer* a_Player );
- /// Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true
- bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
-
- /// Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored
- bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+ /** Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true */
+ bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
- /// Finds a player from a partial or complete player name and calls the callback - case-insensitive
+ /** Calls the callback for the player of the given name; returns true if the player was found and the callback called, false if player not found. Callback return ignored */
+ bool DoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS <<
+
+ /** Finds a player from a partial or complete player name and calls the callback - case-insensitive */
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
- /// Adds the entity into its appropriate chunk; takes ownership of the entity ptr
+ /** Adds the entity into its appropriate chunk; takes ownership of the entity ptr */
void AddEntity(cEntity * a_Entity);
bool HasEntity(int a_UniqueID);
- /// Removes the entity, the entity ptr ownership is assumed taken by the caller
+ /** Removes the entity, the entity ptr ownership is assumed taken by the caller */
void RemoveEntity(cEntity * a_Entity);
- /// Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true
+ /** Calls the callback for each entity in the entire world; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntity(cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true
+ /** Calls the callback for each entity in the specified chunk; returns true if all entities processed, false if the callback aborted by returning true */
bool ForEachEntityInChunk(int a_ChunkX, int a_ChunkZ, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false.
+ /** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param. Returns true if entity found and callback returned false. */
bool DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Compares clients of two chunks, calls the callback accordingly
+ /** Compares clients of two chunks, calls the callback accordingly */
void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback);
- /// Adds client to a chunk, if not already present; returns true if added, false if present
+ /** Adds client to a chunk, if not already present; returns true if added, false if present */
bool AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
- /// Removes client from the chunk specified
+ /** Removes client from the chunk specified */
void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
- /// Removes the client from all chunks it is present in
+ /** Removes the client from all chunks it is present in */
void RemoveClientFromChunks(cClientHandle * a_Client);
- /// Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid+lighted)
+ /** Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is postponed (ChunkSender will send that chunk when it becomes valid+lighted) */
void SendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client);
- /// Removes client from ChunkSender's queue of chunks to be sent
+ /** Removes client from ChunkSender's queue of chunks to be sent */
void RemoveClientFromChunkSender(cClientHandle * a_Client);
- /// Touches the chunk, causing it to be loaded or generated
+ /** Touches the chunk, causing it to be loaded or generated */
void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
- /// Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before)
+ /** Loads the chunk, if not already loaded. Doesn't generate. Returns true if chunk valid (even if already loaded before) */
bool LoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
- /// Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid()
+ /** Loads the chunks specified. Doesn't report failure, other than chunks being !IsValid() */
void LoadChunks(const cChunkCoordsList & a_Chunks);
- /// Marks the chunk as failed-to-load:
+ /** Marks the chunk as failed-to-load: */
void ChunkLoadFailed(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
- /// 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 UpdateSign()
+ /** 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 UpdateSign() */
bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, 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 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()
+ /** 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
- /// Marks (a_Stay == true) or unmarks (a_Stay == false) chunks as non-unloadable. To be used only by cChunkStay!
+ /** 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);
- /// Regenerate the given chunk:
+ /** Regenerate the given chunk: */
void RegenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export
- /// Generates the given chunk, if not already generated
+ /** Generates the given chunk, if not already generated */
void GenerateChunk(int a_ChunkX, int a_ChunkZ); // tolua_export
- /// Queues a chunk for lighting; a_Callback is called after the chunk is lighted
+ /** Queues a chunk for lighting; a_Callback is called after the chunk is lighted */
void QueueLightChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback * a_Callback = NULL);
bool IsChunkLighted(int a_ChunkX, int a_ChunkZ);
- /// Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully
- bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback);
+ /** Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully */
+ virtual bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback);
// tolua_begin
@@ -349,31 +363,34 @@ public:
Prefer cBlockArea::Write() instead, this is the internal implementation; cBlockArea does error checking, too.
a_DataTypes is a bitmask of cBlockArea::baXXX constants ORed together.
*/
- bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes);
+ virtual bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes);
// tolua_begin
- /// Spawns item pickups for each item in the list. May compress pickups if too many entities:
+ /** Spawns item pickups for each item in the list. May compress pickups if too many entities: */
void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed = 1.0, bool IsPlayerCreated = false);
- /// Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified:
+ /** Spawns item pickups for each item in the list. May compress pickups if too many entities. All pickups get the speed specified: */
void SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_SpeedX, double a_SpeedY, double a_SpeedZ, bool IsPlayerCreated = false);
- /// Spawns an falling block entity at the given position. It returns the UniqueID of the spawned falling block.
+ /** Spawns an falling block entity at the given position. It returns the UniqueID of the spawned falling block. */
int SpawnFallingBlock(int a_X, int a_Y, int a_Z, BLOCKTYPE BlockType, NIBBLETYPE BlockMeta);
- /// Spawns an experience orb at the given location with the given reward. It returns the UniqueID of the spawned experience orb.
+ /** Spawns an minecart at the given coordinates. */
+ int SpawnMinecart(double a_X, double a_Y, double a_Z, int a_MinecartType, const cItem & a_Content = cItem(), int a_BlockHeight = 1);
+
+ /** Spawns an experience orb at the given location with the given reward. It returns the UniqueID of the spawned experience orb. */
int SpawnExperienceOrb(double a_X, double a_Y, double a_Z, int a_Reward);
- /// Spawns a new primed TNT entity at the specified block coords and specified fuse duration. Initial velocity is given based on the relative coefficient provided
+ /** Spawns a new primed TNT entity at the specified block coords and specified fuse duration. Initial velocity is given based on the relative coefficient provided */
void SpawnPrimedTNT(double a_X, double a_Y, double a_Z, double a_FuseTimeInSec, double a_InitialVelocityCoeff = 1);
// tolua_end
- /// Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType
+ /** Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType */
void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType);
- /// Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read.
+ /** Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. */
bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure);
// tolua_begin
@@ -384,10 +401,10 @@ public:
double GetSpawnY(void) const { return m_SpawnY; }
double GetSpawnZ(void) const { return m_SpawnZ; }
- /// Wakes up the simulators for the specified block
+ /** Wakes up the simulators for the specified block */
void WakeUpSimulators(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// Wakes up the simulators for the specified area of blocks
+ /** Wakes up the simulators for the specified area of blocks */
void WakeUpSimulatorsInArea(int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ);
// tolua_end
@@ -398,22 +415,22 @@ public:
inline cFluidSimulator * GetLavaSimulator (void) { return m_LavaSimulator; }
inline cRedstoneSimulator * GetRedstoneSimulator(void) { return m_RedstoneSimulator; }
- /// Calls the callback for each block entity in the specified chunk; returns true if all block entities processed, false if the callback aborted by returning true
+ /** Calls the callback for each block entity in the specified chunk; returns true if all block entities processed, false if the callback aborted by returning true */
bool ForEachBlockEntityInChunk(int a_ChunkX, int a_ChunkZ, cBlockEntityCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true
+ /** Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true */
bool ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true
+ /** Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true */
bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback);
- /// Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true
+ /** Calls the callback for each dropper in the specified chunk; returns true if all droppers processed, false if the callback aborted by returning true */
bool ForEachDropperInChunk(int a_ChunkX, int a_ChunkZ, cDropperCallback & a_Callback);
- /// Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true
+ /** Calls the callback for each dropspenser in the specified chunk; returns true if all dropspensers processed, false if the callback aborted by returning true */
bool ForEachDropSpenserInChunk(int a_ChunkX, int a_ChunkZ, cDropSpenserCallback & a_Callback);
- /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true
+ /** Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true */
bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp
/** Does an explosion with the specified strength at the specified coordinate
@@ -431,69 +448,79 @@ public:
*/
void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData); // tolua_export
- /// Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found
+ /** Calls the callback for the block entity at the specified coords; returns false if there's no block entity at those coords, true if found */
bool DoWithBlockEntityAt(int a_BlockX, int a_BlockY, int a_BlockZ, cBlockEntityCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found
+ /** Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found */
bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found
+ /** Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords or callback returns true, returns true if found */
bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found
+ /** Calls the callback for the dropper at the specified coords; returns false if there's no dropper at those coords or callback returns true, returns true if found */
bool DoWithDropperAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropperCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found
+ /** Calls the callback for the dropspenser at the specified coords; returns false if there's no dropspenser at those coords or callback returns true, returns true if found */
bool DoWithDropSpenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found
+ /** Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords or callback returns true, returns true if found */
bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Calls the callback for the noteblock at the specified coords; returns false if there's no noteblock at those coords or callback returns true, returns true if found
+ /** Calls the callback for the noteblock at the specified coords; returns false if there's no noteblock at those coords or callback returns true, returns true if found */
bool DoWithNoteBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cNoteBlockCallback & a_Callback); // Exported in ManualBindings.cpp
+
+ /** Calls the callback for the command block at the specified coords; returns false if there's no command block at those coords or callback returns true, returns true if found */
+ bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Exported in ManualBindings.cpp
- /// Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found
+ /** Retrieves the test on the sign at the specified coords; returns false if there's no sign at those coords, true if found */
bool GetSignLines (int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4); // Exported in ManualBindings.cpp
- /// a_Player is using block entity at [x, y, z], handle that:
+ /** a_Player is using block entity at [x, y, z], handle that: */
void UseBlockEntity(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) {m_ChunkMap->UseBlockEntity(a_Player, a_BlockX, a_BlockY, a_BlockZ); } // tolua_export
- /// Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback
+ /** Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback */
bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback);
void GrowTreeImage(const sSetBlockVector & a_Blocks);
// tolua_begin
- /// Grows a tree at the specified coords, either from a sapling there, or based on the biome
+ /** Grows a tree at the specified coords, either from a sapling there, or based on the biome */
void GrowTree (int a_BlockX, int a_BlockY, int a_BlockZ);
- /// Grows a tree at the specified coords, based on the sapling meta provided
+ /** Grows a tree at the specified coords, based on the sapling meta provided */
void GrowTreeFromSapling(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYPE a_SaplingMeta);
- /// Grows a tree at the specified coords, based on the biome in the place
+ /** Grows a tree at the specified coords, based on the biome in the place */
void GrowTreeByBiome (int a_BlockX, int a_BlockY, int a_BlockZ);
- /// Grows the plant at the specified block to its ripe stage (bonemeal used); returns false if the block is not growable. If a_IsBonemeal is true, block is not grown if not allowed in world.ini
+ /** Grows the plant at the specified block to its ripe stage (bonemeal used); returns false if the block is not growable. If a_IsBonemeal is true, block is not grown if not allowed in world.ini */
bool GrowRipePlant(int a_BlockX, int a_BlockY, int a_BlockZ, bool a_IsByBonemeal = false);
- /// Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config
+ /** Grows a cactus present at the block specified by the amount of blocks specified, up to the max height specified in the config */
void GrowCactus(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
- /// Grows a melon or a pumpkin next to the block specified (assumed to be the stem)
+ /** Grows a melon or a pumpkin next to the block specified (assumed to be the stem) */
void GrowMelonPumpkin(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType);
- /// Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config
+ /** Grows a sugarcane present at the block specified by the amount of blocks specified, up to the max height specified in the config */
void GrowSugarcane(int a_BlockX, int a_BlockY, int a_BlockZ, int a_NumBlocksToGrow);
- /// Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, otherwise uses the world generator to provide the biome value
+ /** Returns the biome at the specified coords. Reads the biome from the chunk, if loaded, otherwise uses the world generator to provide the biome value */
int GetBiomeAt(int a_BlockX, int a_BlockZ);
- /// Returns the name of the world
+ /** Returns the name of the world */
const AString & GetName(void) const { return m_WorldName; }
- /// Returns the name of the world.ini file used by this world
+ /** Returns the name of the world.ini file used by this world */
const AString & GetIniFileName(void) const {return m_IniFileName; }
+
+ /// 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
@@ -524,19 +551,23 @@ public:
if(a_Z < 0 && a_Z % cChunkDef::Width != 0) a_ChunkZ--;
}
- /// Saves all chunks immediately. Dangerous interface, may deadlock, use QueueSaveAllChunks() instead
+ /** Saves all chunks immediately. Dangerous interface, may deadlock, use QueueSaveAllChunks() instead */
void SaveAllChunks(void);
- /// Queues a task to save all chunks onto the tick thread. The prefferred way of saving chunks from external sources
+ /** Queues a task to save all chunks onto the tick thread. The prefferred way of saving chunks from external sources */
void QueueSaveAllChunks(void); // tolua_export
- /// Queues a task onto the tick thread. The task object will be deleted once the task is finished
+ /** Queues a task onto the tick thread. The task object will be deleted once the task is finished */
void QueueTask(cTask * a_Task); // Exported in ManualBindings.cpp
+
+ /** Queues a task onto the tick thread, with the specified delay.
+ The task object will be deleted once the task is finished */
+ void ScheduleTask(int a_DelayTicks, cTask * a_Task);
- /// Returns the number of chunks loaded
+ /** Returns the number of chunks loaded */
int GetNumChunks() const; // tolua_export
- /// Returns the number of chunks loaded and dirty, and in the lighting queue
+ /** Returns the number of chunks loaded and dirty, and in the lighting queue */
void GetChunkStats(int & a_NumValid, int & a_NumDirty, int & a_NumInLightingQueue);
// Various queues length queries (cannot be const, they lock their CS):
@@ -547,13 +578,13 @@ public:
void InitializeSpawn(void);
- /// Starts threads that belong to this world
+ /** Starts threads that belong to this world */
void Start(void);
- /// Stops threads that belong to this world (part of deinit)
+ /** Stops threads that belong to this world (part of deinit) */
void Stop(void);
- /// Processes the blocks queued for ticking with a delay (m_BlockTickQueue[])
+ /** Processes the blocks queued for ticking with a delay (m_BlockTickQueue[]) */
void TickQueuedBlocks(void);
struct BlockTickQueueItem
@@ -564,27 +595,27 @@ public:
int TicksToWait;
};
- /// Queues the block to be ticked after the specified number of game ticks
+ /** Queues the block to be ticked after the specified number of game ticks */
void QueueBlockForTick(int a_BlockX, int a_BlockY, int a_BlockZ, int a_TicksToWait); // tolua_export
// tolua_begin
- /// Casts a thunderbolt at the specified coords
+ /** Casts a thunderbolt at the specified coords */
void CastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ);
- /// Sets the specified weather; resets weather interval; asks and notifies plugins of the change
+ /** Sets the specified weather; resets weather interval; asks and notifies plugins of the change */
void SetWeather (eWeather a_NewWeather);
- /// Forces a weather change in the next game tick
+ /** Forces a weather change in the next game tick */
void ChangeWeather (void);
- /// Returns the current weather. Instead of comparing values directly to the weather constants, use IsWeatherXXX() functions, if possible
+ /** Returns the current weather. Instead of comparing values directly to the weather constants, use IsWeatherXXX() functions, if possible */
eWeather GetWeather (void) const { return m_Weather; };
bool IsWeatherSunny(void) const { return (m_Weather == wSunny); }
bool IsWeatherRain (void) const { return (m_Weather == wRain); }
bool IsWeatherStorm(void) const { return (m_Weather == wStorm); }
- /// Returns true if the current weather has any precipitation - rain or storm
+ /** Returns true if the current weather has any precipitation - rain or storm */
bool IsWeatherWet (void) const { return (m_Weather != wSunny); }
// tolua_end
@@ -593,7 +624,7 @@ public:
cWorldStorage & GetStorage (void) { return m_Storage; }
cChunkMap * GetChunkMap (void) { return m_ChunkMap; }
- /// Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call
+ /** Sets the blockticking to start at the specified block. Only one blocktick per chunk may be set, second call overwrites the first call */
void SetNextBlockTick(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export
int GetMaxSugarcaneHeight(void) const { return m_MaxSugarcaneHeight; } // tolua_export
@@ -601,20 +632,20 @@ public:
bool IsBlockDirectlyWatered(int a_BlockX, int a_BlockY, int a_BlockZ); // tolua_export
- /// Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise
+ /** Spawns a mob of the specified type. Returns the mob's EntityID if recognized and spawned, <0 otherwise */
int SpawnMob(double a_PosX, double a_PosY, double a_PosZ, cMonster::eType a_MonsterType); // tolua_export
int SpawnMobFinalize(cMonster* a_Monster);
- /// Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise
+ /** Creates a projectile of the specified type. Returns the projectile's EntityID if successful, <0 otherwise */
int CreateProjectile(double a_PosX, double a_PosY, double a_PosZ, cProjectileEntity::eKind a_Kind, cEntity * a_Creator, const Vector3d * a_Speed = NULL); // tolua_export
- /// Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread!
+ /** Returns a random number from the m_TickRand in range [0 .. a_Range]. To be used only in the tick thread! */
int GetTickRandomNumber(unsigned a_Range) { return (int)(m_TickRand.randInt(a_Range)); }
- /// Appends all usernames starting with a_Text (case-insensitive) into Results
+ /** Appends all usernames starting with a_Text (case-insensitive) into Results */
void TabCompleteUserName(const AString & a_Text, AStringVector & a_Results);
- /// Get the current darkness level based on the time
+ /** Get the current darkness level based on the time */
NIBBLETYPE GetSkyDarkness() { return m_SkyDarkness; }
private:
@@ -635,17 +666,63 @@ private:
virtual void Execute(void) override;
} ;
+
+ /** Implementation of the callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */
+ class cChunkGeneratorCallbacks :
+ public cChunkGenerator::cChunkSink,
+ public cChunkGenerator::cPluginInterface
+ {
+ cWorld * m_World;
+
+ // cChunkSink overrides:
+ virtual void OnChunkGenerated (cChunkDesc & a_ChunkDesc) override;
+ virtual bool IsChunkValid (int a_ChunkX, int a_ChunkZ) override;
+ virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) override;
+
+ // cPluginInterface overrides:
+ virtual void CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) override;
+ virtual void CallHookChunkGenerated (cChunkDesc & a_ChunkDesc) override;
+
+ public:
+ cChunkGeneratorCallbacks(cWorld & a_World);
+ } ;
+
+
+ /** A container for tasks that have been scheduled for a specific game tick */
+ class cScheduledTask
+ {
+ public:
+ Int64 m_TargetTick;
+ cTask * m_Task;
+
+ /** Creates a new scheduled task; takes ownership of the task object passed to it. */
+ cScheduledTask(Int64 a_TargetTick, cTask * a_Task) :
+ m_TargetTick(a_TargetTick),
+ m_Task(a_Task)
+ {
+ }
+
+ virtual ~cScheduledTask()
+ {
+ delete m_Task;
+ }
+ };
+
+ typedef std::list<cScheduledTask *> cScheduledTasks;
+
AString m_WorldName;
AString m_IniFileName;
- /// Name of the storage schema used to load and save chunks
+ /** Name of the storage schema used to load and save chunks */
AString m_StorageSchema;
- /// The dimension of the world, used by the client to provide correct lighting scheme
+ int m_StorageCompressionFactor;
+
+ /** The dimension of the world, used by the client to provide correct lighting scheme */
eDimension m_Dimension;
- /// This random generator is to be used only in the Tick() method, and thus only in the World-Tick-thread (MTRand is not exactly thread-safe)
+ /** This random generator is to be used only in the Tick() method, and thus only in the World-Tick-thread (MTRand is not exactly thread-safe) */
MTRand m_TickRand;
bool m_IsSpawnExplicitlySet;
@@ -668,6 +745,7 @@ private:
bool m_bEnabledPVP;
bool m_IsDeepSnowEnabled;
bool m_ShouldLavaSpawnFire;
+ bool m_VillagersShouldHarvestCrops;
std::vector<BlockTickQueueItem *> m_BlockTickQueue;
std::vector<BlockTickQueueItem *> m_BlockTickQueueCopy; // Second is for safely removing the objects from the queue
@@ -707,32 +785,47 @@ private:
bool m_IsPumpkinBonemealable;
bool m_IsSaplingBonemealable;
bool m_IsSugarcaneBonemealable;
+
+ bool m_bCommandBlocksEnabled;
cCriticalSection m_CSFastSetBlock;
sSetBlockList m_FastSetBlockQueue;
cChunkGenerator m_Generator;
+
+ cScoreboard m_Scoreboard;
+
+ /** The callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */
+ cChunkGeneratorCallbacks m_GeneratorCallbacks;
cChunkSender m_ChunkSender;
cLightingThread m_Lighting;
cTickThread m_TickThread;
- /// Guards the m_Tasks
+ /** Guards the m_Tasks */
cCriticalSection m_CSTasks;
- /// Tasks that have been queued onto the tick thread; guarded by m_CSTasks
+ /** Tasks that have been queued onto the tick thread; guarded by m_CSTasks */
cTasks m_Tasks;
- /// Guards m_Clients
+ /** Guards the m_ScheduledTasks */
+ cCriticalSection m_CSScheduledTasks;
+
+ /** Tasks that have been queued to be executed on the tick thread at target tick in the future.
+ Ordered by increasing m_TargetTick.
+ Guarded by m_CSScheduledTasks */
+ cScheduledTasks m_ScheduledTasks;
+
+ /** Guards m_Clients */
cCriticalSection m_CSClients;
- /// List of clients in this world, these will be ticked by this world
+ /** List of clients in this world, these will be ticked by this world */
cClientHandleList m_Clients;
- /// Clients that are scheduled for removal (ticked in another world), waiting for TickClients() to remove them
+ /** Clients that are scheduled for removal (ticked in another world), waiting for TickClients() to remove them */
cClientHandleList m_ClientsToRemove;
- /// Clients that are scheduled for adding, waiting for TickClients to add them
+ /** Clients that are scheduled for adding, waiting for TickClients to add them */
cClientHandleList m_ClientsToAdd;
@@ -741,24 +834,27 @@ private:
void Tick(float a_Dt, int a_LastTickDurationMSec);
- /// Handles the weather in each tick
+ /** Handles the weather in each tick */
void TickWeather(float a_Dt);
- /// Handles the mob spawning/moving/destroying each tick
+ /** Handles the mob spawning/moving/destroying each tick */
void TickMobs(float a_Dt);
- /// Executes all tasks queued onto the tick thread
+ /** Executes all tasks queued onto the tick thread */
void TickQueuedTasks(void);
- /// Ticks all clients that are in this world
+ /** Executes all tasks queued onto the tick thread */
+ void TickScheduledTasks(void);
+
+ /** Ticks all clients that are in this world */
void TickClients(float a_Dt);
void UpdateSkyDarkness(void);
- /// <summary>Generates a random spawnpoint on solid land by walking chunks and finding their biomes</summary>
+ /** <summary>Generates a random spawnpoint on solid land by walking chunks and finding their biomes</summary> */
void GenerateRandomSpawn(void);
- /// Creates a new fluid simulator, loads its settings from the inifile (a_FluidName section)
+ /** Creates a new fluid simulator, loads its settings from the inifile (a_FluidName section) */
cFluidSimulator * InitializeFluidSimulator(cIniFile & a_IniFile, const char * a_FluidName, BLOCKTYPE a_SimulateBlock, BLOCKTYPE a_StationaryBlock);
}; // tolua_export
diff --git a/src/WorldStorage/CMakeLists.txt b/src/WorldStorage/CMakeLists.txt
index d431bdf6a..2c83c4662 100644
--- a/src/WorldStorage/CMakeLists.txt
+++ b/src/WorldStorage/CMakeLists.txt
@@ -9,3 +9,5 @@ file(GLOB SOURCE
)
add_library(WorldStorage ${SOURCE})
+
+target_link_libraries(WorldStorage OSSupport)
diff --git a/src/WorldStorage/EnchantmentSerializer.cpp b/src/WorldStorage/EnchantmentSerializer.cpp
new file mode 100644
index 000000000..56072207f
--- /dev/null
+++ b/src/WorldStorage/EnchantmentSerializer.cpp
@@ -0,0 +1,88 @@
+
+#include "Globals.h"
+
+#include "EnchantmentSerializer.h"
+#include "Enchantments.h"
+#include "FastNBT.h"
+
+void EnchantmentSerializer::WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName)
+{
+ // Write the enchantments into the specified NBT writer
+ // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments")
+
+ a_Writer.BeginList(a_ListTagName, TAG_Compound);
+ for (cEnchantments::cMap::const_iterator itr = a_Enchantments.m_Enchantments.begin(), end = a_Enchantments.m_Enchantments.end(); itr != end; ++itr)
+ {
+ a_Writer.BeginCompound("");
+ a_Writer.AddShort("id", itr->first);
+ a_Writer.AddShort("lvl", itr->second);
+ a_Writer.EndCompound();
+ } // for itr - m_Enchantments[]
+ a_Writer.EndList();
+}
+
+
+
+
+
+void EnchantmentSerializer::ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx)
+{
+ // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments)
+
+ // Verify that the tag is a list:
+ if (a_NBT.GetType(a_EnchListTagIdx) != TAG_List)
+ {
+ LOGWARNING("%s: Invalid EnchListTag type: exp %d, got %d. Enchantments not parsed",
+ __FUNCTION__, TAG_List, a_NBT.GetType(a_EnchListTagIdx)
+ );
+ ASSERT(!"Bad EnchListTag type");
+ return;
+ }
+
+ // Verify that the list is of Compounds:
+ if (a_NBT.GetChildrenType(a_EnchListTagIdx) != TAG_Compound)
+ {
+ LOGWARNING("%s: Invalid NBT list children type: exp %d, got %d. Enchantments not parsed",
+ __FUNCTION__, TAG_Compound, a_NBT.GetChildrenType(a_EnchListTagIdx)
+ );
+ ASSERT(!"Bad EnchListTag children type");
+ return;
+ }
+
+ a_Enchantments.Clear();
+
+ // Iterate over all the compound children, parse an enchantment from each:
+ for (int tag = a_NBT.GetFirstChild(a_EnchListTagIdx); tag >= 0; tag = a_NBT.GetNextSibling(tag))
+ {
+ // tag is the compound inside the "ench" list tag
+ ASSERT(a_NBT.GetType(tag) == TAG_Compound);
+
+ // Search for the id and lvl tags' values:
+ int id = -1, lvl = -1;
+ for (int ch = a_NBT.GetFirstChild(tag); ch >= 0; ch = a_NBT.GetNextSibling(ch))
+ {
+ if (a_NBT.GetType(ch) != TAG_Short)
+ {
+ continue;
+ }
+ if (a_NBT.GetName(ch) == "id")
+ {
+ id = a_NBT.GetShort(ch);
+ }
+ else if (a_NBT.GetName(ch) == "lvl")
+ {
+ lvl = a_NBT.GetShort(ch);
+ }
+ } // for ch - children of the compound tag
+
+ if ((id == -1) || (lvl <= 0))
+ {
+ // Failed to parse either the id or the lvl, skip this compound
+ continue;
+ }
+
+ // Store the enchantment:
+ a_Enchantments.m_Enchantments[id] = lvl;
+ } // for tag - children of the ench list tag
+}
+
diff --git a/src/WorldStorage/EnchantmentSerializer.h b/src/WorldStorage/EnchantmentSerializer.h
new file mode 100644
index 000000000..9ed362900
--- /dev/null
+++ b/src/WorldStorage/EnchantmentSerializer.h
@@ -0,0 +1,17 @@
+
+#pragma once
+
+class cEnchantments;
+class cFastNBTWriter;
+class cParsedNBT;
+
+namespace EnchantmentSerializer
+{
+
+ /// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments")
+ void WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName);
+
+ /// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments)
+ void ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx);
+
+};
diff --git a/src/WorldStorage/FastNBT.cpp b/src/WorldStorage/FastNBT.cpp
index e55011069..8f80c3f75 100644
--- a/src/WorldStorage/FastNBT.cpp
+++ b/src/WorldStorage/FastNBT.cpp
@@ -16,9 +16,12 @@
#define NBT_RESERVE_SIZE 200
#endif // NBT_RESERVE_SIZE
-#define RETURN_FALSE_IF_FALSE(X) do { if (!X) return false; } while (0)
-
-
+#ifdef _MSC_VER
+ // Dodge a C4127 (conditional expression is constant) for this specific macro usage
+ #define RETURN_FALSE_IF_FALSE(X) do { if (!X) return false; } while ((false, false))
+#else
+ #define RETURN_FALSE_IF_FALSE(X) do { if (!X) return false; } while (false)
+#endif
@@ -80,7 +83,7 @@ bool cParsedNBT::ReadString(int & a_StringStart, int & a_StringLen)
{
NEEDBYTES(2);
a_StringStart = m_Pos + 2;
- a_StringLen = ntohs(*((short *)(m_Data + m_Pos)));
+ a_StringLen = GetBEShort(m_Data + m_Pos);
if (a_StringLen < 0)
{
// Invalid string length
@@ -99,7 +102,7 @@ bool cParsedNBT::ReadCompound(void)
// Reads the latest tag as a compound
int ParentIdx = m_Tags.size() - 1;
int PrevSibling = -1;
- while (true)
+ for (;;)
{
NEEDBYTES(1);
eTagType TagType = (eTagType)(m_Data[m_Pos]);
@@ -135,7 +138,7 @@ bool cParsedNBT::ReadList(eTagType a_ChildrenType)
// Read the count:
NEEDBYTES(4);
- int Count = ntohl(*((int *)(m_Data + m_Pos)));
+ int Count = GetBEInt(m_Data + m_Pos);
m_Pos += 4;
if (Count < 0)
{
@@ -197,7 +200,7 @@ bool cParsedNBT::ReadTag(void)
case TAG_ByteArray:
{
NEEDBYTES(4);
- int len = ntohl(*((int *)(m_Data + m_Pos)));
+ int len = GetBEInt(m_Data + m_Pos);
m_Pos += 4;
if (len < 0)
{
@@ -229,7 +232,7 @@ bool cParsedNBT::ReadTag(void)
case TAG_IntArray:
{
NEEDBYTES(4);
- int len = ntohl(*((int *)(m_Data + m_Pos)));
+ int len = GetBEInt(m_Data + m_Pos);
m_Pos += 4;
if (len < 0)
{
@@ -276,7 +279,7 @@ int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLen
for (int Child = m_Tags[a_Tag].m_FirstChild; Child != -1; Child = m_Tags[Child].m_NextSibling)
{
if (
- (m_Tags[Child].m_NameLength == a_NameLength) &&
+ (m_Tags[Child].m_NameLength == (int)a_NameLength) &&
(memcmp(m_Data + m_Tags[Child].m_NameStart, a_Name, a_NameLength) == 0)
)
{
@@ -401,7 +404,7 @@ void cFastNBTWriter::EndList(void)
ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List);
// Update the list count:
- *((int *)(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos)) = htonl(m_Stack[m_CurrentStack].m_Count);
+ SetBEInt((char *)(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos), m_Stack[m_CurrentStack].m_Count);
--m_CurrentStack;
}
diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h
index 7323c29cb..b84eda1a1 100644
--- a/src/WorldStorage/FastNBT.h
+++ b/src/WorldStorage/FastNBT.h
@@ -154,13 +154,13 @@ public:
inline Int16 GetShort(int a_Tag) const
{
ASSERT(m_Tags[a_Tag].m_Type == TAG_Short);
- return ntohs(*((Int16 *)(m_Data + m_Tags[a_Tag].m_DataStart)));
+ return GetBEShort(m_Data + m_Tags[a_Tag].m_DataStart);
}
inline Int32 GetInt(int a_Tag) const
{
ASSERT(m_Tags[a_Tag].m_Type == TAG_Int);
- return ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart)));
+ return GetBEInt(m_Data + m_Tags[a_Tag].m_DataStart);
}
inline Int64 GetLong(int a_Tag) const
@@ -172,7 +172,7 @@ public:
inline float GetFloat(int a_Tag) const
{
ASSERT(m_Tags[a_Tag].m_Type == TAG_Float);
- Int32 tmp = ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart)));
+ Int32 tmp = GetBEInt(m_Data + m_Tags[a_Tag].m_DataStart);
return *((float *)&tmp);
}
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index e5043de1f..e46a28caa 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -4,12 +4,14 @@
#include "Globals.h"
#include "NBTChunkSerializer.h"
+#include "EnchantmentSerializer.h"
#include "../BlockID.h"
#include "../ItemGrid.h"
#include "../StringCompression.h"
#include "FastNBT.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/DropperEntity.h"
#include "../BlockEntities/FurnaceEntity.h"
@@ -91,7 +93,7 @@ void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AStrin
{
const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
m_Writer.BeginCompound("tag");
- a_Item.m_Enchantments.WriteToNBTCompound(m_Writer, TagName);
+ EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, m_Writer, TagName);
m_Writer.EndCompound();
}
@@ -219,8 +221,23 @@ void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note)
{
m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Note, "Music");
- m_Writer.AddByte("note", a_Note->GetPitch());
+ AddBasicTileEntity(a_Note, "Music");
+ m_Writer.AddByte("note", a_Note->GetPitch());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_CmdBlock, "Control");
+ m_Writer.AddString("Command", a_CmdBlock->GetCommand());
+ m_Writer.AddInt ("SuccessCount", a_CmdBlock->GetResult());
+ m_Writer.AddString("LastOutput", a_CmdBlock->GetLastOutput());
+ m_Writer.AddByte ("TrackOutput", 1); // TODO 2014-01-18 xdot: Figure out what TrackOutput is and save it.
m_Writer.EndCompound();
}
@@ -257,7 +274,7 @@ void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_C
m_Writer.AddDouble("", a_Entity->GetSpeedZ());
m_Writer.EndList();
m_Writer.BeginList("Rotation", TAG_Double);
- m_Writer.AddDouble("", a_Entity->GetRotation());
+ m_Writer.AddDouble("", a_Entity->GetYaw());
m_Writer.AddDouble("", a_Entity->GetPitch());
m_Writer.EndList();
}
@@ -635,19 +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/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h
index 9d4ac208c..245b68063 100644
--- a/src/WorldStorage/NBTChunkSerializer.h
+++ b/src/WorldStorage/NBTChunkSerializer.h
@@ -21,6 +21,7 @@ class cEntity;
class cBlockEntity;
class cBoat;
class cChestEntity;
+class cCommandBlockEntity;
class cDispenserEntity;
class cDropperEntity;
class cFurnaceEntity;
@@ -92,6 +93,7 @@ protected:
void AddJukeboxEntity (cJukeboxEntity * a_Jukebox);
void AddNoteEntity (cNoteEntity * a_Note);
void AddSignEntity (cSignEntity * a_Sign);
+ void AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock);
// Entities:
void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName);
diff --git a/src/WorldStorage/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp
new file mode 100644
index 000000000..45fd967bd
--- /dev/null
+++ b/src/WorldStorage/SchematicFileSerializer.cpp
@@ -0,0 +1,172 @@
+
+#include "Globals.h"
+
+#include "OSSupport/GZipFile.h"
+#include "FastNBT.h"
+
+#include "SchematicFileSerializer.h"
+
+bool cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName)
+{
+ // Un-GZip the contents:
+ AString Contents;
+ cGZipFile File;
+ if (!File.Open(a_FileName, cGZipFile::fmRead))
+ {
+ LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+ int NumBytesRead = File.ReadRestOfFile(Contents);
+ if (NumBytesRead < 0)
+ {
+ LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead);
+ return false;
+ }
+ File.Close();
+
+ // Parse the NBT:
+ cParsedNBT NBT(Contents.data(), Contents.size());
+ if (!NBT.IsValid())
+ {
+ LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+
+ return LoadFromSchematicNBT(a_BlockArea, NBT);
+}
+
+
+
+
+
+
+bool cSchematicFileSerializer::SaveToSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName)
+{
+ cFastNBTWriter Writer("Schematic");
+ Writer.AddShort("Width", a_BlockArea.m_SizeX);
+ Writer.AddShort("Height", a_BlockArea.m_SizeY);
+ Writer.AddShort("Length", a_BlockArea.m_SizeZ);
+ Writer.AddString("Materials", "Alpha");
+ if (a_BlockArea.HasBlockTypes())
+ {
+ Writer.AddByteArray("Blocks", (const char *)a_BlockArea.m_BlockTypes, a_BlockArea.GetBlockCount());
+ }
+ else
+ {
+ AString Dummy(a_BlockArea.GetBlockCount(), 0);
+ Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
+ }
+ if (a_BlockArea.HasBlockMetas())
+ {
+ Writer.AddByteArray("Data", (const char *)a_BlockArea.m_BlockMetas, a_BlockArea.GetBlockCount());
+ }
+ else
+ {
+ AString Dummy(a_BlockArea.GetBlockCount(), 0);
+ Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
+ }
+ // TODO: Save entities and block entities
+ Writer.BeginList("Entities", TAG_Compound);
+ Writer.EndList();
+ Writer.BeginList("TileEntities", TAG_Compound);
+ Writer.EndList();
+ Writer.Finish();
+
+ // Save to file
+ cGZipFile File;
+ if (!File.Open(a_FileName, cGZipFile::fmWrite))
+ {
+ LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str());
+ return false;
+ }
+ if (!File.Write(Writer.GetResult()))
+ {
+ LOG("Cannot write data to file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+
+bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT)
+{
+ int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
+ if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String))
+ {
+ AString Materials = a_NBT.GetString(TMaterials);
+ if (Materials.compare("Alpha") != 0)
+ {
+ LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str());
+ return false;
+ }
+ }
+ int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width");
+ int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height");
+ int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length");
+ if (
+ (TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) ||
+ (a_NBT.GetType(TSizeX) != TAG_Short) ||
+ (a_NBT.GetType(TSizeY) != TAG_Short) ||
+ (a_NBT.GetType(TSizeZ) != TAG_Short)
+ )
+ {
+ LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)",
+ TSizeX, TSizeY, TSizeZ,
+ a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ)
+ );
+ return false;
+ }
+
+ int SizeX = a_NBT.GetShort(TSizeX);
+ int SizeY = a_NBT.GetShort(TSizeY);
+ int SizeZ = a_NBT.GetShort(TSizeZ);
+ if ((SizeX < 1) || (SizeY < 1) || (SizeZ < 1))
+ {
+ LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ);
+ return false;
+ }
+
+ int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks");
+ int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data");
+ if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray))
+ {
+ LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes);
+ return false;
+ }
+ bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray);
+
+ a_BlockArea.Clear();
+ a_BlockArea.SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (cBlockArea::baTypes | cBlockArea::baMetas) : cBlockArea::baTypes);
+
+ // Copy the block types and metas:
+ int NumBytes = a_BlockArea.m_SizeX * a_BlockArea.m_SizeY * a_BlockArea.m_SizeZ;
+ if (a_NBT.GetDataLength(TBlockTypes) < NumBytes)
+ {
+ LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
+ NumBytes, a_NBT.GetDataLength(TBlockTypes)
+ );
+ NumBytes = a_NBT.GetDataLength(TBlockTypes);
+ }
+ memcpy(a_BlockArea.m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes);
+
+ if (AreMetasPresent)
+ {
+ int NumBytes = a_BlockArea.m_SizeX * a_BlockArea.m_SizeY * a_BlockArea.m_SizeZ;
+ if (a_NBT.GetDataLength(TBlockMetas) < NumBytes)
+ {
+ LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
+ NumBytes, a_NBT.GetDataLength(TBlockMetas)
+ );
+ NumBytes = a_NBT.GetDataLength(TBlockMetas);
+ }
+ memcpy(a_BlockArea.m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes);
+ }
+
+ return true;
+}
+
+
diff --git a/src/WorldStorage/SchematicFileSerializer.h b/src/WorldStorage/SchematicFileSerializer.h
new file mode 100644
index 000000000..9be2e5b57
--- /dev/null
+++ b/src/WorldStorage/SchematicFileSerializer.h
@@ -0,0 +1,29 @@
+
+#pragma once
+
+#include "../BlockArea.h"
+
+
+
+
+
+// fwd: FastNBT.h
+class cParsedNBT;
+
+
+
+
+class cSchematicFileSerializer
+{
+public:
+
+ /// Loads an area from a .schematic file. Returns true if successful
+ static bool LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName);
+
+ /// Saves the area into a .schematic file. Returns true if successful
+ static bool SaveToSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName);
+
+private:
+ /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful.
+ static bool LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT);
+};
diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp
new file mode 100644
index 000000000..c65e13f98
--- /dev/null
+++ b/src/WorldStorage/ScoreboardSerializer.cpp
@@ -0,0 +1,377 @@
+
+// ScoreboardSerializer.cpp
+
+
+#include "Globals.h"
+#include "ScoreboardSerializer.h"
+#include "../StringCompression.h"
+#include "zlib/zlib.h"
+#include "FastNBT.h"
+
+#include "../Scoreboard.h"
+
+
+
+
+
+cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard)
+ : m_ScoreBoard(a_ScoreBoard)
+{
+ AString DataPath;
+ Printf(DataPath, "%s/data", a_WorldName.c_str());
+
+ m_Path = DataPath + "/scoreboard.dat";
+
+ cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
+}
+
+
+
+
+
+bool cScoreboardSerializer::Load(void)
+{
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
+ {
+ return false;
+ }
+
+ AString Uncompressed;
+ int res = UncompressStringGZIP(Data.data(), Data.size(), Uncompressed);
+
+ if (res != Z_OK)
+ {
+ return false;
+ }
+
+ // Parse the NBT data:
+ cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
+ if (!NBT.IsValid())
+ {
+ // NBT Parsing failed
+ return false;
+ }
+
+ return LoadScoreboardFromNBT(NBT);
+}
+
+
+
+
+
+bool cScoreboardSerializer::Save(void)
+{
+ cFastNBTWriter Writer;
+
+ SaveScoreboardToNBT(Writer);
+
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ 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)
+ {
+ return false;
+ }
+
+ File.Write(Compressed.data(), Compressed.size());
+ File.Close();
+
+ return true;
+}
+
+
+
+
+
+void cScoreboardSerializer::SaveScoreboardToNBT(cFastNBTWriter & a_Writer)
+{
+ 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)
+ {
+ const cObjective & Objective = it->second;
+
+ a_Writer.BeginCompound("");
+
+ a_Writer.AddString("CriteriaName", cObjective::TypeToString(Objective.GetType()));
+
+ a_Writer.AddString("DisplayName", Objective.GetDisplayName());
+ a_Writer.AddString("Name", it->first);
+
+ a_Writer.EndCompound();
+ }
+
+ a_Writer.EndList(); // Objectives
+
+ a_Writer.BeginList("PlayerScores", TAG_Compound);
+
+ for (cScoreboard::cObjectiveMap::const_iterator it = m_ScoreBoard->m_Objectives.begin(); it != m_ScoreBoard->m_Objectives.end(); ++it)
+ {
+ const cObjective & Objective = it->second;
+
+ for (cObjective::cScoreMap::const_iterator it2 = Objective.m_Scores.begin(); it2 != Objective.m_Scores.end(); ++it2)
+ {
+ a_Writer.BeginCompound("");
+
+ a_Writer.AddInt("Score", it2->second);
+
+ a_Writer.AddString("Name", it2->first);
+ a_Writer.AddString("Objective", it->first);
+
+ a_Writer.EndCompound();
+ }
+ }
+
+ a_Writer.EndList(); // PlayerScores
+
+ a_Writer.BeginList("Teams", TAG_Compound);
+
+ for (cScoreboard::cTeamMap::const_iterator it = m_ScoreBoard->m_Teams.begin(); it != m_ScoreBoard->m_Teams.end(); ++it)
+ {
+ const cTeam & Team = it->second;
+
+ a_Writer.BeginCompound("");
+
+ a_Writer.AddByte("AllowFriendlyFire", Team.AllowsFriendlyFire() ? 1 : 0);
+ a_Writer.AddByte("SeeFriendlyInvisibles", Team.CanSeeFriendlyInvisible() ? 1 : 0);
+
+ a_Writer.AddString("DisplayName", Team.GetDisplayName());
+ a_Writer.AddString("Name", it->first);
+
+ a_Writer.AddString("Prefix", Team.GetPrefix());
+ a_Writer.AddString("Suffix", Team.GetSuffix());
+
+ a_Writer.BeginList("Players", TAG_String);
+
+ for (cTeam::cPlayerNameSet::const_iterator it2 = Team.m_Players.begin(); it2 != Team.m_Players.end(); ++it2)
+ {
+ a_Writer.AddString("", *it2);
+ }
+
+ a_Writer.EndList();
+
+ a_Writer.EndCompound();
+ }
+
+ a_Writer.EndList(); // Teams
+
+ a_Writer.BeginCompound("DisplaySlots");
+
+ cObjective * Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::E_DISPLAY_SLOT_LIST);
+ a_Writer.AddString("slot_0", (Objective == NULL) ? "" : Objective->GetName());
+
+ Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::E_DISPLAY_SLOT_SIDEBAR);
+ a_Writer.AddString("slot_1", (Objective == NULL) ? "" : Objective->GetName());
+
+ Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::E_DISPLAY_SLOT_NAME);
+ a_Writer.AddString("slot_2", (Objective == NULL) ? "" : Objective->GetName());
+
+ a_Writer.EndCompound(); // DisplaySlots
+
+ a_Writer.EndCompound(); // Data
+}
+
+
+
+
+
+bool cScoreboardSerializer::LoadScoreboardFromNBT(const cParsedNBT & a_NBT)
+{
+ int Data = a_NBT.FindChildByName(0, "data");
+ if (Data < 0)
+ {
+ return false;
+ }
+
+ int Objectives = a_NBT.FindChildByName(Data, "Objectives");
+ if (Objectives < 0)
+ {
+ return false;
+ }
+
+ for (int Child = a_NBT.GetFirstChild(Objectives); Child >= 0; Child = a_NBT.GetNextSibling(Child))
+ {
+ AString CriteriaName, DisplayName, Name;
+
+ int CurrLine = a_NBT.FindChildByName(Child, "CriteriaName");
+ if (CurrLine >= 0)
+ {
+ CriteriaName = a_NBT.GetString(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "DisplayName");
+ if (CurrLine >= 0)
+ {
+ DisplayName = a_NBT.GetString(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "Name");
+ if (CurrLine >= 0)
+ {
+ Name = a_NBT.GetString(CurrLine);
+ }
+
+ cObjective::eType Type = cObjective::StringToType(CriteriaName);
+
+ m_ScoreBoard->RegisterObjective(Name, DisplayName, Type);
+ }
+
+ int PlayerScores = a_NBT.FindChildByName(Data, "PlayerScores");
+ if (PlayerScores < 0)
+ {
+ return false;
+ }
+
+ for (int Child = a_NBT.GetFirstChild(PlayerScores); Child >= 0; Child = a_NBT.GetNextSibling(Child))
+ {
+ AString Name, ObjectiveName;
+
+ cObjective::Score Score;
+
+ int CurrLine = a_NBT.FindChildByName(Child, "Score");
+ if (CurrLine >= 0)
+ {
+ Score = a_NBT.GetInt(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "Name");
+ if (CurrLine >= 0)
+ {
+ Name = a_NBT.GetString(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "Objective");
+ if (CurrLine >= 0)
+ {
+ ObjectiveName = a_NBT.GetString(CurrLine);
+ }
+
+ cObjective * Objective = m_ScoreBoard->GetObjective(ObjectiveName);
+
+ if (Objective)
+ {
+ Objective->SetScore(Name, Score);
+ }
+ }
+
+ int Teams = a_NBT.FindChildByName(Data, "Teams");
+ if (Teams < 0)
+ {
+ return false;
+ }
+
+ for (int Child = a_NBT.GetFirstChild(Teams); Child >= 0; Child = a_NBT.GetNextSibling(Child))
+ {
+ AString Name, DisplayName, Prefix, Suffix;
+
+ bool AllowsFriendlyFire, CanSeeFriendlyInvisible;
+
+ int CurrLine = a_NBT.FindChildByName(Child, "Name");
+ if (CurrLine >= 0)
+ {
+ Name = a_NBT.GetInt(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "DisplayName");
+ if (CurrLine >= 0)
+ {
+ DisplayName = a_NBT.GetInt(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "Prefix");
+ if (CurrLine >= 0)
+ {
+ Prefix = a_NBT.GetInt(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "Suffix");
+ if (CurrLine >= 0)
+ {
+ Suffix = a_NBT.GetInt(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "AllowFriendlyFire");
+ if (CurrLine >= 0)
+ {
+ AllowsFriendlyFire = (a_NBT.GetInt(CurrLine) != 0);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "SeeFriendlyInvisibles");
+ if (CurrLine >= 0)
+ {
+ CanSeeFriendlyInvisible = (a_NBT.GetInt(CurrLine) != 0);
+ }
+
+ cTeam * Team = m_ScoreBoard->RegisterTeam(Name, DisplayName, Prefix, Suffix);
+
+ Team->SetFriendlyFire(AllowsFriendlyFire);
+ Team->SetCanSeeFriendlyInvisible(CanSeeFriendlyInvisible);
+
+ int Players = a_NBT.FindChildByName(Child, "Players");
+ if (Players < 0)
+ {
+ continue;
+ }
+
+ for (int ChildB = a_NBT.GetFirstChild(Players); ChildB >= 0; ChildB = a_NBT.GetNextSibling(ChildB))
+ {
+ Team->AddPlayer(a_NBT.GetString(ChildB));
+ }
+ }
+
+ int DisplaySlots = a_NBT.FindChildByName(Data, "DisplaySlots");
+ if (DisplaySlots < 0)
+ {
+ return false;
+ }
+
+ int CurrLine = a_NBT.FindChildByName(DisplaySlots, "slot_0");
+ if (CurrLine >= 0)
+ {
+ AString Name = a_NBT.GetString(CurrLine);
+
+ m_ScoreBoard->SetDisplay(Name, cScoreboard::E_DISPLAY_SLOT_LIST);
+ }
+
+ CurrLine = a_NBT.FindChildByName(DisplaySlots, "slot_1");
+ if (CurrLine >= 0)
+ {
+ AString Name = a_NBT.GetString(CurrLine);
+
+ m_ScoreBoard->SetDisplay(Name, cScoreboard::E_DISPLAY_SLOT_SIDEBAR);
+ }
+
+ CurrLine = a_NBT.FindChildByName(DisplaySlots, "slot_2");
+ if (CurrLine >= 0)
+ {
+ AString Name = a_NBT.GetString(CurrLine);
+
+ m_ScoreBoard->SetDisplay(Name, cScoreboard::E_DISPLAY_SLOT_NAME);
+ }
+
+ return true;
+}
+
+
+
+
+
+
+
+
diff --git a/src/WorldStorage/ScoreboardSerializer.h b/src/WorldStorage/ScoreboardSerializer.h
new file mode 100644
index 000000000..048fa3ab4
--- /dev/null
+++ b/src/WorldStorage/ScoreboardSerializer.h
@@ -0,0 +1,52 @@
+
+// ScoreboardSerializer.h
+
+// Declares the cScoreboardSerializer class that is used for saving scoreboards into NBT format used by Anvil
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd:
+class cFastNBTWriter;
+class cParsedNBT;
+class cScoreboard;
+
+
+
+
+class cScoreboardSerializer
+{
+public:
+
+ cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard);
+
+ /// Try to load the scoreboard
+ bool Load(void);
+
+ /// Try to save the scoreboard
+ bool Save(void);
+
+
+private:
+
+ void SaveScoreboardToNBT(cFastNBTWriter & a_Writer);
+
+ bool LoadScoreboardFromNBT(const cParsedNBT & a_NBT);
+
+ cScoreboard* m_ScoreBoard;
+
+ AString m_Path;
+
+
+} ;
+
+
+
+
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index 8605930b6..a0f9136d8 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -7,6 +7,7 @@
#include "WSSAnvil.h"
#include "NBTChunkSerializer.h"
#include "FastNBT.h"
+#include "EnchantmentSerializer.h"
#include "zlib/zlib.h"
#include "../World.h"
#include "../BlockID.h"
@@ -15,6 +16,7 @@
#include "../StringCompression.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/DropperEntity.h"
#include "../BlockEntities/FurnaceEntity.h"
@@ -58,8 +60,9 @@ Since only the header is actually in the memory, this number can be high, but st
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWSSAnvil:
-cWSSAnvil::cWSSAnvil(cWorld * a_World) :
- super(a_World)
+cWSSAnvil::cWSSAnvil(cWorld * a_World, int a_CompressionFactor) :
+ super(a_World),
+ m_CompressionFactor(a_CompressionFactor)
{
// Create a level.dat file for mapping tools, if it doesn't already exist:
AString fnam;
@@ -272,7 +275,7 @@ bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data)
}
Writer.Finish();
- CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data);
+ CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data, m_CompressionFactor);
return true;
}
@@ -566,6 +569,10 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
{
LoadChestFromNBT(a_BlockEntities, a_NBT, Child);
}
+ else if (strncmp(a_NBT.GetData(sID), "Control", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadCommandBlockFromNBT(a_BlockEntities, a_NBT, Child);
+ }
else if (strncmp(a_NBT.GetData(sID), "Dropper", a_NBT.GetDataLength(sID)) == 0)
{
LoadDropperFromNBT(a_BlockEntities, a_NBT, Child);
@@ -604,12 +611,18 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx)
{
- int ID = a_NBT.FindChildByName(a_TagIdx, "id");
- if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short))
+ int Type = a_NBT.FindChildByName(a_TagIdx, "id");
+ if ((Type < 0) || (a_NBT.GetType(Type) != TAG_Short))
{
return false;
}
- a_Item.m_ItemType = (ENUM_ITEM_ID)(a_NBT.GetShort(ID));
+ a_Item.m_ItemType = a_NBT.GetShort(Type);
+ if (a_Item.m_ItemType < 0)
+ {
+ LOGD("Encountered an item with negative type (%d). Replacing with an empty item.", a_NBT.GetShort(Type));
+ a_Item.Empty();
+ return true;
+ }
int Damage = a_NBT.FindChildByName(a_TagIdx, "Damage");
if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short))
@@ -638,7 +651,7 @@ bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_
int EnchTag = a_NBT.FindChildByName(TagTag, EnchName);
if (EnchTag > 0)
{
- a_Item.m_Enchantments.ParseFromNBT(a_NBT, EnchTag);
+ EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, a_NBT, EnchTag);
}
return true;
@@ -914,6 +927,43 @@ void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParse
+void cWSSAnvil::LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ std::auto_ptr<cCommandBlockEntity> CmdBlock(new cCommandBlockEntity(x, y, z, m_World));
+
+ int currentLine = a_NBT.FindChildByName(a_TagIdx, "Command");
+ if (currentLine >= 0)
+ {
+ CmdBlock->SetCommand(a_NBT.GetString(currentLine));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "SuccessCount");
+ if (currentLine >= 0)
+ {
+ CmdBlock->SetResult(a_NBT.GetInt(currentLine));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "LastOutput");
+ if (currentLine >= 0)
+ {
+ CmdBlock->SetLastOutput(a_NBT.GetString(currentLine));
+ }
+
+ // TODO 2014-01-18 xdot: Figure out what TrackOutput is and parse it.
+
+ a_BlockEntities.push_back(CmdBlock.release());
+}
+
+
+
+
+
void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength)
{
if (strncmp(a_IDTag, "Boat", a_IDTagLength) == 0)
@@ -1150,7 +1200,7 @@ void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedN
void cWSSAnvil::LoadMinecartRFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
- std::auto_ptr<cEmptyMinecart> Minecart(new cEmptyMinecart(0, 0, 0));
+ std::auto_ptr<cRideableMinecart> Minecart(new cRideableMinecart(0, 0, 0, cItem(), 1)); // TODO: Load the block and the height
if (!LoadEntityBaseFromNBT(*Minecart.get(), a_NBT, a_TagIdx))
{
return;
@@ -1882,17 +1932,22 @@ 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.SetRotation(Rotation[0]);
- a_Entity.SetRoll (Rotation[1]);
+ a_Entity.SetYaw(Rotation[0]);
+ a_Entity.SetRoll(Rotation[1]);
return true;
}
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
index 0a7406267..5093ad083 100644
--- a/src/WorldStorage/WSSAnvil.h
+++ b/src/WorldStorage/WSSAnvil.h
@@ -47,7 +47,7 @@ class cWSSAnvil :
public:
- cWSSAnvil(cWorld * a_World);
+ cWSSAnvil(cWorld * a_World, int a_CompressionFactor);
virtual ~cWSSAnvil();
protected:
@@ -89,6 +89,8 @@ protected:
cCriticalSection m_CS;
cMCAFiles m_Files; // a MRU cache of MCA files
+
+ int m_CompressionFactor;
/// Gets chunk data from the correct file; locks file CS as needed
bool GetChunkData(const cChunkCoords & a_Chunk, AString & a_Data);
@@ -129,14 +131,15 @@ protected:
*/
void LoadItemGridFromNBT(cItemGrid & a_ItemGrid, const cParsedNBT & a_NBT, int a_ItemsTagIdx, int s_SlotOffset = 0);
- void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
- void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
- void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
+ void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength);
diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp
index e2556b96e..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:
{
@@ -193,7 +195,7 @@ cWSSCompact::cPAKFile * cWSSCompact::LoadPAKFile(const cChunkCoords & a_Chunk)
// Load it anew:
AString FileName;
Printf(FileName, "%s/X%i_Z%i.pak", m_World->GetName().c_str(), LayerX, LayerZ );
- cPAKFile * f = new cPAKFile(FileName, LayerX, LayerZ);
+ cPAKFile * f = new cPAKFile(FileName, LayerX, LayerZ, m_CompressionFactor);
if (f == NULL)
{
return NULL;
@@ -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[]
}
@@ -399,8 +389,9 @@ void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_En
return; \
}
-cWSSCompact::cPAKFile::cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ) :
+cWSSCompact::cPAKFile::cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ, int a_CompressionFactor) :
m_FileName(a_FileName),
+ m_CompressionFactor(a_CompressionFactor),
m_LayerX(a_LayerX),
m_LayerZ(a_LayerZ),
m_NumDirty(0),
@@ -648,7 +639,7 @@ void cWSSCompact::cPAKFile::UpdateChunk1To2()
// Re-compress data
AString CompressedData;
{
- int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData);
+ int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData,m_CompressionFactor);
if (errorcode != Z_OK)
{
LOGERROR("Error %d compressing data for chunk [%d, %d]",
@@ -786,7 +777,7 @@ void cWSSCompact::cPAKFile::UpdateChunk2To3()
// Re-compress data
AString CompressedData;
{
- int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData);
+ int errorcode = CompressString(Converted.data(), Converted.size(), CompressedData, m_CompressionFactor);
if (errorcode != Z_OK)
{
LOGERROR("Error %d compressing data for chunk [%d, %d]",
@@ -939,7 +930,7 @@ bool cWSSCompact::cPAKFile::SaveChunkToData(const cChunkCoords & a_Chunk, cWorld
// Compress the data:
AString CompressedData;
- int errorcode = CompressString(Data.data(), Data.size(), CompressedData);
+ int errorcode = CompressString(Data.data(), Data.size(), CompressedData, m_CompressionFactor);
if ( errorcode != Z_OK )
{
LOGERROR("Error %i compressing data for chunk [%d, %d, %d]", errorcode, a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ);
diff --git a/src/WorldStorage/WSSCompact.h b/src/WorldStorage/WSSCompact.h
index 3223a986e..64b8d7f31 100644
--- a/src/WorldStorage/WSSCompact.h
+++ b/src/WorldStorage/WSSCompact.h
@@ -53,7 +53,7 @@ class cWSSCompact :
public cWSSchema
{
public:
- cWSSCompact(cWorld * a_World) : cWSSchema(a_World) {}
+ cWSSCompact(cWorld * a_World, int a_CompressionFactor) : cWSSchema(a_World), m_CompressionFactor(a_CompressionFactor) {}
virtual ~cWSSCompact();
protected:
@@ -74,7 +74,7 @@ protected:
{
public:
- cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ);
+ cPAKFile(const AString & a_FileName, int a_LayerX, int a_LayerZ, int a_CompressionFactor);
~cPAKFile();
bool GetChunkData(const cChunkCoords & a_Chunk, int & a_UncompressedSize, AString & a_Data);
@@ -95,6 +95,7 @@ protected:
protected:
AString m_FileName;
+ int m_CompressionFactor;
int m_LayerX;
int m_LayerZ;
@@ -119,6 +120,8 @@ protected:
cCriticalSection m_CS;
cPAKFiles m_PAKFiles; // A MRU cache of PAK files
+ int m_CompressionFactor;
+
/// Loads the correct PAK file either from cache or from disk, manages the m_PAKFiles cache
cPAKFile * LoadPAKFile(const cChunkCoords & a_Chunk);
diff --git a/src/WorldStorage/WorldStorage.cpp b/src/WorldStorage/WorldStorage.cpp
index f290ec128..711c8612f 100644
--- a/src/WorldStorage/WorldStorage.cpp
+++ b/src/WorldStorage/WorldStorage.cpp
@@ -17,7 +17,6 @@
-
/// If a chunk with this Y coord is de-queued, it is a signal to emit the saved-all message (cWorldStorage::QueueSavedMessage())
#define CHUNK_Y_MESSAGE 2
@@ -63,19 +62,17 @@ cWorldStorage::~cWorldStorage()
{
delete *itr;
} // for itr - m_Schemas[]
- m_LoadQueue.clear();
- m_SaveQueue.clear();
}
-bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName)
+bool cWorldStorage::Start(cWorld * a_World, const AString & a_StorageSchemaName, int a_StorageCompressionFactor )
{
m_World = a_World;
m_StorageSchemaName = a_StorageSchemaName;
- InitSchemas();
+ InitSchemas(a_StorageCompressionFactor);
return super::Start();
}
@@ -98,18 +95,15 @@ void cWorldStorage::WaitForFinish(void)
LOG("Waiting for the world storage to finish saving");
{
- // Cancel all loading requests:
- cCSLock Lock(m_CSQueues);
- m_LoadQueue.clear();
+ m_LoadQueue.Clear();
}
// Wait for the saving to finish:
- WaitForQueuesEmpty();
+ WaitForSaveQueueEmpty();
// Wait for the thread to finish:
m_ShouldTerminate = true;
- m_Event.Set();
- m_evtRemoved.Set(); // Wake up anybody waiting in the WaitForQueuesEmpty() method
+ m_Event.Set(); // Wake up the thread if waiting
super::Wait();
LOG("World storage thread finished");
}
@@ -118,34 +112,36 @@ void cWorldStorage::WaitForFinish(void)
-void cWorldStorage::WaitForQueuesEmpty(void)
+void cWorldStorage::WaitForLoadQueueEmpty(void)
{
- cCSLock Lock(m_CSQueues);
- while (!m_ShouldTerminate && (!m_LoadQueue.empty() || !m_SaveQueue.empty()))
- {
- cCSUnlock Unlock(Lock);
- m_evtRemoved.Wait();
- }
+ m_LoadQueue.BlockTillEmpty();
+}
+
+
+
+
+
+void cWorldStorage::WaitForSaveQueueEmpty(void)
+{
+ m_SaveQueue.BlockTillEmpty();
}
-int cWorldStorage::GetLoadQueueLength(void)
+size_t cWorldStorage::GetLoadQueueLength(void)
{
- cCSLock Lock(m_CSQueues);
- return (int)m_LoadQueue.size();
+ return m_LoadQueue.Size();
}
-int cWorldStorage::GetSaveQueueLength(void)
+size_t cWorldStorage::GetSaveQueueLength(void)
{
- cCSLock Lock(m_CSQueues);
- return (int)m_SaveQueue.size();
+ return m_SaveQueue.Size();
}
@@ -154,21 +150,7 @@ int cWorldStorage::GetSaveQueueLength(void)
void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate)
{
- // Queues the chunk for loading; if not loaded, the chunk will be generated
- {
- cCSLock Lock(m_CSQueues);
-
- // Check if already in the queue:
- for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr)
- {
- if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkY == a_ChunkY) && (itr->m_ChunkZ == a_ChunkZ) && (itr->m_Generate == a_Generate))
- {
- return;
- }
- }
- m_LoadQueue.push_back(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, a_Generate));
- }
-
+ m_LoadQueue.EnqueueItemIfNotPresent(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ, a_Generate));
m_Event.Set();
}
@@ -178,11 +160,7 @@ void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, boo
void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
- {
- cCSLock Lock(m_CSQueues);
- m_SaveQueue.remove (cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); // Don't add twice
- m_SaveQueue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
- }
+ m_SaveQueue.EnqueueItemIfNotPresent(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
m_Event.Set();
}
@@ -192,11 +170,8 @@ void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
void cWorldStorage::QueueSavedMessage(void)
{
- // Pushes a special coord pair into the queue, signalizing a message instead:
- {
- cCSLock Lock(m_CSQueues);
- m_SaveQueue.push_back(cChunkCoords(0, CHUNK_Y_MESSAGE, 0));
- }
+ // Pushes a special coord pair into the queue, signalizing a message instead
+ m_SaveQueue.EnqueueItem(cChunkCoords(0, CHUNK_Y_MESSAGE, 0));
m_Event.Set();
}
@@ -206,18 +181,7 @@ void cWorldStorage::QueueSavedMessage(void)
void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
- cCSLock Lock(m_CSQueues);
- for (sChunkLoadQueue::iterator itr = m_LoadQueue.begin(); itr != m_LoadQueue.end(); ++itr)
- {
- if ((itr->m_ChunkX != a_ChunkX) || (itr->m_ChunkY != a_ChunkY) || (itr->m_ChunkZ != a_ChunkZ))
- {
- continue;
- }
- m_LoadQueue.erase(itr);
- Lock.Unlock();
- m_evtRemoved.Set();
- return;
- } // for itr - m_LoadQueue[]
+ m_LoadQueue.Remove(sChunkLoad(a_ChunkX, a_ChunkY, a_ChunkZ,true));
}
@@ -226,22 +190,18 @@ void cWorldStorage::UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
void cWorldStorage::UnqueueSave(const cChunkCoords & a_Chunk)
{
- {
- cCSLock Lock(m_CSQueues);
- m_SaveQueue.remove(a_Chunk);
- }
- m_evtRemoved.Set();
+ m_SaveQueue.Remove(a_Chunk);
}
-void cWorldStorage::InitSchemas(void)
+void cWorldStorage::InitSchemas(int a_StorageCompressionFactor)
{
// The first schema added is considered the default
- m_Schemas.push_back(new cWSSAnvil (m_World));
- m_Schemas.push_back(new cWSSCompact (m_World));
+ m_Schemas.push_back(new cWSSAnvil (m_World,a_StorageCompressionFactor));
+ m_Schemas.push_back(new cWSSCompact (m_World,a_StorageCompressionFactor));
m_Schemas.push_back(new cWSSForgetful(m_World));
// Add new schemas here
@@ -279,21 +239,19 @@ void cWorldStorage::Execute(void)
while (!m_ShouldTerminate)
{
m_Event.Wait();
-
// Process both queues until they are empty again:
- bool HasMore;
+ bool Success;
do
{
- HasMore = false;
+ Success = false;
if (m_ShouldTerminate)
{
return;
}
- HasMore = LoadOneChunk();
- HasMore = HasMore | SaveOneChunk();
- m_evtRemoved.Set();
- } while (HasMore);
+ Success = LoadOneChunk();
+ Success |= SaveOneChunk();
+ } while (Success);
}
}
@@ -304,19 +262,7 @@ void cWorldStorage::Execute(void)
bool cWorldStorage::LoadOneChunk(void)
{
sChunkLoad ToLoad(0, 0, 0, false);
- bool HasMore;
- bool ShouldLoad = false;
- {
- cCSLock Lock(m_CSQueues);
- if (!m_LoadQueue.empty())
- {
- ToLoad = m_LoadQueue.front();
- m_LoadQueue.pop_front();
- ShouldLoad = true;
- }
- HasMore = !m_LoadQueue.empty();
- }
-
+ bool ShouldLoad = m_LoadQueue.TryDequeueItem(ToLoad);
if (ShouldLoad && !LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ))
{
if (ToLoad.m_Generate)
@@ -330,7 +276,7 @@ bool cWorldStorage::LoadOneChunk(void)
// m_World->ChunkLoadFailed(ToLoad.m_ChunkX, ToLoad.m_ChunkY, ToLoad.m_ChunkZ);
}
}
- return HasMore;
+ return ShouldLoad;
}
@@ -339,33 +285,24 @@ bool cWorldStorage::LoadOneChunk(void)
bool cWorldStorage::SaveOneChunk(void)
{
- cChunkCoords Save(0, 0, 0);
- bool HasMore;
- bool ShouldSave = false;
- {
- cCSLock Lock(m_CSQueues);
- if (!m_SaveQueue.empty())
+ cChunkCoords ToSave(0, 0, 0);
+ bool ShouldSave = m_SaveQueue.TryDequeueItem(ToSave);
+ if(ShouldSave) {
+ if (ToSave.m_ChunkY == CHUNK_Y_MESSAGE)
{
- Save = m_SaveQueue.front();
- m_SaveQueue.pop_front();
- ShouldSave = true;
+ LOGINFO("Saved all chunks in world %s", m_World->GetName().c_str());
+ return ShouldSave;
}
- HasMore = !m_SaveQueue.empty();
- }
- if (Save.m_ChunkY == CHUNK_Y_MESSAGE)
- {
- LOGINFO("Saved all chunks in world %s", m_World->GetName().c_str());
- return HasMore;
- }
- if (ShouldSave && m_World->IsChunkValid(Save.m_ChunkX, Save.m_ChunkZ))
- {
- m_World->MarkChunkSaving(Save.m_ChunkX, Save.m_ChunkZ);
- if (m_SaveSchema->SaveChunk(Save))
+ if (ShouldSave && m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ))
{
- m_World->MarkChunkSaved(Save.m_ChunkX, Save.m_ChunkZ);
+ m_World->MarkChunkSaving(ToSave.m_ChunkX, ToSave.m_ChunkZ);
+ if (m_SaveSchema->SaveChunk(ToSave))
+ {
+ m_World->MarkChunkSaved(ToSave.m_ChunkX, ToSave.m_ChunkZ);
+ }
}
}
- return HasMore;
+ return ShouldSave;
}
diff --git a/src/WorldStorage/WorldStorage.h b/src/WorldStorage/WorldStorage.h
index 007d37571..bb189b6c9 100644
--- a/src/WorldStorage/WorldStorage.h
+++ b/src/WorldStorage/WorldStorage.h
@@ -16,6 +16,7 @@
#include "../ChunkDef.h"
#include "../OSSupport/IsThread.h"
+#include "../OSSupport/Queue.h"
@@ -24,6 +25,8 @@
// fwd:
class cWorld;
+typedef cQueue<cChunkCoords> cChunkCoordsQueue;
+
@@ -73,13 +76,14 @@ public:
void UnqueueLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
void UnqueueSave(const cChunkCoords & a_Chunk);
- bool Start(cWorld * a_World, const AString & a_StorageSchemaName); // Hide the cIsThread's Start() method, we need to provide args
+ bool Start(cWorld * a_World, const AString & a_StorageSchemaName, int a_StorageCompressionFactor); // Hide the cIsThread's Start() method, we need to provide args
void Stop(void); // Hide the cIsThread's Stop() method, we need to signal the event
void WaitForFinish(void);
- void WaitForQueuesEmpty(void);
+ void WaitForLoadQueueEmpty(void);
+ void WaitForSaveQueueEmpty(void);
- int GetLoadQueueLength(void);
- int GetSaveQueueLength(void);
+ size_t GetLoadQueueLength(void);
+ size_t GetSaveQueueLength(void);
protected:
@@ -91,20 +95,30 @@ protected:
bool m_Generate; // If true, the chunk will be generated if it cannot be loaded
sChunkLoad(int a_ChunkX, int a_ChunkY, int a_ChunkZ, bool a_Generate) : m_ChunkX(a_ChunkX), m_ChunkY(a_ChunkY), m_ChunkZ(a_ChunkZ), m_Generate(a_Generate) {}
+
+ bool operator==(const sChunkLoad other) const
+ {
+ return this->m_ChunkX == other.m_ChunkX &&
+ this->m_ChunkY == other.m_ChunkY &&
+ this->m_ChunkZ == other.m_ChunkZ;
+ }
} ;
-
- typedef std::list<sChunkLoad> sChunkLoadQueue;
+
+ struct FuncTable {
+ static void Delete(sChunkLoad) {};
+ static void Combine(sChunkLoad& a_orig, const sChunkLoad a_new)
+ {
+ a_orig.m_Generate |= a_new.m_Generate;
+ };
+ };
+
+ typedef cQueue<sChunkLoad,FuncTable> sChunkLoadQueue;
cWorld * m_World;
AString m_StorageSchemaName;
-
- // Both queues are locked by the same CS
- cCriticalSection m_CSQueues;
+
sChunkLoadQueue m_LoadQueue;
- cChunkCoordsList m_SaveQueue;
-
- cEvent m_Event; // Set when there's any addition to the queues
- cEvent m_evtRemoved; // Set when an item has been removed from the queue, either by the worker thread or the Unqueue methods
+ cChunkCoordsQueue m_SaveQueue;
/// All the storage schemas (all used for loading)
cWSSchemaList m_Schemas;
@@ -112,10 +126,12 @@ protected:
/// The one storage schema used for saving
cWSSchema * m_SaveSchema;
- void InitSchemas(void);
+ void InitSchemas(int a_StorageCompressionFactor);
virtual void Execute(void) override;
+ cEvent m_Event; // Set when there's any addition to the queues
+
/// Loads one chunk from the queue (if any queued); returns true if there are more chunks in the load queue
bool LoadOneChunk(void);
diff --git a/src/main.cpp b/src/main.cpp
index 81c6b41e4..c8cd2d4fe 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -19,6 +19,16 @@ bool g_SERVER_TERMINATED = false; // Set to true when the server terminates, so
+/** If set to true, the protocols will log each player's incoming (C->S) communication to a per-connection logfile */
+bool g_ShouldLogCommIn;
+
+/** If set to true, the protocols will log each player's outgoing (S->C) communication to a per-connection logfile */
+bool g_ShouldLogCommOut;
+
+
+
+
+
/// If defined, a thorough leak finder will be used (debug MSVC only); leaks will be output to the Output window
#define ENABLE_LEAK_FINDER
@@ -47,9 +57,27 @@ void NonCtrlHandler(int a_Signal)
case SIGSEGV:
{
std::signal(SIGSEGV, SIG_DFL);
- LOGWARN("Segmentation fault; MCServer has crashed :(");
+ LOGERROR(" D: | MCServer has encountered an error and needs to close");
+ LOGERROR("Details | SIGSEGV: Segmentation fault");
exit(EXIT_FAILURE);
}
+ case SIGABRT:
+ #ifdef SIGABRT_COMPAT
+ case SIGABRT_COMPAT:
+ #endif
+ {
+ std::signal(a_Signal, SIG_DFL);
+ LOGERROR(" D: | MCServer has encountered an error and needs to close");
+ LOGERROR("Details | SIGABRT: Server self-terminated due to an internal fault");
+ exit(EXIT_FAILURE);
+ break;
+ }
+ case SIGINT:
+ case SIGTERM:
+ {
+ std::signal(a_Signal, SIG_IGN); // Server is shutting down, wait for it...
+ break;
+ }
default: break;
}
}
@@ -200,12 +228,45 @@ int main( int argc, char **argv )
#ifndef _DEBUG
std::signal(SIGSEGV, NonCtrlHandler);
std::signal(SIGTERM, NonCtrlHandler);
- std::signal(SIGINT, NonCtrlHandler);
+ std::signal(SIGINT, NonCtrlHandler);
+ std::signal(SIGABRT, NonCtrlHandler);
+ #ifdef SIGABRT_COMPAT
+ std::signal(SIGABRT_COMPAT, NonCtrlHandler);
+ #endif // SIGABRT_COMPAT
#endif
// DEBUG: test the dumpfile creation:
// *((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_ShouldLogCommIn = true;
+ g_ShouldLogCommOut = true;
+ }
+ if (
+ (NoCaseCompare(argv[i], "/commlogin") == 0) ||
+ (NoCaseCompare(argv[i], "/comminlog") == 0) ||
+ (NoCaseCompare(argv[i], "/logcommin") == 0)
+ )
+ {
+ g_ShouldLogCommIn = true;
+ }
+ if (
+ (NoCaseCompare(argv[i], "/commlogout") == 0) ||
+ (NoCaseCompare(argv[i], "/commoutlog") == 0) ||
+ (NoCaseCompare(argv[i], "/logcommout") == 0)
+ )
+ {
+ g_ShouldLogCommOut = true;
+ }
+ }
+
#if !defined(ANDROID_NDK)
try
#endif