Item stack limit added (server global + per block/item definition).

master
Quentin Bazin 2020-07-18 01:35:59 +02:00
parent 103127ee96
commit a4dfd7a7aa
20 changed files with 108 additions and 41 deletions

View File

@ -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.**

View File

@ -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)`

View File

@ -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.**

View File

@ -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

View File

@ -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<std::string> &shiftDestination() const { return m_shiftDestination; }
void setShiftDestination(const std::string &shiftDestination);

View File

@ -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 {

View File

@ -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; }

View File

@ -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());
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;
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() {

View File

@ -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();

View File

@ -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);
}
}
}

View File

@ -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_

View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -76,6 +76,9 @@ class Item : public gk::ISerializable {
const std::vector<std::string> &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<std::string, u16> m_groups;
std::vector<std::string> m_effectiveOn;
u16 m_maxStackSize = 64;
};
#endif // ITEM_HPP_

View File

@ -33,6 +33,7 @@
// Server
u8 ServerConfig::maxPlayers = 5;
u16 ServerConfig::maxItemStackSize = 64;
// Mod-defined options
std::unordered_map<std::string, sol::object> 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<sol::table>()) {
@ -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) {

View File

@ -37,6 +37,7 @@
namespace ServerConfig {
// Server
extern u8 maxPlayers;
extern u16 maxItemStackSize;
// Mod-defined options
extern std::unordered_map<std::string, sol::object> options;

View File

@ -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());
}

View File

@ -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<std::string>();
@ -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);

View File

@ -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()) {