diff options
Diffstat (limited to '')
109 files changed, 2952 insertions, 1237 deletions
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 7e174e770..8f55eba07 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -1,4 +1,19 @@ +// AllToLua.pkg + +// Defines the bindings that are exported to Lua by the ToLua processor + +/* + Each file referenced in the $cfile is parsed by ToLua, and bindings are generated for classes and functions +marked with "// tolua_export", or between the "// tolua_begin" and "// tolua_end" markers. + + Note that if class D inherits from class B, then class B needs to be parsed before class D, otherwise the +inheritance doesn't work properly (#1789). +*/ + + + + $#include "../Globals.h" // Typedefs from Globals.h, so that we don't have to process that file: @@ -27,14 +42,38 @@ $cfile "WebPlugin.h" $cfile "LuaWindow.h" $cfile "../BlockID.h" -$cfile "../Mobs/MonsterTypes.h" $cfile "../BlockInfo.h" $cfile "../StringUtils.h" $cfile "../Defines.h" $cfile "../ChatColor.h" $cfile "../ClientHandle.h" -$cfile "../Entities/ArrowEntity.h" +$cfile "../Server.h" +$cfile "../World.h" +$cfile "../Inventory.h" +$cfile "../Enchantments.h" +$cfile "../Item.h" +$cfile "../ItemGrid.h" +$cfile "../WebAdmin.h" +$cfile "../Root.h" +$cfile "../Cuboid.h" +$cfile "../BoundingBox.h" +$cfile "../Tracer.h" +$cfile "../BlockArea.h" +$cfile "../Generating/ChunkDesc.h" +$cfile "../CraftingRecipes.h" +$cfile "../UI/Window.h" +$cfile "../CompositeChat.h" +$cfile "../Map.h" +$cfile "../MapManager.h" +$cfile "../Scoreboard.h" +$cfile "../Statistics.h" +$cfile "../Protocol/MojangAPI.h" + +// Entities: $cfile "../Entities/Entity.h" +$cfile "../Entities/Pawn.h" +$cfile "../Entities/ProjectileEntity.h" +$cfile "../Entities/ArrowEntity.h" $cfile "../Entities/EntityEffect.h" $cfile "../Entities/ExpBottleEntity.h" $cfile "../Entities/FireChargeEntity.h" @@ -43,25 +82,21 @@ $cfile "../Entities/Floater.h" $cfile "../Entities/GhastFireballEntity.h" $cfile "../Entities/HangingEntity.h" $cfile "../Entities/ItemFrame.h" -$cfile "../Entities/Pawn.h" $cfile "../Entities/Player.h" $cfile "../Entities/Painting.h" $cfile "../Entities/Pickup.h" -$cfile "../Entities/ProjectileEntity.h" $cfile "../Entities/SplashPotionEntity.h" $cfile "../Entities/ThrownEggEntity.h" $cfile "../Entities/ThrownEnderPearlEntity.h" $cfile "../Entities/ThrownSnowballEntity.h" $cfile "../Entities/TNTEntity.h" $cfile "../Entities/WitherSkullEntity.h" -$cfile "../Server.h" -$cfile "../World.h" -$cfile "../Inventory.h" -$cfile "../Enchantments.h" -$cfile "../Item.h" -$cfile "../ItemGrid.h" -$cfile "../BlockEntities/BeaconEntity.h" +$cfile "../Mobs/MonsterTypes.h" +$cfile "../Mobs/Monster.h" + +// Block entities: $cfile "../BlockEntities/BlockEntity.h" +$cfile "../BlockEntities/BeaconEntity.h" $cfile "../BlockEntities/BlockEntityWithItems.h" $cfile "../BlockEntities/ChestEntity.h" $cfile "../BlockEntities/CommandBlockEntity.h" @@ -76,30 +111,6 @@ $cfile "../BlockEntities/SignEntity.h" $cfile "../BlockEntities/MobHeadEntity.h" $cfile "../BlockEntities/MobSpawnerEntity.h" $cfile "../BlockEntities/FlowerPotEntity.h" -$cfile "../WebAdmin.h" -$cfile "../Root.h" -$cfile "../Cuboid.h" -$cfile "../BoundingBox.h" -$cfile "../Tracer.h" -$cfile "../BlockArea.h" -$cfile "../Generating/ChunkDesc.h" -$cfile "../CraftingRecipes.h" -$cfile "../UI/Window.h" -$cfile "../Mobs/Monster.h" -$cfile "../CompositeChat.h" -$cfile "../Map.h" -$cfile "../MapManager.h" -$cfile "../Scoreboard.h" -$cfile "../Statistics.h" -$cfile "../Protocol/MojangAPI.h" - - - - - -// Need to declare this class so that the usertype is properly registered in Bindings.cpp - -// it seems impossible to register a usertype in ManualBindings.cpp -class cLineBlockTracer; diff --git a/src/Bindings/LuaWindow.cpp b/src/Bindings/LuaWindow.cpp index 35730878d..d4014059b 100644 --- a/src/Bindings/LuaWindow.cpp +++ b/src/Bindings/LuaWindow.cpp @@ -167,6 +167,24 @@ void cLuaWindow::Destroy(void) +void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer& a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas Areas; + for (auto Area : m_SlotAreas) + { + if (Area != a_ClickedArea) + { + Areas.push_back(Area); + } + } + + super::DistributeStackToAreas(a_ItemStack, a_Player, Areas, a_ShouldApply, false); +} + + + + + void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) { if (a_ItemGrid != &m_Contents) diff --git a/src/Bindings/LuaWindow.h b/src/Bindings/LuaWindow.h index dab99a2e2..d4fc58660 100644 --- a/src/Bindings/LuaWindow.h +++ b/src/Bindings/LuaWindow.h @@ -35,8 +35,10 @@ This reference needs to be unreferenced in the Destroy() function. */ // tolua_begin class cLuaWindow : - public cWindow, - public cItemGrid::cListener + public cWindow + // tolua_end + , public cItemGrid::cListener + // tolua_begin { typedef cWindow super; @@ -84,6 +86,7 @@ protected: // cWindow overrides: virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; virtual void Destroy(void) override; + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; // cItemGrid::cListener overrides: virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override; diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index a6ae4869b..cdc01ae09 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -266,6 +266,24 @@ static int tolua_StringSplit(lua_State * tolua_S) +static int tolua_StringSplitWithQuotes(lua_State * tolua_S) +{ + cLuaState S(tolua_S); + + AString str; + AString delim; + + S.GetStackValues(1, str, delim); + + AStringVector Split = StringSplitWithQuotes(str, delim); + S.Push(Split); + return 1; +} + + + + + static int tolua_StringSplitAndTrim(lua_State * tolua_S) { cLuaState LuaState(tolua_S); @@ -620,11 +638,11 @@ static int tolua_DoWithID(lua_State* tolua_S) private: virtual bool Item(Ty2 * a_Item) override { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call - tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); // Push the item + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); // Push function to call + tolua_pushusertype(LuaState, a_Item, a_Item->GetClass()); // Push the item if (TableRef != LUA_REFNIL) { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); // Push the optional callbackdata param } int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); @@ -1281,23 +1299,27 @@ tolua_lerror: class cLuaWorldTask : - public cWorld::cTask + public cWorld::cTask, + public cPluginLua::cResettable { public: cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : - m_Plugin(a_Plugin), + cPluginLua::cResettable(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); + cCSLock Lock(m_CSPlugin); + if (m_Plugin != nullptr) + { + m_Plugin->Call(m_FnRef, &a_World); + } } } ; @@ -1336,7 +1358,9 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S) return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); } - self->QueueTask(make_unique<cLuaWorldTask>(*Plugin, FnRef)); + auto task = std::make_shared<cLuaWorldTask>(*Plugin, FnRef); + Plugin->AddResettable(task); + self->QueueTask(task); return 0; } @@ -1345,23 +1369,27 @@ static int tolua_cWorld_QueueTask(lua_State * tolua_S) class cLuaScheduledWorldTask : - public cWorld::cTask + public cWorld::cTask, + public cPluginLua::cResettable { public: cLuaScheduledWorldTask(cPluginLua & a_Plugin, int a_FnRef) : - m_Plugin(a_Plugin), + cPluginLua::cResettable(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); + cCSLock Lock(m_CSPlugin); + if (m_Plugin != nullptr) + { + m_Plugin->Call(m_FnRef, &a_World); + } } }; @@ -1407,7 +1435,9 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) int DelayTicks = (int)tolua_tonumber(tolua_S, 2, 0); - World->ScheduleTask(DelayTicks, new cLuaScheduledWorldTask(*Plugin, FnRef)); + auto task = std::make_shared<cLuaScheduledWorldTask>(*Plugin, FnRef); + Plugin->AddResettable(task); + World->ScheduleTask(DelayTicks, task); return 0; } @@ -2392,10 +2422,10 @@ static int tolua_md5HexString(lua_State * tolua_S) // Convert the md5 checksum to hex string: std::stringstream Output; - Output << std::hex << std::setw(2) << std::setfill('0'); + Output << std::hex << std::setfill('0'); for (size_t i = 0; i < ARRAYCOUNT(md5Output); i++) { - Output << static_cast<unsigned short>(md5Output[i]); // Need to cast to a number, otherwise a char is output + Output << std::setw(2) << static_cast<unsigned short>(md5Output[i]); // Need to cast to a number, otherwise a char is output } lua_pushlstring(tolua_S, Output.str().c_str(), Output.str().size()); return 1; @@ -2438,10 +2468,10 @@ static int tolua_sha1HexString(lua_State * tolua_S) // Convert the sha1 checksum to hex string: std::stringstream Output; - Output << std::hex << std::setw(2) << std::setfill('0'); + Output << std::hex << std::setfill('0'); for (size_t i = 0; i < ARRAYCOUNT(sha1Output); i++) { - Output << static_cast<unsigned short>(sha1Output[i]); // Need to cast to a number, otherwise a char is output + Output << std::setw(2) << static_cast<unsigned short>(sha1Output[i]); // Need to cast to a number, otherwise a char is output } lua_pushlstring(tolua_S, Output.str().c_str(), Output.str().size()); return 1; @@ -3659,19 +3689,22 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_cclass(tolua_S, "cCryptoHash", "cCryptoHash", "", nullptr); tolua_usertype(tolua_S, "cStringCompression"); tolua_cclass(tolua_S, "cStringCompression", "cStringCompression", "", nullptr); + tolua_usertype(tolua_S, "cLineBlockTracer"); + tolua_cclass(tolua_S, "cLineBlockTracer", "cLineBlockTracer", "", nullptr); // Globals: - tolua_function(tolua_S, "Clamp", tolua_Clamp); - tolua_function(tolua_S, "StringSplit", tolua_StringSplit); - tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim); - tolua_function(tolua_S, "LOG", tolua_LOG); - tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO); - tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN); - tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN); - tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); - tolua_function(tolua_S, "Base64Encode", tolua_Base64Encode); - tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode); - tolua_function(tolua_S, "md5", tolua_md5_obsolete); // OBSOLETE, use cCryptoHash.md5() instead + tolua_function(tolua_S, "Clamp", tolua_Clamp); + tolua_function(tolua_S, "StringSplit", tolua_StringSplit); + tolua_function(tolua_S, "StringSplitWithQuotes", tolua_StringSplitWithQuotes); + tolua_function(tolua_S, "StringSplitAndTrim", tolua_StringSplitAndTrim); + tolua_function(tolua_S, "LOG", tolua_LOG); + tolua_function(tolua_S, "LOGINFO", tolua_LOGINFO); + tolua_function(tolua_S, "LOGWARN", tolua_LOGWARN); + tolua_function(tolua_S, "LOGWARNING", tolua_LOGWARN); + tolua_function(tolua_S, "LOGERROR", tolua_LOGERROR); + tolua_function(tolua_S, "Base64Encode", tolua_Base64Encode); + tolua_function(tolua_S, "Base64Decode", tolua_Base64Decode); + tolua_function(tolua_S, "md5", tolua_md5_obsolete); // OBSOLETE, use cCryptoHash.md5() instead tolua_beginmodule(tolua_S, "cFile"); tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents); diff --git a/src/Bindings/ManualBindings_Network.cpp b/src/Bindings/ManualBindings_Network.cpp index a628eb9ca..628cda7f0 100644 --- a/src/Bindings/ManualBindings_Network.cpp +++ b/src/Bindings/ManualBindings_Network.cpp @@ -129,6 +129,30 @@ static int tolua_cNetwork_CreateUDPEndpoint(lua_State * L) +/** Binds cNetwork::EnumLocalIPAddresses */ +static int tolua_cNetwork_EnumLocalIPAddresses(lua_State * L) +{ + // Function signature: + // cNetwork:EnumLocalIPAddresses() -> {string, ...} + + cLuaState S(L); + if ( + !S.CheckParamUserTable(1, "cNetwork") || + !S.CheckParamEnd(2) + ) + { + return 0; + } + + // Push the enumerated addresses: + S.Push(cNetwork::EnumLocalIPAddresses()); + return 1; +} + + + + + /** Binds cNetwork::HostnameToIP */ static int tolua_cNetwork_HostnameToIP(lua_State * L) { @@ -903,11 +927,12 @@ void ManualBindings::BindNetwork(lua_State * tolua_S) // Fill in the functions (alpha-sorted): tolua_beginmodule(tolua_S, "cNetwork"); - tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect); - tolua_function(tolua_S, "CreateUDPEndpoint", tolua_cNetwork_CreateUDPEndpoint); - tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP); - tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname); - tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen); + tolua_function(tolua_S, "Connect", tolua_cNetwork_Connect); + tolua_function(tolua_S, "CreateUDPEndpoint", tolua_cNetwork_CreateUDPEndpoint); + tolua_function(tolua_S, "EnumLocalIPAddresses", tolua_cNetwork_EnumLocalIPAddresses); + tolua_function(tolua_S, "HostnameToIP", tolua_cNetwork_HostnameToIP); + tolua_function(tolua_S, "IPToHostname", tolua_cNetwork_IPToHostname); + tolua_function(tolua_S, "Listen", tolua_cNetwork_Listen); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cServerHandle"); diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 6210dbed4..3f9fa7655 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -57,6 +57,7 @@ public: virtual bool OnCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) = 0; virtual bool OnDisconnect (cClientHandle & a_Client, const AString & a_Reason) = 0; virtual bool OnEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) = 0; + virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0; virtual bool OnExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split) = 0; virtual bool OnExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; virtual bool OnExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) = 0; @@ -110,12 +111,12 @@ public: Command permissions have already been checked. Returns true if command handled successfully */ - virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) = 0; + virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) = 0; /** Handles the console command split into a_Split. Returns true if command handled successfully. Output is to be sent to the a_Output callback. */ - virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) = 0; + virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) = 0; /// All bound commands are to be removed, do any language-dependent cleanup here virtual void ClearCommands(void) {} diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 500913e76..0a2a8411d 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -6,10 +6,11 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #ifdef __APPLE__ -#define LUA_USE_MACOSX + #define LUA_USE_MACOSX #else -#define LUA_USE_POSIX + #define LUA_USE_POSIX #endif + #include "PluginLua.h" #include "../CommandOutput.h" #include "PluginManager.h" @@ -52,24 +53,40 @@ cPluginLua::~cPluginLua() void cPluginLua::Close(void) { - if (m_LuaState.IsValid()) - { - // Release all the references in the hook map: - for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH) - { - for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR) - { - delete *itrR; - } // for itrR - itrH->second[] - } // for itrH - m_HookMap[] - m_HookMap.clear(); - - m_LuaState.Close(); - } - else + cCSLock Lock(m_CriticalSection); + + // If already closed, bail out: + if (!m_LuaState.IsValid()) { + ASSERT(m_Resettables.empty()); ASSERT(m_HookMap.empty()); + return; } + + // Notify and remove all m_Resettables (unlock the m_CriticalSection while resetting them): + cResettablePtrs resettables; + std::swap(m_Resettables, resettables); + { + cCSUnlock Unlock(Lock); + for (auto resettable: resettables) + { + resettable->Reset(); + } + m_Resettables.clear(); + } // cCSUnlock (m_CriticalSection) + + // Release all the references in the hook map: + for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH) + { + for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR) + { + delete *itrR; + } // for itrR - itrH->second[] + } // for itrH - m_HookMap[] + m_HookMap.clear(); + + // Close the Lua engine: + m_LuaState.Close(); } @@ -857,6 +874,26 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi +bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) +{ + cCSLock Lock(m_CriticalSection); + bool res = false; + cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_TELEPORT]; + for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + { + m_LuaState.Call((int)(**itr), &a_Entity, a_OldPosition, a_NewPosition, cLuaState::Return, res); + if (res) + { + return true; + } + } + return false; +} + + + + + bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { cCSLock Lock(m_CriticalSection); @@ -1445,7 +1482,7 @@ bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, s -bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) +bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) { ASSERT(!a_Split.empty()); CommandMap::iterator cmd = m_Commands.find(a_Split[0]); @@ -1457,7 +1494,7 @@ bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player cCSLock Lock(m_CriticalSection); bool res = false; - m_LuaState.Call(cmd->second, a_Split, &a_Player, cLuaState::Return, res); + m_LuaState.Call(cmd->second, a_Split, &a_Player, a_FullCommand, cLuaState::Return, res); return res; } @@ -1465,7 +1502,7 @@ bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player -bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) +bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) { ASSERT(!a_Split.empty()); CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); @@ -1480,7 +1517,7 @@ bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOut cCSLock Lock(m_CriticalSection); bool res = false; AString str; - m_LuaState.Call(cmd->second, a_Split, cLuaState::Return, res, str); + m_LuaState.Call(cmd->second, a_Split, a_FullCommand, cLuaState::Return, res, str); if (res && !str.empty()) { a_Output.Out(str); @@ -1577,6 +1614,7 @@ const char * cPluginLua::GetHookFnName(int a_HookType) case cPluginManager::HOOK_DISCONNECT: return "OnDisconnect"; case cPluginManager::HOOK_PLAYER_ANIMATION: return "OnPlayerAnimation"; case cPluginManager::HOOK_ENTITY_ADD_EFFECT: return "OnEntityAddEffect"; + case cPluginManager::HOOK_ENTITY_TELEPORT: return "OnEntityTeleport"; case cPluginManager::HOOK_EXECUTE_COMMAND: return "OnExecuteCommand"; case cPluginManager::HOOK_HANDSHAKE: return "OnHandshake"; case cPluginManager::HOOK_KILLING: return "OnKilling"; @@ -1688,6 +1726,16 @@ int cPluginLua::CallFunctionFromForeignState( +void cPluginLua::AddResettable(cPluginLua::cResettablePtr a_Resettable) +{ + cCSLock Lock(m_CriticalSection); + m_Resettables.push_back(a_Resettable); +} + + + + + AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request) { cCSLock Lock(m_CriticalSection); @@ -1805,3 +1853,25 @@ void cPluginLua::CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int + +//////////////////////////////////////////////////////////////////////////////// +// cPluginLua::cResettable: + +cPluginLua::cResettable::cResettable(cPluginLua & a_Plugin): + m_Plugin(&a_Plugin) +{ +} + + + + + +void cPluginLua::cResettable::Reset(void) +{ + cCSLock Lock(m_CSPlugin); + m_Plugin = nullptr; +} + + + + diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index f443f5fc0..c14b02687 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -59,6 +59,37 @@ public: /** RAII lock for m_Plugin.m_CriticalSection */ cCSLock m_Lock; } ; + + + + /** A base class that represents something related to a plugin + The plugin can reset this class so that the instance can continue to exist but will not engage the (possibly non-existent) plugin anymore. + This is used for scheduled tasks etc., so that they can be queued and reset when the plugin is terminated, without removing them from the queue. */ + class cResettable + { + public: + /** Creates a new instance bound to the specified plugin. */ + cResettable(cPluginLua & a_Plugin); + + // Force a virtual destructor in descendants: + virtual ~cResettable() {} + + /** Resets the plugin instance stored within. + The instance will continue to exist, but should not call into the plugin anymore. */ + virtual void Reset(void); + + protected: + /** The plugin that this instance references. + If nullptr, the plugin has already unloaded and the instance should bail out any processing. + Protected against multithreaded access by m_CSPlugin. */ + cPluginLua * m_Plugin; + + /** The mutex protecting m_Plugin against multithreaded access. */ + cCriticalSection m_CSPlugin; + }; + + typedef SharedPtr<cResettable> cResettablePtr; + typedef std::vector<cResettablePtr> cResettablePtrs; cPluginLua(const AString & a_PluginDirectory); @@ -106,6 +137,7 @@ public: virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override; virtual bool OnPlayerShooting (cPlayer & a_Player) override; virtual bool OnPlayerSpawned (cPlayer & a_Player) override; + virtual bool OnEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override; virtual bool OnPlayerTossingItem (cPlayer & a_Player) override; virtual bool OnPlayerUsedBlock (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 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; @@ -130,9 +162,9 @@ public: virtual bool OnWorldStarted (cWorld & a_World) override; virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) override; - virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player) override; + virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) override; - virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) override; + virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) override; virtual void ClearCommands(void) override; @@ -186,42 +218,16 @@ public: int a_ParamEnd ); - // The following templates allow calls to arbitrary Lua functions residing in the plugin: - - /** 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 */ - template <typename FnT, typename ArgT0> bool Call(FnT a_Fn, ArgT0 a_Arg0) + /** Call a Lua function residing in the plugin. */ + template <typename FnT, typename... Args> + bool Call(FnT a_Fn, Args && ... a_Args) { cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn, a_Arg0); + return m_LuaState.Call(a_Fn, a_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 */ - 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 */ - 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); - return m_LuaState.Call(a_Fn, a_Arg0, a_Arg1, a_Arg2, a_Arg3); - } + /** Adds the specified cResettable instance to m_Resettables, so that it is notified when the plugin is being closed. */ + void AddResettable(cResettablePtr a_Resettable); protected: /** Maps command name into Lua function reference */ @@ -233,15 +239,27 @@ protected: /** Maps hook types into arrays of Lua function references to call for each hook type */ typedef std::map<int, cLuaRefs> cHookMap; + + /** The mutex protecting m_LuaState and each of the m_Resettables[] against multithreaded use. */ cCriticalSection m_CriticalSection; + + /** The plugin's Lua state. */ cLuaState m_LuaState; + /** Objects that need notification when the plugin is about to be unloaded. */ + cResettablePtrs m_Resettables; + + /** In-game commands that the plugin has registered. */ CommandMap m_Commands; + + /** Console commands that the plugin has registered. */ CommandMap m_ConsoleCommands; + /** Hooks that the plugin has registered. */ cHookMap m_HookMap; - /** Releases all Lua references and closes the LuaState */ + + /** Releases all Lua references, notifies and removes all m_Resettables[] and closes the m_LuaState. */ void Close(void); } ; // tolua_export diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 9d86c64a2..8935f7dd3 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -505,6 +505,24 @@ bool cPluginManager::CallHookEntityAddEffect(cEntity & a_Entity, int a_EffectTyp +bool cPluginManager::CallHookEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) +{ + FIND_HOOK(HOOK_ENTITY_TELEPORT); + VERIFY_HOOK; + + for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr) + { + if ((*itr)->OnEntityTeleport(a_Entity, a_OldPosition, a_NewPosition)) + { + return true; + } + } + return false; +} + + + + bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split) { FIND_HOOK(HOOK_EXECUTE_COMMAND); @@ -1447,7 +1465,7 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player, ASSERT(cmd->second.m_Plugin != nullptr); - if (!cmd->second.m_Plugin->HandleCommand(Split, a_Player)) + if (!cmd->second.m_Plugin->HandleCommand(Split, a_Player, a_Command)) { return crError; } @@ -1750,7 +1768,7 @@ bool cPluginManager::IsConsoleCommandBound(const AString & a_Command) -bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) +bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command) { if (a_Split.empty()) { @@ -1777,7 +1795,7 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma return false; } - return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output); + return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output, a_Command); } diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index 97e91c1df..4efcbb6f3 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -109,6 +109,7 @@ public: HOOK_PLAYER_RIGHT_CLICKING_ENTITY, HOOK_PLAYER_SHOOTING, HOOK_PLAYER_SPAWNED, + HOOK_ENTITY_TELEPORT, HOOK_PLAYER_TOSSING_ITEM, HOOK_PLAYER_USED_BLOCK, HOOK_PLAYER_USED_ITEM, @@ -190,6 +191,7 @@ public: bool CallHookCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe); bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason); bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier); + bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition); bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split); // If a_Player == nullptr, it is a console cmd bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData); @@ -282,7 +284,7 @@ public: 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 */ - bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output); + bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command); /** Appends all commands beginning with a_Text (case-insensitive) into a_Results. If a_Player is not nullptr, only commands for which the player has permissions are added. diff --git a/src/BlockEntities/BeaconEntity.cpp b/src/BlockEntities/BeaconEntity.cpp index 37ce7a8ab..fb3940ce9 100644 --- a/src/BlockEntities/BeaconEntity.cpp +++ b/src/BlockEntities/BeaconEntity.cpp @@ -4,6 +4,7 @@ #include "BeaconEntity.h" #include "../BlockArea.h" #include "../Entities/Player.h" +#include "../UI/BeaconWindow.h" @@ -289,7 +290,7 @@ void cBeaconEntity::UsedBy(cPlayer * a_Player) OpenWindow(new cBeaconWindow(m_PosX, m_PosY, m_PosZ, this)); Window = GetWindow(); } - + if (Window != nullptr) { // if (a_Player->GetWindow() != Window) diff --git a/src/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h index 2c2ced1cb..740dbca51 100644 --- a/src/BlockEntities/BlockEntityWithItems.h +++ b/src/BlockEntities/BlockEntityWithItems.h @@ -21,7 +21,9 @@ // tolua_begin class cBlockEntityWithItems : public cBlockEntity, + // tolua_end public cItemGrid::cListener, + // tolua_begin public cBlockEntityWindowOwner { typedef cBlockEntity super; diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp index 0cd9c66e0..3821f9aab 100644 --- a/src/BlockEntities/ChestEntity.cpp +++ b/src/BlockEntities/ChestEntity.cpp @@ -4,7 +4,7 @@ #include "ChestEntity.h" #include "../Item.h" #include "../Entities/Player.h" -#include "../UI/Window.h" +#include "../UI/ChestWindow.h" diff --git a/src/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp index 5e98506f1..039f5d360 100644 --- a/src/BlockEntities/DropSpenserEntity.cpp +++ b/src/BlockEntities/DropSpenserEntity.cpp @@ -8,6 +8,7 @@ #include "DropSpenserEntity.h" #include "../Entities/Player.h" #include "../Chunk.h" +#include "../UI/DropSpenserWindow.h" diff --git a/src/BlockEntities/EnderChestEntity.cpp b/src/BlockEntities/EnderChestEntity.cpp index e18490a1e..ab5c5a2de 100644 --- a/src/BlockEntities/EnderChestEntity.cpp +++ b/src/BlockEntities/EnderChestEntity.cpp @@ -4,7 +4,7 @@ #include "EnderChestEntity.h" #include "../Item.h" #include "../Entities/Player.h" -#include "../UI/Window.h" +#include "../UI/EnderChestWindow.h" diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp index cc5a00af7..2621b560b 100644 --- a/src/BlockEntities/FurnaceEntity.cpp +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -2,7 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "FurnaceEntity.h" -#include "../UI/Window.h" +#include "../UI/FurnaceWindow.h" #include "../Entities/Player.h" #include "../Root.h" #include "../Chunk.h" diff --git a/src/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index c2d6cabbb..bfd4b8322 100644 --- a/src/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp @@ -9,6 +9,7 @@ #include "../Entities/Player.h" #include "../Entities/Pickup.h" #include "../Bindings/PluginManager.h" +#include "../UI/HopperWindow.h" #include "ChestEntity.h" #include "FurnaceEntity.h" diff --git a/src/Blocks/BlockAnvil.h b/src/Blocks/BlockAnvil.h index 20514580e..abfa0f782 100644 --- a/src/Blocks/BlockAnvil.h +++ b/src/Blocks/BlockAnvil.h @@ -4,6 +4,7 @@ #include "BlockHandler.h" #include "../World.h" #include "../Entities/Player.h" +#include "../UI/AnvilWindow.h" diff --git a/src/Blocks/BlockBed.cpp b/src/Blocks/BlockBed.cpp index 57b9855d0..e56f4bfe0 100644 --- a/src/Blocks/BlockBed.cpp +++ b/src/Blocks/BlockBed.cpp @@ -1,14 +1,9 @@ #include "Globals.h" #include "BlockBed.h" - - - #include "BroadcastInterface.h" -#include "ChunkInterface.h" #include "Entities/../World.h" #include "Entities/Player.h" -#include "WorldInterface.h" @@ -64,21 +59,22 @@ class cPlayerBedStateUnsetter : public cPlayerListCallback { public: - cPlayerBedStateUnsetter(Vector3i a_Position, cWorldInterface & a_WorldInterface) : - m_Position(a_Position), m_WorldInterface(a_WorldInterface) + cPlayerBedStateUnsetter(Vector3i a_Position, cChunkInterface & a_ChunkInterface) : + m_Position(a_Position), + m_ChunkInterface(a_ChunkInterface) { } virtual bool Item(cPlayer * a_Player) override { + cBlockBedHandler::SetBedOccupationState(m_ChunkInterface, a_Player->GetLastBedPos(), false); a_Player->SetIsInBed(false); - m_WorldInterface.GetBroadcastManager().BroadcastEntityAnimation(*a_Player, 2); return false; } private: Vector3i m_Position; - cWorldInterface & m_WorldInterface; + cChunkInterface & m_ChunkInterface; }; @@ -97,7 +93,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface if (a_WorldInterface.GetTimeOfDay() > 13000) { NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ); - if (Meta & 0x4) + if ((Meta & 0x4) == 0x4) { a_Player->SendMessageFailure("This bed is occupied"); } @@ -105,7 +101,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface { Vector3i PillowDirection(0, 0, 0); - if (Meta & 0x8) + if ((Meta & 0x8) == 0x8) { // Is pillow a_WorldInterface.GetBroadcastManager().BroadcastUseBed(*a_Player, a_BlockX, a_BlockY, a_BlockZ); @@ -122,15 +118,15 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface } } - a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta | 0x4); // Where 0x4 = occupied bit - a_Player->SetIsInBed(true); a_Player->SetBedPos(Vector3i(a_BlockX, a_BlockY, a_BlockZ)); + SetBedOccupationState(a_ChunkInterface, a_Player->GetLastBedPos(), true); + a_Player->SetIsInBed(true); a_Player->SendMessageSuccess("Home position set successfully"); cTimeFastForwardTester Tester; if (a_WorldInterface.ForEachPlayer(Tester)) { - cPlayerBedStateUnsetter Unsetter(Vector3i(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z), a_WorldInterface); + cPlayerBedStateUnsetter Unsetter(Vector3i(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z), a_ChunkInterface); a_WorldInterface.ForEachPlayer(Unsetter); a_WorldInterface.SetTimeOfDay(0); a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x0b); // Clear the "occupied" bit of the bed's block diff --git a/src/Blocks/BlockBed.h b/src/Blocks/BlockBed.h index 5b746110a..46f361686 100644 --- a/src/Blocks/BlockBed.h +++ b/src/Blocks/BlockBed.h @@ -4,9 +4,9 @@ #include "BlockHandler.h" #include "MetaRotator.h" #include "Item.h" +#include "ChunkInterface.h" -class cChunkInterface; class cPlayer; class cWorldInterface; @@ -21,17 +21,14 @@ public: : cMetaRotator<cBlockHandler, 0x3, 0x02, 0x03, 0x00, 0x01, true>(a_BlockType) { } - - + virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override; virtual void OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; - virtual bool IsUseable(void) override { return true; } - virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override { @@ -39,14 +36,12 @@ public: a_Pickups.push_back(cItem(E_ITEM_BED, 1, 0)); } - virtual bool CanDirtGrowGrass(NIBBLETYPE a_Meta) override { return true; } - // Bed specific helper functions static NIBBLETYPE RotationToMetaData(double a_Rotation) { @@ -61,7 +56,6 @@ public: return ((char)a_Rotation + 2) % 4; } - static Vector3i MetaDataToDirection(NIBBLETYPE a_MetaData) { switch (a_MetaData) @@ -73,6 +67,21 @@ public: } return Vector3i(); } + + static void SetBedOccupationState(cChunkInterface & a_ChunkInterface, const Vector3i & a_BedPosition, bool a_IsOccupied) + { + auto Meta = a_ChunkInterface.GetBlockMeta(a_BedPosition.x, a_BedPosition.y, a_BedPosition.z); + if (a_IsOccupied) + { + Meta |= 0x04; // Where 0x4 = occupied bit + } + else + { + Meta &= 0x0b; // Clear the "occupied" bit of the bed's block + } + + a_ChunkInterface.SetBlockMeta(a_BedPosition.x, a_BedPosition.y, a_BedPosition.z, Meta); + } } ; diff --git a/src/Blocks/BlockDirt.h b/src/Blocks/BlockDirt.h index aae6719e2..cc0d845e4 100644 --- a/src/Blocks/BlockDirt.h +++ b/src/Blocks/BlockDirt.h @@ -52,14 +52,26 @@ public: return; } } - + + // Make sure that there is enough light at the source block to spread + if (!a_Chunk.GetWorld()->IsChunkLighted(a_Chunk.GetPosX(), a_Chunk.GetPosZ())) + { + a_Chunk.GetWorld()->QueueLightChunk(a_Chunk.GetPosX(), a_Chunk.GetPosZ()); + return; + } + else if (std::max(a_Chunk.GetBlockLight(a_RelX, a_RelY + 1, a_RelZ), a_Chunk.GetTimeAlteredLight(a_Chunk.GetSkyLight(a_RelX, a_RelY + 1, a_RelZ))) < 9) + { + // Source block is not bright enough to spread + return; + } + // Grass spreads to adjacent dirt blocks: cFastRandom rand; for (int i = 0; i < 2; i++) // Pick two blocks to grow to { - int OfsX = rand.NextInt(3, a_RelX) - 1; // [-1 .. 1] - int OfsY = rand.NextInt(5, a_RelY) - 3; // [-3 .. 1] - int OfsZ = rand.NextInt(3, a_RelZ) - 1; // [-1 .. 1] + int OfsX = rand.NextInt(3) - 1; // [-1 .. 1] + int OfsY = rand.NextInt(5) - 3; // [-3 .. 1] + int OfsZ = rand.NextInt(3) - 1; // [-1 .. 1] BLOCKTYPE DestBlock; NIBBLETYPE DestMeta; diff --git a/src/Blocks/BlockEnchantmentTable.h b/src/Blocks/BlockEnchantmentTable.h index 81d2cb9a0..40001f356 100644 --- a/src/Blocks/BlockEnchantmentTable.h +++ b/src/Blocks/BlockEnchantmentTable.h @@ -2,7 +2,7 @@ #pragma once #include "BlockHandler.h" -#include "../UI/Window.h" +#include "../UI/EnchantingWindow.h" #include "../Entities/Player.h" diff --git a/src/Blocks/BlockPiston.cpp b/src/Blocks/BlockPiston.cpp index 8d245cabe..d9f4913d8 100644 --- a/src/Blocks/BlockPiston.cpp +++ b/src/Blocks/BlockPiston.cpp @@ -61,6 +61,16 @@ void cBlockPistonHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorld +void cBlockPistonHandler::ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) +{ + // Returning Piston Item without Direction-Metavalue + a_Pickups.push_back(cItem(m_BlockType, 1)); +} + + + + + bool cBlockPistonHandler::GetPlacementBlockTypeMeta( cChunkInterface & a_ChunkInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, @@ -169,7 +179,7 @@ void cBlockPistonHandler::ExtendPiston(int a_BlockX, int a_BlockY, int a_BlockZ, a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, pistonBlock, pistonMeta | 0x8); a_World->SetBlock(extx, exty, extz, E_BLOCK_PISTON_EXTENSION, pistonMeta | (IsSticky(pistonBlock) ? 8 : 0), false); - a_World->ScheduleTask(PISTON_TICK_DELAY, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks)); + a_World->ScheduleTask(PISTON_TICK_DELAY, std::make_shared<cWorld::cTaskSendBlockToAllPlayers>(ScheduledBlocks)); } @@ -219,7 +229,7 @@ void cBlockPistonHandler::RetractPiston(int a_BlockX, int a_BlockY, int a_BlockZ std::vector<Vector3i> ScheduledBlocks; ScheduledBlocks.push_back(Vector3i(a_BlockX, a_BlockY, a_BlockZ)); ScheduledBlocks.push_back(Vector3i(tempx, tempy, tempz)); - a_World->ScheduleTask(PISTON_TICK_DELAY + 1, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks)); + a_World->ScheduleTask(PISTON_TICK_DELAY + 1, std::make_shared<cWorld::cTaskSendBlockToAllPlayers>(ScheduledBlocks)); return; } } @@ -229,7 +239,7 @@ void cBlockPistonHandler::RetractPiston(int a_BlockX, int a_BlockY, int a_BlockZ std::vector<Vector3i> ScheduledBlocks; ScheduledBlocks.push_back(Vector3i(a_BlockX, a_BlockY, a_BlockZ)); - a_World->ScheduleTask(PISTON_TICK_DELAY + 1, new cWorld::cTaskSendBlockToAllPlayers(ScheduledBlocks)); + a_World->ScheduleTask(PISTON_TICK_DELAY + 1, std::make_shared<cWorld::cTaskSendBlockToAllPlayers>(ScheduledBlocks)); } diff --git a/src/Blocks/BlockPiston.h b/src/Blocks/BlockPiston.h index f868f4d8e..9a891025a 100644 --- a/src/Blocks/BlockPiston.h +++ b/src/Blocks/BlockPiston.h @@ -15,6 +15,8 @@ public: virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override; + virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override; + virtual bool GetPlacementBlockTypeMeta( cChunkInterface & a_ChunkInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, diff --git a/src/Blocks/BlockWorkbench.h b/src/Blocks/BlockWorkbench.h index 699badaf2..e40e15606 100644 --- a/src/Blocks/BlockWorkbench.h +++ b/src/Blocks/BlockWorkbench.h @@ -2,7 +2,7 @@ #pragma once #include "BlockHandler.h" -#include "../UI/Window.h" +#include "../UI/CraftingWindow.h" #include "../Entities/Player.h" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c39f5f6e6..b91c4f65a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -9,7 +9,7 @@ include_directories (SYSTEM "${CMAKE_CURRENT_SOURCE_DIR}/../lib/libevent/include set(FOLDERS OSSupport HTTPServer Items Blocks Protocol Generating PolarSSL++ Bindings - WorldStorage Mobs Entities Simulator UI BlockEntities Generating/Prefabs + WorldStorage Mobs Entities Simulator BlockEntities UI Generating/Prefabs Noise ) @@ -318,7 +318,7 @@ if (NOT MSVC) target_link_libraries(${EXECUTABLE} OSSupport HTTPServer Bindings Items Blocks Noise Protocol Generating Generating_Prefabs WorldStorage - Mobs Entities Simulator UI BlockEntities PolarSSL++ + Mobs Entities Simulator BlockEntities UI PolarSSL++ ) endif () if (WIN32) diff --git a/src/Chunk.cpp b/src/Chunk.cpp index e05fa4a99..00ac1fdb1 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -776,10 +776,22 @@ void cChunk::BroadcastPendingBlockChanges(void) { return; } - - for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + + if (m_PendingSendBlocks.size() >= 10240) + { + // Resend the full chunk + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + { + m_World->ForceSendChunkTo(m_PosX, m_PosZ, cChunkSender::E_CHUNK_PRIORITY_MEDIUM, (*itr)); + } + } + else { - (*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks); + // Only send block changes + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(), end = m_LoadedByClient.end(); itr != end; ++itr) + { + (*itr)->SendBlockChanges(m_PosX, m_PosZ, m_PendingSendBlocks); + } } m_PendingSendBlocks.clear(); } diff --git a/src/ChunkData.cpp b/src/ChunkData.cpp index 57b27c5e0..80f40d39e 100644 --- a/src/ChunkData.cpp +++ b/src/ChunkData.cpp @@ -361,7 +361,7 @@ void cChunkData::CopyBlockTypes(BLOCKTYPE * a_Dest, size_t a_Idx, size_t a_Lengt } else { - memset(&a_Dest[(i * SectionBlockCount) - a_Idx], 0, sizeof(BLOCKTYPE) * ToCopy); + memset(&a_Dest[(i * SectionBlockCount) + StartPos - a_Idx], 0, sizeof(BLOCKTYPE) * ToCopy); } } } diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp index 2dd04f801..b9d4ed55b 100644 --- a/src/ChunkMap.cpp +++ b/src/ChunkMap.cpp @@ -1322,10 +1322,7 @@ void cChunkMap::SetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ, NIBBLETYP void cChunkMap::SetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, bool a_SendToClients) { cChunkInterface ChunkInterface(this); - if (a_BlockType == E_BLOCK_AIR) - { - BlockHandler(GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnDestroyed(ChunkInterface, *m_World, a_BlockX, a_BlockY, a_BlockZ); - } + BlockHandler(GetBlock(a_BlockX, a_BlockY, a_BlockZ))->OnDestroyed(ChunkInterface, *m_World, a_BlockX, a_BlockY, a_BlockZ); int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ; cChunkDef::AbsoluteToRelative( X, Y, Z, ChunkX, ChunkZ); diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 2e0e86653..ec10ac521 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -12,12 +12,16 @@ #include "BlockEntities/CommandBlockEntity.h" #include "BlockEntities/SignEntity.h" #include "UI/Window.h" +#include "UI/AnvilWindow.h" +#include "UI/BeaconWindow.h" +#include "UI/EnchantingWindow.h" #include "Item.h" #include "Mobs/Monster.h" #include "ChatColor.h" #include "Items/ItemHandler.h" #include "Blocks/BlockHandler.h" #include "Blocks/BlockSlab.h" +#include "Blocks/BlockBed.h" #include "Blocks/ChunkInterface.h" #include "Root.h" @@ -1498,30 +1502,6 @@ void cClientHandle::HandleAnimation(int a_Animation) return; } - // Because the animation ID sent to servers by clients are different to those sent back, we need this - switch (a_Animation) - { - case 0: // No animation - wiki.vg doesn't say that client has something specific for it, so I suppose it will just become -1 - case 1: - case 2: - case 3: - { - a_Animation--; // Offset by -1 - break; - } - case 5: - case 6: - case 7: - { - a_Animation -= 2; // Offset by -2 - break; - } - default: // Anything else is the same - { - break; - } - } - m_Player->GetWorld()->BroadcastEntityAnimation(*m_Player, a_Animation, this); } @@ -1763,7 +1743,9 @@ void cClientHandle::HandleEntityLeaveBed(int a_EntityID) return; } - m_Player->GetWorld()->BroadcastEntityAnimation(*m_Player, 2); + cChunkInterface Interface(GetPlayer()->GetWorld()->GetChunkMap()); + cBlockBedHandler::SetBedOccupationState(Interface, GetPlayer()->GetLastBedPos(), false); + GetPlayer()->SetIsInBed(false); } diff --git a/src/Endianness.h b/src/Endianness.h index 9aeb44986..5692b3811 100644 --- a/src/Endianness.h +++ b/src/Endianness.h @@ -2,7 +2,7 @@ #pragma once #undef ntohll -#define ntohll(x) ((((UInt64)ntohl((u_long)x)) << 32) + ntohl(x >> 32)) +#define ntohll(x) ((((UInt64)ntohl((UInt32)x)) << 32) + ntohl(x >> 32)) @@ -11,10 +11,10 @@ // Changes endianness inline UInt64 HostToNetwork8(const void * a_Value) { - unsigned long long __HostToNetwork8; - memcpy( &__HostToNetwork8, a_Value, sizeof( __HostToNetwork8)); - __HostToNetwork8 = (( ( (unsigned long long)htonl((u_long)__HostToNetwork8)) << 32) + htonl(__HostToNetwork8 >> 32)); - return __HostToNetwork8; + UInt64 buf; + memcpy( &buf, a_Value, sizeof( buf)); + buf = (( ( (UInt64)htonl((UInt32)buf)) << 32) + htonl(buf >> 32)); + return buf; } @@ -23,10 +23,10 @@ inline UInt64 HostToNetwork8(const void * a_Value) inline UInt32 HostToNetwork4(const void* a_Value) { - unsigned int __HostToNetwork4; - memcpy( &__HostToNetwork4, a_Value, sizeof( __HostToNetwork4)); - __HostToNetwork4 = ntohl( __HostToNetwork4); - return __HostToNetwork4; + UInt32 buf; + memcpy( &buf, a_Value, sizeof( buf)); + buf = ntohl( buf); + return buf; } diff --git a/src/Entities/ArrowEntity.cpp b/src/Entities/ArrowEntity.cpp index 0fbbfb681..3c1fabb1b 100644 --- a/src/Entities/ArrowEntity.cpp +++ b/src/Entities/ArrowEntity.cpp @@ -72,14 +72,8 @@ bool cArrowEntity::CanPickup(const cPlayer & a_Player) const void cArrowEntity::OnHitSolidBlock(const Vector3d & a_HitPos, eBlockFace a_HitFace) { - if (GetSpeed().EqualsEps(Vector3d(0, 0, 0), 0.0000001)) - { - SetSpeed(GetLookVector().NormalizeCopy() * 0.1); // Ensure that no division by zero happens later - } - Vector3d Hit = a_HitPos; - Vector3d SinkMovement = (GetSpeed() / 1000); - Hit += SinkMovement * (0.0005 / SinkMovement.Length()); // Make arrow sink into block a centimetre so it lodges (but not to far so it goes black clientside) + Hit += GetSpeed().NormalizeCopy() / 100000; // Make arrow sink into block a bit so it lodges (TODO: investigate how to stop them going so far so that they become black clientside) super::OnHitSolidBlock(Hit, a_HitFace); Vector3i BlockHit = Hit.Floor(); @@ -195,11 +189,6 @@ void cArrowEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (m_IsInGround) { - // When an arrow hits, the client doesn't think its in the ground and keeps on moving, IF BroadcastMovementUpdate() and TeleportEntity was called during flight, AT ALL - // Fix is to simply not sync with the client and send a teleport to confirm pos after arrow has stabilised (around 1 sec after landing) - // We can afford to do this because xoft's algorithm for trajectory is near perfect, so things are pretty close anyway without sync - // Besides, this seems to be what the vanilla server does, note how arrows teleport half a second after they hit to the server position - if (!m_HasTeleported) // Sent a teleport already, don't do again { if (m_HitGroundTimer > std::chrono::milliseconds(500)) diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 07cfb97b2..039c58ee4 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -1263,7 +1263,7 @@ bool cEntity::DetectPortal() { if (GetWorld()->GetDimension() == dimOverworld) { - if (GetWorld()->GetNetherWorldName().empty() && GetWorld()->GetEndWorldName().empty()) + if (GetWorld()->GetLinkedNetherWorldName().empty() && GetWorld()->GetLinkedEndWorldName().empty()) { // Teleportation to either dimension not enabled, don't bother proceeding return false; @@ -1314,7 +1314,7 @@ bool cEntity::DetectPortal() } else { - if (GetWorld()->GetNetherWorldName().empty()) + if (GetWorld()->GetLinkedNetherWorldName().empty()) { return false; } @@ -1327,7 +1327,7 @@ bool cEntity::DetectPortal() ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimNether); } - return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetNetherWorldName(), dimNether, GetWorld()->GetName()), false); + return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedNetherWorldName(), dimNether, GetWorld()->GetName()), false); } } case E_BLOCK_END_PORTAL: @@ -1358,7 +1358,7 @@ bool cEntity::DetectPortal() } else { - if (GetWorld()->GetEndWorldName().empty()) + if (GetWorld()->GetLinkedEndWorldName().empty()) { return false; } @@ -1371,7 +1371,7 @@ bool cEntity::DetectPortal() ((cPlayer *)this)->GetClientHandle()->SendRespawn(dimEnd); } - return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetEndWorldName(), dimEnd, GetWorld()->GetName()), false); + return MoveToWorld(cRoot::Get()->CreateAndInitializeWorld(GetWorld()->GetLinkedEndWorldName(), dimEnd, GetWorld()->GetName()), false); } } @@ -1632,8 +1632,12 @@ void cEntity::TeleportToEntity(cEntity & a_Entity) void cEntity::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { - SetPosition(a_PosX, a_PosY, a_PosZ); - m_World->BroadcastTeleportEntity(*this); + // ask the plugins to allow teleport to the new position. + if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPos, Vector3d(a_PosX, a_PosY, a_PosZ))) + { + SetPosition(a_PosX, a_PosY, a_PosZ); + m_World->BroadcastTeleportEntity(*this); + } } diff --git a/src/Entities/HangingEntity.cpp b/src/Entities/HangingEntity.cpp index a6b9c40c8..a37d8702e 100644 --- a/src/Entities/HangingEntity.cpp +++ b/src/Entities/HangingEntity.cpp @@ -11,7 +11,7 @@ cHangingEntity::cHangingEntity(eEntityType a_EntityType, eBlockFace a_Facing, double a_X, double a_Y, double a_Z) : cEntity(a_EntityType, a_X, a_Y, a_Z, 0.8, 0.8), - m_Facing(a_Facing) + m_Facing(cHangingEntity::BlockFaceToProtocolFace(a_Facing)) { SetMaxHealth(1); SetHealth(1); @@ -21,60 +21,9 @@ cHangingEntity::cHangingEntity(eEntityType a_EntityType, eBlockFace a_Facing, do -void cHangingEntity::SetFacing(eBlockFace a_Facing) -{ - // Y-based faces are not allowed: - switch (a_Facing) - { - case BLOCK_FACE_NONE: - case BLOCK_FACE_YM: - case BLOCK_FACE_YP: - { - LOGWARNING("%s: Invalid facing: %d. Ignoring.", __FUNCTION__, a_Facing); - ASSERT(!"Tried to set a bad facing!"); - return; - } - default: break; - } - - m_Facing = a_Facing; -} - - - - - void cHangingEntity::SpawnOn(cClientHandle & a_ClientHandle) { - int Dir = 0; - - // The client uses different values for item frame directions and block faces. Our constants are for the block faces, so we convert them here to item frame faces - switch (m_Facing) - { - case BLOCK_FACE_ZP: Dir = 0; break; - case BLOCK_FACE_ZM: Dir = 2; break; - case BLOCK_FACE_XM: Dir = 1; break; - case BLOCK_FACE_XP: Dir = 3; break; - default: - { - LOGINFO("Invalid facing (%d) in a cHangingEntity at {%d, %d, %d}, adjusting to BLOCK_FACE_XP.", - m_Facing, (int)GetPosX(), (int)GetPosY(), (int)GetPosZ() - ); - Dir = 3; - } - } - - if ((Dir == 0) || (Dir == 2)) // Probably a client bug, but two directions are flipped and contrary to the norm, so we do -180 - { - SetYaw((Dir * 90) - 180); - } - else - { - SetYaw(Dir * 90); - } - - a_ClientHandle.SendSpawnObject(*this, 71, Dir, (Byte)GetYaw(), (Byte)GetPitch()); - a_ClientHandle.SendEntityMetadata(*this); + SetYaw(GetProtocolFacing() * 90); } diff --git a/src/Entities/HangingEntity.h b/src/Entities/HangingEntity.h index d1ef79a68..507502ac6 100644 --- a/src/Entities/HangingEntity.h +++ b/src/Entities/HangingEntity.h @@ -24,28 +24,82 @@ public: // tolua_begin /** Returns the direction in which the entity is facing. */ - eBlockFace GetFacing() const { return m_Facing; } - + eBlockFace GetFacing() const { return cHangingEntity::ProtocolFaceToBlockFace(m_Facing); } + /** Set the direction in which the entity is facing. */ - void SetFacing(eBlockFace a_Facing); - - /** Returns the X coord of the block in which the entity resides. */ - int GetBlockX() const { return POSX_TOINT; } - - /** Returns the Y coord of the block in which the entity resides. */ - int GetBlockY() const { return POSY_TOINT; } - - /** Returns the Z coord of the block in which the entity resides. */ - int GetBlockZ() const { return POSZ_TOINT; } + void SetFacing(eBlockFace a_Facing) { m_Facing = cHangingEntity::BlockFaceToProtocolFace(a_Facing); } // tolua_end -private: + /** Returns the direction in which the entity is facing. */ + Byte GetProtocolFacing() const { return m_Facing; } - virtual void SpawnOn(cClientHandle & a_ClientHandle) override; - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override {} + /** Set the direction in which the entity is facing. */ + void SetProtocolFacing(Byte a_Facing) + { + ASSERT((a_Facing <= 3) && (a_Facing >= 0)); + m_Facing = a_Facing; + } - eBlockFace m_Facing; +protected: + + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; + virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override + { + UNUSED(a_Dt); + UNUSED(a_Chunk); + } + + /** Converts protocol hanging item facing to eBlockFace values */ + inline static eBlockFace ProtocolFaceToBlockFace(Byte a_ProtocolFace) + { + eBlockFace Dir; + + // The client uses different values for item frame directions and block faces. Our constants are for the block faces, so we convert them here to item frame faces + switch (a_ProtocolFace) + { + case 0: Dir = BLOCK_FACE_ZP; break; + case 2: Dir = BLOCK_FACE_ZM; break; + case 1: Dir = BLOCK_FACE_XM; break; + case 3: Dir = BLOCK_FACE_XP; break; + default: + { + LOGINFO("Invalid facing (%d) in a cHangingEntity, adjusting to BLOCK_FACE_XP.", a_ProtocolFace); + ASSERT(!"Tried to convert a bad facing!"); + + Dir = cHangingEntity::ProtocolFaceToBlockFace(3); + } + } + + return Dir; + } + + /** Converts eBlockFace values to protocol hanging item faces */ + inline static Byte BlockFaceToProtocolFace(eBlockFace a_BlockFace) + { + Byte Dir; + + // The client uses different values for item frame directions and block faces. Our constants are for the block faces, so we convert them here to item frame faces + switch (a_BlockFace) + { + case BLOCK_FACE_ZP: Dir = 0; break; + case BLOCK_FACE_ZM: Dir = 2; break; + case BLOCK_FACE_XM: Dir = 1; break; + case BLOCK_FACE_XP: Dir = 3; break; + default: + { + // Uncomment when entities are initialised with their real data, instead of dummy values: + // LOGINFO("Invalid facing (%d) in a cHangingEntity, adjusting to BLOCK_FACE_XP.", a_BlockFace); + // ASSERT(!"Tried to convert a bad facing!"); + + Dir = cHangingEntity::BlockFaceToProtocolFace(BLOCK_FACE_XP); + } + } + + return Dir; + } + + Byte m_Facing; }; // tolua_export diff --git a/src/Entities/ItemFrame.cpp b/src/Entities/ItemFrame.cpp index dfffcd3ed..4e6e38f1f 100644 --- a/src/Entities/ItemFrame.cpp +++ b/src/Entities/ItemFrame.cpp @@ -92,3 +92,14 @@ void cItemFrame::GetDrops(cItems & a_Items, cEntity * a_Killer) + +void cItemFrame::SpawnOn(cClientHandle & a_ClientHandle) +{ + super::SpawnOn(a_ClientHandle); + a_ClientHandle.SendSpawnObject(*this, 71, GetProtocolFacing(), (Byte)GetYaw(), (Byte)GetPitch()); + a_ClientHandle.SendEntityMetadata(*this); +} + + + + diff --git a/src/Entities/ItemFrame.h b/src/Entities/ItemFrame.h index ced8c37af..2444b26a3 100644 --- a/src/Entities/ItemFrame.h +++ b/src/Entities/ItemFrame.h @@ -42,6 +42,7 @@ private: virtual void OnRightClicked(cPlayer & a_Player) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override; virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override; + virtual void SpawnOn(cClientHandle & a_ClientHandle) override; cItem m_Item; Byte m_ItemRotation; diff --git a/src/Entities/Minecart.cpp b/src/Entities/Minecart.cpp index 776f957f4..a32926838 100644 --- a/src/Entities/Minecart.cpp +++ b/src/Entities/Minecart.cpp @@ -11,6 +11,7 @@ #include "../Chunk.h" #include "Player.h" #include "../BoundingBox.h" +#include "../UI/MinecartWithChestWindow.h" #define NO_SPEED 0.0 #define MAX_SPEED 8 diff --git a/src/Entities/Painting.cpp b/src/Entities/Painting.cpp index 6f6277f28..02a8f6ed0 100644 --- a/src/Entities/Painting.cpp +++ b/src/Entities/Painting.cpp @@ -10,10 +10,9 @@ -cPainting::cPainting(const AString & a_Name, int a_Direction, double a_X, double a_Y, double a_Z) - : cEntity(etPainting, a_X, a_Y, a_Z, 1, 1), - m_Name(a_Name), - m_Direction(a_Direction) +cPainting::cPainting(const AString & a_Name, eBlockFace a_Direction, double a_X, double a_Y, double a_Z) + : cHangingEntity(etPainting, a_Direction, a_X, a_Y, a_Z), + m_Name(a_Name) { } @@ -24,6 +23,7 @@ cPainting::cPainting(const AString & a_Name, int a_Direction, double a_X, double void cPainting::SpawnOn(cClientHandle & a_Client) { + super::SpawnOn(a_Client); a_Client.SendPaintingSpawn(*this); } @@ -31,16 +31,6 @@ void cPainting::SpawnOn(cClientHandle & a_Client) -void cPainting::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) -{ - UNUSED(a_Dt); - UNUSED(a_Chunk); -} - - - - - void cPainting::GetDrops(cItems & a_Items, cEntity * a_Killer) { if ((a_Killer != nullptr) && a_Killer->IsPlayer() && !((cPlayer *)a_Killer)->IsGameModeCreative()) diff --git a/src/Entities/Painting.h b/src/Entities/Painting.h index 6e8a382fc..20968d4f0 100644 --- a/src/Entities/Painting.h +++ b/src/Entities/Painting.h @@ -1,7 +1,7 @@ #pragma once -#include "Entity.h" +#include "HangingEntity.h" @@ -9,9 +9,9 @@ // tolua_begin class cPainting : - public cEntity + public cHangingEntity { - typedef cEntity super; + typedef cHangingEntity super; public: @@ -19,19 +19,14 @@ public: CLASS_PROTODEF(cPainting) - cPainting(const AString & a_Name, int a_Direction, double a_X, double a_Y, double a_Z); + cPainting(const AString & a_Name, eBlockFace a_Direction, double a_X, double a_Y, double a_Z); - // tolua_begin - - const AString & GetName(void) const { return m_Name; } - int GetDirection(void) const { return m_Direction; } - - // tolua_end + /** Returns the protocol name of the painting */ + const AString & GetName(void) const { return m_Name; } // tolua_export private: virtual void SpawnOn(cClientHandle & a_Client) override; - virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; virtual void GetDrops(cItems & a_Items, cEntity * a_Killer) override; virtual void KilledBy(TakeDamageInfo & a_TDI) override { @@ -40,7 +35,6 @@ private: } AString m_Name; - int m_Direction; }; // tolua_export diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index e1d9f4550..c89e7b87c 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -5,7 +5,7 @@ #include <unordered_map> #include "../ChatColor.h" #include "../Server.h" -#include "../UI/Window.h" +#include "../UI/InventoryWindow.h" #include "../UI/WindowOwner.h" #include "../World.h" #include "../Bindings/PluginManager.h" @@ -232,7 +232,7 @@ void cPlayer::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) } bool CanMove = true; - if (!GetPosition().EqualsEps(m_LastPos, 0.01)) // Non negligible change in position from last tick? + if (!GetPosition().EqualsEps(m_LastPos, 0.02)) // Non negligible change in position from last tick? 0.02 tp prevent continous calling while floating sometimes. { // Apply food exhaustion from movement: ApplyFoodExhaustionFromMovement(); @@ -396,6 +396,7 @@ int cPlayer::DeltaExperience(int a_Xp_delta) // Make sure they didn't subtract too much m_CurrentXp = std::max(m_CurrentXp, 0); + // Update total for score calculation if (a_Xp_delta > 0) { @@ -1274,13 +1275,17 @@ unsigned int cPlayer::AwardAchievement(const eStatistic a_Ach) void cPlayer::TeleportToCoords(double a_PosX, double a_PosY, double a_PosZ) { - SetPosition(a_PosX, a_PosY, a_PosZ); - m_LastGroundHeight = static_cast<float>(a_PosY); - m_LastJumpHeight = static_cast<float>(a_PosY); - m_bIsTeleporting = true; + // ask plugins to allow teleport to the new position. + if (!cRoot::Get()->GetPluginManager()->CallHookEntityTeleport(*this, m_LastPos, Vector3d(a_PosX, a_PosY, a_PosZ))) + { + SetPosition(a_PosX, a_PosY, a_PosZ); + m_LastGroundHeight = static_cast<float>(a_PosY); + m_LastJumpHeight = static_cast<float>(a_PosY); + m_bIsTeleporting = true; - m_World->BroadcastTeleportEntity(*this, GetClientHandle()); - m_ClientHandle->SendPlayerMoveLook(); + m_World->BroadcastTeleportEntity(*this, GetClientHandle()); + m_ClientHandle->SendPlayerMoveLook(); + } } @@ -1725,9 +1730,9 @@ bool cPlayer::LoadFromFile(const AString & a_FileName, cWorldPtr & a_World) m_FoodSaturationLevel = root.get("foodSaturation", MAX_FOOD_LEVEL).asDouble(); m_FoodTickTimer = root.get("foodTickTimer", 0).asInt(); m_FoodExhaustionLevel = root.get("foodExhaustion", 0).asDouble(); - m_LifetimeTotalXp = root.get("xpTotal", 0).asInt(); - m_CurrentXp = root.get("xpCurrent", 0).asInt(); - m_IsFlying = root.get("isflying", 0).asBool(); + m_LifetimeTotalXp = root.get("xpTotal", 0).asInt(); + m_CurrentXp = root.get("xpCurrent", 0).asInt(); + m_IsFlying = root.get("isflying", 0).asBool(); m_GameMode = (eGameMode) root.get("gamemode", eGameMode_NotSet).asInt(); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index e02c66bd3..3dae58dc1 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -314,8 +314,18 @@ public: // tolua_end - /** Sets a player's in-bed state; we can't be sure plugins will keep this value updated, so no exporting */ - void SetIsInBed(bool a_Flag) { m_bIsInBed = a_Flag; } + /** Sets a player's in-bed state + We can't be sure plugins will keep this value updated, so no exporting + If value is false (not in bed), will update players of the fact that they have been ejected from the bed + */ + void SetIsInBed(bool a_Flag) + { + m_bIsInBed = a_Flag; + if (!a_Flag) + { + GetWorld()->BroadcastEntityAnimation(*this, 2); + } + } /** Starts eating the currently equipped item. Resets the eating timer and sends the proper animation packet */ void StartEating(void); diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index 4f20bfae6..21ec4e63a 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -334,12 +334,7 @@ AString cProjectileEntity::GetMCAClassName(void) const void cProjectileEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) { super::Tick(a_Dt, a_Chunk); - - // TODO: see BroadcastMovementUpdate; RelativeMove packet jerkiness affects projectiles too (cause of sympton described in cArrowEntity::Tick()) - if (GetProjectileKind() != pkArrow) - { - BroadcastMovementUpdate(); - } + BroadcastMovementUpdate(); } diff --git a/src/FastRandom.cpp b/src/FastRandom.cpp index 515dc25ea..737b13535 100644 --- a/src/FastRandom.cpp +++ b/src/FastRandom.cpp @@ -6,6 +6,12 @@ #include "Globals.h" #include "FastRandom.h" +#ifdef _WIN32 + #define thread_local __declspec(thread) +#endif + +thread_local unsigned int m_Counter = 0; + @@ -86,7 +92,7 @@ public: cFastRandom::cFastRandom(void) : - m_LinearRand(static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count())) + m_LinearRand(m_Counter++) { } @@ -105,18 +111,6 @@ int cFastRandom::NextInt(int a_Range) -int cFastRandom::NextInt(int a_Range, int a_Salt) -{ - m_LinearRand.seed(a_Salt); - std::uniform_int_distribution<> distribution(0, a_Range - 1); - return distribution(m_LinearRand); -} - - - - - - float cFastRandom::NextFloat(float a_Range) { std::uniform_real_distribution<float> distribution(0, a_Range); @@ -128,18 +122,6 @@ float cFastRandom::NextFloat(float a_Range) -float cFastRandom::NextFloat(float a_Range, int a_Salt) -{ - m_LinearRand.seed(a_Salt); - std::uniform_real_distribution<float> distribution(0, a_Range); - return distribution(m_LinearRand); -} - - - - - - int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End) { std::uniform_int_distribution<> distribution(a_Begin, a_End); @@ -154,7 +136,7 @@ int cFastRandom::GenerateRandomInteger(int a_Begin, int a_End) // MTRand: MTRand::MTRand() : - m_MersenneRand(static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count())) + m_MersenneRand(m_Counter++) { } diff --git a/src/FastRandom.h b/src/FastRandom.h index 64a087c97..30395a293 100644 --- a/src/FastRandom.h +++ b/src/FastRandom.h @@ -36,16 +36,10 @@ public: /** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M */ int NextInt(int a_Range); - - /** Returns a random int in the range [0 .. a_Range - 1]; a_Range must be less than 1M; a_Salt is additional source of randomness */ - int NextInt(int a_Range, int a_Salt); /** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M */ float NextFloat(float a_Range); - /** Returns a random float in the range [0 .. a_Range]; a_Range must be less than 1M; a_Salt is additional source of randomness */ - float NextFloat(float a_Range, int a_Salt); - /** Returns a random float between 0 and 1. */ float NextFloat(void) { return NextFloat(1); } diff --git a/src/FurnaceRecipe.cpp b/src/FurnaceRecipe.cpp index 112aa8146..ea952a852 100644 --- a/src/FurnaceRecipe.cpp +++ b/src/FurnaceRecipe.cpp @@ -291,6 +291,22 @@ const cFurnaceRecipe::cRecipe * cFurnaceRecipe::GetRecipeFrom(const cItem & a_In +bool cFurnaceRecipe::IsFuel(const cItem & a_Item) const +{ + for (auto & Fuel : m_pState->Fuel) + { + if ((Fuel.In->m_ItemType == a_Item.m_ItemType) && (Fuel.In->m_ItemCount <= a_Item.m_ItemCount)) + { + return true; + } + } + return false; +} + + + + + int cFurnaceRecipe::GetBurnTime(const cItem & a_Fuel) const { int BestFuel = 0; diff --git a/src/FurnaceRecipe.h b/src/FurnaceRecipe.h index 936ef706d..912b6aba2 100644 --- a/src/FurnaceRecipe.h +++ b/src/FurnaceRecipe.h @@ -34,6 +34,9 @@ public: /** Returns a recipe for the specified input, nullptr if no recipe found */ const cRecipe * GetRecipeFrom(const cItem & a_Ingredient) const; + + /** Returns true if the item is a fuel, false if not. */ + bool IsFuel(const cItem & a_Item) const; /** Returns the amount of time that the specified fuel burns, in ticks */ int GetBurnTime(const cItem & a_Fuel) const; diff --git a/src/Generating/BioGen.cpp b/src/Generating/BioGen.cpp index 9a7ba4d05..867155ad2 100644 --- a/src/Generating/BioGen.cpp +++ b/src/Generating/BioGen.cpp @@ -1036,7 +1036,7 @@ protected: //////////////////////////////////////////////////////////////////////////////// -// cBioGenGrown: +// cBioGenProtGrown: class cBioGenProtGrown: public cBiomeGen diff --git a/src/Generating/CompoGenBiomal.cpp b/src/Generating/CompoGenBiomal.cpp index 030c2baa5..3140bd754 100644 --- a/src/Generating/CompoGenBiomal.cpp +++ b/src/Generating/CompoGenBiomal.cpp @@ -542,6 +542,20 @@ protected: HasHadWater = true; } // for y a_ChunkDesc.SetBlockType(a_RelX, 0, a_RelZ, E_BLOCK_BEDROCK); + + EMCSBiome MesaVersion = a_ChunkDesc.GetBiome(a_RelX, a_RelZ); + if ((MesaVersion == biMesaPlateauF) || (MesaVersion == biMesaPlateauFM)) + { + if (Top < 95 + static_cast<int>(m_MesaFloor.CubicNoise2D(NoiseY * 2, NoiseX * 2) * 6)) + { + return; + } + + BLOCKTYPE Block = (m_MesaFloor.CubicNoise2D(NoiseX * 4, NoiseY * 4) < 0) ? E_BLOCK_DIRT : E_BLOCK_GRASS; + NIBBLETYPE Meta = (Block == E_BLOCK_GRASS) ? 0 : 1; + + a_ChunkDesc.SetBlockTypeMeta(a_RelX, Top, a_RelZ, Block, Meta); + } } diff --git a/src/Generating/ComposableGenerator.cpp b/src/Generating/ComposableGenerator.cpp index bda45ad92..4a670b064 100644 --- a/src/Generating/ComposableGenerator.cpp +++ b/src/Generating/ComposableGenerator.cpp @@ -616,6 +616,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile) int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80); m_FinishGens.push_back(std::make_shared<cVillageGen>(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_CompositedHeightCache)); } + else if (NoCaseCompare(*itr, "Vines") == 0) + { + int Level = a_IniFile.GetValueSetI("Generator", "VinesLevel", 40); + m_FinishGens.push_back(std::make_shared<cFinishGenVines>(Seed, Level)); + } else if (NoCaseCompare(*itr, "WaterLakes") == 0) { int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25); diff --git a/src/Generating/FinishGen.cpp b/src/Generating/FinishGen.cpp index d8fb9c8c0..260253d62 100644 --- a/src/Generating/FinishGen.cpp +++ b/src/Generating/FinishGen.cpp @@ -244,6 +244,100 @@ void cFinishGenTallGrass::GenFinish(cChunkDesc & a_ChunkDesc) //////////////////////////////////////////////////////////////////////////////// +// cFinishGenVines + +bool cFinishGenVines::IsJungleVariant(EMCSBiome a_Biome) +{ + switch (a_Biome) + { + case biJungle: + case biJungleEdge: + case biJungleEdgeM: + case biJungleHills: + case biJungleM: + { + return true; + } + } + + return false; +} + + + + + +void cFinishGenVines::GenFinish(cChunkDesc & a_ChunkDesc) +{ + for (int x = 0; x < cChunkDef::Width; x++) + { + int xx = x + a_ChunkDesc.GetChunkX() * cChunkDef::Width; + for (int z = 0; z < cChunkDef::Width; z++) + { + int zz = z + a_ChunkDesc.GetChunkZ() * cChunkDef::Width; + if (!IsJungleVariant(a_ChunkDesc.GetBiome(x, z))) + { + // Current biome isn't a jungle + continue; + } + + if ((m_Noise.IntNoise2DInt(xx, zz) % 101) < 50) + { + continue; + } + + int Height = a_ChunkDesc.GetHeight(x, z); + for (int y = Height; y > m_Level; y--) + { + if (a_ChunkDesc.GetBlockType(x, y, z) != E_BLOCK_AIR) + { + // Can't place vines in non-air blocks + continue; + } + + if ((m_Noise.IntNoise3DInt(xx, y, zz) % 101) < 50) + { + continue; + } + + std::vector<NIBBLETYPE> Places; + if ((x + 1 < cChunkDef::Width) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x + 1, y, z))) + { + Places.push_back(8); + } + + if ((x - 1 > 0) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x - 1, y, z))) + { + Places.push_back(2); + } + + if ((z + 1 < cChunkDef::Width) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x, y, z + 1))) + { + Places.push_back(1); + } + + if ((z - 1 > 0) && cBlockInfo::FullyOccupiesVoxel(a_ChunkDesc.GetBlockType(x, y, z - 1))) + { + Places.push_back(4); + } + + if (Places.size() == 0) + { + continue; + } + + NIBBLETYPE Meta = Places[m_Noise.IntNoise3DInt(xx, y, zz) % Places.size()]; + a_ChunkDesc.SetBlockTypeMeta(x, y, z, E_BLOCK_VINES, Meta); + } + } + } +} + + + + + +//////////////////////////////////////////////////////////////////////////////// // cFinishGenSprinkleFoliage: bool cFinishGenSprinkleFoliage::TryAddSugarcane(cChunkDesc & a_ChunkDesc, int a_RelX, int a_RelY, int a_RelZ) diff --git a/src/Generating/FinishGen.h b/src/Generating/FinishGen.h index ae6dee590..70696c4f8 100644 --- a/src/Generating/FinishGen.h +++ b/src/Generating/FinishGen.h @@ -118,6 +118,29 @@ protected: +class cFinishGenVines : + public cFinishGen +{ +public: + cFinishGenVines(int a_Seed, int a_Level) : + m_Noise(a_Seed), + m_Level(a_Level) + { + } + + bool IsJungleVariant(EMCSBiome a_Biome); + +protected: + cNoise m_Noise; + int m_Level; + + virtual void GenFinish(cChunkDesc & a_ChunkDesc) override; +}; + + + + + class cFinishGenSoulsandRims : public cFinishGen { diff --git a/src/Generating/HeiGen.cpp b/src/Generating/HeiGen.cpp index 61d087c17..54567cb4d 100644 --- a/src/Generating/HeiGen.cpp +++ b/src/Generating/HeiGen.cpp @@ -10,6 +10,66 @@ #include "DistortedHeightmap.h" #include "EndGen.h" #include "Noise3DGenerator.h" +#include "ProtIntGen.h" + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cHeiGenSteppy: + +class cHeiGenSteppy: + public cTerrainHeightGen +{ +public: + cHeiGenSteppy(int a_Seed) : + m_Seed(a_Seed) + { + m_Gen = + std::make_shared<cProtIntGenWeightAvg<16, 1, 0>>( + std::make_shared<cProtIntGenSmooth> (a_Seed + 1, + std::make_shared<cProtIntGenZoom> (a_Seed + 2, + std::make_shared<cProtIntGenSmooth> (a_Seed + 3, + std::make_shared<cProtIntGenZoom> (a_Seed + 4, + std::make_shared<cProtIntGenAddRnd> (a_Seed + 5, 1, + std::make_shared<cProtIntGenSmooth> (a_Seed + 6, + std::make_shared<cProtIntGenZoom> (a_Seed + 7, + std::make_shared<cProtIntGenRndBetween> (a_Seed + 8, 60, + std::make_shared<cProtIntGenAddRnd> (a_Seed + 9, 1, + std::make_shared<cProtIntGenSmooth> (a_Seed + 1, + std::make_shared<cProtIntGenZoom> (a_Seed + 2, + std::make_shared<cProtIntGenRndBetween> (a_Seed + 3, 60, + std::make_shared<cProtIntGenSmooth> (a_Seed + 4, + std::make_shared<cProtIntGenZoom> (a_Seed + 5, + std::make_shared<cProtIntGenRndBetween> (a_Seed + 6, 60, + std::make_shared<cProtIntGenRndChoice> (a_Seed + 7, 10, 50, 50, + std::make_shared<cProtIntGenSmooth> (a_Seed + 8, + std::make_shared<cProtIntGenZoom> (a_Seed + 9, + std::make_shared<cProtIntGenRndChoice> (a_Seed + 1, 10, 50, 50, + std::make_shared<cProtIntGenAddRnd> (a_Seed + 2, 2, + std::make_shared<cProtIntGenZoom> (a_Seed + 3, + std::make_shared<cProtIntGenZoom> (a_Seed + 4, + std::make_shared<cProtIntGenChoice> (a_Seed + 5, 10) + ))))))))))))))))))))))); + } + + // cTerrainHeightGen overrides: + virtual void GenHeightMap(int a_ChunkX, int a_ChunkZ, cChunkDef::HeightMap & a_HeightMap) override + { + int heights[cChunkDef::Width * cChunkDef::Width]; + m_Gen->GetInts(a_ChunkX * cChunkDef::Width, a_ChunkZ * cChunkDef::Width, cChunkDef::Width, cChunkDef::Width, heights); + for (size_t i = 0; i < ARRAYCOUNT(heights); i++) + { + a_HeightMap[i] = static_cast<HEIGHTTYPE>(std::max(std::min(60 + heights[i], cChunkDef::Height - 60), 40)); + } + } + +protected: + int m_Seed; + + SharedPtr<cProtIntGen> m_Gen; +}; @@ -821,6 +881,10 @@ cTerrainHeightGenPtr cTerrainHeightGen::CreateHeightGen(cIniFile & a_IniFile, cB // Return an empty pointer, the caller will create the proper generator: return cTerrainHeightGenPtr(); } + else if (NoCaseCompare(HeightGenName, "Steppy") == 0) + { + res = std::make_shared<cHeiGenSteppy>(a_Seed); + } else if (NoCaseCompare(HeightGenName, "Noise3D") == 0) { // Not a heightmap-based generator, but it used to be accessible via HeightGen, so we need to skip making the default out of it diff --git a/src/Generating/ProtIntGen.h b/src/Generating/ProtIntGen.h index 73ed27096..e709222fe 100644 --- a/src/Generating/ProtIntGen.h +++ b/src/Generating/ProtIntGen.h @@ -318,6 +318,350 @@ protected: +/** Averages the values of the underlying 2 * 2 neighbors. */ +class cProtIntGenAvgValues : + public cProtIntGen +{ + typedef cProtIntGen super; + +public: + cProtIntGenAvgValues(Underlying a_Underlying) : + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + int lowerSizeX = a_SizeX + 1; + int lowerSizeZ = a_SizeZ + 1; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerData[m_BufferSize]; + m_Underlying->GetInts(a_MinX, a_MinZ, lowerSizeX, lowerSizeZ, lowerData); + + // Average - add all 4 "neighbors" and divide by 4: + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int idxLower = x + lowerSizeX * z; + a_Values[x + a_SizeX * z] = ( + lowerData[idxLower] + lowerData[idxLower + 1] + + lowerData[idxLower + lowerSizeX] + lowerData[idxLower + lowerSizeX + 1] + ) / 4; + } + } + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Averages the values of the underlying 4 * 4 neighbors. */ +class cProtIntGenAvg4Values : + public cProtIntGen +{ + typedef cProtIntGen super; + +public: + cProtIntGenAvg4Values(Underlying a_Underlying) : + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + int lowerSizeX = a_SizeX + 4; + int lowerSizeZ = a_SizeZ + 4; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerData[m_BufferSize]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerData); + + // Calculate the weighted average of all 16 "neighbors": + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int idxLower1 = x + lowerSizeX * z; + int idxLower2 = idxLower1 + lowerSizeX; + int idxLower3 = idxLower1 + 2 * lowerSizeX; + int idxLower4 = idxLower1 + 3 * lowerSizeX; + a_Values[x + a_SizeX * z] = ( + 1 * lowerData[idxLower1] + 2 * lowerData[idxLower1 + 1] + 2 * lowerData[idxLower1 + 2] + 1 * lowerData[idxLower1 + 3] + + 2 * lowerData[idxLower2] + 32 * lowerData[idxLower2 + 1] + 32 * lowerData[idxLower2 + 2] + 2 * lowerData[idxLower2 + 3] + + 2 * lowerData[idxLower3] + 32 * lowerData[idxLower3 + 1] + 32 * lowerData[idxLower3 + 2] + 2 * lowerData[idxLower3 + 3] + + 1 * lowerData[idxLower4] + 2 * lowerData[idxLower4 + 1] + 2 * lowerData[idxLower4 + 2] + 1 * lowerData[idxLower4 + 3] + ) / 148; + } + } + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Averages the values of the underlying 3 * 3 neighbors with custom weight. */ +template <int WeightCenter, int WeightCardinal, int WeightDiagonal> +class cProtIntGenWeightAvg : + public cProtIntGen +{ + typedef cProtIntGen super; + +public: + cProtIntGenWeightAvg(Underlying a_Underlying) : + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + int lowerSizeX = a_SizeX + 3; + int lowerSizeZ = a_SizeZ + 3; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerData[m_BufferSize]; + m_Underlying->GetInts(a_MinX, a_MinZ, lowerSizeX, lowerSizeZ, lowerData); + + // Calculate the weighted average the neighbors: + for (int z = 0; z < a_SizeZ; z++) + { + for (int x = 0; x < a_SizeX; x++) + { + int idxLower1 = x + lowerSizeX * z; + int idxLower2 = idxLower1 + lowerSizeX; + int idxLower3 = idxLower1 + 2 * lowerSizeX; + a_Values[x + a_SizeX * z] = ( + WeightDiagonal * lowerData[idxLower1] + WeightCardinal * lowerData[idxLower1 + 1] + WeightDiagonal * lowerData[idxLower1 + 2] + + WeightCardinal * lowerData[idxLower2] + WeightCenter * lowerData[idxLower2 + 1] + WeightCardinal * lowerData[idxLower2 + 2] + + WeightDiagonal * lowerData[idxLower3] + WeightCardinal * lowerData[idxLower3 + 1] + WeightDiagonal * lowerData[idxLower3 + 2] + ) / (4 * WeightDiagonal + 4 * WeightCardinal + WeightCenter); + } + } + } + +protected: + Underlying m_Underlying; +}; + + + + + +/** Replaces random values of the underlying data with random integers in the specified range [Min .. Min + Range). */ +class cProtIntGenRndChoice : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenRndChoice(int a_Seed, int a_ChancePct, int a_Min, int a_Range, Underlying a_Underlying) : + super(a_Seed), + m_ChancePct(a_ChancePct), + m_Min(a_Min), + m_Range(a_Range), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + + // Replace random values: + for (int z = 0; z < a_SizeZ; z++) + { + int BaseZ = a_MinZ + z; + for (int x = 0; x < a_SizeX; x++) + { + if (((super::m_Noise.IntNoise2DInt(BaseZ, a_MinX + x) / 13) % 101) < m_ChancePct) + { + a_Values[x + a_SizeX * z] = m_Min + (super::m_Noise.IntNoise2DInt(a_MinX + x, BaseZ) / 7) % m_Range; + } + } // for x + } // for z + } + +protected: + int m_ChancePct; + int m_Min; + int m_Range; + Underlying m_Underlying; +}; + + + + + +/** Adds a random value in range [-a_HalfRange, +a_HalfRange] to each of the underlying values. */ +class cProtIntGenAddRnd : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenAddRnd(int a_Seed, int a_HalfRange, Underlying a_Underlying) : + super(a_Seed), + m_Range(a_HalfRange * 2 + 1), + m_HalfRange(a_HalfRange), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + m_Underlying->GetInts(a_MinX, a_MinZ, a_SizeX, a_SizeZ, a_Values); + + // Add the random values: + for (int z = 0; z < a_SizeZ; z++) + { + int NoiseZ = a_MinZ + z; + for (int x = 0; x < a_SizeX; x++) + { + int noiseVal = ((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % m_Range) - m_HalfRange; + a_Values[x + z * a_SizeX] += noiseVal; + } + } + } + +protected: + int m_Range; + int m_HalfRange; + Underlying m_Underlying; +}; + + + + + +/** Replaces random underlying values with the average of the neighbors. */ +class cProtIntGenRndAvg : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenRndAvg(int a_Seed, int a_AvgChancePct, Underlying a_Underlying) : + super(a_Seed), + m_AvgChancePct(a_AvgChancePct), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + int lowerSizeX = a_SizeX + 2; + int lowerSizeZ = a_SizeZ + 2; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerData[m_BufferSize]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerData); + + // Average random values: + for (int z = 0; z < a_SizeZ; z++) + { + int NoiseZ = a_MinZ + z; + for (int x = 0; x < a_SizeX; x++) + { + int idxLower = x + 1 + lowerSizeX * (z + 1); + if (((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % 100) > m_AvgChancePct) + { + // Average the 4 neighbors: + a_Values[x + z * a_SizeX] = ( + lowerData[idxLower - 1] + lowerData[idxLower + 1] + + lowerData[idxLower - lowerSizeX] + lowerData[idxLower + lowerSizeX] + ) / 4; + } + else + { + // Keep the underlying value: + a_Values[x + z * a_SizeX] = lowerData[idxLower]; + } + } + } + } + +protected: + int m_AvgChancePct; + Underlying m_Underlying; +}; + + + + + +/** Replaces random underlying values with a random value in between the max and min of the neighbors. */ +class cProtIntGenRndBetween : + public cProtIntGenWithNoise +{ + typedef cProtIntGenWithNoise super; + +public: + cProtIntGenRndBetween(int a_Seed, int a_AvgChancePct, Underlying a_Underlying) : + super(a_Seed), + m_AvgChancePct(a_AvgChancePct), + m_Underlying(a_Underlying) + { + } + + + virtual void GetInts(int a_MinX, int a_MinZ, int a_SizeX, int a_SizeZ, int * a_Values) override + { + // Generate the underlying values: + int lowerSizeX = a_SizeX + 2; + int lowerSizeZ = a_SizeZ + 2; + ASSERT(lowerSizeX * lowerSizeZ <= m_BufferSize); + int lowerData[m_BufferSize]; + m_Underlying->GetInts(a_MinX - 1, a_MinZ - 1, lowerSizeX, lowerSizeZ, lowerData); + + // Average random values: + for (int z = 0; z < a_SizeZ; z++) + { + int NoiseZ = a_MinZ + z; + for (int x = 0; x < a_SizeX; x++) + { + int idxLower = x + 1 + lowerSizeX * (z + 1); + if (((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ) / 7) % 100) > m_AvgChancePct) + { + // Chose a value in between the min and max neighbor: + int min = std::min(std::min(lowerData[idxLower - 1], lowerData[idxLower + 1]), std::min(lowerData[idxLower - lowerSizeX], lowerData[idxLower + lowerSizeX])); + int max = std::max(std::max(lowerData[idxLower - 1], lowerData[idxLower + 1]), std::max(lowerData[idxLower - lowerSizeX], lowerData[idxLower + lowerSizeX])); + a_Values[x + z * a_SizeX] = min + ((super::m_Noise.IntNoise2DInt(a_MinX + x, NoiseZ + 10) / 7) % (max - min + 1)); + } + else + { + // Keep the underlying value: + a_Values[x + z * a_SizeX] = lowerData[idxLower]; + } + } + } + } + +protected: + int m_AvgChancePct; + Underlying m_Underlying; +}; + + + + + /** Converts land biomes at the edge of an ocean into the respective beach biome. */ class cProtIntGenBeaches : public cProtIntGen diff --git a/src/Generating/Trees.cpp b/src/Generating/Trees.cpp index a10e0f4f1..9e72a688f 100644 --- a/src/Generating/Trees.cpp +++ b/src/Generating/Trees.cpp @@ -224,9 +224,6 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No case biMegaTaiga: case biMegaTaigaHills: case biExtremeHillsPlus: - case biMesa: - case biMesaPlateauF: - case biMesaPlateau: case biSunflowerPlains: case biDesertM: case biExtremeHillsM: @@ -239,9 +236,6 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No case biMegaSpruceTaiga: case biMegaSpruceTaigaHills: case biExtremeHillsPlusM: - case biMesaBryce: - case biMesaPlateauFM: - case biMesaPlateauM: { // TODO: These need their special trees GetBirchTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); @@ -264,6 +258,16 @@ void GetTreeImageByBiome(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_No return; } + case biMesa: + case biMesaPlateauF: + case biMesaPlateau: + case biMesaBryce: + case biMesaPlateauFM: + case biMesaPlateauM: + { + GetSmallAppleTreeImage(a_BlockX, a_BlockY, a_BlockZ, a_Noise, a_Seq, a_LogBlocks, a_OtherBlocks); + } + case biDesert: case biDesertHills: case biRiver: diff --git a/src/Inventory.h b/src/Inventory.h index 4e76bc0d3..311f64562 100644 --- a/src/Inventory.h +++ b/src/Inventory.h @@ -31,7 +31,9 @@ You can use the invArmorOffset, invInventoryOffset and invHotbarOffset constants */ class cInventory : + // tolua_end public cItemGrid::cListener + // tolua_begin { public: diff --git a/src/Items/ItemPainting.h b/src/Items/ItemPainting.h index a2a77ce21..d6f2e24b4 100644 --- a/src/Items/ItemPainting.h +++ b/src/Items/ItemPainting.h @@ -21,30 +21,17 @@ public: virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override { - if (a_Dir == BLOCK_FACE_NONE) + if ((a_Dir == BLOCK_FACE_NONE) || (a_Dir == BLOCK_FACE_YM) || (a_Dir == BLOCK_FACE_YP)) { - // Client sends this if clicked on top or bottom face + // Paintings can't be flatly placed return false; } AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir); // Make sure block that will be occupied is free BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); - AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_Dir, true); // We want the clicked block, so go back again if (Block == E_BLOCK_AIR) { - int Dir = 0; - - // The client uses different values for painting directions and block faces. Our constants are for the block faces, so we convert them here to painting faces - switch (a_Dir) - { - case BLOCK_FACE_ZP: break; // Initialised to zero - case BLOCK_FACE_ZM: Dir = 2; break; - case BLOCK_FACE_XM: Dir = 1; break; - case BLOCK_FACE_XP: Dir = 3; break; - default: ASSERT(!"Unhandled block face when trying spawn painting!"); return false; - } - static const struct // Define all the possible painting titles { AString Title; @@ -78,7 +65,7 @@ public: { "BurningSkull" } }; - cPainting * Painting = new cPainting(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, Dir, a_BlockX, a_BlockY, a_BlockZ); + cPainting * Painting = new cPainting(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, a_Dir, a_BlockX, a_BlockY, a_BlockZ); Painting->Initialize(*a_World); if (!a_Player->IsGameModeCreative()) diff --git a/src/Map.cpp b/src/Map.cpp index fbde00cf7..5e57cc8ec 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -66,10 +66,8 @@ void cMapDecorator::Update(void) { cFastRandom Random; - Int64 WorldAge = m_Player->GetWorld()->GetWorldAge(); - // TODO 2014-02-19 xdot: Refine - m_Rot = Random.NextInt(16, (int) WorldAge); + m_Rot = Random.NextInt(16); } else { @@ -189,6 +189,11 @@ public: return "cMap"; } + const char * GetClass(void) // Needed for ManualBindings' DoWith templates + { + return "cMap"; + } + protected: diff --git a/src/MobSpawner.cpp b/src/MobSpawner.cpp index 541135996..7a5238fd8 100644 --- a/src/MobSpawner.cpp +++ b/src/MobSpawner.cpp @@ -110,8 +110,7 @@ eMonsterType cMobSpawner::ChooseMobType(EMCSBiome a_Biome) if (allowedMobsSize > 0) { std::set<eMonsterType>::iterator itr = allowedMobs.begin(); - static int Counter = 0; - int iRandom = m_Random.NextInt((int)allowedMobsSize, Counter++); + int iRandom = m_Random.NextInt((int)allowedMobsSize); for (int i = 0; i < iRandom; i++) { @@ -187,7 +186,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R (BlockBelow == E_BLOCK_GRASS) || (BlockBelow == E_BLOCK_LEAVES) || (BlockBelow == E_BLOCK_NEW_LEAVES) ) && (a_RelY >= 62) && - (Random.NextInt(3, a_Biome) != 0) + (Random.NextInt(3) != 0) ); } @@ -248,7 +247,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R (!cBlockInfo::IsTransparent(BlockBelow)) && (SkyLight <= 7) && (BlockLight <= 7) && - (Random.NextInt(2, a_Biome) == 0) + (Random.NextInt(2) == 0) ); } @@ -272,7 +271,7 @@ bool cMobSpawner::CanSpawnHere(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_R (TargetBlock == E_BLOCK_AIR) && (BlockAbove == E_BLOCK_AIR) && (!cBlockInfo::IsTransparent(BlockBelow)) && - (Random.NextInt(20, a_Biome) == 0) + (Random.NextInt(20) == 0) ); } diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index 72317d66b..526b39e39 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -85,7 +85,7 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if (ReachedFinalDestination() && !LineOfSight.Trace(GetPosition(), AttackDirection, (int)AttackDirection.Length())) { // Attack if reached destination, target isn't null, and have a clear line of sight to target (so won't attack through walls) - Attack(a_Dt / 1000); + Attack(a_Dt); } } @@ -95,8 +95,7 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) void cAggressiveMonster::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt.count() * m_AttackRate; - + m_AttackInterval += (static_cast<float>(a_Dt.count()) / 1000) * m_AttackRate; if ((m_Target == nullptr) || (m_AttackInterval < 3.0)) { return; diff --git a/src/Mobs/Blaze.cpp b/src/Mobs/Blaze.cpp index 172ccd071..89eeb3709 100644 --- a/src/Mobs/Blaze.cpp +++ b/src/Mobs/Blaze.cpp @@ -32,7 +32,7 @@ void cBlaze::GetDrops(cItems & a_Drops, cEntity * a_Killer) void cBlaze::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt.count() * m_AttackRate; + m_AttackInterval += (static_cast<float>(a_Dt.count()) / 1000) * m_AttackRate; if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { diff --git a/src/Mobs/Ghast.cpp b/src/Mobs/Ghast.cpp index ea0295102..d17047ab7 100644 --- a/src/Mobs/Ghast.cpp +++ b/src/Mobs/Ghast.cpp @@ -34,7 +34,7 @@ void cGhast::GetDrops(cItems & a_Drops, cEntity * a_Killer) void cGhast::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt.count() * m_AttackRate; + m_AttackInterval += (static_cast<float>(a_Dt.count()) / 1000) * m_AttackRate; if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { diff --git a/src/Mobs/Skeleton.cpp b/src/Mobs/Skeleton.cpp index dd59d6454..331c8e8ad 100644 --- a/src/Mobs/Skeleton.cpp +++ b/src/Mobs/Skeleton.cpp @@ -69,8 +69,7 @@ void cSkeleton::MoveToPosition(const Vector3d & a_Position) void cSkeleton::Attack(std::chrono::milliseconds a_Dt) { - m_AttackInterval += a_Dt.count() * m_AttackRate; - + m_AttackInterval += (static_cast<float>(a_Dt.count()) / 1000) * m_AttackRate; if ((m_Target != nullptr) && (m_AttackInterval > 3.0)) { // Setting this higher gives us more wiggle room for attackrate diff --git a/src/OSSupport/CMakeLists.txt b/src/OSSupport/CMakeLists.txt index 81b37ef0e..0d3c9a63e 100644 --- a/src/OSSupport/CMakeLists.txt +++ b/src/OSSupport/CMakeLists.txt @@ -13,6 +13,7 @@ SET (SRCS HostnameLookup.cpp IPLookup.cpp IsThread.cpp + NetworkInterfaceEnum.cpp NetworkSingleton.cpp Semaphore.cpp ServerHandleImpl.cpp diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 5dd596223..95a935bbe 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -318,6 +318,9 @@ public: If a_Port is 0, the OS is free to assign any port number it likes to the endpoint. Returns the endpoint object that can be interacted with. */ static cUDPEndpointPtr CreateUDPEndpoint(UInt16 a_Port, cUDPEndpoint::cCallbacks & a_Callbacks); + + /** Returns all local IP addresses for network interfaces currently available. */ + static AStringVector EnumLocalIPAddresses(void); }; diff --git a/src/OSSupport/NetworkInterfaceEnum.cpp b/src/OSSupport/NetworkInterfaceEnum.cpp new file mode 100644 index 000000000..c4af1e93c --- /dev/null +++ b/src/OSSupport/NetworkInterfaceEnum.cpp @@ -0,0 +1,185 @@ + +// NetworkInterfaceEnum.cpp + +// Implements the cNetwork::EnumLocalIPAddresses() interface enumeration function + +#include "Globals.h" +#include "Network.h" +#include "event2/util.h" +#ifdef _WIN32 + #include <IPHlpApi.h> + #pragma comment(lib, "IPHLPAPI.lib") +#else // _WIN32 + #include <sys/types.h> + #include <ifaddrs.h> + #include <netinet/in.h> + #include <arpa/inet.h> +#endif // else _WIN32 + + + + + +#ifdef SELF_TEST + +static class cEnumIPAddressTest +{ +public: + cEnumIPAddressTest(void) + { + printf("Enumerating all IP addresses...\n"); + auto IPs = cNetwork::EnumLocalIPAddresses(); + for (auto & ip: IPs) + { + printf(" %s\n", ip.c_str()); + } + printf("Done.\n"); + } +} g_EnumIPAddressTest; + +#endif // SELF_TEST + + + + + +#ifdef _WIN32 + +/** Converts the SOCKET_ADDRESS structure received from the OS into an IP address string. */ +static AString PrintAddress(SOCKET_ADDRESS & a_Addr) +{ + char IP[128]; + switch (a_Addr.lpSockaddr->sa_family) + { + case AF_INET: + { + auto sin = reinterpret_cast<const sockaddr_in *>(a_Addr.lpSockaddr); + evutil_inet_ntop(a_Addr.lpSockaddr->sa_family, &(sin->sin_addr), IP, sizeof(IP)); + break; + } + case AF_INET6: + { + auto sin = reinterpret_cast<const sockaddr_in6 *>(a_Addr.lpSockaddr); + evutil_inet_ntop(a_Addr.lpSockaddr->sa_family, &(sin->sin6_addr), IP, sizeof(IP)); + break; + } + default: + { + IP[0] = 0; + break; + } + } + return IP; +} + +#else // _WIN32 + +static AString PrintAddress(ifaddrs * InterfaceAddress) +{ + switch (InterfaceAddress->ifa_addr->sa_family) + { + case AF_INET: + { // IPv4 + char AddressBuffer[INET_ADDRSTRLEN]; + sockaddr_in InternetSocket; + + std::memcpy(&InternetSocket, InterfaceAddress->ifa_addr, sizeof(InternetSocket)); + inet_ntop(AF_INET, &InternetSocket.sin_addr, AddressBuffer, INET_ADDRSTRLEN); + return AddressBuffer; + } + case AF_INET6: + { // IPv6 + char AddressBuffer[INET6_ADDRSTRLEN]; + sockaddr_in6 InternetSocket; + + std::memcpy(&InternetSocket, InterfaceAddress->ifa_addr, sizeof(InternetSocket)); + inet_ntop(AF_INET6, &InternetSocket.sin6_addr, AddressBuffer, INET6_ADDRSTRLEN); + return AddressBuffer; + } + default: + { + LOG("Unknown address family: %i", InterfaceAddress->ifa_addr->sa_family); + return ""; + } + } +} + +#endif // else _WIN32 + + + + + +AStringVector cNetwork::EnumLocalIPAddresses(void) +{ + AStringVector res; + + #ifdef _WIN32 + + // Query the OS for all adapters' addresses: + char buffer[64 KiB]; // A buffer backing the address list + PIP_ADAPTER_ADDRESSES pAddresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(&buffer); + ULONG outBufLen = sizeof(buffer); + DWORD dwRetVal = GetAdaptersAddresses( + AF_UNSPEC, + GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, nullptr, + pAddresses, &outBufLen + ); + if (dwRetVal != ERROR_SUCCESS) + { + LOG("GetAdaptersAddresses() failed: %u", dwRetVal); + return res; + } + + // Enumerate all active adapters + for (auto pCurrAddresses = pAddresses; pCurrAddresses != nullptr; pCurrAddresses = pCurrAddresses->Next) + { + if (pCurrAddresses->OperStatus != IfOperStatusUp) + { + // Adapter not active, skip it: + continue; + } + + // Collect all IP addresses on this adapter: + for (auto pUnicast = pCurrAddresses->FirstUnicastAddress; pUnicast != nullptr; pUnicast = pUnicast->Next) + { + auto Address = PrintAddress(pUnicast->Address); + if (!Address.empty()) + { + res.push_back(Address); + } + } // for pUnicast + } // for pCurrAddresses + + #else // _WIN32 + + struct ifaddrs * ifAddrStruct = nullptr; + getifaddrs(&ifAddrStruct); + + for (auto ifa = ifAddrStruct; ifa != nullptr; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == nullptr) + { + continue; + } + + auto Address = PrintAddress(ifa); + if (!Address.empty()) + { + res.emplace_back(Address); + } + } + + if (ifAddrStruct != nullptr) + { + freeifaddrs(ifAddrStruct); + } + + #endif // else _WIN32 + + return res; +} + + + + diff --git a/src/OSSupport/UDPEndpointImpl.cpp b/src/OSSupport/UDPEndpointImpl.cpp index ece521ab8..31ca107ce 100644 --- a/src/OSSupport/UDPEndpointImpl.cpp +++ b/src/OSSupport/UDPEndpointImpl.cpp @@ -197,6 +197,15 @@ cUDPEndpointImpl::cUDPEndpointImpl(UInt16 a_Port, cUDPEndpoint::cCallbacks & a_C +cUDPEndpointImpl::~cUDPEndpointImpl() +{ + Close(); +} + + + + + void cUDPEndpointImpl::Close(void) { if (m_Port == 0) diff --git a/src/OSSupport/UDPEndpointImpl.h b/src/OSSupport/UDPEndpointImpl.h index 75942b0cf..0e28d0b13 100644 --- a/src/OSSupport/UDPEndpointImpl.h +++ b/src/OSSupport/UDPEndpointImpl.h @@ -35,6 +35,8 @@ public: If a_Port is 0, the OS is free to assign any port number it likes to the endpoint. */ cUDPEndpointImpl(UInt16 a_Port, cUDPEndpoint::cCallbacks & a_Callbacks); + ~cUDPEndpointImpl(); + // cUDPEndpoint overrides: virtual void Close(void) override; virtual bool IsOpen(void) const override; diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index f78c2e54b..9abe81238 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -637,7 +637,7 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting) Pkt.WriteInt(static_cast<int>(a_Painting.GetPosX())); Pkt.WriteInt(static_cast<int>(a_Painting.GetPosY())); Pkt.WriteInt(static_cast<int>(a_Painting.GetPosZ())); - Pkt.WriteInt(a_Painting.GetDirection()); + Pkt.WriteInt(a_Painting.GetProtocolFacing()); } diff --git a/src/Protocol/Protocol18x.cpp b/src/Protocol/Protocol18x.cpp index 22280f800..2d1a473d1 100644 --- a/src/Protocol/Protocol18x.cpp +++ b/src/Protocol/Protocol18x.cpp @@ -632,19 +632,11 @@ void cProtocol180::SendPaintingSpawn(const cPainting & a_Painting) double PosY = a_Painting.GetPosY(); double PosZ = a_Painting.GetPosZ(); - switch (a_Painting.GetDirection()) - { - case 0: PosZ += 1; break; - case 1: PosX -= 1; break; - case 2: PosZ -= 1; break; - case 3: PosX += 1; break; - } - cPacketizer Pkt(*this, 0x10); // Spawn Painting packet Pkt.WriteVarInt(a_Painting.GetUniqueID()); Pkt.WriteString(a_Painting.GetName().c_str()); Pkt.WritePosition((int)PosX, (int)PosY, (int)PosZ); - Pkt.WriteChar(a_Painting.GetDirection()); + Pkt.WriteChar(a_Painting.GetProtocolFacing()); } @@ -2105,9 +2097,7 @@ void cProtocol180::HandlePacketLoginStart(cByteBuffer & a_ByteBuffer) void cProtocol180::HandlePacketAnimation(cByteBuffer & a_ByteBuffer) { - HANDLE_READ(a_ByteBuffer, ReadBEInt, int, EntityID); - HANDLE_READ(a_ByteBuffer, ReadByte, Byte, Animation); - m_Client->HandleAnimation(Animation); + m_Client->HandleAnimation(1); // Packet exists solely for arm-swing notification } diff --git a/src/Server.cpp b/src/Server.cpp index 3f61be378..df2c7deef 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -549,7 +549,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac } #endif - else if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output)) + else if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output, a_Cmd)) { a_Output.Finished(); return; diff --git a/src/Simulator/FloodyFluidSimulator.cpp b/src/Simulator/FloodyFluidSimulator.cpp index bcd083294..a9481edb0 100644 --- a/src/Simulator/FloodyFluidSimulator.cpp +++ b/src/Simulator/FloodyFluidSimulator.cpp @@ -108,8 +108,9 @@ void cFloodyFluidSimulator::SimulateBlock(cChunk * a_Chunk, int a_RelX, int a_Re { SpreadXZ(a_Chunk, a_RelX, a_RelY, a_RelZ, NewMeta); } + // If source creation is on, check for it here: - else if ( + if ( (m_NumNeighborsForSource > 0) && // Source creation is on (MyMeta == m_Falloff) && // Only exactly one block away from a source (fast bail-out) !IsPassableForFluid(Below) && // Only exactly 1 block deep diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index 4eb2d48b6..4adc6a0a0 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -140,6 +140,54 @@ AStringVector StringSplit(const AString & str, const AString & delim) +AStringVector StringSplitWithQuotes(const AString & str, const AString & delim) +{ + AStringVector results; + + size_t cutAt = 0; + size_t Prev = 0; + size_t cutAtQuote = 0; + + while ((cutAt = str.find_first_of(delim, Prev)) != str.npos) + { + AString current = str.substr(Prev, cutAt - Prev); + if ((current.front() == '"') || (current.front() == '\'')) + { + Prev += 1; + cutAtQuote = str.find_first_of(current.front(), Prev); + if (cutAtQuote != str.npos) + { + current = str.substr(Prev, cutAtQuote - Prev); + cutAt = cutAtQuote + 1; + } + } + + results.push_back(std::move(current)); + Prev = cutAt + 1; + } + + if (Prev < str.length()) + { + AString current = str.substr(Prev); + + // If the remant is wrapped in matching quotes, remove them: + if ( + (current.length() >= 2) && + ((current.front() == '"') || (current.front() == '\'')) && + (current.front() == current.back()) + ) + { + current = current.substr(1, current.length() - 2); + } + + results.push_back(current); + } + + return results; +} + + + AStringVector StringSplitAndTrim(const AString & str, const AString & delim) { diff --git a/src/StringUtils.h b/src/StringUtils.h index bc3bb7a2c..785197763 100644 --- a/src/StringUtils.h +++ b/src/StringUtils.h @@ -41,6 +41,11 @@ extern AString & AppendPrintf (AString & a_Dst, const char * format, ...) FORMAT Return the splitted strings as a stringvector. */ extern AStringVector StringSplit(const AString & str, const AString & delim); +/** Split the string at any of the listed delimiters. Keeps quoted content together +Resolves issue #490 +Return the splitted strings as a stringvector. */ +extern AStringVector StringSplitWithQuotes(const AString & str, const AString & delim); + /** Split the string at any of the listed delimiters and trim each value. Returns the splitted strings as a stringvector. */ extern AStringVector StringSplitAndTrim(const AString & str, const AString & delim); diff --git a/src/UI/AnvilWindow.cpp b/src/UI/AnvilWindow.cpp new file mode 100644 index 000000000..daa35cf47 --- /dev/null +++ b/src/UI/AnvilWindow.cpp @@ -0,0 +1,83 @@ + +// AnvilWindow.cpp + +// Representing the UI window for the anvil block + +#include "Globals.h" +#include "AnvilWindow.h" +#include "SlotArea.h" + + + + +cAnvilWindow::cAnvilWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : + cWindow(wtAnvil, "Repair"), + m_RepairedItemName(""), + m_BlockX(a_BlockX), + m_BlockY(a_BlockY), + m_BlockZ(a_BlockZ) +{ + m_AnvilSlotArea = new cSlotAreaAnvil(*this); + m_SlotAreas.push_back(m_AnvilSlotArea); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +AString cAnvilWindow::GetRepairedItemName(void) const +{ + return m_RepairedItemName; +} + + + + + +void cAnvilWindow::SetRepairedItemName(const AString & a_Name, cPlayer * a_Player) +{ + m_RepairedItemName = a_Name; + if (a_Player != nullptr) + { + m_AnvilSlotArea->UpdateResult(*a_Player); + } +} + + + + + +void cAnvilWindow::GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ) +{ + a_PosX = m_BlockX; + a_PosY = m_BlockY; + a_PosZ = m_BlockZ; +} + + + + + +void cAnvilWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Anvil Slot + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + } + else + { + // Inventory or Hotbar + AreasInOrder.push_back(m_SlotAreas[0]); /* Anvil */ + } + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); +} + + + + diff --git a/src/UI/AnvilWindow.h b/src/UI/AnvilWindow.h new file mode 100644 index 000000000..e23c744fe --- /dev/null +++ b/src/UI/AnvilWindow.h @@ -0,0 +1,45 @@ + +// AnvilWindow.h + +// Representing the UI window for the anvil block + + + + + +#pragma once + +#include "Window.h" + + + + + +class cAnvilWindow : + public cWindow +{ + typedef cWindow super; + +public: + cAnvilWindow(int a_BlockX, int a_BlockY, int a_BlockZ); + + /** Gets the repaired item name. */ + AString GetRepairedItemName(void) const; + + /** Set the repaired item name. */ + void SetRepairedItemName(const AString & a_Name, cPlayer * a_Player); + + /** Gets the Position from the Anvil */ + void GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ); + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; + +protected: + cSlotAreaAnvil * m_AnvilSlotArea; + AString m_RepairedItemName; + int m_BlockX, m_BlockY, m_BlockZ; +}; + + + + diff --git a/src/UI/BeaconWindow.cpp b/src/UI/BeaconWindow.cpp new file mode 100644 index 000000000..c1efa78ad --- /dev/null +++ b/src/UI/BeaconWindow.cpp @@ -0,0 +1,76 @@ + +// BeaconWindow.cpp + +// Representing the UI window for the beacon block + +#include "Globals.h" +#include "BeaconWindow.h" +#include "SlotArea.h" +#include "../BlockEntities/BeaconEntity.h" +#include "../Entities/Player.h" + + + + + +cBeaconWindow::cBeaconWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconEntity * a_Beacon) : + cWindow(wtBeacon, "Beacon"), + m_Beacon(a_Beacon) +{ + m_SlotAreas.push_back(new cSlotAreaBeacon(m_Beacon, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +void cBeaconWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Beacon Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true); + } + else + { + if (cSlotAreaBeacon::IsPlaceableItem(a_ItemStack.m_ItemType) && (a_ItemStack.m_ItemCount == 1)) + { + AreasInOrder.push_back(m_SlotAreas[0]); /* Beacon */ + } + + if (a_ClickedArea == m_SlotAreas[1]) + { + // Inventory Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + } + else + { + // Hotbar Area + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + } + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } +} + + + + + +void cBeaconWindow::OpenedByPlayer(cPlayer & a_Player) +{ + super::OpenedByPlayer(a_Player); + + a_Player.GetClientHandle()->SendWindowProperty(*this, 0, m_Beacon->GetBeaconLevel()); + a_Player.GetClientHandle()->SendWindowProperty(*this, 1, m_Beacon->GetPrimaryEffect()); + a_Player.GetClientHandle()->SendWindowProperty(*this, 2, m_Beacon->GetSecondaryEffect()); +} + + + + diff --git a/src/UI/BeaconWindow.h b/src/UI/BeaconWindow.h new file mode 100644 index 000000000..fa28b41ba --- /dev/null +++ b/src/UI/BeaconWindow.h @@ -0,0 +1,40 @@ + +// BeaconWindow.h + +// Representing the UI window for the beacon block + + + + + +#pragma once + +#include "Window.h" +#include "../Entities/Player.h" + + + + + +class cBeaconWindow : + public cWindow +{ + typedef cWindow super; + +public: + cBeaconWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconEntity * a_Beacon); + + cBeaconEntity * GetBeaconEntity(void) const { return m_Beacon; } + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; + + // cWindow Overrides: + virtual void OpenedByPlayer(cPlayer & a_Player) override; + +protected: + cBeaconEntity * m_Beacon; +}; + + + + diff --git a/src/UI/CMakeLists.txt b/src/UI/CMakeLists.txt index 2b094ef1d..ef4afc40a 100644 --- a/src/UI/CMakeLists.txt +++ b/src/UI/CMakeLists.txt @@ -6,11 +6,32 @@ include_directories ("${PROJECT_SOURCE_DIR}/../") SET (SRCS SlotArea.cpp - Window.cpp) + Window.cpp + AnvilWindow.cpp + BeaconWindow.cpp + ChestWindow.cpp + CraftingWindow.cpp + DropSpenserWindow.cpp + EnchantingWindow.cpp + EnderChestWindow.cpp + FurnaceWindow.cpp + HopperWindow.cpp + InventoryWindow.cpp) SET (HDRS SlotArea.h Window.h + AnvilWindow.h + BeaconWindow.h + ChestWindow.h + CraftingWindow.h + DropSpenserWindow.h + EnchantingWindow.h + EnderChestWindow.h + FurnaceWindow.h + HopperWindow.h + InventoryWindow.h + MinecartWithChestWindow.h WindowOwner.h) if(NOT MSVC) diff --git a/src/UI/ChestWindow.cpp b/src/UI/ChestWindow.cpp new file mode 100644 index 000000000..3766b132d --- /dev/null +++ b/src/UI/ChestWindow.cpp @@ -0,0 +1,141 @@ + +// ChestWindow.cpp + +// Representing the UI window for the chest block + +#include "Globals.h" +#include "ChestWindow.h" +#include "../BlockEntities/ChestEntity.h" +#include "../Entities/Player.h" + + + + + +cChestWindow::cChestWindow(cChestEntity * a_Chest) : + cWindow(wtChest, (a_Chest->GetBlockType() == E_BLOCK_CHEST) ? "Chest" : "Trapped Chest"), + m_World(a_Chest->GetWorld()), + m_BlockX(a_Chest->GetPosX()), + m_BlockY(a_Chest->GetPosY()), + m_BlockZ(a_Chest->GetPosZ()), + m_PrimaryChest(a_Chest), + m_SecondaryChest(nullptr) +{ + m_SlotAreas.push_back(new cSlotAreaChest(a_Chest, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); + + // Play the opening sound: + m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); + + // Send out the chest-open packet: + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_Chest->GetBlockType()); +} + + + + + +cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest) : + cWindow(wtChest, (a_PrimaryChest->GetBlockType() == E_BLOCK_CHEST) ? "Double Chest" : "Double Trapped Chest"), + m_World(a_PrimaryChest->GetWorld()), + m_BlockX(a_PrimaryChest->GetPosX()), + m_BlockY(a_PrimaryChest->GetPosY()), + m_BlockZ(a_PrimaryChest->GetPosZ()), + m_PrimaryChest(a_PrimaryChest), + m_SecondaryChest(a_SecondaryChest) +{ + m_SlotAreas.push_back(new cSlotAreaDoubleChest(a_PrimaryChest, a_SecondaryChest, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); + + // Play the opening sound: + m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); + + // Send out the chest-open packet: + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_PrimaryChest->GetBlockType()); +} + + + + + +cChestWindow::~cChestWindow() +{ + // Send out the chest-close packet: + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, m_PrimaryChest->GetBlockType()); + + m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); +} + + + + + +bool cChestWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) +{ + int ChunkX, ChunkZ; + + m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() - 1); + cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ); + m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + + if (m_SecondaryChest != nullptr) + { + m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() - 1); + cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ); + m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + } + + cWindow::ClosedByPlayer(a_Player, a_CanRefuse); + return true; +} + + + + + +void cChestWindow::OpenedByPlayer(cPlayer & a_Player) +{ + int ChunkX, ChunkZ; + + m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() + 1); + cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ); + m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + + if (m_SecondaryChest != nullptr) + { + m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() + 1); + cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ); + m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); + } + + cWindow::OpenedByPlayer(a_Player); +} + + + + + +void cChestWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Chest Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true); + } + else + { + // Hotbar or Inventory + AreasInOrder.push_back(m_SlotAreas[0]); /* Chest */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } +} + + + + diff --git a/src/UI/ChestWindow.h b/src/UI/ChestWindow.h new file mode 100644 index 000000000..a3b20cdd9 --- /dev/null +++ b/src/UI/ChestWindow.h @@ -0,0 +1,45 @@ + +// ChestWindow.h + +// Representing the UI window for the chest block + + + + + +#pragma once + +#include "Window.h" + + + + + +class cChestWindow : + public cWindow +{ + typedef cWindow super; + +public: + cChestWindow(cChestEntity * a_Chest); + + cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest); + + ~cChestWindow(); + + virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; + + virtual void OpenedByPlayer(cPlayer & a_Player) override; + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; + +protected: + cWorld * m_World; + int m_BlockX, m_BlockY, m_BlockZ; // Position of the chest, for the window-close packet + cChestEntity * m_PrimaryChest; + cChestEntity * m_SecondaryChest; +}; + + + + diff --git a/src/UI/CraftingWindow.cpp b/src/UI/CraftingWindow.cpp new file mode 100644 index 000000000..ca44056f9 --- /dev/null +++ b/src/UI/CraftingWindow.cpp @@ -0,0 +1,61 @@ + +// CraftingWindow.cpp + +// Representing the UI window for the crafting block + +#include "Globals.h" +#include "CraftingWindow.h" +#include "SlotArea.h" + + + + +cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : + cWindow(wtWorkbench, "Crafting Table") +{ + m_SlotAreas.push_back(new cSlotAreaCrafting(3, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +void cCraftingWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Crafting Area + if (a_Slot == 0) + { + // Result Slot + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + } + else + { + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + } + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, (a_Slot == 0)); + } + else if (a_ClickedArea == m_SlotAreas[1]) + { + // Inventory Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } + else + { + // Hotbar + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } +} + + + + diff --git a/src/UI/CraftingWindow.h b/src/UI/CraftingWindow.h new file mode 100644 index 000000000..01b2da73a --- /dev/null +++ b/src/UI/CraftingWindow.h @@ -0,0 +1,31 @@ + +// CraftingWindow.h + +// Representing the UI window for the crafting block + + + + + +#pragma once + +#include "Window.h" + + + + + +class cCraftingWindow : + public cWindow +{ + typedef cWindow super; + +public: + cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ); + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; +}; + + + + diff --git a/src/UI/DropSpenserWindow.cpp b/src/UI/DropSpenserWindow.cpp new file mode 100644 index 000000000..aeb7c64b7 --- /dev/null +++ b/src/UI/DropSpenserWindow.cpp @@ -0,0 +1,46 @@ + +// DropSpenserWindow.cpp + +// Representing the UI window for the dropper/dispenser block + +#include "Globals.h" +#include "DropSpenserWindow.h" + + + + + +cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser) : + cWindow(wtDropSpenser, (a_DropSpenser->GetBlockType() == E_BLOCK_DISPENSER) ? "Dispenser" : "Dropper") +{ + m_SlotAreas.push_back(new cSlotAreaItemGrid(a_DropSpenser->GetContents(), *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +void cDropSpenserWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // DropSpenser Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true); + } + else + { + // Inventory or Hotbar + AreasInOrder.push_back(m_SlotAreas[0]); /* DropSpenser */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } +} + + + + diff --git a/src/UI/DropSpenserWindow.h b/src/UI/DropSpenserWindow.h new file mode 100644 index 000000000..edff936e5 --- /dev/null +++ b/src/UI/DropSpenserWindow.h @@ -0,0 +1,32 @@ + +// DropSpenserWindow.h + +// Representing the UI window for the dropper/dispenser block + + + + + +#pragma once + +#include "Window.h" +#include "../BlockEntities/DropSpenserEntity.h" + + + + + +class cDropSpenserWindow : + public cWindow +{ + typedef cWindow super; + +public: + cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser); + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; +}; + + + + diff --git a/src/UI/EnchantingWindow.cpp b/src/UI/EnchantingWindow.cpp new file mode 100644 index 000000000..fe21ee83d --- /dev/null +++ b/src/UI/EnchantingWindow.cpp @@ -0,0 +1,100 @@ + +// EnchantingWindow.cpp + +// Representing the UI window for the enchanting block + +#include "Globals.h" +#include "EnchantingWindow.h" +#include "SlotArea.h" + + + + + +cEnchantingWindow::cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : + cWindow(wtEnchantment, "Enchant"), + m_SlotArea(), + m_BlockX(a_BlockX), + m_BlockY(a_BlockY), + m_BlockZ(a_BlockZ) +{ + m_SlotArea = new cSlotAreaEnchanting(*this, m_BlockX, m_BlockY, m_BlockZ); + m_SlotAreas.push_back(m_SlotArea); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +void cEnchantingWindow::SetProperty(short a_Property, short a_Value, cPlayer & a_Player) +{ + if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue))) + { + ASSERT(!"a_Property is invalid"); + return; + } + + m_PropertyValue[a_Property] = a_Value; + super::SetProperty(a_Property, a_Value, a_Player); +} + + + + + + +void cEnchantingWindow::SetProperty(short a_Property, short a_Value) +{ + if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue))) + { + ASSERT(!"a_Property is invalid"); + return; + } + + m_PropertyValue[a_Property] = a_Value; + super::SetProperty(a_Property, a_Value); +} + + + + + +short cEnchantingWindow::GetPropertyValue(short a_Property) +{ + if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue))) + { + ASSERT(!"a_Property is invalid"); + return 0; + } + + return m_PropertyValue[a_Property]; +} + + + + + +void cEnchantingWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Enchanting Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true); + } + else + { + // Inventory or Hotbar + AreasInOrder.push_back(m_SlotAreas[0]); /* Enchanting */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } +} + + + + diff --git a/src/UI/EnchantingWindow.h b/src/UI/EnchantingWindow.h new file mode 100644 index 000000000..bf805c6c8 --- /dev/null +++ b/src/UI/EnchantingWindow.h @@ -0,0 +1,44 @@ + +// EnchantingWindow.h + +// Representing the UI window for the enchanting block + + + + + +#pragma once + +#include "Window.h" + + + + + +class cEnchantingWindow : + public cWindow +{ + typedef cWindow super; + +public: + cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ); + + virtual void SetProperty(short a_Property, short a_Value, cPlayer & a_Player) override; + + virtual void SetProperty(short a_Property, short a_Value) override; + + /** Return the value of a property */ + short GetPropertyValue(short a_Property); + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; + + cSlotArea * m_SlotArea; + +protected: + short m_PropertyValue[3]; + int m_BlockX, m_BlockY, m_BlockZ; +}; + + + + diff --git a/src/UI/EnderChestWindow.cpp b/src/UI/EnderChestWindow.cpp new file mode 100644 index 000000000..a5484468f --- /dev/null +++ b/src/UI/EnderChestWindow.cpp @@ -0,0 +1,71 @@ + +// EnderChestWindow.cpp + +// Representing the UI window for the enderchest block + +#include "Globals.h" +#include "../World.h" +#include "EnderChestWindow.h" +#include "SlotArea.h" + + + + + +cEnderChestWindow::cEnderChestWindow(cEnderChestEntity * a_EnderChest) : + cWindow(wtChest, "Ender Chest"), + m_World(a_EnderChest->GetWorld()), + m_BlockX(a_EnderChest->GetPosX()), + m_BlockY(a_EnderChest->GetPosY()), + m_BlockZ(a_EnderChest->GetPosZ()) +{ + m_SlotAreas.push_back(new cSlotAreaEnderChest(a_EnderChest, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); + + // Play the opening sound: + m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); + + // Send out the chest-open packet: + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_ENDER_CHEST); +} + + + + + +cEnderChestWindow::~cEnderChestWindow() +{ + // Send out the chest-close packet: + m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_ENDER_CHEST); + + // Play the closing sound + m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); +} + + + + + +void cEnderChestWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Chest Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true); + } + else + { + // Hotbar or Inventory + AreasInOrder.push_back(m_SlotAreas[0]); /* Chest */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } +} + + + + diff --git a/src/UI/EnderChestWindow.h b/src/UI/EnderChestWindow.h new file mode 100644 index 000000000..006a490bf --- /dev/null +++ b/src/UI/EnderChestWindow.h @@ -0,0 +1,38 @@ + +// EnderChestWindow.h + +// Representing the UI window for the enderchest block + + + + + +#pragma once + +#include "Window.h" +#include "../BlockEntities/EnderChestEntity.h" + + + + + +class cEnderChestWindow : + public cWindow +{ + typedef cWindow super; + +public: + cEnderChestWindow(cEnderChestEntity * a_EnderChest); + + ~cEnderChestWindow(); + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; + +protected: + cWorld * m_World; + int m_BlockX, m_BlockY, m_BlockZ; // Position of the enderchest, for the window-close packet +}; + + + + diff --git a/src/UI/FurnaceWindow.cpp b/src/UI/FurnaceWindow.cpp new file mode 100644 index 000000000..132439ff3 --- /dev/null +++ b/src/UI/FurnaceWindow.cpp @@ -0,0 +1,74 @@ + +// FurnaceWindow.cpp + +// Representing the UI window for the furnace block + +#include "Globals.h" +#include "FurnaceWindow.h" +#include "SlotArea.h" +#include "../FurnaceRecipe.h" +#include "../Root.h" + + + + + +cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace) : + cWindow(wtFurnace, "Furnace") +{ + m_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +void cFurnaceWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Furnace Area + if (a_Slot == 2) + { + // Result Slot + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true); + } + else + { + // Furnace Input/Fuel Slot + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } + } + else + { + cFurnaceRecipe * FurnaceRecipes = cRoot::Get()->GetFurnaceRecipe(); + if ((FurnaceRecipes->GetRecipeFrom(a_ItemStack) != nullptr) || (FurnaceRecipes->IsFuel(a_ItemStack))) + { + // The item is a valid input item or fuel + AreasInOrder.push_back(m_SlotAreas[0]); /* Furnace Area */ + } + else if (a_ClickedArea == m_SlotAreas[1]) + { + // Inventory Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + } + else + { + // Hotbar Area + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + } + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } +} + + + + diff --git a/src/UI/FurnaceWindow.h b/src/UI/FurnaceWindow.h new file mode 100644 index 000000000..845505f8e --- /dev/null +++ b/src/UI/FurnaceWindow.h @@ -0,0 +1,32 @@ + +// FurnaceWindow.h + +// Representing the UI window for the furnace block + + + + + +#pragma once + +#include "Window.h" + + + + + +class cFurnaceWindow : + public cWindow +{ + typedef cWindow super; + +public: + cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace); + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; + +}; + + + + diff --git a/src/UI/HopperWindow.cpp b/src/UI/HopperWindow.cpp new file mode 100644 index 000000000..79f0767e8 --- /dev/null +++ b/src/UI/HopperWindow.cpp @@ -0,0 +1,48 @@ + +// HopperWindow.cpp + +// Representing the UI window for the hopper block + +#include "Globals.h" +#include "../BlockEntities/HopperEntity.h" +#include "HopperWindow.h" +#include "../BlockEntities/DropperEntity.h" + + + + + +cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper) : + super(wtHopper, "Hopper") +{ + m_SlotAreas.push_back(new cSlotAreaItemGrid(a_Hopper->GetContents(), *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +void cHopperWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Hopper Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true); + } + else + { + // Inventory or Hotbar + AreasInOrder.push_back(m_SlotAreas[0]); /* Hopper */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } +} + + + + diff --git a/src/UI/HopperWindow.h b/src/UI/HopperWindow.h new file mode 100644 index 000000000..2dec08666 --- /dev/null +++ b/src/UI/HopperWindow.h @@ -0,0 +1,32 @@ + +// HopperWindow.h + +// Representing the UI window for the hopper block + + + + + +#pragma once + +#include "Window.h" + + + + + +class cHopperWindow : + public cWindow +{ + typedef cWindow super; + +public: + cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper); + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; + +}; + + + + diff --git a/src/UI/InventoryWindow.cpp b/src/UI/InventoryWindow.cpp new file mode 100644 index 000000000..0f876e559 --- /dev/null +++ b/src/UI/InventoryWindow.cpp @@ -0,0 +1,73 @@ + +// InventoryWindow.cpp + +// Representing the UI window for the player inventory + +#include "Globals.h" +#include "InventoryWindow.h" +#include "SlotArea.h" + + + + + +cInventoryWindow::cInventoryWindow(cPlayer & a_Player) : + cWindow(wtInventory, "Inventory"), + m_Player(a_Player) +{ + m_SlotAreas.push_back(new cSlotAreaCrafting(2, *this)); // The creative inventory doesn't display it, but it's still counted into slot numbers + m_SlotAreas.push_back(new cSlotAreaArmor(*this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); +} + + + + + +void cInventoryWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) +{ + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Crafting Area + if (a_Slot == 0) + { + // Result Slot + AreasInOrder.push_back(m_SlotAreas[3]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[2]); /* Inventory */ + } + else + { + AreasInOrder.push_back(m_SlotAreas[2]); /* Inventory */ + AreasInOrder.push_back(m_SlotAreas[3]); /* Hotbar */ + } + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, (a_Slot == 0)); + } + else if (a_ClickedArea == m_SlotAreas[1]) + { + // Armor Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Inventory */ + AreasInOrder.push_back(m_SlotAreas[3]); /* Hotbar */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } + else if (a_ClickedArea == m_SlotAreas[2]) + { + // Inventory Area + AreasInOrder.push_back(m_SlotAreas[1]); /* Armor */ + AreasInOrder.push_back(m_SlotAreas[3]); /* Hotbar */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } + else + { + // Hotbar + AreasInOrder.push_back(m_SlotAreas[1]); /* Armor */ + AreasInOrder.push_back(m_SlotAreas[2]); /* Inventory */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } +} + + + + diff --git a/src/UI/InventoryWindow.h b/src/UI/InventoryWindow.h new file mode 100644 index 000000000..10952d37f --- /dev/null +++ b/src/UI/InventoryWindow.h @@ -0,0 +1,34 @@ + +// InventoryWindow.h + +// Representing the UI window for the player inventory + + + + + +#pragma once + +#include "Window.h" + + + + + +class cInventoryWindow : + public cWindow +{ + typedef cWindow super; + +public: + cInventoryWindow(cPlayer & a_Player); + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; + +protected: + cPlayer & m_Player; +}; + + + + diff --git a/src/UI/MinecartWithChestWindow.h b/src/UI/MinecartWithChestWindow.h new file mode 100644 index 000000000..a2b5283a6 --- /dev/null +++ b/src/UI/MinecartWithChestWindow.h @@ -0,0 +1,67 @@ + +// MinecartWithChestWindow.h + +// Representing the UI window for the minecart chest entity + + + + + +#pragma once + +#include "Window.h" +#include "../Entities/Minecart.h" + + + + + +class cMinecartWithChestWindow : + public cWindow +{ + typedef cWindow super; + +public: + cMinecartWithChestWindow(cMinecartWithChest * a_ChestCart) : + cWindow(wtChest, "Minecart with Chest"), + m_ChestCart(a_ChestCart) + { + m_SlotAreas.push_back(new cSlotAreaMinecartWithChest(a_ChestCart, *this)); + m_SlotAreas.push_back(new cSlotAreaInventory(*this)); + m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); + + a_ChestCart->GetWorld()->BroadcastSoundEffect("random.chestopen", a_ChestCart->GetPosX(), a_ChestCart->GetPosY(), a_ChestCart->GetPosZ(), 1, 1); + } + + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea* a_ClickedArea, bool a_ShouldApply) override + { + cSlotAreas AreasInOrder; + + if (a_ClickedArea == m_SlotAreas[0]) + { + // Chest Area + AreasInOrder.push_back(m_SlotAreas[2]); /* Hotbar */ + AreasInOrder.push_back(m_SlotAreas[1]); /* Inventory */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, true); + } + else + { + // Hotbar or Inventory + AreasInOrder.push_back(m_SlotAreas[0]); /* Chest */ + super::DistributeStackToAreas(a_ItemStack, a_Player, AreasInOrder, a_ShouldApply, false); + } + } + + + ~cMinecartWithChestWindow() + { + m_ChestCart->GetWorld()->BroadcastSoundEffect("random.chestclosed", m_ChestCart->GetPosX(), m_ChestCart->GetPosY(), m_ChestCart->GetPosZ(), 1, 1); + } + +private: + cMinecartWithChest * m_ChestCart; +}; + + + + diff --git a/src/UI/SlotArea.cpp b/src/UI/SlotArea.cpp index e784569d9..37683a8e5 100644 --- a/src/UI/SlotArea.cpp +++ b/src/UI/SlotArea.cpp @@ -1,3 +1,4 @@ + // SlotArea.cpp // Implements the cSlotArea class and its descendants @@ -12,6 +13,7 @@ #include "../BlockEntities/FurnaceEntity.h" #include "../Entities/Minecart.h" #include "../Items/ItemHandler.h" +#include "AnvilWindow.h" #include "Window.h" #include "../CraftingRecipes.h" #include "../Root.h" @@ -205,7 +207,7 @@ void cSlotArea::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ { // Make a copy of the slot, distribute it among the other areas, then update the slot to contain the leftover: cItem Slot(*GetSlot(a_SlotNum, a_Player)); - m_ParentWindow.DistributeStack(Slot, a_Player, this, true); + m_ParentWindow.DistributeStack(Slot, a_SlotNum, a_Player, this, true); if (Slot.IsEmpty()) { // Empty the slot completely, the client doesn't like left-over ItemType with zero count @@ -340,31 +342,31 @@ void cSlotArea::OnPlayerRemoved(cPlayer & a_Player) -void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) +void cSlotArea::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) { for (int i = 0; i < m_NumSlots; i++) { - const cItem * Slot = GetSlot(i, a_Player); + int SlotNum = (a_BackFill) ? (m_NumSlots - 1 - i) : i; + + const cItem * Slot = GetSlot(SlotNum, a_Player); if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) { // Different items continue; } - int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; + char NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; if (NumFit <= 0) { // Full stack already continue; } - if (NumFit > a_ItemStack.m_ItemCount) - { - NumFit = a_ItemStack.m_ItemCount; - } + NumFit = std::min(NumFit, a_ItemStack.m_ItemCount); + if (a_ShouldApply) { cItem NewSlot(a_ItemStack); NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit; - SetSlot(i, a_Player, NewSlot); + SetSlot(SlotNum, a_Player, NewSlot); } a_ItemStack.m_ItemCount -= NumFit; if (a_ItemStack.IsEmpty()) @@ -589,12 +591,13 @@ void cSlotAreaCrafting::SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & -void cSlotAreaCrafting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) +void cSlotAreaCrafting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) { UNUSED(a_ItemStack); UNUSED(a_Player); UNUSED(a_ShouldApply); UNUSED(a_KeepEmptySlots); + UNUSED(a_BackFill); } @@ -656,7 +659,7 @@ void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player) { // Try distributing the result. If it fails, bail out: cItem ResultCopy(Result); - m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, false); + m_ParentWindow.DistributeStack(ResultCopy, 0, a_Player, this, false); if (!ResultCopy.IsEmpty()) { // Couldn't distribute all of it. Bail out @@ -665,7 +668,7 @@ void cSlotAreaCrafting::ShiftClickedResult(cPlayer & a_Player) // Distribute the result, this time for real: ResultCopy = Result; - m_ParentWindow.DistributeStack(ResultCopy, a_Player, this, true); + m_ParentWindow.DistributeStack(ResultCopy, 0, a_Player, this, true); // Remove the ingredients from the crafting grid and update the recipe: cCraftingRecipe & Recipe = GetRecipeForPlayer(a_Player); @@ -769,7 +772,7 @@ void cSlotAreaCrafting::HandleCraftItem(const cItem & a_Result, cPlayer & a_Play //////////////////////////////////////////////////////////////////////////////// // cSlotAreaAnvil: -cSlotAreaAnvil::cSlotAreaAnvil(cAnvilWindow & a_ParentWindow) : +cSlotAreaAnvil::cSlotAreaAnvil(cWindow & a_ParentWindow) : cSlotAreaTemporary(3, a_ParentWindow), m_MaximumCost(0), m_StackSizeToBeUsedInRepair(0) @@ -894,7 +897,7 @@ void cSlotAreaAnvil::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem return; } - m_ParentWindow.DistributeStack(Slot, a_Player, this, true); + m_ParentWindow.DistributeStack(Slot, a_SlotNum, a_Player, this, true); if (Slot.IsEmpty()) { Slot.Empty(); @@ -910,31 +913,31 @@ void cSlotAreaAnvil::ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem -void cSlotAreaAnvil::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) +void cSlotAreaAnvil::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) { for (int i = 0; i < 2; i++) { - const cItem * Slot = GetSlot(i, a_Player); + int SlotNum = (a_BackFill) ? (2 - 1 - i) : i; + + const cItem * Slot = GetSlot(SlotNum, a_Player); if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) { // Different items continue; } - int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; + char NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; if (NumFit <= 0) { // Full stack already continue; } - if (NumFit > a_ItemStack.m_ItemCount) - { - NumFit = a_ItemStack.m_ItemCount; - } + NumFit = std::min(NumFit, a_ItemStack.m_ItemCount); + if (a_ShouldApply) { cItem NewSlot(a_ItemStack); NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit; - SetSlot(i, a_Player, NewSlot); + SetSlot(SlotNum, a_Player, NewSlot); } a_ItemStack.m_ItemCount -= NumFit; if (a_ItemStack.IsEmpty()) @@ -1051,7 +1054,7 @@ void cSlotAreaAnvil::UpdateResult(cPlayer & a_Player) cItem SecondInput(*GetSlot(1, a_Player)); cItem Output(*GetSlot(2, a_Player)); - if (Input.IsEmpty() && !Output.IsEmpty()) + if (Input.IsEmpty()) { Output.Empty(); SetSlot(2, a_Player, Output); @@ -1335,7 +1338,7 @@ void cSlotAreaBeacon::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ -void cSlotAreaBeacon::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) +void cSlotAreaBeacon::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) { const cItem * Slot = GetSlot(0, a_Player); if (!Slot->IsEmpty() || !IsPlaceableItem(a_ItemStack.m_ItemType) || (a_ItemStack.m_ItemCount != 1)) @@ -1390,13 +1393,12 @@ void cSlotAreaBeacon::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) //////////////////////////////////////////////////////////////////////////////// // cSlotAreaEnchanting: -cSlotAreaEnchanting::cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ) : +cSlotAreaEnchanting::cSlotAreaEnchanting(cWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ) : cSlotAreaTemporary(1, a_ParentWindow), m_BlockX(a_BlockX), m_BlockY(a_BlockY), m_BlockZ(a_BlockZ) { - a_ParentWindow.m_SlotArea = this; } @@ -1503,7 +1505,7 @@ void cSlotAreaEnchanting::Clicked(cPlayer & a_Player, int a_SlotNum, eClickActio -void cSlotAreaEnchanting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots) +void cSlotAreaEnchanting::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_Apply, bool a_KeepEmptySlots, bool a_BackFill) { const cItem * Slot = GetSlot(0, a_Player); if (!Slot->IsEmpty()) @@ -1833,38 +1835,50 @@ void cSlotAreaFurnace::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a -void cSlotAreaFurnace::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) +void cSlotAreaFurnace::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) { - for (int i = 0; i < 2; i++) + int SlotNum; + cFurnaceRecipe * FurnaceRecipes = cRoot::Get()->GetFurnaceRecipe(); + + if (FurnaceRecipes->GetRecipeFrom(a_ItemStack) != nullptr) { - const cItem * Slot = GetSlot(i, a_Player); - if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) - { - // Different items - continue; - } - int NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; - if (NumFit <= 0) - { - // Full stack already - continue; - } - if (NumFit > a_ItemStack.m_ItemCount) - { - NumFit = a_ItemStack.m_ItemCount; - } - if (a_ShouldApply) - { - cItem NewSlot(a_ItemStack); - NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit; - SetSlot(i, a_Player, NewSlot); - } - a_ItemStack.m_ItemCount -= NumFit; - if (a_ItemStack.IsEmpty()) - { - return; - } - } // for i - Slots + SlotNum = 0; + } + else if (FurnaceRecipes->IsFuel(a_ItemStack)) + { + SlotNum = 1; + } + else + { + return; + } + + const cItem * Slot = GetSlot(SlotNum, a_Player); + if (!Slot->IsEqual(a_ItemStack) && (!Slot->IsEmpty() || a_KeepEmptySlots)) + { + // Different items + return; + } + + char NumFit = ItemHandler(Slot->m_ItemType)->GetMaxStackSize() - Slot->m_ItemCount; + if (NumFit <= 0) + { + // Full stack already + return; + } + NumFit = std::min(NumFit, a_ItemStack.m_ItemCount); + + if (a_ShouldApply) + { + cItem NewSlot(a_ItemStack); + NewSlot.m_ItemCount = Slot->m_ItemCount + NumFit; + SetSlot(SlotNum, a_Player, NewSlot); + } + a_ItemStack.m_ItemCount -= NumFit; + if (a_ItemStack.IsEmpty()) + { + return; + } } @@ -2013,7 +2027,7 @@ void cSlotAreaInventoryBase::SetSlot(int a_SlotNum, cPlayer & a_Player, const cI //////////////////////////////////////////////////////////////////////////////// // cSlotAreaArmor: -void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) +void cSlotAreaArmor::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) { if (ItemCategory::IsHelmet(a_ItemStack.m_ItemType) && GetSlot(0, a_Player)->IsEmpty()) { diff --git a/src/UI/SlotArea.h b/src/UI/SlotArea.h index 1eeeb9836..e39d372c9 100644 --- a/src/UI/SlotArea.h +++ b/src/UI/SlotArea.h @@ -17,12 +17,10 @@ class cWindow; class cPlayer; class cBeaconEntity; class cChestEntity; -class cDropSpenserEntity; class cEnderChestEntity; class cFurnaceEntity; class cMinecartWithChest; class cCraftingRecipe; -class cEnchantingWindow; class cWorld; @@ -73,7 +71,7 @@ public: if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes) If a_KeepEmptySlots is true, empty slots will be skipped and won't be filled */ - virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots); + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill); /// Called on DblClicking to collect all stackable items into hand. /// The items are accumulated in a_Dragging and removed from the slots immediately. @@ -158,7 +156,7 @@ public: } /** Distributing the stack is allowed only for compatible items (helmets into helmet slot etc.) */ - virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override; /** Called when a player clicks in the window. Parameters taken from the click packet. */ virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; @@ -246,7 +244,7 @@ public: virtual void SetSlot (int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; // Distributing items into this area is completely disabled - virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override; protected: @@ -285,12 +283,12 @@ class cSlotAreaAnvil : typedef cSlotAreaTemporary super; public: - cSlotAreaAnvil(cAnvilWindow & a_ParentWindow); + cSlotAreaAnvil(cWindow & a_ParentWindow); // cSlotArea overrides: virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; virtual void ShiftClicked(cPlayer & a_Player, int a_SlotNum, const cItem & a_ClickedItem) override; - virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override; // cSlotAreaTemporary overrides: virtual void OnPlayerRemoved(cPlayer & a_Player) override; @@ -326,10 +324,10 @@ public: cSlotAreaBeacon(cBeaconEntity * a_Beacon, cWindow & a_ParentWindow); virtual ~cSlotAreaBeacon(); - bool IsPlaceableItem(short a_ItemType); + static bool IsPlaceableItem(short a_ItemType); virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; - virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override; virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; @@ -350,11 +348,11 @@ class cSlotAreaEnchanting : typedef cSlotAreaTemporary super; public: - cSlotAreaEnchanting(cEnchantingWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ); + cSlotAreaEnchanting(cWindow & a_ParentWindow, int a_BlockX, int a_BlockY, int a_BlockZ); // cSlotArea overrides: virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; - virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override; virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; // cSlotAreaTemporary overrides: @@ -439,7 +437,7 @@ public: virtual ~cSlotAreaFurnace(); virtual void Clicked(cPlayer & a_Player, int a_SlotNum, eClickAction a_ClickAction, const cItem & a_ClickedItem) override; - virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots) override; + virtual void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, bool a_ShouldApply, bool a_KeepEmptySlots, bool a_BackFill) override; virtual const cItem * GetSlot(int a_SlotNum, cPlayer & a_Player) const override; virtual void SetSlot(int a_SlotNum, cPlayer & a_Player, const cItem & a_Item) override; diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp index 1598dd3e7..bb2e2a807 100644 --- a/src/UI/Window.cpp +++ b/src/UI/Window.cpp @@ -32,7 +32,6 @@ cWindow::cWindow(WindowType a_WindowType, const AString & a_WindowTitle) : m_WindowType(a_WindowType), m_WindowTitle(a_WindowTitle), m_IsDestroyed(false), - m_ShouldDistributeToHotbarFirst(true), m_Owner(nullptr) { if (a_WindowType == wtInventory) @@ -392,43 +391,23 @@ bool cWindow::ForEachClient(cItemCallback<cClientHandle> & a_Callback) -void cWindow::DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, cSlotArea * a_ExcludeArea, bool a_ShouldApply) +void cWindow::DistributeStackToAreas(cItem & a_ItemStack, cPlayer & a_Player, cSlotAreas & a_AreasInOrder, bool a_ShouldApply, bool a_BackFill) { - // Ask each slot area to take as much of the stack as it can. - // First ask only slots that already have the same kind of item - // Then ask any remaining slots - for (int Pass = 0; Pass < 2; ++Pass) + /* Ask each slot area to take as much of the stack as it can. + First ask only slots that already have the same kind of item + Then ask any remaining slots */ + for (size_t Pass = 0; Pass < 2; Pass++) { - if (m_ShouldDistributeToHotbarFirst) + for (auto SlotArea : a_AreasInOrder) { - // First distribute into the hotbar: - if (a_ExcludeArea != m_SlotAreas.back()) - { - m_SlotAreas.back()->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0)); - if (a_ItemStack.IsEmpty()) - { - // Distributed it all - return; - } - } - } - - // The distribute to all other areas: - cSlotAreas::iterator end = m_ShouldDistributeToHotbarFirst ? (m_SlotAreas.end() - 1) : m_SlotAreas.end(); - for (cSlotAreas::iterator itr = m_SlotAreas.begin(); itr != end; ++itr) - { - if (*itr == a_ExcludeArea) - { - continue; - } - (*itr)->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0)); + SlotArea->DistributeStack(a_ItemStack, a_Player, a_ShouldApply, (Pass == 0), a_BackFill); if (a_ItemStack.IsEmpty()) { // Distributed it all return; } - } // for itr - m_SlotAreas[] - } // for Pass - repeat twice + } + } } @@ -779,401 +758,3 @@ void cWindow::SetProperty(short a_Property, short a_Value, cPlayer & a_Player) - -//////////////////////////////////////////////////////////////////////////////// -// cInventoryWindow: - -cInventoryWindow::cInventoryWindow(cPlayer & a_Player) : - cWindow(wtInventory, "Inventory"), - m_Player(a_Player) -{ - m_SlotAreas.push_back(new cSlotAreaCrafting(2, *this)); // The creative inventory doesn't display it, but it's still counted into slot numbers - m_SlotAreas.push_back(new cSlotAreaArmor(*this)); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cCraftingWindow: - -cCraftingWindow::cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : - cWindow(wtWorkbench, "Crafting Table") -{ - m_SlotAreas.push_back(new cSlotAreaCrafting(3, *this)); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cAnvilWindow: - -cAnvilWindow::cAnvilWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : - cWindow(wtAnvil, "Repair"), - m_RepairedItemName(""), - m_BlockX(a_BlockX), - m_BlockY(a_BlockY), - m_BlockZ(a_BlockZ) -{ - m_AnvilSlotArea = new cSlotAreaAnvil(*this); - m_SlotAreas.push_back(m_AnvilSlotArea); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); -} - - - - - -void cAnvilWindow::SetRepairedItemName(const AString & a_Name, cPlayer * a_Player) -{ - m_RepairedItemName = a_Name; - - if (a_Player != nullptr) - { - m_AnvilSlotArea->UpdateResult(*a_Player); - } -} - - - - - -void cAnvilWindow::GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ) -{ - a_PosX = m_BlockX; - a_PosY = m_BlockY; - a_PosZ = m_BlockZ; -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cBeaconWindow: - -cBeaconWindow::cBeaconWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconEntity * a_Beacon) : - cWindow(wtBeacon, "Beacon"), - m_Beacon(a_Beacon) -{ - m_ShouldDistributeToHotbarFirst = true; - m_SlotAreas.push_back(new cSlotAreaBeacon(m_Beacon, *this)); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); -} - - - - - -void cBeaconWindow::OpenedByPlayer(cPlayer & a_Player) -{ - super::OpenedByPlayer(a_Player); - - a_Player.GetClientHandle()->SendWindowProperty(*this, 0, m_Beacon->GetBeaconLevel()); - a_Player.GetClientHandle()->SendWindowProperty(*this, 1, m_Beacon->GetPrimaryEffect()); - a_Player.GetClientHandle()->SendWindowProperty(*this, 2, m_Beacon->GetSecondaryEffect()); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cEnchantingWindow: - -cEnchantingWindow::cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ) : - cWindow(wtEnchantment, "Enchant"), - m_SlotArea(), - m_BlockX(a_BlockX), - m_BlockY(a_BlockY), - m_BlockZ(a_BlockZ) -{ - m_SlotAreas.push_back(new cSlotAreaEnchanting(*this, m_BlockX, m_BlockY, m_BlockZ)); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); -} - - - - - -void cEnchantingWindow::SetProperty(short a_Property, short a_Value) -{ - if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue))) - { - ASSERT(!"a_Property is invalid"); - return; - } - - m_PropertyValue[a_Property] = a_Value; - super::SetProperty(a_Property, a_Value); -} - - - - - -void cEnchantingWindow::SetProperty(short a_Property, short a_Value, cPlayer & a_Player) -{ - if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue))) - { - ASSERT(!"a_Property is invalid"); - return; - } - - m_PropertyValue[a_Property] = a_Value; - super::SetProperty(a_Property, a_Value, a_Player); -} - - - - - -short cEnchantingWindow::GetPropertyValue(short a_Property) -{ - if ((a_Property < 0) || ((size_t)a_Property >= ARRAYCOUNT(m_PropertyValue))) - { - ASSERT(!"a_Property is invalid"); - return 0; - } - - return m_PropertyValue[a_Property]; -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cChestWindow: - -cChestWindow::cChestWindow(cChestEntity * a_Chest) : - cWindow(wtChest, (a_Chest->GetBlockType() == E_BLOCK_CHEST) ? "Chest" : "Trapped Chest"), - m_World(a_Chest->GetWorld()), - m_BlockX(a_Chest->GetPosX()), - m_BlockY(a_Chest->GetPosY()), - m_BlockZ(a_Chest->GetPosZ()), - m_PrimaryChest(a_Chest), - m_SecondaryChest(nullptr) -{ - m_SlotAreas.push_back(new cSlotAreaChest(a_Chest, *this)); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); - - // Play the opening sound: - m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); - - // Send out the chest-open packet: - m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_Chest->GetBlockType()); -} - - - - - -cChestWindow::cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest) : - cWindow(wtChest, (a_PrimaryChest->GetBlockType() == E_BLOCK_CHEST) ? "Double Chest" : "Double Trapped Chest"), - m_World(a_PrimaryChest->GetWorld()), - m_BlockX(a_PrimaryChest->GetPosX()), - m_BlockY(a_PrimaryChest->GetPosY()), - m_BlockZ(a_PrimaryChest->GetPosZ()), - m_PrimaryChest(a_PrimaryChest), - m_SecondaryChest(a_SecondaryChest) -{ - m_SlotAreas.push_back(new cSlotAreaDoubleChest(a_PrimaryChest, a_SecondaryChest, *this)); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); - - m_ShouldDistributeToHotbarFirst = false; - - // Play the opening sound: - m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); - - // Send out the chest-open packet: - m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, a_PrimaryChest->GetBlockType()); -} - - - - - -void cChestWindow::OpenedByPlayer(cPlayer & a_Player) -{ - int ChunkX, ChunkZ; - - m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() + 1); - cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ); - m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); - - if (m_SecondaryChest != nullptr) - { - m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() + 1); - cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ); - m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); - } - - cWindow::OpenedByPlayer(a_Player); -} - - - - - -bool cChestWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) -{ - int ChunkX, ChunkZ; - - m_PrimaryChest->SetNumberOfPlayers(m_PrimaryChest->GetNumberOfPlayers() - 1); - cChunkDef::BlockToChunk(m_PrimaryChest->GetPosX(), m_PrimaryChest->GetPosZ(), ChunkX, ChunkZ); - m_PrimaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); - - if (m_SecondaryChest != nullptr) - { - m_SecondaryChest->SetNumberOfPlayers(m_SecondaryChest->GetNumberOfPlayers() - 1); - cChunkDef::BlockToChunk(m_SecondaryChest->GetPosX(), m_SecondaryChest->GetPosZ(), ChunkX, ChunkZ); - m_SecondaryChest->GetWorld()->MarkRedstoneDirty(ChunkX, ChunkZ); - } - - cWindow::ClosedByPlayer(a_Player, a_CanRefuse); - return true; -} - - - - - -cChestWindow::~cChestWindow() -{ - // Send out the chest-close packet: - m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, m_PrimaryChest->GetBlockType()); - - m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cMinecartWithChestWindow: - -cMinecartWithChestWindow::cMinecartWithChestWindow(cMinecartWithChest * a_ChestCart) : - cWindow(wtChest, "Minecart with Chest"), - m_ChestCart(a_ChestCart) -{ - m_ShouldDistributeToHotbarFirst = false; - m_SlotAreas.push_back(new cSlotAreaMinecartWithChest(a_ChestCart, *this)); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); - - a_ChestCart->GetWorld()->BroadcastSoundEffect("random.chestopen", a_ChestCart->GetPosX(), a_ChestCart->GetPosY(), a_ChestCart->GetPosZ(), 1, 1); -} - - - - - -cMinecartWithChestWindow::~cMinecartWithChestWindow() -{ - m_ChestCart->GetWorld()->BroadcastSoundEffect("random.chestclosed", m_ChestCart->GetPosX(), m_ChestCart->GetPosY(), m_ChestCart->GetPosZ(), 1, 1); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cDropSpenserWindow: - -cDropSpenserWindow::cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_DropSpenser) : - cWindow(wtDropSpenser, (a_DropSpenser->GetBlockType() == E_BLOCK_DISPENSER) ? "Dispenser" : "Dropper") -{ - m_ShouldDistributeToHotbarFirst = false; - m_SlotAreas.push_back(new cSlotAreaItemGrid(a_DropSpenser->GetContents(), *this)); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cEnderChestWindow: - -cEnderChestWindow::cEnderChestWindow(cEnderChestEntity * a_EnderChest) : - cWindow(wtChest, "Ender Chest"), - m_World(a_EnderChest->GetWorld()), - m_BlockX(a_EnderChest->GetPosX()), - m_BlockY(a_EnderChest->GetPosY()), - m_BlockZ(a_EnderChest->GetPosZ()) -{ - m_ShouldDistributeToHotbarFirst = false; - m_SlotAreas.push_back(new cSlotAreaEnderChest(a_EnderChest, *this)); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); - - // Play the opening sound: - m_World->BroadcastSoundEffect("random.chestopen", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); - - // Send out the chest-open packet: - m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 1, E_BLOCK_ENDER_CHEST); -} - - - - - -cEnderChestWindow::~cEnderChestWindow() -{ - // Send out the chest-close packet: - m_World->BroadcastBlockAction(m_BlockX, m_BlockY, m_BlockZ, 1, 0, E_BLOCK_ENDER_CHEST); - - // Play the closing sound - m_World->BroadcastSoundEffect("random.chestclosed", (double)m_BlockX, (double)m_BlockY, (double)m_BlockZ, 1, 1); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cHopperWindow: - -cHopperWindow::cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper) : - super(wtHopper, "Hopper") -{ - m_ShouldDistributeToHotbarFirst = false; - m_SlotAreas.push_back(new cSlotAreaItemGrid(a_Hopper->GetContents(), *this)); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cFurnaceWindow: - -cFurnaceWindow::cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace) : - cWindow(wtFurnace, "Furnace") -{ - m_ShouldDistributeToHotbarFirst = false; - m_SlotAreas.push_back(new cSlotAreaFurnace(a_Furnace, *this)); - m_SlotAreas.push_back(new cSlotAreaInventory(*this)); - m_SlotAreas.push_back(new cSlotAreaHotBar(*this)); -} - - - - diff --git a/src/UI/Window.h b/src/UI/Window.h index e62176d50..9821aade1 100644 --- a/src/UI/Window.h +++ b/src/UI/Window.h @@ -19,7 +19,6 @@ class cPlayer; class cWindowOwner; class cClientHandle; class cChestEntity; -class cDropSpenserEntity; class cEnderChestEntity; class cFurnaceEntity; class cHopperEntity; @@ -154,14 +153,19 @@ public: /** Called on shift-clicking to distribute the stack into other areas; Modifies a_ItemStack as it is distributed! if a_ShouldApply is true, the changes are written into the slots; + if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes) */ + virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) = 0; + + /** Called from DistributeStack() to distribute the stack into a_AreasInOrder; Modifies a_ItemStack as it is distributed! + If a_ShouldApply is true, the changes are written into the slots; if a_ShouldApply is false, only a_ItemStack is modified to reflect the number of fits (for fit-testing purposes) - */ - void DistributeStack(cItem & a_ItemStack, cPlayer & a_Player, cSlotArea * a_ExcludeArea, bool a_ShouldApply); + If a_BackFill is true, the areas will be filled from the back (right side). (Example: Empty Hotbar -> Item get in slot 8, not slot 0) */ + void DistributeStackToAreas(cItem & a_ItemStack, cPlayer & a_Player, cSlotAreas & a_AreasInOrder, bool a_ShouldApply, bool a_BackFill); - /// Called on DblClicking to collect all stackable items from all areas into hand, starting with the specified area. - /// The items are accumulated in a_Dragging and removed from the SlotAreas immediately. - /// If a_CollectFullStacks is false, slots with full stacks in the area are skipped while collecting. - /// Returns true if full stack has been collected, false if there's space remaining to fill. + /** Called on DblClicking to collect all stackable items from all areas into hand, starting with the specified area. + The items are accumulated in a_Dragging and removed from the SlotAreas immediately. + If a_CollectFullStacks is false, slots with full stacks in the area are skipped while collecting. + Returns true if full stack has been collected, false if there's space remaining to fill. */ bool CollectItemsToHand(cItem & a_Dragging, cSlotArea & a_Area, cPlayer & a_Player, bool a_CollectFullStacks); /// Used by cSlotAreas to send individual slots to clients, a_RelativeSlotNum is the slot number relative to a_SlotArea @@ -178,7 +182,6 @@ protected: cPlayerList m_OpenedBy; bool m_IsDestroyed; - bool m_ShouldDistributeToHotbarFirst; ///< If set (default), shift+click tries to distribute to hotbar first, then other areas. False for doublechests cWindowOwner * m_Owner; @@ -219,188 +222,3 @@ protected: - -class cCraftingWindow : - public cWindow -{ - typedef cWindow super; -public: - cCraftingWindow(int a_BlockX, int a_BlockY, int a_BlockZ); -} ; - - - - - -class cAnvilWindow : - public cWindow -{ - typedef cWindow super; -public: - cAnvilWindow(int a_BlockX, int a_BlockY, int a_BlockZ); - - /** Gets the repaired item name. */ - AString GetRepairedItemName(void) const { return m_RepairedItemName; } - - /** Set the repaired item name. */ - void SetRepairedItemName(const AString & a_Name, cPlayer * a_Player); - - /** Gets the Position from the Anvil */ - void GetBlockPos(int & a_PosX, int & a_PosY, int & a_PosZ); - -protected: - cSlotAreaAnvil * m_AnvilSlotArea; - AString m_RepairedItemName; - int m_BlockX, m_BlockY, m_BlockZ; -} ; - - - - - -class cBeaconWindow : - public cWindow -{ - typedef cWindow super; -public: - cBeaconWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cBeaconEntity * a_Beacon); - - cBeaconEntity * GetBeaconEntity(void) const { return m_Beacon; } - - // cWindow Overrides: - virtual void OpenedByPlayer(cPlayer & a_Player) override; - -protected: - cBeaconEntity * m_Beacon; -} ; - - - - - -class cEnchantingWindow : - public cWindow -{ - typedef cWindow super; -public: - cEnchantingWindow(int a_BlockX, int a_BlockY, int a_BlockZ); - virtual void SetProperty(short a_Property, short a_Value, cPlayer & a_Player) override; - virtual void SetProperty(short a_Property, short a_Value) override; - - /** Return the Value of a Property */ - short GetPropertyValue(short a_Property); - - cSlotArea * m_SlotArea; - -protected: - short m_PropertyValue[3]; - int m_BlockX, m_BlockY, m_BlockZ; -}; - - - - - -class cFurnaceWindow : - public cWindow -{ - typedef cWindow super; -public: - cFurnaceWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceEntity * a_Furnace); -} ; - - - - - -class cDropSpenserWindow : - public cWindow -{ - typedef cWindow super; -public: - cDropSpenserWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cDropSpenserEntity * a_Dispenser); -} ; - - - - - -class cHopperWindow : - public cWindow -{ - typedef cWindow super; -public: - cHopperWindow(int a_BlockX, int a_BlockY, int a_BlockZ, cHopperEntity * a_Hopper); -} ; - - - - - -class cChestWindow : - public cWindow -{ -public: - cChestWindow(cChestEntity * a_Chest); - cChestWindow(cChestEntity * a_PrimaryChest, cChestEntity * a_SecondaryChest); - ~cChestWindow(); - - virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; - virtual void OpenedByPlayer(cPlayer & a_Player) override; - -protected: - cWorld * m_World; - int m_BlockX, m_BlockY, m_BlockZ; // Position of the chest, for the window-close packet - cChestEntity * m_PrimaryChest; - cChestEntity * m_SecondaryChest; -} ; - - - - - -class cMinecartWithChestWindow : - public cWindow -{ -public: - cMinecartWithChestWindow(cMinecartWithChest * a_ChestCart); - ~cMinecartWithChestWindow(); -private: - cMinecartWithChest * m_ChestCart; -}; - - - - - -class cEnderChestWindow : - public cWindow -{ -public: - cEnderChestWindow(cEnderChestEntity * a_EnderChest); - ~cEnderChestWindow(); - -protected: - cWorld * m_World; - int m_BlockX, m_BlockY, m_BlockZ; // Position of the enderchest, for the window-close packet -}; - - - - - -class cInventoryWindow : - public cWindow -{ -public: - cInventoryWindow(cPlayer & a_Player); - -protected: - cPlayer & m_Player; - -} ; - - - - - diff --git a/src/WebAdmin.h b/src/WebAdmin.h index 86a8a9a4b..1e1a9bfa9 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -95,7 +95,9 @@ struct sWebAdminPage // tolua_begin class cWebAdmin : + // tolua_end public cHTTPServer::cCallbacks + // tolua_begin { public: // tolua_end diff --git a/src/World.cpp b/src/World.cpp index 474f77b81..7d63a8a52 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -259,9 +259,9 @@ void cWorld::cTickThread::Execute(void) //////////////////////////////////////////////////////////////////////////////// // cWorld: -cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AString & a_OverworldName) : +cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AString & a_LinkedOverworldName) : m_WorldName(a_WorldName), - m_OverworldName(a_OverworldName), + m_LinkedOverworldName(a_LinkedOverworldName), m_IniFileName(m_WorldName + "/world.ini"), m_StorageSchema("Default"), #ifdef __arm__ @@ -604,12 +604,12 @@ void cWorld::Start(void) if (GetDimension() == dimOverworld) { - m_NetherWorldName = IniFile.GetValueSet("LinkedWorlds", "NetherWorldName", GetName() + "_nether"); - m_EndWorldName = IniFile.GetValueSet("LinkedWorlds", "EndWorldName", GetName() + "_end"); + m_LinkedNetherWorldName = IniFile.GetValueSet("LinkedWorlds", "NetherWorldName", GetName() + "_nether"); + m_LinkedEndWorldName = IniFile.GetValueSet("LinkedWorlds", "EndWorldName", GetName() + "_end"); } else { - m_OverworldName = IniFile.GetValueSet("LinkedWorlds", "OverworldName", GetLinkedOverworldName()); + m_LinkedOverworldName = IniFile.GetValueSet("LinkedWorlds", "OverworldName", GetLinkedOverworldName()); } // Adjust the enum-backed variables into their respective bounds: @@ -667,18 +667,23 @@ void cWorld::Start(void) void cWorld::GenerateRandomSpawn(void) { LOGD("Generating random spawnpoint..."); - + bool foundSpawnPoint = false; // Look for a spawn point at most 100 chunks away from map center: for (int i = 0; i < 100; i++) { EMCSBiome biome = GetBiomeAt((int)m_SpawnX, (int)m_SpawnZ); + if ( (biome != biOcean) && (biome != biFrozenOcean) && // The biome is acceptable (don't want a small ocean island) !IsBlockWaterOrIce(GetBlock((int)m_SpawnX, GetHeight((int)m_SpawnX, (int)m_SpawnZ), (int)m_SpawnZ)) // The terrain is acceptable (don't want to spawn inside a lake / river) ) { - // A good spawnpoint was found - break; + if (CheckPlayerSpawnPoint((int)m_SpawnX, GetHeight((int)m_SpawnX, (int)m_SpawnZ), (int)m_SpawnZ)) + { + // A good spawnpoint was found + foundSpawnPoint = true; + break; + } } // Try a neighboring chunk: if ((GetTickRandomNumber(4) % 2) == 0) // Randomise whether to increment X or Z coords @@ -692,8 +697,60 @@ void cWorld::GenerateRandomSpawn(void) } // for i - 100* m_SpawnY = (double)GetHeight((int)m_SpawnX, (int)m_SpawnZ) + 1.6f; // 1.6f to accomodate player height + if (foundSpawnPoint) + { + LOGINFO("Generated random spawnpoint position at {%i, %i, %i}", (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ); + } + else + { + LOGINFO("Did not find an acceptable spawnpoint. Generated a random spawnpoint position at {%i, %i, %i}", (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ); + } // Maybe widen the search instead? + +} + + + + + +bool cWorld::CheckPlayerSpawnPoint(int a_PosX, int a_PosY, int a_PosZ) +{ + // Check that spawnblock and surrounding blocks are neither solid nor water / lava + static const struct + { + int x, z; + } Coords[] = + { + { 0, 0 }, + { -1, 0 }, + { 1, 0 }, + { 0, -1 }, + { 0, 1 }, + }; + for (size_t i = 0; i < ARRAYCOUNT(Coords); i++) + { + BLOCKTYPE BlockType = GetBlock(a_PosX + Coords[i].x, a_PosY, a_PosZ + Coords[i].x); + if (cBlockInfo::IsSolid(BlockType) || IsBlockLiquid(BlockType)) + { + return false; + } + } // for i - Coords[] - LOGINFO("Generated random spawnpoint position {%i, %i, %i}", (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ); + // Check that the block below is solid: + if (!cBlockInfo::IsSolid(GetBlock(a_PosX, a_PosY - 1, a_PosZ))) + { + return false; + } + + // Check that all the blocks above the spawnpoint are not solid: + for (int i = a_PosY; i < cChunkDef::Height; i++) + { + BLOCKTYPE BlockType = GetBlock(a_PosX, i, a_PosZ); + if (cBlockInfo::IsSolid(BlockType)) + { + return false; + } + } + return true; } @@ -827,18 +884,18 @@ void cWorld::Stop(void) IniFile.ReadFile(m_IniFileName); if (GetDimension() == dimOverworld) { - IniFile.SetValue("LinkedWorlds", "NetherWorldName", m_NetherWorldName); - IniFile.SetValue("LinkedWorlds", "EndWorldName", m_EndWorldName); + IniFile.SetValue("LinkedWorlds", "NetherWorldName", m_LinkedNetherWorldName); + IniFile.SetValue("LinkedWorlds", "EndWorldName", m_LinkedEndWorldName); } else { - IniFile.SetValue("LinkedWorlds", "OverworldName", m_OverworldName); + IniFile.SetValue("LinkedWorlds", "OverworldName", m_LinkedOverworldName); } - IniFile.SetValueI("Physics", "TNTShrapnelLevel", (int)m_TNTShrapnelLevel); + IniFile.SetValueI("Physics", "TNTShrapnelLevel", static_cast<int>(m_TNTShrapnelLevel)); IniFile.SetValueB("Mechanics", "CommandBlocksEnabled", m_bCommandBlocksEnabled); IniFile.SetValueB("Mechanics", "UseChatPrefixes", m_bUseChatPrefixes); IniFile.SetValueB("General", "IsDaylightCycleEnabled", m_IsDaylightCycleEnabled); - IniFile.SetValueI("General", "Weather", (int)m_Weather); + IniFile.SetValueI("General", "Weather", static_cast<int>(m_Weather)); IniFile.SetValueI("General", "TimeInTicks", GetTimeOfDay()); IniFile.WriteFile(m_IniFileName); @@ -2851,6 +2908,20 @@ bool cWorld::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback & a_ bool cWorld::DoWithEntityByID(int a_UniqueID, cEntityCallback & a_Callback) { + // First check the entities-to-add: + { + cCSLock Lock(m_CSEntitiesToAdd); + for (auto & ent: m_EntitiesToAdd) + { + if (ent->GetUniqueID() == a_UniqueID) + { + a_Callback.Item(ent); + return true; + } + } // for ent - m_EntitiesToAdd[] + } + + // Then check the chunkmap: return m_ChunkMap->DoWithEntityByID(a_UniqueID, a_Callback); } @@ -3096,14 +3167,14 @@ void cWorld::SaveAllChunks(void) void cWorld::QueueSaveAllChunks(void) { - QueueTask(make_unique<cWorld::cTaskSaveAllChunks>()); + QueueTask(std::make_shared<cWorld::cTaskSaveAllChunks>()); } -void cWorld::QueueTask(std::unique_ptr<cTask> a_Task) +void cWorld::QueueTask(cTaskPtr a_Task) { cCSLock Lock(m_CSTasks); m_Tasks.push_back(std::move(a_Task)); @@ -3113,7 +3184,7 @@ void cWorld::QueueTask(std::unique_ptr<cTask> a_Task) -void cWorld::ScheduleTask(int a_DelayTicks, cTask * a_Task) +void cWorld::ScheduleTask(int a_DelayTicks, cTaskPtr a_Task) { Int64 TargetTick = a_DelayTicks + std::chrono::duration_cast<cTickTimeLong>(m_WorldAge).count(); @@ -3123,11 +3194,11 @@ void cWorld::ScheduleTask(int a_DelayTicks, cTask * a_Task) { if ((*itr)->m_TargetTick >= TargetTick) { - m_ScheduledTasks.insert(itr, make_unique<cScheduledTask>(TargetTick, a_Task)); + m_ScheduledTasks.insert(itr, cScheduledTaskPtr(new cScheduledTask(TargetTick, a_Task))); return; } } - m_ScheduledTasks.push_back(make_unique<cScheduledTask>(TargetTick, a_Task)); + m_ScheduledTasks.push_back(cScheduledTaskPtr(new cScheduledTask(TargetTick, a_Task))); } @@ -3578,7 +3649,7 @@ void cWorld::cTaskUnloadUnusedChunks::Run(cWorld & a_World) //////////////////////////////////////////////////////////////////////////////// -// cWorld::cTaskSendBlockTo +// cWorld::cTaskSendBlockToAllPlayers cWorld::cTaskSendBlockToAllPlayers::cTaskSendBlockToAllPlayers(std::vector<Vector3i> & a_SendQueue) : m_SendQueue(a_SendQueue) diff --git a/src/World.h b/src/World.h index 3cac71a36..ffc10a9e4 100644 --- a/src/World.h +++ b/src/World.h @@ -106,7 +106,8 @@ public: virtual void Run(cWorld & a_World) = 0; } ; - typedef std::vector<std::unique_ptr<cTask>> cTasks; + typedef SharedPtr<cTask> cTaskPtr; + typedef std::vector<cTaskPtr> cTasks; class cTaskSaveAllChunks : @@ -673,14 +674,14 @@ public: bool ShouldBroadcastAchievementMessages(void) const { return m_BroadcastAchievementMessages; } - AString GetNetherWorldName(void) const { return m_NetherWorldName; } - void SetNetherWorldName(const AString & a_Name) { m_NetherWorldName = a_Name; } + AString GetLinkedNetherWorldName(void) const { return m_LinkedNetherWorldName; } + void SetLinkedNetherWorldName(const AString & a_Name) { m_LinkedNetherWorldName = a_Name; } - AString GetEndWorldName(void) const { return m_EndWorldName; } - void SetEndWorldName(const AString & a_Name) { m_EndWorldName = a_Name; } + AString GetLinkedEndWorldName(void) const { return m_LinkedEndWorldName; } + void SetLinkedEndWorldName(const AString & a_Name) { m_LinkedEndWorldName = a_Name; } - AString GetLinkedOverworldName(void) const { return m_OverworldName; } - void SetLinkedOverworldName(const AString & a_Name) { m_OverworldName = a_Name; } + AString GetLinkedOverworldName(void) const { return m_LinkedOverworldName; } + void SetLinkedOverworldName(const AString & a_Name) { m_LinkedOverworldName = a_Name; } // tolua_end @@ -691,11 +692,10 @@ public: void QueueSaveAllChunks(void); // tolua_export /** Queues a task onto the tick thread. The task object will be deleted once the task is finished */ - void QueueTask(std::unique_ptr<cTask> a_Task); // Exported in ManualBindings.cpp + void QueueTask(cTaskPtr 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); + /** Queues a task onto the tick thread, with the specified delay. */ + void ScheduleTask(int a_DelayTicks, cTaskPtr a_Task); /** Returns the number of chunks loaded */ int GetNumChunks() const; // tolua_export @@ -867,20 +867,16 @@ private: { public: Int64 m_TargetTick; - cTask * m_Task; + cTaskPtr m_Task; /** Creates a new scheduled task; takes ownership of the task object passed to it. */ - cScheduledTask(Int64 a_TargetTick, cTask * a_Task) : + cScheduledTask(Int64 a_TargetTick, cTaskPtr a_Task) : m_TargetTick(a_TargetTick), m_Task(a_Task) { } - virtual ~cScheduledTask() - { - delete m_Task; - m_Task = nullptr; - } + virtual ~cScheduledTask() {} }; typedef std::unique_ptr<cScheduledTask> cScheduledTaskPtr; @@ -889,10 +885,9 @@ private: AString m_WorldName; - /** The name of the world that a portal in this world should link to - Only has effect if this world is a nether or end world, as it is used by entities to see which world to teleport to when in a portal - */ - AString m_OverworldName; + /** The name of the overworld that portals in this world should link to. + Only has effect if this world is a Nether or End world. */ + AString m_LinkedOverworldName; AString m_IniFileName; @@ -985,11 +980,13 @@ private: /** The maximum view distance that a player can have in this world. */ int m_MaxViewDistance; - /** Name of the nether world */ - AString m_NetherWorldName; + /** Name of the nether world - where Nether portals should teleport. + Only used when this world is an Overworld. */ + AString m_LinkedNetherWorldName; - /** Name of the end world */ - AString m_EndWorldName; + /** Name of the End world - where End portals should teleport. + Only used when this world is an Overworld. */ + AString m_LinkedEndWorldName; cChunkGenerator m_Generator; @@ -1049,7 +1046,7 @@ private: cSetChunkDataPtrs m_SetChunkDataQueue; - cWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_OverworldName = ""); + cWorld(const AString & a_WorldName, eDimension a_Dimension = dimOverworld, const AString & a_LinkedOverworldName = ""); virtual ~cWorld(); void Tick(std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec); @@ -1077,6 +1074,9 @@ private: /** <summary>Generates a random spawnpoint on solid land by walking chunks and finding their biomes</summary> */ void GenerateRandomSpawn(void); + /** Check if player starting point is acceptable **/ + bool CheckPlayerSpawnPoint(int a_PosX, int a_PosY, int a_PosZ); + /** Chooses a reasonable transition from the current weather to a new weather **/ eWeather ChooseNewWeather(void); diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp index c87397542..10231ae3b 100644 --- a/src/WorldStorage/NBTChunkSerializer.cpp +++ b/src/WorldStorage/NBTChunkSerializer.cpp @@ -36,20 +36,9 @@ #include "../Entities/ExpOrb.h" #include "../Entities/HangingEntity.h" #include "../Entities/ItemFrame.h" +#include "../Entities/Painting.h" -#include "../Mobs/Monster.h" -#include "../Mobs/Bat.h" -#include "../Mobs/Creeper.h" -#include "../Mobs/Enderman.h" -#include "../Mobs/Horse.h" -#include "../Mobs/MagmaCube.h" -#include "../Mobs/Sheep.h" -#include "../Mobs/Slime.h" -#include "../Mobs/Skeleton.h" -#include "../Mobs/Villager.h" -#include "../Mobs/Wither.h" -#include "../Mobs/Wolf.h" -#include "../Mobs/Zombie.h" +#include "../Mobs/IncludeAllMonsters.h" @@ -726,24 +715,10 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile) void cNBTChunkSerializer::AddHangingEntity(cHangingEntity * a_Hanging) { - m_Writer.AddInt("TileX", a_Hanging->GetBlockX()); - m_Writer.AddInt("TileY", a_Hanging->GetBlockY()); - m_Writer.AddInt("TileZ", a_Hanging->GetBlockZ()); - switch (a_Hanging->GetFacing()) - { - case BLOCK_FACE_XM: m_Writer.AddByte("Facing", 1); break; - case BLOCK_FACE_XP: m_Writer.AddByte("Facing", 3); break; - case BLOCK_FACE_ZM: m_Writer.AddByte("Facing", 2); break; - case BLOCK_FACE_ZP: m_Writer.AddByte("Facing", 0); break; - - case BLOCK_FACE_YM: - case BLOCK_FACE_YP: - case BLOCK_FACE_NONE: - { - // These directions are invalid, but they may have been previously loaded, so keep them. - break; - } - } + m_Writer.AddInt("TileX", FloorC(a_Hanging->GetPosX())); + m_Writer.AddInt("TileY", FloorC(a_Hanging->GetPosY())); + m_Writer.AddInt("TileZ", FloorC(a_Hanging->GetPosZ())); + m_Writer.AddByte("Facing", a_Hanging->GetProtocolFacing()); } @@ -790,6 +765,19 @@ void cNBTChunkSerializer::AddItemFrameEntity(cItemFrame * a_ItemFrame) +void cNBTChunkSerializer::AddPaintingEntity(cPainting * a_Painting) +{ + m_Writer.BeginCompound(""); + AddBasicEntity(a_Painting, "Painting"); + AddHangingEntity(a_Painting); + m_Writer.AddString("Motive", a_Painting->GetName()); + m_Writer.EndCompound(); +} + + + + + void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart) { m_Writer.BeginList("Items", TAG_Compound); @@ -888,7 +876,7 @@ void cNBTChunkSerializer::Entity(cEntity * a_Entity) case cEntity::etTNT: AddTNTEntity ((cTNTEntity *) a_Entity); break; case cEntity::etExpOrb: AddExpOrbEntity ((cExpOrb *) a_Entity); break; case cEntity::etItemFrame: AddItemFrameEntity ((cItemFrame *) a_Entity); break; - case cEntity::etPainting: /* TODO */ break; + case cEntity::etPainting: AddPaintingEntity (reinterpret_cast<cPainting *>(a_Entity)); break; case cEntity::etPlayer: return; // Players aren't saved into the world default: { diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h index 4c066b9af..f30cd59d5 100644 --- a/src/WorldStorage/NBTChunkSerializer.h +++ b/src/WorldStorage/NBTChunkSerializer.h @@ -48,6 +48,7 @@ class cTNTEntity; class cExpOrb; class cHangingEntity; class cItemFrame; +class cPainting; class cEntityEffect; @@ -123,6 +124,7 @@ protected: void AddTNTEntity (cTNTEntity * a_TNT); void AddExpOrbEntity (cExpOrb * a_ExpOrb); void AddItemFrameEntity (cItemFrame * a_ItemFrame); + void AddPaintingEntity (cPainting * a_Painting); void AddMinecartChestContents(cMinecartWithChest * a_Minecart); diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index cc8b8d3f5..7244bcb73 100755 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -50,6 +50,7 @@ #include "../Entities/ExpOrb.h" #include "../Entities/HangingEntity.h" #include "../Entities/ItemFrame.h" +#include "../Entities/Painting.h" #include "../Protocol/MojangAPI.h" #include "Server.h" @@ -1337,6 +1338,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a { LoadPickupFromNBT(a_Entities, a_NBT, a_EntityTagIdx); } + else if (strncmp(a_IDTag, "Painting", a_IDTagLength) == 0) + { + LoadPaintingFromNBT(a_Entities, a_NBT, a_EntityTagIdx); + } else if (strncmp(a_IDTag, "PrimedTnt", a_IDTagLength) == 0) { LoadTNTFromNBT(a_Entities, a_NBT, a_EntityTagIdx); @@ -1747,52 +1752,22 @@ void cWSSAnvil::LoadHangingFromNBT(cHangingEntity & a_Hanging, const cParsedNBT { // "Facing" tag is the prime source of the Facing; if not available, translate from older "Direction" or "Dir" int Facing = a_NBT.FindChildByName(a_TagIdx, "Facing"); - if (Facing > 0) + if (Facing < 0) { - Facing = (int)a_NBT.GetByte(Facing); - if ((Facing >= 2) && (Facing <= 5)) - { - a_Hanging.SetFacing(static_cast<eBlockFace>(Facing)); - } - } - else - { - Facing = a_NBT.FindChildByName(a_TagIdx, "Direction"); - if (Facing > 0) - { - switch ((int)a_NBT.GetByte(Facing)) - { - case 0: a_Hanging.SetFacing(BLOCK_FACE_ZM); break; - case 1: a_Hanging.SetFacing(BLOCK_FACE_XM); break; - case 2: a_Hanging.SetFacing(BLOCK_FACE_ZP); break; - case 3: a_Hanging.SetFacing(BLOCK_FACE_XP); break; - } - } - else - { - Facing = a_NBT.FindChildByName(a_TagIdx, "Dir"); // Has values 0 and 2 swapped - if (Facing > 0) - { - switch ((int)a_NBT.GetByte(Facing)) - { - case 0: a_Hanging.SetFacing(BLOCK_FACE_ZP); break; - case 1: a_Hanging.SetFacing(BLOCK_FACE_XM); break; - case 2: a_Hanging.SetFacing(BLOCK_FACE_ZM); break; - case 3: a_Hanging.SetFacing(BLOCK_FACE_XP); break; - } - } - } + return; } + a_Hanging.SetProtocolFacing(a_NBT.GetByte(Facing)); + int TileX = a_NBT.FindChildByName(a_TagIdx, "TileX"); int TileY = a_NBT.FindChildByName(a_TagIdx, "TileY"); int TileZ = a_NBT.FindChildByName(a_TagIdx, "TileZ"); if ((TileX > 0) && (TileY > 0) && (TileZ > 0)) { a_Hanging.SetPosition( - (double)a_NBT.GetInt(TileX), - (double)a_NBT.GetInt(TileY), - (double)a_NBT.GetInt(TileZ) + static_cast<double>(a_NBT.GetInt(TileX)), + static_cast<double>(a_NBT.GetInt(TileY)), + static_cast<double>(a_NBT.GetInt(TileZ)) ); } } @@ -1838,6 +1813,29 @@ void cWSSAnvil::LoadItemFrameFromNBT(cEntityList & a_Entities, const cParsedNBT +void cWSSAnvil::LoadPaintingFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) +{ + // Load painting name: + int MotiveTag = a_NBT.FindChildByName(a_TagIdx, "Motive"); + if ((MotiveTag < 0) || (a_NBT.GetType(MotiveTag) != TAG_String)) + { + return; + } + + std::unique_ptr<cPainting> Painting(new cPainting(a_NBT.GetString(MotiveTag), BLOCK_FACE_NONE, 0.0, 0.0, 0.0)); + if (!LoadEntityBaseFromNBT(*Painting.get(), a_NBT, a_TagIdx)) + { + return; + } + + LoadHangingFromNBT(*Painting.get(), a_NBT, a_TagIdx); + a_Entities.push_back(Painting.release()); +} + + + + + void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx) { std::unique_ptr<cArrowEntity> Arrow(new cArrowEntity(nullptr, 0, 0, 0, Vector3d(0, 0, 0))); diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h index 362796614..892645785 100755 --- a/src/WorldStorage/WSSAnvil.h +++ b/src/WorldStorage/WSSAnvil.h @@ -166,6 +166,7 @@ protected: void LoadExpOrbFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadHangingFromNBT (cHangingEntity & a_Hanging, const cParsedNBT & a_NBT, int a_TagIdx); void LoadItemFrameFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); + void LoadPaintingFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx); diff --git a/src/main.cpp b/src/main.cpp index 20609a2f8..428e89e93 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -282,8 +282,12 @@ int main( int argc, char **argv) } } // for i - argv[] + // Initialize logging subsystem: cLogger::InitiateMultithreading(); + // Initialize LibEvent: + cNetworkSingleton::Get(); + #if !defined(ANDROID_NDK) try #endif |