diff options
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/client.cpp | 29 | ||||
-rw-r--r-- | src/client.h | 5 | ||||
-rw-r--r-- | src/craftdef.cpp | 1 | ||||
-rw-r--r-- | src/guiInventoryMenu.cpp | 1 | ||||
-rw-r--r-- | src/guiInventoryMenu.h | 2 | ||||
-rw-r--r-- | src/inventory.cpp | 471 | ||||
-rw-r--r-- | src/inventory.h | 178 | ||||
-rw-r--r-- | src/inventorymanager.cpp | 544 | ||||
-rw-r--r-- | src/inventorymanager.h | 228 | ||||
-rw-r--r-- | src/scriptapi.cpp | 519 | ||||
-rw-r--r-- | src/server.cpp | 88 | ||||
-rw-r--r-- | src/server.h | 5 |
13 files changed, 1328 insertions, 744 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2088c8b79..3b3b326c4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -94,6 +94,7 @@ configure_file( ) set(common_SRCS + inventorymanager.cpp mods.cpp serverremoteplayer.cpp content_abm.cpp diff --git a/src/client.cpp b/src/client.cpp index d092c6a10..cfdc713db 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1782,6 +1782,34 @@ InventoryContext *Client::getInventoryContext() return &m_inventory_context; } +Inventory* Client::getInventory(const InventoryLocation &loc) +{ + switch(loc.type){ + case InventoryLocation::UNDEFINED: + {} + break; + case InventoryLocation::PLAYER: + { + Player *player = m_env.getPlayer(loc.name.c_str()); + if(!player) + return NULL; + return &player->inventory; + } + break; + case InventoryLocation::NODEMETA: + { + NodeMetadata *meta = m_env.getMap().getNodeMetadata(loc.p); + if(!meta) + return NULL; + return meta->getInventory(); + } + break; + default: + assert(0); + } + return NULL; +} +#if 0 Inventory* Client::getInventory(InventoryContext *c, std::string id) { if(id == "current_player") @@ -1810,6 +1838,7 @@ Inventory* Client::getInventory(InventoryContext *c, std::string id) infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl; return NULL; } +#endif void Client::inventoryAction(InventoryAction *a) { sendInventoryAction(a); diff --git a/src/client.h b/src/client.h index 2f212dad8..e74fec5a7 100644 --- a/src/client.h +++ b/src/client.h @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "clientobject.h" #include "utility.h" // For IntervalLimiter #include "gamedef.h" +#include "inventorymanager.h" struct MeshMakeData; class IGameDef; @@ -245,7 +246,9 @@ public: InventoryContext *getInventoryContext(); - Inventory* getInventory(InventoryContext *c, std::string id); + /* InventoryManager interface */ + Inventory* getInventory(const InventoryLocation &loc); + //Inventory* getInventory(InventoryContext *c, std::string id); void inventoryAction(InventoryAction *a); // Gets closest object pointed by the shootline diff --git a/src/craftdef.cpp b/src/craftdef.cpp index e701a4279..0cbb74ea0 100644 --- a/src/craftdef.cpp +++ b/src/craftdef.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "utility.h" #include "gamedef.h" #include "inventory.h" +#include "inventorymanager.h" // checkItemCombination CraftPointerInput::~CraftPointerInput() { diff --git a/src/guiInventoryMenu.cpp b/src/guiInventoryMenu.cpp index d9a98b5b0..552e10db2 100644 --- a/src/guiInventoryMenu.cpp +++ b/src/guiInventoryMenu.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <IGUIStaticText.h> #include <IGUIFont.h> #include "log.h" +#include "inventorymanager.h" void drawInventoryItem(video::IVideoDriver *driver, gui::IGUIFont *font, diff --git a/src/guiInventoryMenu.h b/src/guiInventoryMenu.h index c8fe0d068..359268687 100644 --- a/src/guiInventoryMenu.h +++ b/src/guiInventoryMenu.h @@ -27,6 +27,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "modalMenu.h" class ITextureSource; +class InventoryContext; +class InventoryManager; void drawInventoryItem(video::IVideoDriver *driver, gui::IGUIFont *font, diff --git a/src/inventory.cpp b/src/inventory.cpp index dd2713ca9..0d38bed78 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -37,7 +37,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "scriptapi.h" #include "strfnd.h" #include "nameidmapping.h" // For loading legacy MaterialItems -#include "serverremoteplayer.h" /* InventoryItem @@ -567,6 +566,22 @@ void InventoryList::clearItems() //setDirty(true); } +void InventoryList::setSize(u32 newsize) +{ + if(newsize < m_items.size()){ + for(u32 i=newsize; i<m_items.size(); i++){ + if(m_items[i]) + delete m_items[i]; + } + m_items.erase(newsize, m_items.size() - newsize); + } else { + for(u32 i=m_items.size(); i<newsize; i++){ + m_items.push_back(NULL); + } + } + m_size = newsize; +} + void InventoryList::serialize(std::ostream &os) const { //os.imbue(std::locale("C")); @@ -1041,458 +1056,4 @@ const s32 Inventory::getListIndex(const std::string &name) const return -1; } -/* - InventoryAction -*/ - -InventoryAction * InventoryAction::deSerialize(std::istream &is) -{ - std::string type; - std::getline(is, type, ' '); - - InventoryAction *a = NULL; - - if(type == "Move") - { - a = new IMoveAction(is); - } - else if(type == "Drop") - { - a = new IDropAction(is); - } - - return a; -} - -static std::string describeC(const struct InventoryContext *c) -{ - if(c->current_player == NULL) - return "current_player=NULL"; - else - return std::string("current_player=") + c->current_player->getName(); -} - -IMoveAction::IMoveAction(std::istream &is) -{ - std::string ts; - - std::getline(is, ts, ' '); - count = stoi(ts); - - std::getline(is, from_inv, ' '); - - std::getline(is, from_list, ' '); - - std::getline(is, ts, ' '); - from_i = stoi(ts); - - std::getline(is, to_inv, ' '); - - std::getline(is, to_list, ' '); - - std::getline(is, ts, ' '); - to_i = stoi(ts); -} - -void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr, - ServerEnvironment *env) -{ - Inventory *inv_from = mgr->getInventory(c, from_inv); - Inventory *inv_to = mgr->getInventory(c, to_inv); - - if(!inv_from){ - infostream<<"IMoveAction::apply(): FAIL: source inventory not found: " - <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" - <<", to_inv=\""<<to_inv<<"\""<<std::endl; - return; - } - if(!inv_to){ - infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: " - "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" - <<", to_inv=\""<<to_inv<<"\""<<std::endl; - return; - } - - InventoryList *list_from = inv_from->getList(from_list); - InventoryList *list_to = inv_to->getList(to_list); - - /* - If a list doesn't exist or the source item doesn't exist - */ - if(!list_from){ - infostream<<"IMoveAction::apply(): FAIL: source list not found: " - <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" - <<", from_list=\""<<from_list<<"\""<<std::endl; - return; - } - if(!list_to){ - infostream<<"IMoveAction::apply(): FAIL: destination list not found: " - <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\"" - <<", to_list=\""<<to_list<<"\""<<std::endl; - return; - } - if(list_from->getItem(from_i) == NULL) - { - infostream<<"IMoveAction::apply(): FAIL: source item not found: " - <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" - <<", from_list=\""<<from_list<<"\"" - <<" from_i="<<from_i<<std::endl; - return; - } - /* - If the source and the destination slots are the same - */ - if(inv_from == inv_to && list_from == list_to && from_i == to_i) - { - infostream<<"IMoveAction::apply(): FAIL: source and destination slots " - <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list - <<"\" i="<<from_i<<std::endl; - return; - } - - // Take item from source list - InventoryItem *item1 = NULL; - if(count == 0) - item1 = list_from->changeItem(from_i, NULL); - else - item1 = list_from->takeItem(from_i, count); - - // Try to add the item to destination list - InventoryItem *olditem = item1; - item1 = list_to->addItem(to_i, item1); - - // If something is returned, the item was not fully added - if(item1 != NULL) - { - // If olditem is returned, nothing was added. - bool nothing_added = (item1 == olditem); - - // If something else is returned, part of the item was left unadded. - // Add the other part back to the source item - list_from->addItem(from_i, item1); - - // If olditem is returned, nothing was added. - // Swap the items - if(nothing_added) - { - // Take item from source list - item1 = list_from->changeItem(from_i, NULL); - // Adding was not possible, swap the items. - InventoryItem *item2 = list_to->changeItem(to_i, item1); - // Put item from destination list to the source list - list_from->changeItem(from_i, item2); - } - } - - mgr->inventoryModified(c, from_inv); - if(from_inv != to_inv) - mgr->inventoryModified(c, to_inv); - - infostream<<"IMoveAction::apply(): moved at " - <<"["<<describeC(c)<<"]" - <<" from inv=\""<<from_inv<<"\"" - <<" list=\""<<from_list<<"\"" - <<" i="<<from_i - <<" to inv=\""<<to_inv<<"\"" - <<" list=\""<<to_list<<"\"" - <<" i="<<to_i - <<std::endl; -} - -IDropAction::IDropAction(std::istream &is) -{ - std::string ts; - - std::getline(is, ts, ' '); - count = stoi(ts); - - std::getline(is, from_inv, ' '); - - std::getline(is, from_list, ' '); - - std::getline(is, ts, ' '); - from_i = stoi(ts); -} - -void IDropAction::apply(InventoryContext *c, InventoryManager *mgr, - ServerEnvironment *env) -{ - if(c->current_player == NULL){ - infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl; - return; - } - - // Do NOT cast directly to ServerActiveObject*, it breaks - // because of multiple inheritance. - ServerActiveObject *dropper = - static_cast<ServerActiveObject*>( - static_cast<ServerRemotePlayer*>( - c->current_player - )); - - Inventory *inv_from = mgr->getInventory(c, from_inv); - - if(!inv_from){ - infostream<<"IDropAction::apply(): FAIL: source inventory not found: " - <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl; - return; - } - - InventoryList *list_from = inv_from->getList(from_list); - - /* - If a list doesn't exist or the source item doesn't exist - */ - if(!list_from){ - infostream<<"IDropAction::apply(): FAIL: source list not found: " - <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" - <<", from_list=\""<<from_list<<"\""<<std::endl; - return; - } - InventoryItem *item = list_from->getItem(from_i); - if(item == NULL) - { - infostream<<"IDropAction::apply(): FAIL: source item not found: " - <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" - <<", from_list=\""<<from_list<<"\"" - <<" from_i="<<from_i<<std::endl; - return; - } - - v3f pos = dropper->getBasePosition(); - pos.Y += 0.5*BS; - - s16 count2 = count; - if(count2 == 0) - count2 = -1; - - /* - Drop the item - */ - bool remove = item->dropOrPlace(env, dropper, pos, false, count2); - if(remove) - list_from->deleteItem(from_i); - - mgr->inventoryModified(c, from_inv); - - infostream<<"IDropAction::apply(): dropped " - <<"["<<describeC(c)<<"]" - <<" from inv=\""<<from_inv<<"\"" - <<" list=\""<<from_list<<"\"" - <<" i="<<from_i - <<std::endl; -} - -/* - Craft checking system -*/ - -bool ItemSpec::checkItem(const InventoryItem *item) const -{ - if(type == ITEM_NONE) - { - // Has to be no item - if(item != NULL) - return false; - return true; - } - - // There should be an item - if(item == NULL) - return false; - - std::string itemname = item->getName(); - - if(type == ITEM_MATERIAL) - { - if(itemname != "MaterialItem") - return false; - MaterialItem *mitem = (MaterialItem*)item; - if(num != 65535){ - if(mitem->getMaterial() != num) - return false; - } else { - if(mitem->getNodeName() != name) - return false; - } - } - else if(type == ITEM_CRAFT) - { - if(itemname != "CraftItem") - return false; - CraftItem *mitem = (CraftItem*)item; - if(mitem->getSubName() != name) - return false; - } - else if(type == ITEM_TOOL) - { - // Not supported yet - assert(0); - } - else if(type == ITEM_MBO) - { - // Not supported yet - assert(0); - } - else - { - // Not supported yet - assert(0); - } - return true; -} - -bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs) -{ - u16 items_min_x = 100; - u16 items_max_x = 100; - u16 items_min_y = 100; - u16 items_max_y = 100; - for(u16 y=0; y<3; y++) - for(u16 x=0; x<3; x++) - { - if(items[y*3 + x] == NULL) - continue; - if(items_min_x == 100 || x < items_min_x) - items_min_x = x; - if(items_min_y == 100 || y < items_min_y) - items_min_y = y; - if(items_max_x == 100 || x > items_max_x) - items_max_x = x; - if(items_max_y == 100 || y > items_max_y) - items_max_y = y; - } - // No items at all, just return false - if(items_min_x == 100) - return false; - - u16 items_w = items_max_x - items_min_x + 1; - u16 items_h = items_max_y - items_min_y + 1; - - u16 specs_min_x = 100; - u16 specs_max_x = 100; - u16 specs_min_y = 100; - u16 specs_max_y = 100; - for(u16 y=0; y<3; y++) - for(u16 x=0; x<3; x++) - { - if(specs[y*3 + x].type == ITEM_NONE) - continue; - if(specs_min_x == 100 || x < specs_min_x) - specs_min_x = x; - if(specs_min_y == 100 || y < specs_min_y) - specs_min_y = y; - if(specs_max_x == 100 || x > specs_max_x) - specs_max_x = x; - if(specs_max_y == 100 || y > specs_max_y) - specs_max_y = y; - } - // No specs at all, just return false - if(specs_min_x == 100) - return false; - - u16 specs_w = specs_max_x - specs_min_x + 1; - u16 specs_h = specs_max_y - specs_min_y + 1; - - // Different sizes - if(items_w != specs_w || items_h != specs_h) - return false; - - for(u16 y=0; y<specs_h; y++) - for(u16 x=0; x<specs_w; x++) - { - u16 items_x = items_min_x + x; - u16 items_y = items_min_y + y; - u16 specs_x = specs_min_x + x; - u16 specs_y = specs_min_y + y; - const InventoryItem *item = items[items_y * 3 + items_x]; - const ItemSpec &spec = specs[specs_y * 3 + specs_x]; - - if(spec.checkItem(item) == false) - return false; - } - - return true; -} - -bool checkItemCombination(const InventoryItem * const * items, - const InventoryItem * const * specs) -{ - u16 items_min_x = 100; - u16 items_max_x = 100; - u16 items_min_y = 100; - u16 items_max_y = 100; - for(u16 y=0; y<3; y++) - for(u16 x=0; x<3; x++) - { - if(items[y*3 + x] == NULL) - continue; - if(items_min_x == 100 || x < items_min_x) - items_min_x = x; - if(items_min_y == 100 || y < items_min_y) - items_min_y = y; - if(items_max_x == 100 || x > items_max_x) - items_max_x = x; - if(items_max_y == 100 || y > items_max_y) - items_max_y = y; - } - // No items at all, just return false - if(items_min_x == 100) - return false; - - u16 items_w = items_max_x - items_min_x + 1; - u16 items_h = items_max_y - items_min_y + 1; - - u16 specs_min_x = 100; - u16 specs_max_x = 100; - u16 specs_min_y = 100; - u16 specs_max_y = 100; - for(u16 y=0; y<3; y++) - for(u16 x=0; x<3; x++) - { - if(specs[y*3 + x] == NULL) - continue; - if(specs_min_x == 100 || x < specs_min_x) - specs_min_x = x; - if(specs_min_y == 100 || y < specs_min_y) - specs_min_y = y; - if(specs_max_x == 100 || x > specs_max_x) - specs_max_x = x; - if(specs_max_y == 100 || y > specs_max_y) - specs_max_y = y; - } - // No specs at all, just return false - if(specs_min_x == 100) - return false; - - u16 specs_w = specs_max_x - specs_min_x + 1; - u16 specs_h = specs_max_y - specs_min_y + 1; - - // Different sizes - if(items_w != specs_w || items_h != specs_h) - return false; - - for(u16 y=0; y<specs_h; y++) - for(u16 x=0; x<specs_w; x++) - { - u16 items_x = items_min_x + x; - u16 items_y = items_min_y + y; - u16 specs_x = specs_min_x + x; - u16 specs_y = specs_min_y + y; - const InventoryItem *item = items[items_y * 3 + items_x]; - const InventoryItem *spec = specs[specs_y * 3 + specs_x]; - - if(item == NULL && spec == NULL) - continue; - if(item == NULL && spec != NULL) - return false; - if(item != NULL && spec == NULL) - return false; - if(!spec->isSubsetOf(item)) - return false; - } - - return true; -} - //END diff --git a/src/inventory.h b/src/inventory.h index 795d32d3e..15de3c8e7 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -419,6 +419,7 @@ public: InventoryList(std::string name, u32 size); ~InventoryList(); void clearItems(); + void setSize(u32 newsize); void serialize(std::ostream &os) const; void deSerialize(std::istream &is, IGameDef *gamedef); @@ -513,182 +514,5 @@ private: core::array<InventoryList*> m_lists; }; -class Player; - -struct InventoryContext -{ - Player *current_player; - - InventoryContext(): - current_player(NULL) - {} -}; - -struct InventoryAction; - -class InventoryManager -{ -public: - InventoryManager(){} - virtual ~InventoryManager(){} - - /* - Get a pointer to an inventory specified by id. - id can be: - - "current_player" - - "nodemeta:X,Y,Z" - */ - virtual Inventory* getInventory(InventoryContext *c, std::string id) - {return NULL;} - // Used on the server by InventoryAction::apply and other stuff - virtual void inventoryModified(InventoryContext *c, std::string id) - {} - // Used on the client - virtual void inventoryAction(InventoryAction *a) - {} -}; - -#define IACTION_MOVE 0 -#define IACTION_DROP 1 - -struct InventoryAction -{ - static InventoryAction * deSerialize(std::istream &is); - - virtual u16 getType() const = 0; - virtual void serialize(std::ostream &os) const = 0; - virtual void apply(InventoryContext *c, InventoryManager *mgr, - ServerEnvironment *env) = 0; - virtual ~InventoryAction() {}; -}; - -struct IMoveAction : public InventoryAction -{ - // count=0 means "everything" - u16 count; - std::string from_inv; - std::string from_list; - s16 from_i; - std::string to_inv; - std::string to_list; - s16 to_i; - - IMoveAction() - { - count = 0; - from_i = -1; - to_i = -1; - } - - IMoveAction(std::istream &is); - - u16 getType() const - { - return IACTION_MOVE; - } - - void serialize(std::ostream &os) const - { - os<<"Move "; - os<<count<<" "; - os<<from_inv<<" "; - os<<from_list<<" "; - os<<from_i<<" "; - os<<to_inv<<" "; - os<<to_list<<" "; - os<<to_i; - } - - void apply(InventoryContext *c, InventoryManager *mgr, - ServerEnvironment *env); -}; - -struct IDropAction : public InventoryAction -{ - // count=0 means "everything" - u16 count; - std::string from_inv; - std::string from_list; - s16 from_i; - - IDropAction() - { - count = 0; - from_i = -1; - } - - IDropAction(std::istream &is); - - u16 getType() const - { - return IACTION_DROP; - } - - void serialize(std::ostream &os) const - { - os<<"Drop "; - os<<count<<" "; - os<<from_inv<<" "; - os<<from_list<<" "; - os<<from_i; - } - - void apply(InventoryContext *c, InventoryManager *mgr, - ServerEnvironment *env); -}; - -/* - Craft checking system -*/ - -enum ItemSpecType -{ - ITEM_NONE, - ITEM_MATERIAL, - ITEM_CRAFT, - ITEM_TOOL, - ITEM_MBO -}; - -struct ItemSpec -{ - enum ItemSpecType type; - // Only other one of these is used - std::string name; - u16 num; - - ItemSpec(): - type(ITEM_NONE) - { - } - ItemSpec(enum ItemSpecType a_type, std::string a_name): - type(a_type), - name(a_name), - num(65535) - { - } - ItemSpec(enum ItemSpecType a_type, u16 a_num): - type(a_type), - name(""), - num(a_num) - { - } - - bool checkItem(const InventoryItem *item) const; -}; - -/* - items: a pointer to an array of 9 pointers to items - specs: a pointer to an array of 9 ItemSpecs -*/ -bool checkItemCombination(const InventoryItem * const*items, const ItemSpec *specs); - -/* - items: a pointer to an array of 9 pointers to items - specs: a pointer to an array of 9 pointers to items -*/ -bool checkItemCombination(const InventoryItem * const * items, - const InventoryItem * const * specs); - #endif diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp new file mode 100644 index 000000000..195438d26 --- /dev/null +++ b/src/inventorymanager.cpp @@ -0,0 +1,544 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "inventorymanager.h" +#include "serverremoteplayer.h" +#include "log.h" +#include "mapblock.h" // getNodeBlockPos + +/* + InventoryManager +*/ + +// Wrapper for old code +Inventory* InventoryManager::getInventory(InventoryContext *c, std::string id) +{ + if(id == "current_player") + { + assert(c->current_player); + InventoryLocation loc; + loc.setPlayer(c->current_player->getName()); + return getInventory(loc); + } + + Strfnd fn(id); + std::string id0 = fn.next(":"); + + if(id0 == "nodemeta") + { + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + + InventoryLocation loc; + loc.setNodeMeta(p); + return getInventory(loc); + } + + errorstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl; + return NULL; +} +// Wrapper for old code +void InventoryManager::inventoryModified(InventoryContext *c, std::string id) +{ + if(id == "current_player") + { + assert(c->current_player); + InventoryLocation loc; + loc.setPlayer(c->current_player->getName()); + setInventoryModified(loc); + return; + } + + Strfnd fn(id); + std::string id0 = fn.next(":"); + + if(id0 == "nodemeta") + { + v3s16 p; + p.X = stoi(fn.next(",")); + p.Y = stoi(fn.next(",")); + p.Z = stoi(fn.next(",")); + v3s16 blockpos = getNodeBlockPos(p); + + InventoryLocation loc; + loc.setNodeMeta(p); + setInventoryModified(loc); + return; + } + + errorstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl; +} + +/* + InventoryAction +*/ + +InventoryAction * InventoryAction::deSerialize(std::istream &is) +{ + std::string type; + std::getline(is, type, ' '); + + InventoryAction *a = NULL; + + if(type == "Move") + { + a = new IMoveAction(is); + } + else if(type == "Drop") + { + a = new IDropAction(is); + } + + return a; +} + +static std::string describeC(const struct InventoryContext *c) +{ + if(c->current_player == NULL) + return "current_player=NULL"; + else + return std::string("current_player=") + c->current_player->getName(); +} + +IMoveAction::IMoveAction(std::istream &is) +{ + std::string ts; + + std::getline(is, ts, ' '); + count = stoi(ts); + + std::getline(is, from_inv, ' '); + + std::getline(is, from_list, ' '); + + std::getline(is, ts, ' '); + from_i = stoi(ts); + + std::getline(is, to_inv, ' '); + + std::getline(is, to_list, ' '); + + std::getline(is, ts, ' '); + to_i = stoi(ts); +} + +void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr, + ServerEnvironment *env) +{ + Inventory *inv_from = mgr->getInventory(c, from_inv); + Inventory *inv_to = mgr->getInventory(c, to_inv); + + if(!inv_from){ + infostream<<"IMoveAction::apply(): FAIL: source inventory not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", to_inv=\""<<to_inv<<"\""<<std::endl; + return; + } + if(!inv_to){ + infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: " + "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", to_inv=\""<<to_inv<<"\""<<std::endl; + return; + } + + InventoryList *list_from = inv_from->getList(from_list); + InventoryList *list_to = inv_to->getList(to_list); + + /* + If a list doesn't exist or the source item doesn't exist + */ + if(!list_from){ + infostream<<"IMoveAction::apply(): FAIL: source list not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", from_list=\""<<from_list<<"\""<<std::endl; + return; + } + if(!list_to){ + infostream<<"IMoveAction::apply(): FAIL: destination list not found: " + <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\"" + <<", to_list=\""<<to_list<<"\""<<std::endl; + return; + } + if(list_from->getItem(from_i) == NULL) + { + infostream<<"IMoveAction::apply(): FAIL: source item not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", from_list=\""<<from_list<<"\"" + <<" from_i="<<from_i<<std::endl; + return; + } + /* + If the source and the destination slots are the same + */ + if(inv_from == inv_to && list_from == list_to && from_i == to_i) + { + infostream<<"IMoveAction::apply(): FAIL: source and destination slots " + <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list + <<"\" i="<<from_i<<std::endl; + return; + } + + // Take item from source list + InventoryItem *item1 = NULL; + if(count == 0) + item1 = list_from->changeItem(from_i, NULL); + else + item1 = list_from->takeItem(from_i, count); + + // Try to add the item to destination list + InventoryItem *olditem = item1; + item1 = list_to->addItem(to_i, item1); + + // If something is returned, the item was not fully added + if(item1 != NULL) + { + // If olditem is returned, nothing was added. + bool nothing_added = (item1 == olditem); + + // If something else is returned, part of the item was left unadded. + // Add the other part back to the source item + list_from->addItem(from_i, item1); + + // If olditem is returned, nothing was added. + // Swap the items + if(nothing_added) + { + // Take item from source list + item1 = list_from->changeItem(from_i, NULL); + // Adding was not possible, swap the items. + InventoryItem *item2 = list_to->changeItem(to_i, item1); + // Put item from destination list to the source list + list_from->changeItem(from_i, item2); + } + } + + mgr->inventoryModified(c, from_inv); + if(from_inv != to_inv) + mgr->inventoryModified(c, to_inv); + + infostream<<"IMoveAction::apply(): moved at " + <<"["<<describeC(c)<<"]" + <<" from inv=\""<<from_inv<<"\"" + <<" list=\""<<from_list<<"\"" + <<" i="<<from_i + <<" to inv=\""<<to_inv<<"\"" + <<" list=\""<<to_list<<"\"" + <<" i="<<to_i + <<std::endl; +} + +IDropAction::IDropAction(std::istream &is) +{ + std::string ts; + + std::getline(is, ts, ' '); + count = stoi(ts); + + std::getline(is, from_inv, ' '); + + std::getline(is, from_list, ' '); + + std::getline(is, ts, ' '); + from_i = stoi(ts); +} + +void IDropAction::apply(InventoryContext *c, InventoryManager *mgr, + ServerEnvironment *env) +{ + if(c->current_player == NULL){ + infostream<<"IDropAction::apply(): FAIL: current_player is NULL"<<std::endl; + return; + } + + // Do NOT cast directly to ServerActiveObject*, it breaks + // because of multiple inheritance. + ServerActiveObject *dropper = + static_cast<ServerActiveObject*>( + static_cast<ServerRemotePlayer*>( + c->current_player + )); + + Inventory *inv_from = mgr->getInventory(c, from_inv); + + if(!inv_from){ + infostream<<"IDropAction::apply(): FAIL: source inventory not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl; + return; + } + + InventoryList *list_from = inv_from->getList(from_list); + + /* + If a list doesn't exist or the source item doesn't exist + */ + if(!list_from){ + infostream<<"IDropAction::apply(): FAIL: source list not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", from_list=\""<<from_list<<"\""<<std::endl; + return; + } + InventoryItem *item = list_from->getItem(from_i); + if(item == NULL) + { + infostream<<"IDropAction::apply(): FAIL: source item not found: " + <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\"" + <<", from_list=\""<<from_list<<"\"" + <<" from_i="<<from_i<<std::endl; + return; + } + + v3f pos = dropper->getBasePosition(); + pos.Y += 0.5*BS; + + s16 count2 = count; + if(count2 == 0) + count2 = -1; + + /* + Drop the item + */ + bool remove = item->dropOrPlace(env, dropper, pos, false, count2); + if(remove) + list_from->deleteItem(from_i); + + mgr->inventoryModified(c, from_inv); + + infostream<<"IDropAction::apply(): dropped " + <<"["<<describeC(c)<<"]" + <<" from inv=\""<<from_inv<<"\"" + <<" list=\""<<from_list<<"\"" + <<" i="<<from_i + <<std::endl; +} + +/* + Craft checking system +*/ + +bool ItemSpec::checkItem(const InventoryItem *item) const +{ + if(type == ITEM_NONE) + { + // Has to be no item + if(item != NULL) + return false; + return true; + } + + // There should be an item + if(item == NULL) + return false; + + std::string itemname = item->getName(); + + if(type == ITEM_MATERIAL) + { + if(itemname != "MaterialItem") + return false; + MaterialItem *mitem = (MaterialItem*)item; + if(num != 65535){ + if(mitem->getMaterial() != num) + return false; + } else { + if(mitem->getNodeName() != name) + return false; + } + } + else if(type == ITEM_CRAFT) + { + if(itemname != "CraftItem") + return false; + CraftItem *mitem = (CraftItem*)item; + if(mitem->getSubName() != name) + return false; + } + else if(type == ITEM_TOOL) + { + // Not supported yet + assert(0); + } + else if(type == ITEM_MBO) + { + // Not supported yet + assert(0); + } + else + { + // Not supported yet + assert(0); + } + return true; +} + +bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs) +{ + u16 items_min_x = 100; + u16 items_max_x = 100; + u16 items_min_y = 100; + u16 items_max_y = 100; + for(u16 y=0; y<3; y++) + for(u16 x=0; x<3; x++) + { + if(items[y*3 + x] == NULL) + continue; + if(items_min_x == 100 || x < items_min_x) + items_min_x = x; + if(items_min_y == 100 || y < items_min_y) + items_min_y = y; + if(items_max_x == 100 || x > items_max_x) + items_max_x = x; + if(items_max_y == 100 || y > items_max_y) + items_max_y = y; + } + // No items at all, just return false + if(items_min_x == 100) + return false; + + u16 items_w = items_max_x - items_min_x + 1; + u16 items_h = items_max_y - items_min_y + 1; + + u16 specs_min_x = 100; + u16 specs_max_x = 100; + u16 specs_min_y = 100; + u16 specs_max_y = 100; + for(u16 y=0; y<3; y++) + for(u16 x=0; x<3; x++) + { + if(specs[y*3 + x].type == ITEM_NONE) + continue; + if(specs_min_x == 100 || x < specs_min_x) + specs_min_x = x; + if(specs_min_y == 100 || y < specs_min_y) + specs_min_y = y; + if(specs_max_x == 100 || x > specs_max_x) + specs_max_x = x; + if(specs_max_y == 100 || y > specs_max_y) + specs_max_y = y; + } + // No specs at all, just return false + if(specs_min_x == 100) + return false; + + u16 specs_w = specs_max_x - specs_min_x + 1; + u16 specs_h = specs_max_y - specs_min_y + 1; + + // Different sizes + if(items_w != specs_w || items_h != specs_h) + return false; + + for(u16 y=0; y<specs_h; y++) + for(u16 x=0; x<specs_w; x++) + { + u16 items_x = items_min_x + x; + u16 items_y = items_min_y + y; + u16 specs_x = specs_min_x + x; + u16 specs_y = specs_min_y + y; + const InventoryItem *item = items[items_y * 3 + items_x]; + const ItemSpec &spec = specs[specs_y * 3 + specs_x]; + + if(spec.checkItem(item) == false) + return false; + } + + return true; +} + +bool checkItemCombination(const InventoryItem * const * items, + const InventoryItem * const * specs) +{ + u16 items_min_x = 100; + u16 items_max_x = 100; + u16 items_min_y = 100; + u16 items_max_y = 100; + for(u16 y=0; y<3; y++) + for(u16 x=0; x<3; x++) + { + if(items[y*3 + x] == NULL) + continue; + if(items_min_x == 100 || x < items_min_x) + items_min_x = x; + if(items_min_y == 100 || y < items_min_y) + items_min_y = y; + if(items_max_x == 100 || x > items_max_x) + items_max_x = x; + if(items_max_y == 100 || y > items_max_y) + items_max_y = y; + } + // No items at all, just return false + if(items_min_x == 100) + return false; + + u16 items_w = items_max_x - items_min_x + 1; + u16 items_h = items_max_y - items_min_y + 1; + + u16 specs_min_x = 100; + u16 specs_max_x = 100; + u16 specs_min_y = 100; + u16 specs_max_y = 100; + for(u16 y=0; y<3; y++) + for(u16 x=0; x<3; x++) + { + if(specs[y*3 + x] == NULL) + continue; + if(specs_min_x == 100 || x < specs_min_x) + specs_min_x = x; + if(specs_min_y == 100 || y < specs_min_y) + specs_min_y = y; + if(specs_max_x == 100 || x > specs_max_x) + specs_max_x = x; + if(specs_max_y == 100 || y > specs_max_y) + specs_max_y = y; + } + // No specs at all, just return false + if(specs_min_x == 100) + return false; + + u16 specs_w = specs_max_x - specs_min_x + 1; + u16 specs_h = specs_max_y - specs_min_y + 1; + + // Different sizes + if(items_w != specs_w || items_h != specs_h) + return false; + + for(u16 y=0; y<specs_h; y++) + for(u16 x=0; x<specs_w; x++) + { + u16 items_x = items_min_x + x; + u16 items_y = items_min_y + y; + u16 specs_x = specs_min_x + x; + u16 specs_y = specs_min_y + y; + const InventoryItem *item = items[items_y * 3 + items_x]; + const InventoryItem *spec = specs[specs_y * 3 + specs_x]; + + if(item == NULL && spec == NULL) + continue; + if(item == NULL && spec != NULL) + return false; + if(item != NULL && spec == NULL) + return false; + if(!spec->isSubsetOf(item)) + return false; + } + + return true; +} + + diff --git a/src/inventorymanager.h b/src/inventorymanager.h new file mode 100644 index 000000000..4abe5e73d --- /dev/null +++ b/src/inventorymanager.h @@ -0,0 +1,228 @@ +/* +Minetest-c55 +Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#ifndef INVENTORYMANAGER_HEADER +#define INVENTORYMANAGER_HEADER + +#include "inventory.h" + +// Should probably somehow replace InventoryContext over time +struct InventoryLocation +{ + enum Type{ + UNDEFINED, + PLAYER, + NODEMETA + } type; + + std::string name; // PLAYER + v3s16 p; // NODEMETA + + void setPlayer(const std::string &name_) + { + type = PLAYER; + name = name_; + } + void setNodeMeta(v3s16 p_) + { + type = NODEMETA; + p = p_; + } +}; + +class Player; + +struct InventoryContext +{ + Player *current_player; + + InventoryContext(): + current_player(NULL) + {} +}; + +struct InventoryAction; + +class InventoryManager +{ +public: + InventoryManager(){} + virtual ~InventoryManager(){} + + // Get an inventory or set it modified (so it will be updated over + // network or so) + virtual Inventory* getInventory(const InventoryLocation &loc){return NULL;} + virtual void setInventoryModified(const InventoryLocation &loc){} + + // Used on the client to send an action to the server + virtual void inventoryAction(InventoryAction *a){} + + // (Deprecated; these wrap to the latter ones) + // Get a pointer to an inventory specified by id. id can be: + // - "current_player" + // - "nodemeta:X,Y,Z" + Inventory* getInventory(InventoryContext *c, std::string id); + // Used on the server by InventoryAction::apply and other stuff + void inventoryModified(InventoryContext *c, std::string id); +}; + +#define IACTION_MOVE 0 +#define IACTION_DROP 1 + +struct InventoryAction +{ + static InventoryAction * deSerialize(std::istream &is); + + virtual u16 getType() const = 0; + virtual void serialize(std::ostream &os) const = 0; + virtual void apply(InventoryContext *c, InventoryManager *mgr, + ServerEnvironment *env) = 0; +}; + +struct IMoveAction : public InventoryAction +{ + // count=0 means "everything" + u16 count; + std::string from_inv; + std::string from_list; + s16 from_i; + std::string to_inv; + std::string to_list; + s16 to_i; + + IMoveAction() + { + count = 0; + from_i = -1; + to_i = -1; + } + + IMoveAction(std::istream &is); + + u16 getType() const + { + return IACTION_MOVE; + } + + void serialize(std::ostream &os) const + { + os<<"Move "; + os<<count<<" "; + os<<from_inv<<" "; + os<<from_list<<" "; + os<<from_i<<" "; + os<<to_inv<<" "; + os<<to_list<<" "; + os<<to_i; + } + + void apply(InventoryContext *c, InventoryManager *mgr, + ServerEnvironment *env); +}; + +struct IDropAction : public InventoryAction +{ + // count=0 means "everything" + u16 count; + std::string from_inv; + std::string from_list; + s16 from_i; + + IDropAction() + { + count = 0; + from_i = -1; + } + + IDropAction(std::istream &is); + + u16 getType() const + { + return IACTION_DROP; + } + + void serialize(std::ostream &os) const + { + os<<"Drop "; + os<<count<<" "; + os<<from_inv<<" "; + os<<from_list<<" "; + os<<from_i; + } + + void apply(InventoryContext *c, InventoryManager *mgr, + ServerEnvironment *env); +}; + +/* + Craft checking system +*/ + +enum ItemSpecType +{ + ITEM_NONE, + ITEM_MATERIAL, + ITEM_CRAFT, + ITEM_TOOL, + ITEM_MBO +}; + +struct ItemSpec +{ + enum ItemSpecType type; + // Only other one of these is used + std::string name; + u16 num; + + ItemSpec(): + type(ITEM_NONE) + { + } + ItemSpec(enum ItemSpecType a_type, std::string a_name): + type(a_type), + name(a_name), + num(65535) + { + } + ItemSpec(enum ItemSpecType a_type, u16 a_num): + type(a_type), + name(""), + num(a_num) + { + } + + bool checkItem(const InventoryItem *item) const; +}; + +/* + items: a pointer to an array of 9 pointers to items + specs: a pointer to an array of 9 ItemSpecs +*/ +bool checkItemCombination(const InventoryItem * const*items, const ItemSpec *specs); + +/* + items: a pointer to an array of 9 pointers to items + specs: a pointer to an array of 9 pointers to items +*/ +bool checkItemCombination(const InventoryItem * const * items, + const InventoryItem * const * specs); + + +#endif + diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp index 1a50e1e34..f7a2ce9aa 100644 --- a/src/scriptapi.cpp +++ b/src/scriptapi.cpp @@ -204,6 +204,20 @@ static v2f read_v2f(lua_State *L, int index) return p; } +static Server* get_server(lua_State *L) +{ + // Get server from registry + lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); + return (Server*)lua_touserdata(L, -1); +} + +static ServerEnvironment* get_env(lua_State *L) +{ + // Get environment from registry + lua_getfield(L, LUA_REGISTRYINDEX, "minetest_env"); + return (ServerEnvironment*)lua_touserdata(L, -1); +} + static v3f readFloatPos(lua_State *L, int index) { v3f pos; @@ -551,6 +565,68 @@ static void inventory_get_list_to_lua(Inventory *inv, const char *name, } } +static void push_stack_item(lua_State *L, InventoryItem *item0) +{ + if(item0 == NULL){ + lua_pushnil(L); + } + if(std::string("MaterialItem") == item0->getName()){ + MaterialItem *item = (MaterialItem*)item0; + lua_newtable(L); + lua_pushstring(L, "NodeItem"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, item->getNodeName().c_str()); + lua_setfield(L, -2, "name"); + } + else if(std::string("CraftItem") == item0->getName()){ + CraftItem *item = (CraftItem*)item0; + lua_newtable(L); + lua_pushstring(L, "CraftItem"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, item->getSubName().c_str()); + lua_setfield(L, -2, "name"); + } + else if(std::string("ToolItem") == item0->getName()){ + ToolItem *item = (ToolItem*)item0; + lua_newtable(L); + lua_pushstring(L, "ToolItem"); + lua_setfield(L, -2, "type"); + lua_pushstring(L, item->getToolName().c_str()); + lua_setfield(L, -2, "name"); + lua_pushstring(L, itos(item->getWear()).c_str()); + lua_setfield(L, -2, "wear"); + } + else{ + errorstream<<"push_stack_item: Unknown item name: \"" + <<item0->getName()<<"\""<<std::endl; + lua_pushnil(L); + } +} + +static InventoryItem* check_stack_item(lua_State *L, int index) +{ + if(index < 0) + index = lua_gettop(L) + 1 + index; + if(lua_isnil(L, index)) + return NULL; + if(!lua_istable(L, index)) + throw LuaError(L, "check_stack_item: Item not nil or table"); + // A very crappy implementation for now + // Will be replaced when unified namespace for items is made + std::string type = getstringfield_default(L, index, "type", ""); + std::string name = getstringfield_default(L, index, "name", ""); + std::string num = getstringfield_default(L, index, "wear", "_"); + if(num == "_") + num = 1; + std::string itemstring = type + " \"" + name + "\" " + num; + try{ + return InventoryItem::deSerialize(itemstring, get_server(L)); + }catch(SerializationError &e){ + throw LuaError(L, std::string("check_stack_item: " + "internal error (itemstring=\"")+itemstring+"\")"); + } +} + /* ToolDiggingProperties */ @@ -835,12 +911,9 @@ static int l_register_tool(lua_State *L) luaL_checktype(L, 2, LUA_TTABLE); int table = 2; - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); - // And get the writable tool definition manager from the server + // Get the writable tool definition manager from the server IWritableToolDefManager *tooldef = - server->getWritableToolDefManager(); + get_server(L)->getWritableToolDefManager(); ToolDefinition def = read_tool_definition(L, table); @@ -857,12 +930,9 @@ static int l_register_craftitem(lua_State *L) luaL_checktype(L, 2, LUA_TTABLE); int table = 2; - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); - // And get the writable CraftItem definition manager from the server + // Get the writable CraftItem definition manager from the server IWritableCraftItemDefManager *craftitemdef = - server->getWritableCraftItemDefManager(); + get_server(L)->getWritableCraftItemDefManager(); // Check if on_drop is defined lua_getfield(L, table, "on_drop"); @@ -907,12 +977,9 @@ static int l_register_node(lua_State *L) luaL_checktype(L, 2, LUA_TTABLE); int nodedef_table = 2; - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); - // And get the writable node definition manager from the server + // Get the writable node definition manager from the server IWritableNodeDefManager *nodedef = - server->getWritableNodeDefManager(); + get_server(L)->getWritableNodeDefManager(); // Get default node definition from registry lua_getfield(L, LUA_REGISTRYINDEX, "minetest_nodedef_default"); @@ -1147,12 +1214,9 @@ static int l_alias_node(lua_State *L) std::string name = luaL_checkstring(L, 1); std::string convert_to = luaL_checkstring(L, 2); - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); - // And get the writable node definition manager from the server + // Get the writable node definition manager from the server IWritableNodeDefManager *nodedef = - server->getWritableNodeDefManager(); + get_server(L)->getWritableNodeDefManager(); nodedef->setAlias(name, convert_to); @@ -1165,12 +1229,9 @@ static int l_alias_tool(lua_State *L) std::string name = luaL_checkstring(L, 1); std::string convert_to = luaL_checkstring(L, 2); - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); - // And get the writable tool definition manager from the server + // Get the writable tool definition manager from the server IWritableToolDefManager *tooldef = - server->getWritableToolDefManager(); + get_server(L)->getWritableToolDefManager(); tooldef->setAlias(name, convert_to); @@ -1183,12 +1244,9 @@ static int l_alias_craftitem(lua_State *L) std::string name = luaL_checkstring(L, 1); std::string convert_to = luaL_checkstring(L, 2); - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); - // And get the writable CraftItem definition manager from the server + // Get the writable CraftItem definition manager from the server IWritableCraftItemDefManager *craftitemdef = - server->getWritableCraftItemDefManager(); + get_server(L)->getWritableCraftItemDefManager(); craftitemdef->setAlias(name, convert_to); @@ -1202,12 +1260,9 @@ static int l_register_craft(lua_State *L) luaL_checktype(L, 1, LUA_TTABLE); int table0 = 1; - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); - // And get the writable craft definition manager from the server + // Get the writable craft definition manager from the server IWritableCraftDefManager *craftdef = - server->getWritableCraftDefManager(); + get_server(L)->getWritableCraftDefManager(); std::string output; int width = 0; @@ -1295,8 +1350,7 @@ static int l_chat_send_all(lua_State *L) { const char *text = luaL_checkstring(L, 1); // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); + Server *server = get_server(L); // Send server->notifyPlayers(narrow_to_wide(text)); return 0; @@ -1308,8 +1362,7 @@ static int l_chat_send_player(lua_State *L) const char *name = luaL_checkstring(L, 1); const char *text = luaL_checkstring(L, 2); // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); + Server *server = get_server(L); // Send server->notifyPlayer(name, narrow_to_wide(text)); return 0; @@ -1320,8 +1373,7 @@ static int l_get_player_privs(lua_State *L) { const char *name = luaL_checkstring(L, 1); // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); + Server *server = get_server(L); // Do it lua_newtable(L); int table = lua_gettop(L); @@ -1413,12 +1465,338 @@ static void luaentity_get(lua_State *L, u16 id) } /* - Reference wrappers + Object wrappers */ #define method(class, name) {#name, class::l_##name} /* + InvStack +*/ + +class InvStack +{ +private: + InventoryItem *m_stack; + + static const char className[]; + static const luaL_reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L) { + InvStack *o = *(InvStack **)(lua_touserdata(L, 1)); + delete o; + return 0; + } + + // take_item(self) + static int l_take_item(lua_State *L) + { + InvStack *o = checkobject(L, 1); + push_stack_item(L, o->m_stack); + if(o->m_stack->getCount() == 1){ + delete o->m_stack; + o->m_stack = NULL; + } else { + o->m_stack->remove(1); + } + return 1; + } + + // put_item(self, item) -> true/false + static int l_put_item(lua_State *L) + { + InvStack *o = checkobject(L, 1); + InventoryItem *item = check_stack_item(L, 2); + if(!item){ // nil can always be inserted + lua_pushboolean(L, true); + return 1; + } + if(!item->addableTo(o->m_stack)){ + lua_pushboolean(L, false); + return 1; + } + o->m_stack->add(1); + delete item; + lua_pushboolean(L, true); + return 1; + } + +public: + InvStack(InventoryItem *item=NULL): + m_stack(item) + { + } + + ~InvStack() + { + delete m_stack; + } + + static InvStack* checkobject(lua_State *L, int narg) + { + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if(!ud) luaL_typerror(L, narg, className); + return *(InvStack**)ud; // unbox pointer + } + + InventoryItem* getItemCopy() + { + if(!m_stack) + return NULL; + return m_stack->clone(); + } + + // Creates an InvStack and leaves it on top of stack + static int create_object(lua_State *L) + { + InventoryItem *item = NULL; + if(lua_isstring(L, 1)){ + std::string itemstring = lua_tostring(L, 1); + if(itemstring != ""){ + try{ + IGameDef *gdef = get_server(L); + item = InventoryItem::deSerialize(itemstring, gdef); + }catch(SerializationError &e){ + } + } + } + InvStack *o = new InvStack(item); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; + } + // Not callable from Lua + static int create(lua_State *L, InventoryItem *item) + { + InvStack *o = new InvStack(item); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; + } + + static void Register(lua_State *L) + { + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Can be created from Lua (InvStack::create(itemstring)) + lua_register(L, className, create_object); + } +}; +const char InvStack::className[] = "InvStack"; +const luaL_reg InvStack::methods[] = { + method(InvStack, take_item), + method(InvStack, put_item), + {0,0} +}; + +/* + InvRef +*/ + +class InvRef +{ +private: + InventoryLocation m_loc; + + static const char className[]; + static const luaL_reg methods[]; + + static InvRef *checkobject(lua_State *L, int narg) + { + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if(!ud) luaL_typerror(L, narg, className); + return *(InvRef**)ud; // unbox pointer + } + + static Inventory* getinv(lua_State *L, InvRef *ref) + { + return get_server(L)->getInventory(ref->m_loc); + } + + static InventoryList* getlist(lua_State *L, InvRef *ref, + const char *listname) + { + Inventory *inv = getinv(L, ref); + if(!inv) + return NULL; + return inv->getList(listname); + } + + static InventoryItem* getitem(lua_State *L, InvRef *ref, + const char *listname, int i) + { + InventoryList *list = getlist(L, ref, listname); + if(!list) + return NULL; + return list->getItem(i); + } + + static void reportInventoryChange(lua_State *L, InvRef *ref) + { + // Inform other things that the inventory has changed + get_server(L)->setInventoryModified(ref->m_loc); + } + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L) { + InvRef *o = *(InvRef **)(lua_touserdata(L, 1)); + delete o; + return 0; + } + + // set_size(self, listname, size) + static int l_set_size(lua_State *L) + { + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + int newsize = luaL_checknumber(L, 3); + Inventory *inv = getinv(L, ref); + if(newsize == 0){ + inv->deleteList(listname); + return 0; + } + InventoryList *list = inv->getList(listname); + if(list){ + list->setSize(newsize); + } else { + list = inv->addList(listname, newsize); + } + return 0; + } + + // get_stack(self, listname, i) + static int l_get_stack(lua_State *L) + { + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + int i = luaL_checknumber(L, 3); + InventoryItem *item = getitem(L, ref, listname, i); + if(!item){ + InvStack::create(L, NULL); + return 1; + } + InvStack::create(L, item->clone()); + return 1; + } + + // set_stack(self, listname, i, stack) + static int l_set_stack(lua_State *L) + { + InvRef *ref = checkobject(L, 1); + const char *listname = luaL_checkstring(L, 2); + int i = luaL_checknumber(L, 3); + InvStack *stack = InvStack::checkobject(L, 4); + InventoryList *list = getlist(L, ref, listname); + if(!list){ + lua_pushboolean(L, false); + return 1; + } + InventoryItem *newitem = stack->getItemCopy(); + InventoryItem *olditem = list->changeItem(i, newitem); + bool success = (olditem != newitem); + delete olditem; + lua_pushboolean(L, success); + return 1; + } + +public: + InvRef(const InventoryLocation &loc): + m_loc(loc) + { + } + + ~InvRef() + { + } + + // Creates an InvRef and leaves it on top of stack + // Not callable from Lua; all references are created on the C side. + static void create(lua_State *L, const InventoryLocation &loc) + { + InvRef *o = new InvRef(loc); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + } + static void createPlayer(lua_State *L, Player *player) + { + InventoryLocation loc; + loc.setPlayer(player->getName()); + create(L, loc); + } + static void createNodeMeta(lua_State *L, v3s16 p) + { + InventoryLocation loc; + loc.setNodeMeta(p); + create(L, loc); + } + + static void Register(lua_State *L) + { + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); // fill methodtable + lua_pop(L, 1); // drop methodtable + + // Cannot be created from Lua + //lua_register(L, className, create_object); + } +}; +const char InvRef::className[] = "InvRef"; +const luaL_reg InvRef::methods[] = { + method(InvRef, set_size), + method(InvRef, get_stack), + method(InvRef, set_stack), + {0,0} +}; + +/* NodeMetaRef */ @@ -2189,41 +2567,7 @@ private: if(player == NULL) return 0; // Do it InventoryItem *item0 = player->getWieldedItem(); - if(item0 == NULL){ - lua_pushnil(L); - return 1; - } - if(std::string("MaterialItem") == item0->getName()){ - MaterialItem *item = (MaterialItem*)item0; - lua_newtable(L); - lua_pushstring(L, "NodeItem"); - lua_setfield(L, -2, "type"); - lua_pushstring(L, item->getNodeName().c_str()); - lua_setfield(L, -2, "name"); - } - else if(std::string("CraftItem") == item0->getName()){ - CraftItem *item = (CraftItem*)item0; - lua_newtable(L); - lua_pushstring(L, "CraftItem"); - lua_setfield(L, -2, "type"); - lua_pushstring(L, item->getSubName().c_str()); - lua_setfield(L, -2, "name"); - } - else if(std::string("ToolItem") == item0->getName()){ - ToolItem *item = (ToolItem*)item0; - lua_newtable(L); - lua_pushstring(L, "ToolItem"); - lua_setfield(L, -2, "type"); - lua_pushstring(L, item->getToolName().c_str()); - lua_setfield(L, -2, "name"); - lua_pushstring(L, itos(item->getWear()).c_str()); - lua_setfield(L, -2, "wear"); - } - else{ - errorstream<<"l_get_wielded_item: Unknown item name: \"" - <<item0->getName()<<"\""<<std::endl; - lua_pushnil(L); - } + push_stack_item(L, item0); return 1; } @@ -3236,12 +3580,9 @@ void scriptapi_environment_on_placenode(lua_State *L, v3s16 p, MapNode newnode, //infostream<<"scriptapi_environment_on_placenode"<<std::endl; StackUnroller stack_unroller(L); - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); - // And get the writable node definition manager from the server + // Get the writable node definition manager from the server IWritableNodeDefManager *ndef = - server->getWritableNodeDefManager(); + get_server(L)->getWritableNodeDefManager(); // Get minetest.registered_on_placenodes lua_getglobal(L, "minetest"); @@ -3271,12 +3612,9 @@ void scriptapi_environment_on_dignode(lua_State *L, v3s16 p, MapNode oldnode, //infostream<<"scriptapi_environment_on_dignode"<<std::endl; StackUnroller stack_unroller(L); - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); - // And get the writable node definition manager from the server + // Get the writable node definition manager from the server IWritableNodeDefManager *ndef = - server->getWritableNodeDefManager(); + get_server(L)->getWritableNodeDefManager(); // Get minetest.registered_on_dignodes lua_getglobal(L, "minetest"); @@ -3306,12 +3644,9 @@ void scriptapi_environment_on_punchnode(lua_State *L, v3s16 p, MapNode node, //infostream<<"scriptapi_environment_on_punchnode"<<std::endl; StackUnroller stack_unroller(L); - // Get server from registry - lua_getfield(L, LUA_REGISTRYINDEX, "minetest_server"); - Server *server = (Server*)lua_touserdata(L, -1); - // And get the writable node definition manager from the server + // Get the writable node definition manager from the server IWritableNodeDefManager *ndef = - server->getWritableNodeDefManager(); + get_server(L)->getWritableNodeDefManager(); // Get minetest.registered_on_punchnodes lua_getglobal(L, "minetest"); diff --git a/src/server.cpp b/src/server.cpp index 4a0e952a8..3679195f3 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2458,7 +2458,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) craftresult and immediately moved to the free slot. */ do{ - Inventory *inv_to = getInventory(&c, ma->to_inv); + Inventory *inv_to = InventoryManager::getInventory(&c, ma->to_inv); if(!inv_to) break; InventoryList *list_to = inv_to->getList(ma->to_list); if(!list_to) break; @@ -3517,6 +3517,68 @@ void Server::onMapEditEvent(MapEditEvent *event) m_unsent_map_edit_queue.push_back(e); } +Inventory* Server::getInventory(const InventoryLocation &loc) +{ + switch(loc.type){ + case InventoryLocation::UNDEFINED: + {} + break; + case InventoryLocation::PLAYER: + { + Player *player = m_env->getPlayer(loc.name.c_str()); + if(!player) + return NULL; + return &player->inventory; + } + break; + case InventoryLocation::NODEMETA: + { + NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); + if(!meta) + return NULL; + return meta->getInventory(); + } + break; + default: + assert(0); + } + return NULL; +} +void Server::setInventoryModified(const InventoryLocation &loc) +{ + switch(loc.type){ + case InventoryLocation::UNDEFINED: + {} + break; + case InventoryLocation::PLAYER: + { + ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*> + (m_env->getPlayer(loc.name.c_str())); + if(!srp) + return; + srp->m_inventory_not_sent = true; + } + break; + case InventoryLocation::NODEMETA: + { + v3s16 blockpos = getNodeBlockPos(loc.p); + + NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p); + if(meta) + meta->inventoryModified(); + + MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); + if(block) + block->raiseModified(MOD_STATE_WRITE_NEEDED); + + setBlockNotSent(blockpos); + } + break; + default: + assert(0); + } +} +#if 0 Inventory* Server::getInventory(InventoryContext *c, std::string id) { if(id == "current_player") @@ -3534,12 +3596,10 @@ Inventory* Server::getInventory(InventoryContext *c, std::string id) p.X = stoi(fn.next(",")); p.Y = stoi(fn.next(",")); p.Z = stoi(fn.next(",")); - NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); - if(meta) - return meta->getInventory(); - infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): " - <<"no metadata found"<<std::endl; - return NULL; + + InventoryLocation loc; + loc.setNodeMeta(p); + return getInventory(loc); } infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl; @@ -3567,21 +3627,15 @@ void Server::inventoryModified(InventoryContext *c, std::string id) p.Z = stoi(fn.next(",")); v3s16 blockpos = getNodeBlockPos(p); - NodeMetadata *meta = m_env->getMap().getNodeMetadata(p); - if(meta) - meta->inventoryModified(); - - MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos); - if(block) - block->raiseModified(MOD_STATE_WRITE_NEEDED); - - setBlockNotSent(blockpos); - + InventoryLocation loc; + loc.setNodeMeta(p); + setInventoryModified(loc); return; } infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl; } +#endif core::list<PlayerInfo> Server::getPlayerInfo() { diff --git a/src/server.h b/src/server.h index ac8938175..129b4ac81 100644 --- a/src/server.h +++ b/src/server.h @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" // For SER_FMT_VER_INVALID #include "serverremoteplayer.h" #include "mods.h" +#include "inventorymanager.h" struct LuaState; typedef struct lua_State lua_State; class IWritableToolDefManager; @@ -413,8 +414,8 @@ public: /* Shall be called with the environment and the connection locked. */ - Inventory* getInventory(InventoryContext *c, std::string id); - void inventoryModified(InventoryContext *c, std::string id); + Inventory* getInventory(const InventoryLocation &loc); + void setInventoryModified(const InventoryLocation &loc); // Connection must be locked when called std::wstring getStatusString(); |