From a4dfd7a7aa927e26279cfb38306a535b161429d8 Mon Sep 17 00:00:00 2001 From: Quentin Bazin Date: Sat, 18 Jul 2020 01:35:59 +0200 Subject: [PATCH] Item stack limit added (server global + per block/item definition). --- docs/lua-api-block.md | 11 ++++++ docs/lua-api-cpp.md | 2 +- docs/lua-api-item.md | 11 ++++++ mods/default/tools.lua | 1 + source/client/gui/AbstractInventoryWidget.hpp | 2 +- source/client/gui/CraftingWidget.cpp | 8 ++-- source/client/gui/CraftingWidget.hpp | 2 +- source/client/gui/InventoryWidget.cpp | 29 +++++++------- source/client/gui/InventoryWidget.hpp | 2 +- source/client/gui/MouseItemWidget.cpp | 16 ++++++-- source/common/core/EngineConfig.hpp | 2 + source/common/inventory/Inventory.cpp | 39 ++++++++++++++----- source/common/inventory/Inventory.hpp | 4 +- source/common/inventory/Item.cpp | 4 +- source/common/inventory/Item.hpp | 5 +++ source/server/core/ServerConfig.cpp | 3 ++ source/server/core/ServerConfig.hpp | 1 + source/server/lua/LuaMod.cpp | 5 ++- source/server/lua/loader/LuaBlockLoader.cpp | 2 + source/server/lua/loader/LuaItemLoader.cpp | 2 + 20 files changed, 109 insertions(+), 42 deletions(-) diff --git a/docs/lua-api-block.md b/docs/lua-api-block.md index cc7249200..6355c66d4 100644 --- a/docs/lua-api-block.md +++ b/docs/lua-api-block.md @@ -162,6 +162,17 @@ item_drop = { } -- this is the default drop of a 'default:cobblestone' block ``` +### `max_stack_size` + +Max amount of blocks in a stack. + +Example: +```lua +max_stack_size = 64 +``` + +If not defined, it defaults to the server config. + ### `name` Label of the block. **Mandatory field.** diff --git a/docs/lua-api-cpp.md b/docs/lua-api-cpp.md index baacbec39..d80c2ed46 100644 --- a/docs/lua-api-cpp.md +++ b/docs/lua-api-cpp.md @@ -73,7 +73,7 @@ ## Inventory -- `void add_stack(string name, u16 amount)` +- `ItemStack add_stack(string name, u16 amount)` - `ItemStack get_stack(u16 x, u16 y)` - `void set_stack(u16 x, u16 y, string name, u16 amount)` diff --git a/docs/lua-api-item.md b/docs/lua-api-item.md index b003841fa..7299c1ee1 100644 --- a/docs/lua-api-item.md +++ b/docs/lua-api-item.md @@ -70,6 +70,17 @@ Example: mining_speed = 1 -- this is the default value ``` +### `max_stack_size` + +Max amount of items in a stack. + +Example: +```lua +max_stack_size = 64 +``` + +If not defined, it defaults to the server config. + ### `name` Label of the item. **Mandatory field.** diff --git a/mods/default/tools.lua b/mods/default/tools.lua index 002abc92e..592288347 100644 --- a/mods/default/tools.lua +++ b/mods/default/tools.lua @@ -30,6 +30,7 @@ function register_tool(name, material, mining_speed, harvest_capability) id = material .. "_" .. name, name = material:gsub("^%l", string.upper) .. " " .. name:gsub("^%l", string.upper), tiles = material .. "_" .. name .. ".png", + max_stack_size = 1, } if mining_speed then diff --git a/source/client/gui/AbstractInventoryWidget.hpp b/source/client/gui/AbstractInventoryWidget.hpp index 7fa233fd5..f179d8e87 100644 --- a/source/client/gui/AbstractInventoryWidget.hpp +++ b/source/client/gui/AbstractInventoryWidget.hpp @@ -35,7 +35,7 @@ class AbstractInventoryWidget : public Widget { : Widget(parent), m_isReadOnly(isReadOnly) {} virtual bool sendItemStackToDest(const ItemWidget *itemStack, AbstractInventoryWidget *dest) = 0; - virtual bool receiveItemStack(const ItemWidget *itemStack, AbstractInventoryWidget *src) = 0; + virtual ItemStack receiveItemStack(const ItemWidget *itemStack, AbstractInventoryWidget *src) = 0; const std::vector &shiftDestination() const { return m_shiftDestination; } void setShiftDestination(const std::string &shiftDestination); diff --git a/source/client/gui/CraftingWidget.cpp b/source/client/gui/CraftingWidget.cpp index 565e1fb28..a0ceb8655 100644 --- a/source/client/gui/CraftingWidget.cpp +++ b/source/client/gui/CraftingWidget.cpp @@ -91,15 +91,15 @@ bool CraftingWidget::sendItemStackToDest(const ItemWidget *itemStack, AbstractIn return false; } -bool CraftingWidget::receiveItemStack(const ItemWidget *itemStack, AbstractInventoryWidget *) { - bool stackAdded = m_craftingInventory.addStack(itemStack->stack().item().stringID(), itemStack->stack().amount()); +ItemStack CraftingWidget::receiveItemStack(const ItemWidget *itemStack, AbstractInventoryWidget *) { + ItemStack stackRet = m_craftingInventory.addStack(itemStack->stack().item().stringID(), itemStack->stack().amount()); - if (stackAdded) { + if (stackRet.amount() != itemStack->stack().amount()) { m_craftingInventoryWidget.update(); m_craftingInventoryWidget.sendUpdatePacket(); } - return stackAdded; + return stackRet; } void CraftingWidget::draw(gk::RenderTarget &target, gk::RenderStates states) const { diff --git a/source/client/gui/CraftingWidget.hpp b/source/client/gui/CraftingWidget.hpp index fc2d081a0..0bf196778 100644 --- a/source/client/gui/CraftingWidget.hpp +++ b/source/client/gui/CraftingWidget.hpp @@ -42,7 +42,7 @@ class CraftingWidget : public AbstractInventoryWidget { void update() override; bool sendItemStackToDest(const ItemWidget *itemStack, AbstractInventoryWidget *dest) override; - bool receiveItemStack(const ItemWidget *itemStack, AbstractInventoryWidget *src) override; + ItemStack receiveItemStack(const ItemWidget *itemStack, AbstractInventoryWidget *src) override; ItemWidget *currentItemWidget() const { return m_craftingResultInventoryWidget.currentItemWidget() ? m_craftingResultInventoryWidget.currentItemWidget() : m_craftingInventoryWidget.currentItemWidget(); } InventoryWidget *currentInventoryWidget() const { return m_currentInventoryWidget; } diff --git a/source/client/gui/InventoryWidget.cpp b/source/client/gui/InventoryWidget.cpp index 4477004db..dcd686bb4 100644 --- a/source/client/gui/InventoryWidget.cpp +++ b/source/client/gui/InventoryWidget.cpp @@ -80,31 +80,32 @@ void InventoryWidget::update() { } bool InventoryWidget::sendItemStackToDest(const ItemWidget *itemStack, AbstractInventoryWidget *dest) { - if (dest->doItemMatchFilter(itemStack->stack().item()) && dest->receiveItemStack(itemStack, this)) { - if (dest != this) - m_inventory->clearStack(itemStack->x(), itemStack->y()); - - update(); - sendUpdatePacket(); - return true; + if (dest->doItemMatchFilter(itemStack->stack().item())) { + ItemStack stackRet = dest->receiveItemStack(itemStack, this); + if (stackRet.amount() != itemStack->stack().amount()) { + if (dest != this && stackRet.amount() == 0) + m_inventory->clearStack(itemStack->x(), itemStack->y()); + + update(); + sendUpdatePacket(); + return true; + } } return false; } -bool InventoryWidget::receiveItemStack(const ItemWidget *itemStack, AbstractInventoryWidget *src) { - ItemStack stack = itemStack->stack(); +ItemStack InventoryWidget::receiveItemStack(const ItemWidget *itemStack, AbstractInventoryWidget *src) { + const ItemStack &stack = itemStack->stack(); if (src == this) m_inventory->clearStack(itemStack->x(), itemStack->y()); - bool stackAdded = m_inventory->addStack(stack.item().stringID(), stack.amount(), m_offset, m_size); + ItemStack stackRet = m_inventory->addStack(stack.item().stringID(), stack.amount(), m_offset, m_size); - if (stackAdded) + if (stackRet.amount() != stack.amount()) sendUpdatePacket(); - else if (src == this) - m_inventory->setStack(itemStack->x(), itemStack->y(), stack.item().stringID(), stack.amount()); - return stackAdded; + return stackRet; } void InventoryWidget::sendUpdatePacket() { diff --git a/source/client/gui/InventoryWidget.hpp b/source/client/gui/InventoryWidget.hpp index 10c1381a6..b774a7765 100644 --- a/source/client/gui/InventoryWidget.hpp +++ b/source/client/gui/InventoryWidget.hpp @@ -48,7 +48,7 @@ class InventoryWidget : public AbstractInventoryWidget { void update() override; bool sendItemStackToDest(const ItemWidget *itemStack, AbstractInventoryWidget *dest) override; - bool receiveItemStack(const ItemWidget *itemStack, AbstractInventoryWidget *src) override; + ItemStack receiveItemStack(const ItemWidget *itemStack, AbstractInventoryWidget *src) override; void sendUpdatePacket(); diff --git a/source/client/gui/MouseItemWidget.cpp b/source/client/gui/MouseItemWidget.cpp index 5a7fe1315..7847bb15f 100644 --- a/source/client/gui/MouseItemWidget.cpp +++ b/source/client/gui/MouseItemWidget.cpp @@ -24,6 +24,7 @@ * * ===================================================================================== */ +#include "EngineConfig.hpp" #include "InventoryWidget.hpp" #include "MouseItemWidget.hpp" @@ -81,7 +82,7 @@ void MouseItemWidget::leftClickBehaviour() { if (!m_currentInventoryWidget->inventory()->isUnlimited()) swapItems(*currentItemWidget, m_currentInventoryWidget->isReadOnly()); else if (getStack().amount() == 0 && currentItemWidget->stack().amount() != 0) - setStack(currentItemWidget->stack().item().stringID(), 64); + setStack(currentItemWidget->stack().item().stringID(), currentItemWidget->stack().item().maxStackSize()); m_currentInventoryWidget->sendUpdatePacket(); } @@ -198,12 +199,19 @@ void MouseItemWidget::swapItems(ItemWidget &widget, bool isReadOnly) { setStack(widgetItemName, widgetItemAmount); } else if (!isReadOnly) { - widget.setStack(widgetItemName, widgetItemAmount + stack().amount()); - setStack("", 0); + u16 sum = widgetItemAmount + stack().amount(); + if (sum > stack().item().maxStackSize()) { + widget.setStack(widgetItemName, stack().item().maxStackSize()); + setStack(widgetItemName, sum - stack().item().maxStackSize()); + } + else { + widget.setStack(widgetItemName, sum); + setStack(BLOCK_AIR, 0); + } } else { setStack(stack().item().stringID(), stack().amount() + widgetItemAmount); - widget.setStack("", 0); + widget.setStack(BLOCK_AIR, 0); } } } diff --git a/source/common/core/EngineConfig.hpp b/source/common/core/EngineConfig.hpp index efee86ea0..992da55da 100644 --- a/source/common/core/EngineConfig.hpp +++ b/source/common/core/EngineConfig.hpp @@ -59,6 +59,8 @@ namespace { static_assert(CHUNK_DEPTH >= -128 && CHUNK_DEPTH < 128, "CHUNK_DEPTH out of range"); constexpr int SEALEVEL = 4; + + constexpr const char *BLOCK_AIR = "_:air"; } #endif // ENGINECONFIG_HPP_ diff --git a/source/common/inventory/Inventory.cpp b/source/common/inventory/Inventory.cpp index 95c7267e5..06d525ae3 100644 --- a/source/common/inventory/Inventory.cpp +++ b/source/common/inventory/Inventory.cpp @@ -32,25 +32,44 @@ void Inventory::setStack(u16 x, u16 y, const std::string &stringID, u16 amount) m_hasChanged = true; } -bool Inventory::addStack(const std::string &stringID, u16 amount, u16 offset, u16 size, bool mergeOnly) { - for (std::size_t i = offset ; i < (size ? offset + size : m_items.size()) ; ++i) { - if (m_items[i].item().id() == 0 && !mergeOnly) { - m_items[i] = ItemStack(stringID, amount); +ItemStack Inventory::addStack(const std::string &stringID, u16 amount, u16 offset, u16 size, bool mergeOnly) { + ItemStack ret{stringID, amount}; + for (std::size_t i = offset ; ret.amount() && i < (size ? offset + size : m_items.size()) ; ++i) { + const Item &item = m_items[i].item(); + if (item.id() == 0 && !mergeOnly) { + if (ret.amount() > item.maxStackSize()) { + m_items[i] = ItemStack(stringID, item.maxStackSize()); + ret.setAmount(ret.amount() - item.maxStackSize()); + } + else { + m_items[i] = ItemStack(stringID, ret.amount()); + ret.setAmount(0); + } m_hasChanged = true; - return true; } - else if (m_items[i].item().stringID() == stringID) { - m_items[i] = ItemStack(stringID, m_items[i].amount() + amount); + else if (item.stringID() == stringID) { + u16 sum = m_items[i].amount() + ret.amount(); + if (sum > item.maxStackSize()) { + m_items[i] = ItemStack(stringID, item.maxStackSize()); + ret.setAmount(sum - item.maxStackSize()); + } + else { + m_items[i] = ItemStack(stringID, sum); + ret.setAmount(0); + } + m_hasChanged = true; - return true; } } - return false; + if (!m_hasChanged && mergeOnly && ret.amount()) + return addStack(ret.item().stringID(), ret.amount(), offset, size); + + return ret; } // NOTE: This fonction is only used by Lua since default parameters don't work properly -bool Inventory::addStack2(const std::string &stringID, u16 amount) { +ItemStack Inventory::addStack2(const std::string &stringID, u16 amount) { return addStack(stringID, amount, 0, 0); } diff --git a/source/common/inventory/Inventory.hpp b/source/common/inventory/Inventory.hpp index fe4ea70bf..acfb3f197 100644 --- a/source/common/inventory/Inventory.hpp +++ b/source/common/inventory/Inventory.hpp @@ -43,8 +43,8 @@ class Inventory : public gk::ISerializable { const ItemStack &getStack(u16 x, u16 y) const { return m_items.at(x + y * m_width); } ItemStack &getStackRef(u16 x, u16 y) { return m_items.at(x + y * m_width); } void setStack(u16 x, u16 y, const std::string &stringID, u16 amount = 1); - bool addStack(const std::string &stringID, u16 amount = 1, u16 offset = 0, u16 size = 0, bool mergeOnly = false); - bool addStack2(const std::string &stringID, u16 amount = 1); // Needed for Lua + ItemStack addStack(const std::string &stringID, u16 amount = 1, u16 offset = 0, u16 size = 0, bool mergeOnly = false); + ItemStack addStack2(const std::string &stringID, u16 amount = 1); // Needed for Lua void clearStack(u16 x, u16 y); void serialize(sf::Packet &packet) const override; diff --git a/source/common/inventory/Item.cpp b/source/common/inventory/Item.cpp index fff575348..5a5458e32 100644 --- a/source/common/inventory/Item.cpp +++ b/source/common/inventory/Item.cpp @@ -38,13 +38,13 @@ Item::Item(u32 id, const TilesDef &tiles, const std::string &stringID, const std void Item::serialize(sf::Packet &packet) const { packet << m_id << m_tiles << m_stringID << m_label << m_isBlock << m_miningSpeed << m_harvestCapability << m_groups << m_canBeActivated - << m_effectiveOn; + << m_effectiveOn << m_maxStackSize; } void Item::deserialize(sf::Packet &packet) { packet >> m_id >> m_tiles >> m_stringID >> m_label >> m_isBlock >> m_miningSpeed >> m_harvestCapability >> m_groups >> m_canBeActivated - >> m_effectiveOn; + >> m_effectiveOn >> m_maxStackSize; } // Please update 'docs/lua-api-cpp.md' if you change this diff --git a/source/common/inventory/Item.hpp b/source/common/inventory/Item.hpp index 851a8d1ca..8dbb38942 100644 --- a/source/common/inventory/Item.hpp +++ b/source/common/inventory/Item.hpp @@ -76,6 +76,9 @@ class Item : public gk::ISerializable { const std::vector &effectiveOn() const { return m_effectiveOn; } void addEffectiveBlock(const std::string &blockID) { m_effectiveOn.emplace_back(blockID); } + u16 maxStackSize() const { return m_maxStackSize; } + void setMaxStackSize(u16 maxStackSize) { m_maxStackSize = maxStackSize; } + static void initUsertype(sol::state &lua); protected: @@ -96,6 +99,8 @@ class Item : public gk::ISerializable { std::unordered_map m_groups; std::vector m_effectiveOn; + + u16 m_maxStackSize = 64; }; #endif // ITEM_HPP_ diff --git a/source/server/core/ServerConfig.cpp b/source/server/core/ServerConfig.cpp index 1c52895fb..50ebb9f62 100644 --- a/source/server/core/ServerConfig.cpp +++ b/source/server/core/ServerConfig.cpp @@ -33,6 +33,7 @@ // Server u8 ServerConfig::maxPlayers = 5; +u16 ServerConfig::maxItemStackSize = 64; // Mod-defined options std::unordered_map ServerConfig::options; @@ -45,6 +46,7 @@ void ServerConfig::loadConfigFromFile(const char *file) { lua.safe_script_file(file); maxPlayers = lua["max_players"].get_or(maxPlayers); + maxItemStackSize = lua["max_item_stack_size"].get_or(maxItemStackSize); if (lua["mod_options"].valid() && lua["mod_options"].get_type() == sol::type::table) { for (auto &it : lua["mod_options"].get()) { @@ -63,6 +65,7 @@ void ServerConfig::loadConfigFromFile(const char *file) { void ServerConfig::saveConfigToFile(const char *filename) { std::ofstream file{filename, std::ofstream::out | std::ofstream::trunc}; file << "max_players = " << (u16)maxPlayers << std::endl; + file << "max_item_stack_size = " << maxItemStackSize << std::endl; file << "mod_options = {" << std::endl; for (auto &it : options) { diff --git a/source/server/core/ServerConfig.hpp b/source/server/core/ServerConfig.hpp index 5364f5086..b2e7e207b 100644 --- a/source/server/core/ServerConfig.hpp +++ b/source/server/core/ServerConfig.hpp @@ -37,6 +37,7 @@ namespace ServerConfig { // Server extern u8 maxPlayers; + extern u16 maxItemStackSize; // Mod-defined options extern std::unordered_map options; diff --git a/source/server/lua/LuaMod.cpp b/source/server/lua/LuaMod.cpp index eaf8f669d..6065ad0d3 100644 --- a/source/server/lua/LuaMod.cpp +++ b/source/server/lua/LuaMod.cpp @@ -109,8 +109,9 @@ void LuaMod::despawnEntity(EntityWrapper &entity) { void LuaMod::giveItemStack(ServerPlayer &player, ItemStack *itemStack) { if (itemStack) { // FIXME: This should probably be moved to a mod - if (!player.inventory().addStack(itemStack->item().stringID(), itemStack->amount(), 9, 24, true)) - player.inventory().addStack(itemStack->item().stringID(), itemStack->amount(), 0, 9); + ItemStack stackRet = player.inventory().addStack(itemStack->item().stringID(), itemStack->amount(), 9, 24, true); + if (stackRet.amount() != 0) + player.inventory().addStack(stackRet.item().stringID(), stackRet.amount(), 0, 9); m_worldController.server()->sendPlayerInvUpdate(player.clientID(), player.client()); } diff --git a/source/server/lua/loader/LuaBlockLoader.cpp b/source/server/lua/loader/LuaBlockLoader.cpp index 7ecb23232..bce3b389e 100644 --- a/source/server/lua/loader/LuaBlockLoader.cpp +++ b/source/server/lua/loader/LuaBlockLoader.cpp @@ -30,6 +30,7 @@ #include "LuaMod.hpp" #include "Registry.hpp" #include "ServerBlock.hpp" +#include "ServerConfig.hpp" void LuaBlockLoader::loadBlock(const sol::table &table) const { std::string stringID = m_mod.id() + ":" + table["id"].get(); @@ -57,6 +58,7 @@ void LuaBlockLoader::loadBlock(const sol::table &table) const { } item->setIsBlock(true); + item->setMaxStackSize(table["max_stack_size"].get_or(ServerConfig::maxItemStackSize)); loadGroups(block, table, item); diff --git a/source/server/lua/loader/LuaItemLoader.cpp b/source/server/lua/loader/LuaItemLoader.cpp index 54b4ae25f..a645408a5 100644 --- a/source/server/lua/loader/LuaItemLoader.cpp +++ b/source/server/lua/loader/LuaItemLoader.cpp @@ -29,6 +29,7 @@ #include "LuaItemLoader.hpp" #include "LuaMod.hpp" #include "Registry.hpp" +#include "ServerConfig.hpp" #include "ServerItem.hpp" void LuaItemLoader::loadItem(const sol::table &table) const { @@ -42,6 +43,7 @@ void LuaItemLoader::loadItem(const sol::table &table) const { item.setHarvestCapability(table["harvest_capability"].get_or(0)); item.setMiningSpeed(table["mining_speed"].get_or(1)); item.setOnItemActivated(table["on_item_activated"]); + item.setMaxStackSize(table["max_stack_size"].get_or(ServerConfig::maxItemStackSize)); sol::object groupsObject = table["groups"]; if (groupsObject.valid()) {