Detached inventories

This commit is contained in:
Perttu Ahola 2012-07-24 20:57:17 +03:00
parent 96eac87d47
commit 2ac20982e0
12 changed files with 222 additions and 85 deletions

@ -677,6 +677,7 @@ size[<W>,<H>]
^ deprecated: invsize[<W>,<H>;] ^ deprecated: invsize[<W>,<H>;]
list[<inventory location>;<list name>;<X>,<Y>;<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 ^ Show an inventory list
image[<X>,<Y>;<W>,<H>;<texture name>] 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. ^ When clicked, fields will be sent and the form will quit.
Inventory location: Inventory location:
- "context": Selected node metadata (deprecated: "current_name") - "context": Selected node metadata (deprecated: "current_name")
- "current_player": Player to whom the menu is shown - "current_player": Player to whom the menu is shown
- "player:<name>": Any player - "player:<name>": Any player
- "nodemeta:<X>,<Y>,<Z>": Any node metadata - "nodemeta:<X>,<Y>,<Z>": Any node metadata
- "detached:<name>": A detached inventory
Helper functions Helper functions
----------------- -----------------
@ -847,6 +850,9 @@ Inventory:
minetest.get_inventory(location) -> InvRef minetest.get_inventory(location) -> InvRef
^ location = eg. {type="player", name="celeron55"} ^ location = eg. {type="player", name="celeron55"}
{type="node", pos={x=, y=, z=}} {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: Item handling:
minetest.inventorycube(img1, img2, img3) minetest.inventorycube(img1, img2, img3)

@ -524,6 +524,12 @@ minetest.register_craft({
end) end)
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", { minetest.register_chatcommand("test1", {
params = "", params = "",
description = "Test 1: Modify player's inventory view", 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;main;5,3.5;8,4;]"..
"list[current_player;craft;8,0;3,3;]".. "list[current_player;craft;8,0;3,3;]"..
"list[current_player;craftpreview;12,1;1,1;]".. "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[0.5,7;2,1;button1;Button 1]"..
"button_exit[2.5,7;2,1;button2;Exit Button]" "button_exit[2.5,7;2,1;button2;Exit Button]"
) )

@ -304,6 +304,15 @@ Client::~Client()
sleep_ms(100); sleep_ms(100);
delete m_inventory_from_server; 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) void Client::connect(Address address)
@ -1698,6 +1707,24 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
assert(player != NULL); assert(player != NULL);
player->inventory_formspec = deSerializeLongString(is); 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 else
{ {
infostream<<"Client: Ignoring unknown command " infostream<<"Client: Ignoring unknown command "
@ -2090,6 +2117,13 @@ Inventory* Client::getInventory(const InventoryLocation &loc)
return meta->getInventory(); return meta->getInventory();
} }
break; break;
case InventoryLocation::DETACHED:
{
if(m_detached_inventories.count(loc.name) == 0)
return NULL;
return m_detached_inventories[loc.name];
}
break;
default: default:
assert(0); assert(0);
} }

@ -393,6 +393,10 @@ private:
// Privileges // Privileges
std::set<std::string> m_privileges; std::set<std::string> m_privileges;
// Detached inventories
// key = name
std::map<std::string, Inventory*> m_detached_inventories;
}; };
#endif // !CLIENT_HEADER #endif // !CLIENT_HEADER

@ -64,6 +64,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
PROTOCOL_VERSION 12: PROTOCOL_VERSION 12:
TOSERVER_INVENTORY_FIELDS TOSERVER_INVENTORY_FIELDS
16-bit node ids 16-bit node ids
TOCLIENT_DETACHED_INVENTORY
*/ */
#define PROTOCOL_VERSION 12 #define PROTOCOL_VERSION 12
@ -321,6 +322,14 @@ enum ToClientCommand
u32 len u32 len
u8[len] formspec u8[len] formspec
*/ */
TOCLIENT_DETACHED_INVENTORY = 0x43,
/*
[0] u16 command
u16 len
u8[len] name
[2] serialized inventory
*/
}; };
enum ToServerCommand enum ToServerCommand

@ -257,10 +257,13 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
<<", pos=("<<pos.X<<","<<pos.Y<<")" <<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")" <<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl; <<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) if(bp_set != 2)
errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl; errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom)); m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
} }
else if(type == "image") 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++) 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 x = (i%s.geom.X) * spacing.X;
s32 y = (i/s.geom.X) * spacing.Y; s32 y = (i/s.geom.X) * spacing.Y;
v2s32 p0(x,y); v2s32 p0(x,y);
core::rect<s32> rect = imgrect + s.pos + p0; core::rect<s32> rect = imgrect + s.pos + p0;
if(rect.isPointInside(p)) 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++) 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 x = (i%s.geom.X) * spacing.X;
s32 y = (i/s.geom.X) * spacing.Y; s32 y = (i/s.geom.X) * spacing.Y;
v2s32 p(x,y); v2s32 p(x,y);
core::rect<s32> rect = imgrect + s.pos + p; core::rect<s32> rect = imgrect + s.pos + p;
ItemStack item; ItemStack item;
if(ilist) if(ilist)
item = ilist->getItem(i); item = ilist->getItem(item_i);
bool selected = m_selected_item bool selected = m_selected_item
&& m_invmgr->getInventory(m_selected_item->inventoryloc) == inv && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv

@ -86,11 +86,12 @@ class GUIFormSpecMenu : public GUIModalMenu
} }
ListDrawSpec(const InventoryLocation &a_inventoryloc, ListDrawSpec(const InventoryLocation &a_inventoryloc,
const std::string &a_listname, 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), inventoryloc(a_inventoryloc),
listname(a_listname), listname(a_listname),
pos(a_pos), 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; std::string listname;
v2s32 pos; v2s32 pos;
v2s32 geom; v2s32 geom;
s32 start_item_i;
}; };
struct ImageDrawSpec struct ImageDrawSpec

@ -41,30 +41,25 @@ std::string InventoryLocation::dump() const
void InventoryLocation::serialize(std::ostream &os) const void InventoryLocation::serialize(std::ostream &os) const
{ {
switch(type){ switch(type){
case InventoryLocation::UNDEFINED: case InventoryLocation::UNDEFINED:
{ os<<"undefined";
os<<"undefined";
}
break; break;
case InventoryLocation::CURRENT_PLAYER: case InventoryLocation::CURRENT_PLAYER:
{ os<<"current_player";
os<<"current_player";
}
break; break;
case InventoryLocation::PLAYER: case InventoryLocation::PLAYER:
{ os<<"player:"<<name;
os<<"player:"<<name;
}
break; break;
case InventoryLocation::NODEMETA: case InventoryLocation::NODEMETA:
{ os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
}
break; break;
default: case InventoryLocation::DETACHED:
assert(0); os<<"detached:"<<name;
} break;
default:
assert(0);
}
} }
void InventoryLocation::deSerialize(std::istream &is) void InventoryLocation::deSerialize(std::istream &is)
@ -94,6 +89,11 @@ void InventoryLocation::deSerialize(std::istream &is)
p.Y = stoi(fn.next(",")); p.Y = stoi(fn.next(","));
p.Z = stoi(fn.next(",")); p.Z = stoi(fn.next(","));
} }
else if(tname == "detached")
{
type = InventoryLocation::DETACHED;
std::getline(is, name, '\n');
}
else else
{ {
infostream<<"Unknown InventoryLocation type=\""<<tname<<"\""<<std::endl; infostream<<"Unknown InventoryLocation type=\""<<tname<<"\""<<std::endl;

@ -32,9 +32,10 @@ struct InventoryLocation
CURRENT_PLAYER, CURRENT_PLAYER,
PLAYER, PLAYER,
NODEMETA, NODEMETA,
DETACHED,
} type; } type;
std::string name; // PLAYER std::string name; // PLAYER, DETACHED
v3s16 p; // NODEMETA v3s16 p; // NODEMETA
InventoryLocation() InventoryLocation()
@ -59,6 +60,11 @@ struct InventoryLocation
type = NODEMETA; type = NODEMETA;
p = p_; p = p_;
} }
void setDetached(const std::string &name_)
{
type = DETACHED;
name = name_;
}
void applyCurrentPlayer(const std::string &name_) void applyCurrentPlayer(const std::string &name_)
{ {
@ -80,13 +86,11 @@ public:
InventoryManager(){} InventoryManager(){}
virtual ~InventoryManager(){} virtual ~InventoryManager(){}
// Get an inventory or set it modified (so it will be updated over // Get an inventory (server and client)
// network or so)
virtual Inventory* getInventory(const InventoryLocation &loc){return NULL;} 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){} virtual void setInventoryModified(const InventoryLocation &loc){}
// Send inventory action to server (only on client)
// Used on the client to send an action to the server
virtual void inventoryAction(InventoryAction *a){} virtual void inventoryAction(InventoryAction *a){}
}; };

@ -4567,6 +4567,9 @@ static int l_get_inventory(lua_State *L)
lua_getfield(L, 1, "pos"); lua_getfield(L, 1, "pos");
v3s16 pos = check_v3s16(L, -1); v3s16 pos = check_v3s16(L, -1);
loc.setNodeMeta(pos); loc.setNodeMeta(pos);
} else if(type == "detached"){
std::string name = checkstringfield(L, 1, "name");
loc.setDetached(name);
} }
if(get_server(L)->getInventory(loc) != NULL) if(get_server(L)->getInventory(loc) != NULL)
@ -4576,6 +4579,20 @@ static int l_get_inventory(lua_State *L)
return 1; 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]) // get_dig_params(groups, tool_capabilities[, time_from_last_punch])
static int l_get_dig_params(lua_State *L) 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}, {"chat_send_player", l_chat_send_player},
{"get_player_privs", l_get_player_privs}, {"get_player_privs", l_get_player_privs},
{"get_inventory", l_get_inventory}, {"get_inventory", l_get_inventory},
{"create_detached_inventory", l_create_detached_inventory},
{"get_dig_params", l_get_dig_params}, {"get_dig_params", l_get_dig_params},
{"get_hit_params", l_get_hit_params}, {"get_hit_params", l_get_hit_params},
{"get_current_modname", l_get_current_modname}, {"get_current_modname", l_get_current_modname},

@ -1160,6 +1160,15 @@ Server::~Server()
// Deinitialize scripting // Deinitialize scripting
infostream<<"Server: Deinitializing scripting"<<std::endl; infostream<<"Server: Deinitializing scripting"<<std::endl;
script_deinit(m_lua); 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) void Server::start(unsigned short port)
@ -2250,10 +2259,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
// Send inventory // Send inventory
UpdateCrafting(peer_id); UpdateCrafting(peer_id);
SendInventory(peer_id); SendInventory(peer_id);
// Send HP // Send HP
SendPlayerHP(peer_id); SendPlayerHP(peer_id);
// Send detached inventories
sendDetachedInventories(peer_id);
// Show death screen if necessary // Show death screen if necessary
if(player->hp == 0) if(player->hp == 0)
SendDeathscreen(m_con, peer_id, false, v3f(0,0,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; delete a;
return; 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 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; delete a;
return; 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 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; delete a;
return; 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 // Do the action
@ -3318,6 +3279,13 @@ Inventory* Server::getInventory(const InventoryLocation &loc)
return meta->getInventory(); return meta->getInventory();
} }
break; break;
case InventoryLocation::DETACHED:
{
if(m_detached_inventories.count(loc.name) == 0)
return NULL;
return m_detached_inventories[loc.name];
}
break;
default: default:
assert(0); assert(0);
} }
@ -3352,6 +3320,11 @@ void Server::setInventoryModified(const InventoryLocation &loc)
setBlockNotSent(blockpos); setBlockNotSent(blockpos);
} }
break; break;
case InventoryLocation::DETACHED:
{
sendDetachedInventoryToAll(loc.name);
}
break;
default: default:
assert(0); 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 Something random
*/ */
@ -4493,6 +4511,21 @@ void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags); 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 // IGameDef interface
// Under envlock // Under envlock
IItemDefManager* Server::getItemDefManager() IItemDefManager* Server::getItemDefManager()

@ -538,6 +538,9 @@ public:
void queueBlockEmerge(v3s16 blockpos, bool allow_generate); 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 // Envlock and conlock should be locked when using Lua
lua_State *getLua(){ return m_lua; } lua_State *getLua(){ return m_lua; }
@ -627,6 +630,10 @@ private:
void sendMediaAnnouncement(u16 peer_id); void sendMediaAnnouncement(u16 peer_id);
void sendRequestedMedia(u16 peer_id, void sendRequestedMedia(u16 peer_id,
const core::list<MediaRequest> &tosend); 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 Something random
@ -828,6 +835,12 @@ private:
*/ */
std::map<s32, ServerPlayingSound> m_playing_sounds; std::map<s32, ServerPlayingSound> m_playing_sounds;
s32 m_next_sound_id; s32 m_next_sound_id;
/*
Detached inventories (behind m_env_mutex)
*/
// key = name
std::map<std::string, Inventory*> m_detached_inventories;
}; };
/* /*