diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 117d4b24e9624511e603e353ecd1b60e109c69b3..ebad1dad2c62ceaca1319afd8272659e9e51314c 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -888,6 +888,9 @@ minetest.get_inventory(location) -> InvRef
 minetest.create_detached_inventory(name, callbacks) -> InvRef
 ^ callbacks: See "Detached inventory callbacks"
 ^ Creates a detached inventory. If it already exists, it is cleared.
+minetest.show_formspec(playername, formspec)
+^ playername: name of player to show formspec
+^ formspec: formspec to display
 
 Item handling:
 minetest.inventorycube(img1, img2, img3)
diff --git a/src/client.cpp b/src/client.cpp
index cb7afe29f8fe8bc5ed0bc39e5d5dd95c07938c48..216d86cd412c6d0ffa0e9ae3cd1d8593614887e5 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -1900,6 +1900,20 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 		}
 		inv->deSerialize(is);
 	}
+	else if(command == TOCLIENT_SHOW_FORMSPEC)
+	{
+		std::string datastring((char*)&data[2], datasize-2);
+		std::istringstream is(datastring, std::ios_base::binary);
+
+		std::string formspec = deSerializeLongString(is);
+
+		ClientEvent event;
+		event.type = CE_SHOW_FORMSPEC;
+		// pointer is required as event is a struct only!
+		// adding a std:string to a struct isn't possible
+		event.show_formspec.formspec = new std::string(formspec);
+		m_client_event_queue.push_back(event);
+	}
 	else
 	{
 		infostream<<"Client: Ignoring unknown command "
diff --git a/src/client.h b/src/client.h
index 155b4386bd74ffac8c05c1698d180472bc997273..e46da6b0ec840fa168f7ab7f3e697bd289a5ebd3 100644
--- a/src/client.h
+++ b/src/client.h
@@ -154,7 +154,8 @@ enum ClientEventType
 	CE_PLAYER_DAMAGE,
 	CE_PLAYER_FORCE_MOVE,
 	CE_DEATHSCREEN,
-	CE_TEXTURES_UPDATED
+	CE_TEXTURES_UPDATED,
+	CE_SHOW_FORMSPEC
 };
 
 struct ClientEvent
@@ -176,6 +177,9 @@ struct ClientEvent
 			f32 camera_point_target_y;
 			f32 camera_point_target_z;
 		} deathscreen;
+		struct{
+			std::string* formspec;
+		} show_formspec;
 		struct{
 		} textures_updated;
 	};
diff --git a/src/clientserver.h b/src/clientserver.h
index db551a90c410c37164a8de69e80c4a352047c3a2..bb7e1181efafbe16b688530de51b3962af922127 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -77,9 +77,11 @@ SharedBuffer<u8> makePacket_TOCLIENT_TIME_OF_DAY(u16 time, float time_speed);
 		GENERIC_CMD_SET_ATTACHMENT
 	PROTOCOL_VERSION 15:
 		Serialization format changes
+	PROTOCOL_VERSION 16:
+		TOCLIENT_SHOW_FORMSPEC
 */
 
-#define LATEST_PROTOCOL_VERSION 15
+#define LATEST_PROTOCOL_VERSION 16
 
 // Server's supported network protocol range
 #define SERVER_PROTOCOL_VERSION_MIN 13
@@ -354,6 +356,12 @@ enum ToClientCommand
 		u8[len] name
 		[2] serialized inventory
 	*/
+	TOCLIENT_SHOW_FORMSPEC = 0x44,
+	/*
+		[0] u16 command
+		u16 len
+		u8[len] formspec
+	*/
 };
 
 enum ToServerCommand
diff --git a/src/game.cpp b/src/game.cpp
index 541127f5db065f1edb4685cc64035cf8172feaac..15bf3f09f60e759ffceac470a1b9704a50e05011 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -192,6 +192,32 @@ class PlayerInventoryFormSource: public IFormSource
 	Client *m_client;
 };
 
+class FormspecFormSource: public IFormSource
+{
+public:
+	FormspecFormSource(std::string formspec,FormspecFormSource** game_formspec)
+	{
+		m_formspec = formspec;
+		m_game_formspec = game_formspec;
+	}
+
+	~FormspecFormSource()
+	{
+		*m_game_formspec = 0;
+	}
+
+	void setForm(std::string formspec) {
+		m_formspec = formspec;
+	}
+
+	std::string getForm()
+	{
+		return m_formspec;
+	}
+
+	std::string m_formspec;
+	FormspecFormSource** m_game_formspec;
+};
 /*
 	Hotbar draw routine
 */
@@ -901,6 +927,7 @@ void the_game(
 	bool simple_singleplayer_mode
 )
 {
+	FormspecFormSource* current_formspec = 0;
 	video::IVideoDriver* driver = device->getVideoDriver();
 	scene::ISceneManager* smgr = device->getSceneManager();
 	
@@ -2067,6 +2094,27 @@ void the_game(
 					player->setPosition(player->getPosition() + v3f(0,-BS,0));
 					camera.update(player, busytime, screensize);*/
 				}
+				else if (event.type == CE_SHOW_FORMSPEC)
+				{
+					if (current_formspec == 0)
+					{
+						/* Create menu */
+						current_formspec = new FormspecFormSource(*(event.show_formspec.formspec),&current_formspec);
+
+						GUIFormSpecMenu *menu =
+								new GUIFormSpecMenu(device, guiroot, -1,
+										&g_menumgr,
+										&client, gamedef);
+						menu->setFormSource(current_formspec);
+						menu->drop();
+					}
+					else
+					{
+						/* update menu */
+						current_formspec->setForm(*(event.show_formspec.formspec));
+					}
+					delete(event.show_formspec.formspec);
+				}
 				else if(event.type == CE_TEXTURES_UPDATED)
 				{
 					update_wielded_item_trigger = true;
diff --git a/src/scriptapi.cpp b/src/scriptapi.cpp
index d086b7e510549242b6005045df246ac9b9e31958..83987fc9b477818014906895b9eed52480f20641 100644
--- a/src/scriptapi.cpp
+++ b/src/scriptapi.cpp
@@ -4899,6 +4899,21 @@ static int l_create_detached_inventory_raw(lua_State *L)
 	return 1;
 }
 
+// create_detached_formspec_raw(name)
+static int l_show_formspec(lua_State *L)
+{
+	const char *playername = luaL_checkstring(L, 1);
+	const char *formspec = luaL_checkstring(L, 2);
+
+	if(get_server(L)->showFormspec(playername,formspec))
+	{
+		lua_pushboolean(L, true);
+	}else{
+		lua_pushboolean(L, false);
+	}
+	return 1;
+}
+
 // get_dig_params(groups, tool_capabilities[, time_from_last_punch])
 static int l_get_dig_params(lua_State *L)
 {
@@ -5228,6 +5243,7 @@ static const struct luaL_Reg minetest_f [] = {
 	{"unban_player_or_ip", l_unban_player_of_ip},
 	{"get_inventory", l_get_inventory},
 	{"create_detached_inventory_raw", l_create_detached_inventory_raw},
+	{"show_formspec", l_show_formspec},
 	{"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 39407f961f4d80236f1a1bcb036c070fb6ec96fc..f4b5ee872a80c5848ee86463039fe6fc4caca026 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -3638,6 +3638,24 @@ void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
 	// Send as reliable
 	m_con.Send(peer_id, 0, data, true);
 }
+void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec)
+{
+	DSTACK(__FUNCTION_NAME);
+
+	std::ostringstream os(std::ios_base::binary);
+	u8 buf[12];
+
+	// Write command
+	writeU16(buf, TOCLIENT_SHOW_FORMSPEC);
+	os.write((char*)buf, 2);
+	os<<serializeLongString(formspec);
+
+	// 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::BroadcastChatMessage(const std::wstring &message)
 {
@@ -4578,6 +4596,20 @@ void Server::notifyPlayer(const char *name, const std::wstring msg)
 	SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
 }
 
+bool Server::showFormspec(const char *playername, const std::string &formspec)
+{
+	Player *player = m_env->getPlayer(playername);
+
+	if(!player)
+	{
+		infostream<<"showFormspec: couldn't find player:"<<playername<<std::endl;
+		return false;
+	}
+
+	SendShowFormspecMessage(player->peer_id,formspec);
+	return true;
+}
+
 void Server::notifyPlayers(const std::wstring msg)
 {
 	BroadcastChatMessage(msg);
diff --git a/src/server.h b/src/server.h
index ce826ae529a62dee58cb2a40467a99d85ccc0ece..19c29cbd711353518a832ae7815c8098e833027a 100644
--- a/src/server.h
+++ b/src/server.h
@@ -583,6 +583,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 		m_async_fatal_error.set(error);
 	}
 
+	bool showFormspec(const char *name, const std::string &formspec);
 private:
 
 	// con::PeerHandler implementation.
@@ -620,6 +621,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	void SendMovePlayer(u16 peer_id);
 	void SendPlayerPrivileges(u16 peer_id);
 	void SendPlayerInventoryFormspec(u16 peer_id);
+	void SendShowFormspecMessage(u16 peer_id, const std::string formspec);
 	/*
 		Send a node removal/addition event to all clients except ignore_id.
 		Additionally, if far_players!=NULL, players further away than