From 6a76c226e10e92c3e3339096f07f8ab065e2098b Mon Sep 17 00:00:00 2001 From: Kahrl Date: Thu, 12 Jan 2012 06:10:39 +0100 Subject: The huge item definition and item namespace unification patch (itemdef), see http://c55.me/minetest/wiki/doku.php?id=changes:itemdef --- src/inventory.h | 517 +++++++++++++++----------------------------------------- 1 file changed, 139 insertions(+), 378 deletions(-) (limited to 'src/inventory.h') diff --git a/src/inventory.h b/src/inventory.h index 15de3c8e7..3f5d83589 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -23,460 +23,221 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include #include "common_irrlicht.h" #include "debug.h" -#include "mapnode.h" // For content_t +#include "itemdef.h" -#define QUANTITY_ITEM_MAX_COUNT 99 +struct ToolDiggingProperties; -class ServerActiveObject; -class ServerEnvironment; -struct PointedThing; -class ITextureSource; -class IGameDef; - -class InventoryItem +struct ItemStack { -public: - InventoryItem(IGameDef *gamedef, u16 count); - virtual ~InventoryItem(); - - static InventoryItem* deSerialize(std::istream &is, IGameDef *gamedef); - static InventoryItem* deSerialize(const std::string &str, - IGameDef *gamedef); - - virtual const char* getName() const = 0; - // Shall write the name and the parameters - virtual void serialize(std::ostream &os) const = 0; - // Shall make an exact clone of the item - virtual InventoryItem* clone() = 0; - // Return the name of the image for this item - virtual std::string getImageBasename() const { return ""; } -#ifndef SERVER - // Shall return an image of the item (or NULL) - virtual video::ITexture * getImage() const - { return NULL; } - // Shall return an image of the item without embellishments (or NULL) - virtual video::ITexture * getImageRaw() const - { return getImage(); } -#endif - // Shall return a text to show in the GUI - virtual std::string getText() { return ""; } + ItemStack(): name(""), count(0), wear(0), metadata("") {} + ItemStack(std::string name_, u16 count_, + u16 wear, std::string metadata_, + IItemDefManager *itemdef); + ~ItemStack() {} + + // Serialization + void serialize(std::ostream &os) const; + void deSerialize(std::istream &is, IItemDefManager *itemdef); + void deSerialize(const std::string &s, IItemDefManager *itemdef); + // Returns the string used for inventory - virtual std::string getItemString(); - - // Shall return false if item is not known and cannot be used - virtual bool isKnown() const { return true; } + std::string getItemString() const; /* Quantity methods */ - // Return true if the item can be add()ed to the other - virtual bool addableTo(const InventoryItem *other) const - { return false; } - // Return true if the other item contains this item - virtual bool isSubsetOf(const InventoryItem *other) const - { return false; } - // Remove the other item from this one if possible and return true - // Return false if not possible - virtual bool removeOther(const InventoryItem *other) - { return false; } - - u16 getCount() const - { return m_count; } - void setCount(u16 count) - { m_count = count; } - - u16 freeSpace() const + bool empty() const { - u16 max = getStackMax(); - if(m_count > max) - return 0; - return max - m_count; + return count == 0; } - void add(u16 count) + void clear() { - m_count += count; - } - void remove(u16 count) - { - assert(m_count >= count); - m_count -= count; + name = ""; + count = 0; + wear = 0; + metadata = ""; } - /* - Other properties - */ - - // Maximum size of a stack - virtual u16 getStackMax() const {return 1;} - // Whether it can be used - virtual bool isUsable() const {return false;} - // Whether it can be cooked - virtual bool isCookable() const {return false;} - // Result of cooking (can randomize) - virtual InventoryItem *createCookResult() const {return NULL;} - // Time of cooking - virtual float getCookTime() const {return 3.0;} - // Whether it can be burned (<0 = cannot be burned) - virtual float getBurnTime() const {return -1;} - // Gets amount of items that dropping one ItemSAO will decrement - // -1 means as many as possible - virtual s16 getDropCount() const { return -1; } - // Whether this item can point to liquids - virtual bool areLiquidsPointable() const { return false; } - - // Creates an object from the item and places it in the world. - // If return value is true, item should be removed. - virtual bool dropOrPlace(ServerEnvironment *env, - ServerActiveObject *dropper, - v3f pos, bool place, s16 count); - - // Eat, press, activate, whatever. - // Called when item is left-clicked while in hand. - // If returns true, item shall be deleted. - virtual bool use(ServerEnvironment *env, - ServerActiveObject *user, - const PointedThing& pointed){return false;} - -protected: - IGameDef *m_gamedef; - u16 m_count; -}; - -class MaterialItem : public InventoryItem -{ -public: - MaterialItem(IGameDef *gamedef, std::string nodename, u16 count); - // Legacy constructor - MaterialItem(IGameDef *gamedef, content_t content, u16 count); - /* - Implementation interface - */ - virtual const char* getName() const - { - return "MaterialItem"; - } - virtual void serialize(std::ostream &os) const - { - os<<"node"; - os<<" \""; - os<getName()) != "MaterialItem") - return false; - MaterialItem *m = (MaterialItem*)other; - if(m->m_nodename != m_nodename) - return false; - return true; - } - virtual bool isSubsetOf(const InventoryItem *other) const - { - if(std::string(other->getName()) != "MaterialItem") - return false; - MaterialItem *m = (MaterialItem*)other; - if(m->m_nodename != m_nodename) - return false; - return m_count <= m->m_count; + assert(count >= n); + count -= n; + if(count == 0) + clear(); // reset name, wear and metadata too } - virtual bool removeOther(const InventoryItem *other) - { - if(!other->isSubsetOf(this)) - return false; - MaterialItem *m = (MaterialItem*)other; - m_count += m->m_count; - return true; - } - - u16 getStackMax() const - { - return QUANTITY_ITEM_MAX_COUNT; - } - - /* - Other properties - */ - bool isCookable() const; - InventoryItem *createCookResult() const; - float getCookTime() const; - float getBurnTime() const; - /* - Special properties (not part of virtual interface) - */ - std::string getNodeName() const - { return m_nodename; } - content_t getMaterial() const; -private: - std::string m_nodename; -}; -/* - An item that is used as a mid-product when crafting. - Subnames: - - Stick -*/ -class CraftItem : public InventoryItem -{ -public: - CraftItem(IGameDef *gamedef, std::string subname, u16 count); - /* - Implementation interface - */ - virtual const char* getName() const - { - return "CraftItem"; - } - virtual void serialize(std::ostream &os) const - { - os<<"craft"; - os<<" \""; - os<getName()) != "CraftItem") - return false; - CraftItem *m = (CraftItem*)other; - if(m->m_subname != m_subname) - return false; - return true; - } - virtual bool isSubsetOf(const InventoryItem *other) const - { - if(std::string(other->getName()) != "CraftItem") - return false; - CraftItem *m = (CraftItem*)other; - if(m->m_subname != m_subname) - return false; - return m_count <= m->m_count; - } - virtual bool removeOther(const InventoryItem *other) + // Maximum size of a stack + u16 getStackMax(IItemDefManager *itemdef) const { - if(!other->isSubsetOf(this)) - return false; - CraftItem *m = (CraftItem*)other; - m_count += m->m_count; - return true; + s16 max = itemdef->get(name).stack_max; + return (max >= 0) ? max : 0; } - /* - Other properties - */ - - u16 getStackMax() const; - bool isUsable() const; - bool isCookable() const; - InventoryItem *createCookResult() const; - float getCookTime() const; - float getBurnTime() const; - s16 getDropCount() const; - bool areLiquidsPointable() const; - - bool dropOrPlace(ServerEnvironment *env, - ServerActiveObject *dropper, - v3f pos, bool place, s16 count); - bool use(ServerEnvironment *env, - ServerActiveObject *user, - const PointedThing& pointed); - - /* - Special methods - */ - std::string getSubName() + // Number of items that can be added to this stack + u16 freeSpace(IItemDefManager *itemdef) const { - return m_subname; + u16 max = getStackMax(itemdef); + if(count > max) + return 0; + return max - count; } -private: - std::string m_subname; -}; -class ToolItem : public InventoryItem -{ -public: - ToolItem(IGameDef *gamedef, std::string toolname, u16 wear); - /* - Implementation interface - */ - virtual const char* getName() const - { - return "ToolItem"; - } - virtual void serialize(std::ostream &os) const - { - os<<"tool"; - os<<" \""; - os<isKnown(name); } - std::string getImageBasename() const; -#ifndef SERVER - video::ITexture * getImage() const; - video::ITexture * getImageRaw() const; -#endif - - std::string getText() + // Returns a pointer to the item definition struct, + // or a fallback one (name="unknown") if the item is unknown. + const ItemDefinition& getDefinition( + IItemDefManager *itemdef) const { - return ""; + return itemdef->get(name); } - - virtual bool isKnown() const; - virtual bool isSubsetOf(const InventoryItem *other) const + // Get tool digging properties, or those of the hand if not a tool + const ToolDiggingProperties& getToolDiggingProperties( + IItemDefManager *itemdef) const { - if(std::string(other->getName()) != "ToolItem") - return false; - ToolItem *m = (ToolItem*)other; - if(m->m_toolname != m_toolname) - return false; - return m_wear <= m->m_wear; - } - virtual bool removeOther(const InventoryItem *other) - { - if(!other->isSubsetOf(this)) - return false; - ToolItem *m = (ToolItem*)other; - m_wear -= m->m_wear; - return true; + ToolDiggingProperties *prop; + prop = itemdef->get(name).tool_digging_properties; + if(prop == NULL) + prop = itemdef->get("").tool_digging_properties; + assert(prop != NULL); + return *prop; } - /* - Special methods - */ - std::string getToolName() - { - return m_toolname; - } - u16 getWear() + // Wear out (only tools) + // Returns true if the item is (was) a tool + bool addWear(s32 amount, IItemDefManager *itemdef) { - return m_wear; - } - // Returns true if weared out - bool addWear(u16 add) - { - if(m_wear >= 65535 - add) + if(getDefinition(itemdef).type == ITEM_TOOL) { - m_wear = 65535; + if(amount > 65535 - wear) + clear(); + else if(amount < -wear) + wear = 0; + else + wear += amount; return true; } else { - m_wear += add; return false; } } -private: - std::string m_toolname; - u16 m_wear; + + // If possible, adds newitem to this item. + // If cannot be added at all, returns the item back. + // If can be added partly, decremented item is returned back. + // If can be added fully, empty item is returned. + ItemStack addItem(const ItemStack &newitem, + IItemDefManager *itemdef); + + // Checks whether newitem could be added. + // If restitem is non-NULL, it receives the part of newitem that + // would be left over after adding. + bool itemFits(const ItemStack &newitem, + ItemStack *restitem, // may be NULL + IItemDefManager *itemdef) const; + + // Takes some items. + // If there are not enough, takes as many as it can. + // Returns empty item if couldn't take any. + ItemStack takeItem(u32 takecount); + + // Similar to takeItem, but keeps this ItemStack intact. + ItemStack peekItem(u32 peekcount) const; + + /* + Properties + */ + std::string name; + u16 count; + u16 wear; + std::string metadata; }; class InventoryList { public: - InventoryList(std::string name, u32 size); + InventoryList(std::string name, u32 size, IItemDefManager *itemdef); ~InventoryList(); void clearItems(); void setSize(u32 newsize); void serialize(std::ostream &os) const; - void deSerialize(std::istream &is, IGameDef *gamedef); + void deSerialize(std::istream &is); InventoryList(const InventoryList &other); InventoryList & operator = (const InventoryList &other); const std::string &getName() const; - u32 getSize(); + u32 getSize() const; // Count used slots - u32 getUsedSlots(); - u32 getFreeSlots(); - - /*bool getDirty(){ return m_dirty; } - void setDirty(bool dirty=true){ m_dirty = dirty; }*/ - - // Get pointer to item - const InventoryItem * getItem(u32 i) const; - InventoryItem * getItem(u32 i); - // Returns old item (or NULL). Parameter can be NULL. - InventoryItem * changeItem(u32 i, InventoryItem *newitem); + u32 getUsedSlots() const; + u32 getFreeSlots() const; + + // Get reference to item + const ItemStack& getItem(u32 i) const; + ItemStack& getItem(u32 i); + // Returns old item. Parameter can be an empty item. + ItemStack changeItem(u32 i, const ItemStack &newitem); // Delete item void deleteItem(u32 i); - // Adds an item to a suitable place. Returns leftover item. - // If all went into the list, returns NULL. - InventoryItem * addItem(InventoryItem *newitem); + // Adds an item to a suitable place. Returns leftover item (possibly empty). + ItemStack addItem(const ItemStack &newitem); // If possible, adds item to given slot. // If cannot be added at all, returns the item back. // If can be added partly, decremented item is returned back. - // If can be added fully, NULL is returned. - InventoryItem * addItem(u32 i, InventoryItem *newitem); + // If can be added fully, empty item is returned. + ItemStack addItem(u32 i, const ItemStack &newitem); // Checks whether the item could be added to the given slot - bool itemFits(const u32 i, const InventoryItem *newitem); + // If restitem is non-NULL, it receives the part of newitem that + // would be left over after adding. + bool itemFits(const u32 i, const ItemStack &newitem, + ItemStack *restitem = NULL) const; // Checks whether there is room for a given item - bool roomForItem(const InventoryItem *item); + bool roomForItem(const ItemStack &item) const; + + // Checks whether the given count of the given item name + // exists in this inventory list. + bool containsItem(const ItemStack &item) const; - // Checks whether there is room for a given item aftr it has been cooked - bool roomForCookedItem(const InventoryItem *item); + // Removes the given count of the given item name from + // this inventory list. Walks the list in reverse order. + // If not as many items exist as requested, removes as + // many as possible. + // Returns the items that were actually removed. + ItemStack removeItem(const ItemStack &item); // Takes some items from a slot. // If there are not enough, takes as many as it can. - // Returns NULL if couldn't take any. - InventoryItem * takeItem(u32 i, u32 count); + // Returns empty item if couldn't take any. + ItemStack takeItem(u32 i, u32 takecount); - // Decrements amount of every material item - void decrementMaterials(u16 count); + // Similar to takeItem, but keeps the slot intact. + ItemStack peekItem(u32 i, u32 peekcount) const; - void print(std::ostream &o); - private: - core::array m_items; + std::vector m_items; u32 m_size; std::string m_name; - //bool m_dirty; + IItemDefManager *m_itemdef; }; class Inventory @@ -486,20 +247,19 @@ public: void clear(); - Inventory(); + Inventory(IItemDefManager *itemdef); Inventory(const Inventory &other); Inventory & operator = (const Inventory &other); void serialize(std::ostream &os) const; - void deSerialize(std::istream &is, IGameDef *gamedef); + void deSerialize(std::istream &is); InventoryList * addList(const std::string &name, u32 size); InventoryList * getList(const std::string &name); const InventoryList * getList(const std::string &name) const; bool deleteList(const std::string &name); - // A shorthand for adding items. - // Returns NULL if the item was fully added, leftover otherwise. - InventoryItem * addItem(const std::string &listname, InventoryItem *newitem) + // A shorthand for adding items. Returns leftover item (possibly empty). + ItemStack addItem(const std::string &listname, const ItemStack &newitem) { InventoryList *list = getList(listname); if(list == NULL) @@ -511,7 +271,8 @@ private: // -1 if not found const s32 getListIndex(const std::string &name) const; - core::array m_lists; + std::vector m_lists; + IItemDefManager *m_itemdef; }; #endif -- cgit v1.2.3 From b4dd5d3bd7d2152fdf02e0e7422b1305caf151f2 Mon Sep 17 00:00:00 2001 From: Kahrl Date: Sun, 22 Jan 2012 00:49:02 +0100 Subject: Client-side prediction of inventory changes, and some inventory menu fixes --- src/client.cpp | 40 +++++++++++++++ src/client.h | 2 + src/guiInventoryMenu.cpp | 30 ++++++++--- src/inventory.cpp | 43 ++++++++++++++++ src/inventory.h | 4 ++ src/inventorymanager.cpp | 129 ++++++++++++++++++++++++++++------------------- src/inventorymanager.h | 7 +++ src/server.cpp | 12 +++++ 8 files changed, 210 insertions(+), 57 deletions(-) (limited to 'src/inventory.h') diff --git a/src/client.cpp b/src/client.cpp index 602f0cf84..0463aa81c 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -225,6 +225,8 @@ Client::Client( m_server_ser_ver(SER_FMT_VER_INVALID), m_playeritem(0), m_inventory_updated(false), + m_inventory_from_server(NULL), + m_inventory_from_server_age(0.0), m_time_of_day(0), m_map_seed(0), m_password(password), @@ -269,6 +271,8 @@ Client::~Client() m_mesh_update_thread.setRun(false); while(m_mesh_update_thread.IsRunning()) sleep_ms(100); + + delete m_inventory_from_server; } void Client::connect(Address address) @@ -657,6 +661,30 @@ void Client::step(float dtime) } } } + + /* + If the server didn't update the inventory in a while, revert + the local inventory (so the player notices the lag problem + and knows something is wrong). + */ + if(m_inventory_from_server) + { + float interval = 10.0; + float count_before = floor(m_inventory_from_server_age / interval); + + m_inventory_from_server_age += dtime; + + float count_after = floor(m_inventory_from_server_age / interval); + + if(count_after != count_before) + { + // Do this every seconds after TOCLIENT_INVENTORY + // Reset the locally changed inventory to the authoritative inventory + Player *player = m_env.getLocalPlayer(); + player->inventory = *m_inventory_from_server; + m_inventory_updated = true; + } + } } // Virtual methods from con::PeerHandler @@ -975,6 +1003,10 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) m_inventory_updated = true; + delete m_inventory_from_server; + m_inventory_from_server = new Inventory(player->inventory); + m_inventory_from_server_age = 0.0; + //infostream<<"Client got player inventory:"<inventory.print(infostream); } @@ -1931,7 +1963,15 @@ Inventory* Client::getInventory(const InventoryLocation &loc) } void Client::inventoryAction(InventoryAction *a) { + /* + Send it to the server + */ sendInventoryAction(a); + + /* + Predict some local inventory changes + */ + a->clientApply(this, this); } ClientActiveObject * Client::getSelectedActiveObject( diff --git a/src/client.h b/src/client.h index 6d5e5c525..efdf315f7 100644 --- a/src/client.h +++ b/src/client.h @@ -367,6 +367,8 @@ private: u8 m_server_ser_ver; u16 m_playeritem; bool m_inventory_updated; + Inventory *m_inventory_from_server; + float m_inventory_from_server_age; core::map m_active_blocks; PacketCounter m_packetcounter; // Received from the server. 0-23999 diff --git a/src/guiInventoryMenu.cpp b/src/guiInventoryMenu.cpp index a4b16b251..cd371d062 100644 --- a/src/guiInventoryMenu.cpp +++ b/src/guiInventoryMenu.cpp @@ -537,6 +537,11 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) s.i = -1; // make it invalid again } + bool identical = (m_selected_item != NULL) && s.isValid() && + (inv_selected == inv_s) && + (m_selected_item->listname == s.listname) && + (m_selected_item->i == s.i); + // buttons: 0 = left, 1 = right, 2 = middle // up/down: 0 = down (press), 1 = up (release), 2 = unknown event int button = 0; @@ -602,13 +607,26 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) if(s.isValid()) { - // Clicked another slot: move + // Clicked a slot: move if(button == 1) // right move_amount = 1; else if(button == 2) // middle move_amount = MYMIN(m_selected_amount, 10); else // left move_amount = m_selected_amount; + dstream << "move_amount=" << move_amount<<"\n"; + dstream << "m_selected_amount=" << m_selected_amount<<"\n"; + + if(identical) + { + if(move_amount >= m_selected_amount) + m_selected_amount = 0; + else + m_selected_amount -= move_amount; + move_amount = 0; + } + dstream << "move_amount=" << move_amount<<"\n"; + dstream << "m_selected_amount=" << m_selected_amount<<"\n"; } else if(getAbsoluteClippingRect().isPointInside(m_pointer)) { @@ -636,9 +654,7 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) if(m_selected_item != NULL && m_selected_dragging && s.isValid()) { - if((inv_selected != inv_s) || - (m_selected_item->listname != s.listname) || - (m_selected_item->i != s.i)) + if(!identical) { // Dragged to different slot: move all selected move_amount = m_selected_amount; @@ -675,18 +691,19 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event) if(leftover.count == stack_from.count) { // Swap the stacks + m_selected_amount -= stack_to.count; } else if(leftover.empty()) { // Item fits + m_selected_amount -= move_amount; } else { // Item only fits partially move_amount -= leftover.count; + m_selected_amount -= move_amount; } - assert(move_amount > 0 && move_amount <= m_selected_amount); - m_selected_amount -= move_amount; infostream<<"Handing IACTION_MOVE to manager"<addItem(dest_i, item1); + + // If something is returned, the item was not fully added + if(!item1.empty()) + { + // If olditem is returned, nothing was added. + bool nothing_added = (item1.count == oldcount); + + // If something else is returned, part of the item was left unadded. + // Add the other part back to the source item + addItem(i, item1); + + // If olditem is returned, nothing was added. + // Swap the items + if(nothing_added) + { + // Take item from source list + item1 = changeItem(i, ItemStack()); + // Adding was not possible, swap the items. + ItemStack item2 = dest->changeItem(dest_i, item1); + // Put item from destination list to the source list + changeItem(i, item2); + } + } +} + /* Inventory */ diff --git a/src/inventory.h b/src/inventory.h index 3f5d83589..fcac5f647 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -233,6 +233,10 @@ public: // Similar to takeItem, but keeps the slot intact. ItemStack peekItem(u32 i, u32 peekcount) const; + // Move an item to a different list (or a different stack in the same list) + // count is the maximum number of items to move (0 for everything) + void moveItem(u32 i, InventoryList *dest, u32 dest_i, u32 count = 0); + private: std::vector m_items; u32 m_size; diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp index b42a80673..b04a1c177 100644 --- a/src/inventorymanager.cpp +++ b/src/inventorymanager.cpp @@ -133,6 +133,10 @@ InventoryAction * InventoryAction::deSerialize(std::istream &is) return a; } +/* + IMoveAction +*/ + IMoveAction::IMoveAction(std::istream &is) { std::string ts; @@ -193,59 +197,13 @@ void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGame <<", to_list=\""<getItem(from_i).empty()) - { - infostream<<"IMoveAction::apply(): FAIL: source item not found: " - <<"from_inv=\""<