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>;]
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)

@ -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]"
)

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

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

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

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

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

@ -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";
}
case InventoryLocation::CURRENT_PLAYER:
os<<"current_player";
break;
case InventoryLocation::PLAYER:
{
os<<"player:"<<name;
}
case InventoryLocation::PLAYER:
os<<"player:"<<name;
break;
case InventoryLocation::NODEMETA:
{
os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
}
case InventoryLocation::NODEMETA:
os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
break;
default:
assert(0);
}
case InventoryLocation::DETACHED:
os<<"detached:"<<name;
break;
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;

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

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

@ -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)
@ -2254,6 +2263,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 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()

@ -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; }
@ -628,6 +631,10 @@ private:
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;
};
/*