aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/lua_api.txt6
-rw-r--r--games/minimal/mods/experimental/init.lua7
-rw-r--r--src/client.cpp34
-rw-r--r--src/client.h4
-rw-r--r--src/clientserver.h9
-rw-r--r--src/guiFormSpecMenu.cpp17
-rw-r--r--src/guiFormSpecMenu.h6
-rw-r--r--src/inventorymanager.cpp40
-rw-r--r--src/inventorymanager.h16
-rw-r--r--src/scriptapi.cpp18
-rw-r--r--src/server.cpp137
-rw-r--r--src/server.h13
12 files changed, 222 insertions, 85 deletions
diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 176745a2d..c9fafd65f 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -677,6 +677,7 @@ size[<W>,<H>]
^ deprecated: invsize[<W>,<H>;]
list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;]
+list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]
^ Show an inventory list
image[<X>,<Y>;<W>,<H>;<texture name>]
@@ -726,10 +727,12 @@ image_button_exit[<X>,<Y>;<W>,<H>;<texture name>;<name>;<label>]
^ When clicked, fields will be sent and the form will quit.
Inventory location:
+
- "context": Selected node metadata (deprecated: "current_name")
- "current_player": Player to whom the menu is shown
- "player:<name>": Any player
- "nodemeta:<X>,<Y>,<Z>": Any node metadata
+- "detached:<name>": A detached inventory
Helper functions
-----------------
@@ -847,6 +850,9 @@ Inventory:
minetest.get_inventory(location) -> InvRef
^ location = eg. {type="player", name="celeron55"}
{type="node", pos={x=, y=, z=}}
+ {type="detached", name="creative"}
+minetest.create_detached_inventory(name) -> InvRef
+^ Creates a detached inventory. If it already exists, it is cleared.
Item handling:
minetest.inventorycube(img1, img2, img3)
diff --git a/games/minimal/mods/experimental/init.lua b/games/minimal/mods/experimental/init.lua
index 3f50263e0..498c64623 100644
--- a/games/minimal/mods/experimental/init.lua
+++ b/games/minimal/mods/experimental/init.lua
@@ -524,6 +524,12 @@ minetest.register_craft({
end)
end)]]
+-- Create a detached inventory
+local inv = minetest.create_detached_inventory("test_inventory")
+inv:set_size("main", 4*6)
+inv:add_item("main", "experimental:tester_tool_1")
+inv:add_item("main", "experimental:tnt 5")
+
minetest.register_chatcommand("test1", {
params = "",
description = "Test 1: Modify player's inventory view",
@@ -538,6 +544,7 @@ minetest.register_chatcommand("test1", {
"list[current_player;main;5,3.5;8,4;]"..
"list[current_player;craft;8,0;3,3;]"..
"list[current_player;craftpreview;12,1;1,1;]"..
+ "list[detached:test_inventory;main;0,0;4,6;0]"..
"button[0.5,7;2,1;button1;Button 1]"..
"button_exit[2.5,7;2,1;button2;Exit Button]"
)
diff --git a/src/client.cpp b/src/client.cpp
index 3a2edede3..e47bce142 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -304,6 +304,15 @@ Client::~Client()
sleep_ms(100);
delete m_inventory_from_server;
+
+ // Delete detached inventories
+ {
+ for(std::map<std::string, Inventory*>::iterator
+ i = m_detached_inventories.begin();
+ i != m_detached_inventories.end(); i++){
+ delete i->second;
+ }
+ }
}
void Client::connect(Address address)
@@ -1698,6 +1707,24 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
assert(player != NULL);
player->inventory_formspec = deSerializeLongString(is);
}
+ else if(command == TOCLIENT_DETACHED_INVENTORY)
+ {
+ std::string datastring((char*)&data[2], datasize-2);
+ std::istringstream is(datastring, std::ios_base::binary);
+
+ std::string name = deSerializeString(is);
+
+ infostream<<"Client: Detached inventory update: \""<<name<<"\""<<std::endl;
+
+ Inventory *inv = NULL;
+ if(m_detached_inventories.count(name) > 0)
+ inv = m_detached_inventories[name];
+ else{
+ inv = new Inventory(m_itemdef);
+ m_detached_inventories[name] = inv;
+ }
+ inv->deSerialize(is);
+ }
else
{
infostream<<"Client: Ignoring unknown command "
@@ -2090,6 +2117,13 @@ Inventory* Client::getInventory(const InventoryLocation &loc)
return meta->getInventory();
}
break;
+ case InventoryLocation::DETACHED:
+ {
+ if(m_detached_inventories.count(loc.name) == 0)
+ return NULL;
+ return m_detached_inventories[loc.name];
+ }
+ break;
default:
assert(0);
}
diff --git a/src/client.h b/src/client.h
index f751220f7..154c8bb00 100644
--- a/src/client.h
+++ b/src/client.h
@@ -393,6 +393,10 @@ private:
// Privileges
std::set<std::string> m_privileges;
+
+ // Detached inventories
+ // key = name
+ std::map<std::string, Inventory*> m_detached_inventories;
};
#endif // !CLIENT_HEADER
diff --git a/src/clientserver.h b/src/clientserver.h
index d43220962..a6ba8fe45 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -64,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
PROTOCOL_VERSION 12:
TOSERVER_INVENTORY_FIELDS
16-bit node ids
+ TOCLIENT_DETACHED_INVENTORY
*/
#define PROTOCOL_VERSION 12
@@ -321,6 +322,14 @@ enum ToClientCommand
u32 len
u8[len] formspec
*/
+
+ TOCLIENT_DETACHED_INVENTORY = 0x43,
+ /*
+ [0] u16 command
+ u16 len
+ u8[len] name
+ [2] serialized inventory
+ */
};
enum ToServerCommand
diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp
index b2fee9c0d..3eb056625 100644
--- a/src/guiFormSpecMenu.cpp
+++ b/src/guiFormSpecMenu.cpp
@@ -257,10 +257,13 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
<<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl;
- f.next("]");
+ std::string start_i_s = f.next("]");
+ s32 start_i = 0;
+ if(start_i_s != "")
+ start_i = stoi(start_i_s);
if(bp_set != 2)
- errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
- m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom));
+ errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
+ m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
}
else if(type == "image")
{
@@ -531,13 +534,14 @@ GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
{
+ s32 item_i = i + s.start_item_i;
s32 x = (i%s.geom.X) * spacing.X;
s32 y = (i/s.geom.X) * spacing.Y;
v2s32 p0(x,y);
core::rect<s32> rect = imgrect + s.pos + p0;
if(rect.isPointInside(p))
{
- return ItemSpec(s.inventoryloc, s.listname, i);
+ return ItemSpec(s.inventoryloc, s.listname, item_i);
}
}
}
@@ -576,13 +580,16 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
{
+ u32 item_i = i + s.start_item_i;
+ if(item_i >= ilist->getSize())
+ break;
s32 x = (i%s.geom.X) * spacing.X;
s32 y = (i/s.geom.X) * spacing.Y;
v2s32 p(x,y);
core::rect<s32> rect = imgrect + s.pos + p;
ItemStack item;
if(ilist)
- item = ilist->getItem(i);
+ item = ilist->getItem(item_i);
bool selected = m_selected_item
&& m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h
index 8e9557566..f0a5988e9 100644
--- a/src/guiFormSpecMenu.h
+++ b/src/guiFormSpecMenu.h
@@ -86,11 +86,12 @@ class GUIFormSpecMenu : public GUIModalMenu
}
ListDrawSpec(const InventoryLocation &a_inventoryloc,
const std::string &a_listname,
- v2s32 a_pos, v2s32 a_geom):
+ v2s32 a_pos, v2s32 a_geom, s32 a_start_item_i):
inventoryloc(a_inventoryloc),
listname(a_listname),
pos(a_pos),
- geom(a_geom)
+ geom(a_geom),
+ start_item_i(a_start_item_i)
{
}
@@ -98,6 +99,7 @@ class GUIFormSpecMenu : public GUIModalMenu
std::string listname;
v2s32 pos;
v2s32 geom;
+ s32 start_item_i;
};
struct ImageDrawSpec
diff --git a/src/inventorymanager.cpp b/src/inventorymanager.cpp
index 25257238b..1369cb0d7 100644
--- a/src/inventorymanager.cpp
+++ b/src/inventorymanager.cpp
@@ -41,30 +41,25 @@ std::string InventoryLocation::dump() const
void InventoryLocation::serialize(std::ostream &os) const
{
- switch(type){
- case InventoryLocation::UNDEFINED:
- {
- os<<"undefined";
- }
+ switch(type){
+ case InventoryLocation::UNDEFINED:
+ os<<"undefined";
+ break;
+ case InventoryLocation::CURRENT_PLAYER:
+ os<<"current_player";
break;
- case InventoryLocation::CURRENT_PLAYER:
- {
- os<<"current_player";
- }
+ case InventoryLocation::PLAYER:
+ os<<"player:"<<name;
break;
- case InventoryLocation::PLAYER:
- {
- os<<"player:"<<name;
- }
+ case InventoryLocation::NODEMETA:
+ os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
break;
- case InventoryLocation::NODEMETA:
- {
- os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
- }
+ case InventoryLocation::DETACHED:
+ os<<"detached:"<<name;
break;
- default:
- assert(0);
- }
+ default:
+ assert(0);
+ }
}
void InventoryLocation::deSerialize(std::istream &is)
@@ -94,6 +89,11 @@ void InventoryLocation::deSerialize(std::istream &is)
p.Y = stoi(fn.next(","));
p.Z = stoi(fn.next(","));
}
+ else if(tname == "detached")
+ {
+ type = InventoryLocation::DETACHED;
+ std::getline(is, name, '\n');
+ }
else
{
infostream<<"Unknown InventoryLocation type=\""<<tname<<"\""<<std::endl;
diff --git a/src/inventorymanager.h b/src/inventorymanager.h
index 009db4836..dae14f1a6 100644
--- a/src/inventorymanager.h
+++ b/src/inventorymanager.h
@@ -32,9 +32,10 @@ struct InventoryLocation
CURRENT_PLAYER,
PLAYER,
NODEMETA,
+ DETACHED,
} type;
- std::string name; // PLAYER
+ std::string name; // PLAYER, DETACHED
v3s16 p; // NODEMETA
InventoryLocation()
@@ -59,6 +60,11 @@ struct InventoryLocation
type = NODEMETA;
p = p_;
}
+ void setDetached(const std::string &name_)
+ {
+ type = DETACHED;
+ name = name_;
+ }
void applyCurrentPlayer(const std::string &name_)
{
@@ -80,13 +86,11 @@ public:
InventoryManager(){}
virtual ~InventoryManager(){}
- // Get an inventory or set it modified (so it will be updated over
- // network or so)
+ // Get an inventory (server and client)
virtual Inventory* getInventory(const InventoryLocation &loc){return NULL;}
- virtual std::string getInventoryOwner(const InventoryLocation &loc){return "";}
+ // Set modified (will be saved and sent over network; only on server)
virtual void setInventoryModified(const InventoryLocation &loc){}
-
- // Used on the client to send an action to the server
+ // Send inventory action to server (only on client)
virtual void inventoryAction(InventoryAction *a){}
};
diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp
index 12d2a8247..9959ddd74 100644
--- a/src/scriptapi.cpp
+++ b/src/scriptapi.cpp
@@ -4567,6 +4567,9 @@ static int l_get_inventory(lua_State *L)
lua_getfield(L, 1, "pos");
v3s16 pos = check_v3s16(L, -1);
loc.setNodeMeta(pos);
+ } else if(type == "detached"){
+ std::string name = checkstringfield(L, 1, "name");
+ loc.setDetached(name);
}
if(get_server(L)->getInventory(loc) != NULL)
@@ -4576,6 +4579,20 @@ static int l_get_inventory(lua_State *L)
return 1;
}
+// create_detached_inventory(name)
+static int l_create_detached_inventory(lua_State *L)
+{
+ const char *name = luaL_checkstring(L, 1);
+ if(get_server(L)->createDetachedInventory(name) != NULL){
+ InventoryLocation loc;
+ loc.setDetached(name);
+ InvRef::create(L, loc);
+ }else{
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
// get_dig_params(groups, tool_capabilities[, time_from_last_punch])
static int l_get_dig_params(lua_State *L)
{
@@ -4849,6 +4866,7 @@ static const struct luaL_Reg minetest_f [] = {
{"chat_send_player", l_chat_send_player},
{"get_player_privs", l_get_player_privs},
{"get_inventory", l_get_inventory},
+ {"create_detached_inventory", l_create_detached_inventory},
{"get_dig_params", l_get_dig_params},
{"get_hit_params", l_get_hit_params},
{"get_current_modname", l_get_current_modname},
diff --git a/src/server.cpp b/src/server.cpp
index b3cbea6a4..c7698a106 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -1160,6 +1160,15 @@ Server::~Server()
// Deinitialize scripting
infostream<<"Server: Deinitializing scripting"<<std::endl;
script_deinit(m_lua);
+
+ // Delete detached inventories
+ {
+ for(std::map<std::string, Inventory*>::iterator
+ i = m_detached_inventories.begin();
+ i != m_detached_inventories.end(); i++){
+ delete i->second;
+ }
+ }
}
void Server::start(unsigned short port)
@@ -2250,10 +2259,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Send inventory
UpdateCrafting(peer_id);
SendInventory(peer_id);
-
+
// Send HP
SendPlayerHP(peer_id);
+ // Send detached inventories
+ sendDetachedInventories(peer_id);
+
// Show death screen if necessary
if(player->hp == 0)
SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
@@ -2532,30 +2544,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
delete a;
return;
}
-
- // If player is not an admin, check for ownership of src and dst
- /*if(!checkPriv(player->getName(), "server"))
- {
- std::string owner_from = getInventoryOwner(ma->from_inv);
- if(owner_from != "" && owner_from != player->getName())
- {
- infostream<<"WARNING: "<<player->getName()
- <<" tried to access an inventory that"
- <<" belongs to "<<owner_from<<std::endl;
- delete a;
- return;
- }
-
- std::string owner_to = getInventoryOwner(ma->to_inv);
- if(owner_to != "" && owner_to != player->getName())
- {
- infostream<<"WARNING: "<<player->getName()
- <<" tried to access an inventory that"
- <<" belongs to "<<owner_to<<std::endl;
- delete a;
- return;
- }
- }*/
}
/*
Handle restrictions and special cases of the drop action
@@ -2574,19 +2562,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
delete a;
return;
}
- // If player is not an admin, check for ownership
- /*else if(!checkPriv(player->getName(), "server"))
- {
- std::string owner_from = getInventoryOwner(da->from_inv);
- if(owner_from != "" && owner_from != player->getName())
- {
- infostream<<"WARNING: "<<player->getName()
- <<" tried to access an inventory that"
- <<" belongs to "<<owner_from<<std::endl;
- delete a;
- return;
- }
- }*/
}
/*
Handle restrictions and special cases of the craft action
@@ -2611,20 +2586,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
delete a;
return;
}
-
- // If player is not an admin, check for ownership of inventory
- /*if(!checkPriv(player->getName(), "server"))
- {
- std::string owner_craft = getInventoryOwner(ca->craft_inv);
- if(owner_craft != "" && owner_craft != player->getName())
- {
- infostream<<"WARNING: "<<player->getName()
- <<" tried to access an inventory that"
- <<" belongs to "<<owner_craft<<std::endl;
- delete a;
- return;
- }
- }*/
}
// Do the action
@@ -3318,6 +3279,13 @@ Inventory* Server::getInventory(const InventoryLocation &loc)
return meta->getInventory();
}
break;
+ case InventoryLocation::DETACHED:
+ {
+ if(m_detached_inventories.count(loc.name) == 0)
+ return NULL;
+ return m_detached_inventories[loc.name];
+ }
+ break;
default:
assert(0);
}
@@ -3352,6 +3320,11 @@ void Server::setInventoryModified(const InventoryLocation &loc)
setBlockNotSent(blockpos);
}
break;
+ case InventoryLocation::DETACHED:
+ {
+ sendDetachedInventoryToAll(loc.name);
+ }
+ break;
default:
assert(0);
}
@@ -4309,6 +4282,51 @@ void Server::sendRequestedMedia(u16 peer_id,
}
}
+void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
+{
+ if(m_detached_inventories.count(name) == 0){
+ errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
+ return;
+ }
+ Inventory *inv = m_detached_inventories[name];
+
+ std::ostringstream os(std::ios_base::binary);
+ writeU16(os, TOCLIENT_DETACHED_INVENTORY);
+ os<<serializeString(name);
+ inv->serialize(os);
+
+ // Make data buffer
+ std::string s = os.str();
+ SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+ // Send as reliable
+ m_con.Send(peer_id, 0, data, true);
+}
+
+void Server::sendDetachedInventoryToAll(const std::string &name)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ for(core::map<u16, RemoteClient*>::Iterator
+ i = m_clients.getIterator();
+ i.atEnd() == false; i++){
+ RemoteClient *client = i.getNode()->getValue();
+ sendDetachedInventory(name, client->peer_id);
+ }
+}
+
+void Server::sendDetachedInventories(u16 peer_id)
+{
+ DSTACK(__FUNCTION_NAME);
+
+ for(std::map<std::string, Inventory*>::iterator
+ i = m_detached_inventories.begin();
+ i != m_detached_inventories.end(); i++){
+ const std::string &name = i->first;
+ //Inventory *inv = i->second;
+ sendDetachedInventory(name, peer_id);
+ }
+}
+
/*
Something random
*/
@@ -4493,6 +4511,21 @@ void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
}
+Inventory* Server::createDetachedInventory(const std::string &name)
+{
+ if(m_detached_inventories.count(name) > 0){
+ infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
+ delete m_detached_inventories[name];
+ } else {
+ infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
+ }
+ Inventory *inv = new Inventory(m_itemdef);
+ assert(inv);
+ m_detached_inventories[name] = inv;
+ sendDetachedInventoryToAll(name);
+ return inv;
+}
+
// IGameDef interface
// Under envlock
IItemDefManager* Server::getItemDefManager()
diff --git a/src/server.h b/src/server.h
index f170cf7e1..4316bc21f 100644
--- a/src/server.h
+++ b/src/server.h
@@ -538,6 +538,9 @@ public:
void queueBlockEmerge(v3s16 blockpos, bool allow_generate);
+ // Creates or resets inventory
+ Inventory* createDetachedInventory(const std::string &name);
+
// Envlock and conlock should be locked when using Lua
lua_State *getLua(){ return m_lua; }
@@ -627,6 +630,10 @@ private:
void sendMediaAnnouncement(u16 peer_id);
void sendRequestedMedia(u16 peer_id,
const core::list<MediaRequest> &tosend);
+
+ void sendDetachedInventory(const std::string &name, u16 peer_id);
+ void sendDetachedInventoryToAll(const std::string &name);
+ void sendDetachedInventories(u16 peer_id);
/*
Something random
@@ -828,6 +835,12 @@ private:
*/
std::map<s32, ServerPlayingSound> m_playing_sounds;
s32 m_next_sound_id;
+
+ /*
+ Detached inventories (behind m_env_mutex)
+ */
+ // key = name
+ std::map<std::string, Inventory*> m_detached_inventories;
};
/*