Add formspec theming using prepended strings

This commit is contained in:
Andrew Ward 2018-03-28 16:04:41 +01:00 committed by GitHub
parent 040b878cd5
commit 2323842dd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 150 additions and 18 deletions

@ -2001,6 +2001,10 @@ supported. It is a string, with a somewhat strange format.
Spaces and newlines can be inserted between the blocks, as is used in the Spaces and newlines can be inserted between the blocks, as is used in the
examples. examples.
WARNING: Minetest allows you to add elements to every single formspec instance
using player:set_formspec_prepend(), which may be the reason backgrounds are
appearing when you don't expect them to. See `no_prepend[]`
### Examples ### Examples
#### Chest #### Chest
@ -2053,6 +2057,10 @@ examples.
* `position` and `anchor` elements need suitable values to avoid a formspec * `position` and `anchor` elements need suitable values to avoid a formspec
extending off the game window due to particular game window sizes. extending off the game window due to particular game window sizes.
#### `no_prepend[]`
* Must be used after the `size`, `position`, and `anchor` elements (if present).
* Disables player:set_formspec_prepend() from applying to this formspec.
#### `container[<X>,<Y>]` #### `container[<X>,<Y>]`
* Start of a container block, moves all physical elements in the container by * Start of a container block, moves all physical elements in the container by
(X, Y). (X, Y).
@ -4046,6 +4054,13 @@ This is basically a reference to a C++ `ServerActiveObject`
* Redefine player's inventory form * Redefine player's inventory form
* Should usually be called in `on_joinplayer` * Should usually be called in `on_joinplayer`
* `get_inventory_formspec()`: returns a formspec string * `get_inventory_formspec()`: returns a formspec string
* `set_formspec_prepend(formspec)`:
* the formspec string will be added to every formspec shown to the user,
except for those with a no_prepend[] tag.
* This should be used to set style elements such as background[] and
bgcolor[], any non-style elements (eg: label) may result in weird behaviour.
* Only affects formspecs shown after this is called.
* `get_formspec_prepend(formspec)`: returns a formspec string.
* `get_player_control()`: returns table with player pressed keys * `get_player_control()`: returns table with player pressed keys
* The table consists of fields with boolean value representing the pressed * The table consists of fields with boolean value representing the pressed
keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up. keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up.

@ -224,7 +224,8 @@ public:
void handleCommand_UpdatePlayerList(NetworkPacket* pkt); void handleCommand_UpdatePlayerList(NetworkPacket* pkt);
void handleCommand_ModChannelMsg(NetworkPacket *pkt); void handleCommand_ModChannelMsg(NetworkPacket *pkt);
void handleCommand_ModChannelSignal(NetworkPacket *pkt); void handleCommand_ModChannelSignal(NetworkPacket *pkt);
void handleCommand_SrpBytesSandB(NetworkPacket* pkt); void handleCommand_SrpBytesSandB(NetworkPacket *pkt);
void handleCommand_FormspecPrepend(NetworkPacket *pkt);
void handleCommand_CSMFlavourLimits(NetworkPacket *pkt); void handleCommand_CSMFlavourLimits(NetworkPacket *pkt);
void ProcessData(NetworkPacket *pkt); void ProcessData(NetworkPacket *pkt);
@ -432,6 +433,10 @@ public:
bool sendModChannelMessage(const std::string &channel, const std::string &message); bool sendModChannelMessage(const std::string &channel, const std::string &message);
ModChannel *getModChannel(const std::string &channel); ModChannel *getModChannel(const std::string &channel);
const std::string &getFormspecPrepend() const
{
return m_env.getLocalPlayer()->formspec_prepend;
}
private: private:
void loadMods(); void loadMods();
bool checkBuiltinIntegrity(); bool checkBuiltinIntegrity();

@ -78,7 +78,7 @@ public:
void step(f32 dtime); void step(f32 dtime);
virtual void setLocalPlayer(LocalPlayer *player); virtual void setLocalPlayer(LocalPlayer *player);
LocalPlayer *getLocalPlayer() { return m_local_player; } LocalPlayer *getLocalPlayer() const { return m_local_player; }
/* /*
ClientSimpleObjects ClientSimpleObjects

@ -2028,7 +2028,7 @@ void Game::openInventory()
|| !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) { || !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) {
TextDest *txt_dst = new TextDestPlayerInventory(client); TextDest *txt_dst = new TextDestPlayerInventory(client);
GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src,
txt_dst); txt_dst, client->getFormspecPrepend());
cur_formname = ""; cur_formname = "";
current_formspec->setFormSpec(fs_src->getForm(), inventoryloc); current_formspec->setFormSpec(fs_src->getForm(), inventoryloc);
} }
@ -2535,7 +2535,7 @@ void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation
new TextDestPlayerInventory(client, *(event->show_formspec.formname)); new TextDestPlayerInventory(client, *(event->show_formspec.formname));
GUIFormSpecMenu::create(current_formspec, client, &input->joystick, GUIFormSpecMenu::create(current_formspec, client, &input->joystick,
fs_src, txt_dst); fs_src, txt_dst, client->getFormspecPrepend());
cur_formname = *(event->show_formspec.formname); cur_formname = *(event->show_formspec.formname);
} }
@ -2548,7 +2548,8 @@ void Game::handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrienta
FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec); FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec);
LocalFormspecHandler *txt_dst = LocalFormspecHandler *txt_dst =
new LocalFormspecHandler(*event->show_formspec.formname, client); new LocalFormspecHandler(*event->show_formspec.formname, client);
GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, txt_dst); GUIFormSpecMenu::create(current_formspec, client, &input->joystick,
fs_src, txt_dst, client->getFormspecPrepend());
delete event->show_formspec.formspec; delete event->show_formspec.formspec;
delete event->show_formspec.formname; delete event->show_formspec.formname;
@ -3210,7 +3211,7 @@ void Game::handlePointingAtNode(const PointedThing &pointed,
TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client); TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client);
GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src,
txt_dst); txt_dst, client->getFormspecPrepend());
cur_formname.clear(); cur_formname.clear();
current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc); current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
@ -4105,7 +4106,8 @@ void Game::showPauseMenu()
FormspecFormSource *fs_src = new FormspecFormSource(os.str()); FormspecFormSource *fs_src = new FormspecFormSource(os.str());
LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU"); LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src, txt_dst); GUIFormSpecMenu::create(current_formspec, client, &input->joystick,
fs_src, txt_dst, client->getFormspecPrepend());
current_formspec->setFocus("btn_continue"); current_formspec->setFocus("btn_continue");
current_formspec->doPause = true; current_formspec->doPause = true;
} }

@ -165,6 +165,7 @@ GUIEngine::GUIEngine(JoystickController *joystick,
m_texture_source, m_texture_source,
m_formspecgui, m_formspecgui,
m_buttonhandler, m_buttonhandler,
"",
false); false);
m_menu->allowClose(false); m_menu->allowClose(false);

@ -86,11 +86,13 @@ inline u32 clamp_u8(s32 value)
GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick, GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr,
Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst, Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst,
bool remap_dbl_click) : std::string formspecPrepend,
bool remap_dbl_click):
GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr), GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr),
m_invmgr(client), m_invmgr(client),
m_tsrc(tsrc), m_tsrc(tsrc),
m_client(client), m_client(client),
m_formspec_prepend(formspecPrepend),
m_form_src(fsrc), m_form_src(fsrc),
m_text_dst(tdst), m_text_dst(tdst),
m_joystick(joystick), m_joystick(joystick),
@ -128,11 +130,12 @@ GUIFormSpecMenu::~GUIFormSpecMenu()
} }
void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client, void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client,
JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest) JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest,
const std::string &formspecPrepend)
{ {
if (cur_formspec == nullptr) { if (cur_formspec == nullptr) {
cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr, cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr,
client, client->getTextureSource(), fs_src, txt_dest); client, client->getTextureSource(), fs_src, txt_dest, formspecPrepend);
cur_formspec->doPause = false; cur_formspec->doPause = false;
/* /*
@ -144,6 +147,7 @@ void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client,
*/ */
} else { } else {
cur_formspec->setFormspecPrepend(formspecPrepend);
cur_formspec->setFormSource(fs_src); cur_formspec->setFormSource(fs_src);
cur_formspec->setTextDest(txt_dest); cur_formspec->setTextDest(txt_dest);
} }
@ -2008,7 +2012,6 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
); );
} }
m_slotbg_n = video::SColor(255,128,128,128); m_slotbg_n = video::SColor(255,128,128,128);
m_slotbg_h = video::SColor(255,192,192,192); m_slotbg_h = video::SColor(255,192,192,192);
@ -2040,7 +2043,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
/* try to read version from first element only */ /* try to read version from first element only */
if (!elements.empty()) { if (!elements.empty()) {
if ( parseVersionDirect(elements[0]) ) { if (parseVersionDirect(elements[0])) {
i++; i++;
} }
} }
@ -2067,6 +2070,18 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
} }
} }
/* "no_prepend" element is always after "position" (or "size" element) if it used */
bool enable_prepends = true;
for (; i < elements.size(); i++) {
if (elements[i].empty())
break;
std::vector<std::string> parts = split(elements[i], '[');
if (parts[0] == "no_prepend")
enable_prepends = false;
else
break;
}
if (mydata.explicit_size) { if (mydata.explicit_size) {
// compute scaling for specified form size // compute scaling for specified form size
@ -2173,12 +2188,19 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
mydata.basepos = getBasePos(); mydata.basepos = getBasePos();
m_tooltip_element->setOverrideFont(m_font); m_tooltip_element->setOverrideFont(m_font);
gui::IGUISkin* skin = Environment->getSkin(); gui::IGUISkin *skin = Environment->getSkin();
sanity_check(skin); sanity_check(skin);
gui::IGUIFont *old_font = skin->getFont(); gui::IGUIFont *old_font = skin->getFont();
skin->setFont(m_font); skin->setFont(m_font);
pos_offset = v2s32(); pos_offset = v2s32();
if (enable_prepends) {
std::vector<std::string> prepend_elements = split(m_formspec_prepend, ']');
for (const auto &element : prepend_elements)
parseElement(&mydata, element);
}
for (; i< elements.size(); i++) { for (; i< elements.size(); i++) {
parseElement(&mydata, elements[i]); parseElement(&mydata, elements[i]);
} }

@ -287,12 +287,14 @@ public:
ISimpleTextureSource *tsrc, ISimpleTextureSource *tsrc,
IFormSource* fs_src, IFormSource* fs_src,
TextDest* txt_dst, TextDest* txt_dst,
std::string formspecPrepend,
bool remap_dbl_click = true); bool remap_dbl_click = true);
~GUIFormSpecMenu(); ~GUIFormSpecMenu();
static void create(GUIFormSpecMenu *&cur_formspec, Client *client, static void create(GUIFormSpecMenu *&cur_formspec, Client *client,
JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest); JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest,
const std::string &formspecPrepend);
void setFormSpec(const std::string &formspec_string, void setFormSpec(const std::string &formspec_string,
const InventoryLocation &current_inventory_location) const InventoryLocation &current_inventory_location)
@ -302,6 +304,11 @@ public:
regenerateGui(m_screensize_old); regenerateGui(m_screensize_old);
} }
void setFormspecPrepend(const std::string &formspecPrepend)
{
m_formspec_prepend = formspecPrepend;
}
// form_src is deleted by this GUIFormSpecMenu // form_src is deleted by this GUIFormSpecMenu
void setFormSource(IFormSource *form_src) void setFormSource(IFormSource *form_src)
{ {
@ -378,6 +385,7 @@ protected:
Client *m_client; Client *m_client;
std::string m_formspec_string; std::string m_formspec_string;
std::string m_formspec_prepend;
InventoryLocation m_current_inventory_location; InventoryLocation m_current_inventory_location;
std::vector<ListDrawSpec> m_inventorylists; std::vector<ListDrawSpec> m_inventorylists;

@ -121,6 +121,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
null_command_handler, null_command_handler,
null_command_handler, null_command_handler,
{ "TOCLIENT_SRP_BYTES_S_B", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_SrpBytesSandB }, // 0x60 { "TOCLIENT_SRP_BYTES_S_B", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_SrpBytesSandB }, // 0x60
{ "TOCLIENT_FORMSPEC_PREPEND", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_FormspecPrepend }, // 0x61,
}; };
const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false }; const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false };

@ -1324,6 +1324,15 @@ void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
Send(&resp_pkt); Send(&resp_pkt);
} }
void Client::handleCommand_FormspecPrepend(NetworkPacket *pkt)
{
LocalPlayer *player = m_env.getLocalPlayer();
assert(player != NULL);
// Store formspec in LocalPlayer
*pkt >> player->formspec_prepend;
}
void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt) void Client::handleCommand_CSMFlavourLimits(NetworkPacket *pkt)
{ {
*pkt >> m_csm_flavour_limits >> m_csm_noderange_limit; *pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;

@ -187,6 +187,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
'zoom_fov'. 'zoom_fov'.
Nodebox version 5 Nodebox version 5
Add disconnected nodeboxes Add disconnected nodeboxes
Add TOCLIENT_FORMSPEC_PREPEND
*/ */
#define LATEST_PROTOCOL_VERSION 36 #define LATEST_PROTOCOL_VERSION 36
@ -644,7 +645,13 @@ enum ToClientCommand
std::string bytes_B std::string bytes_B
*/ */
TOCLIENT_NUM_MSG_TYPES = 0x61, TOCLIENT_FORMSPEC_PREPEND = 0x61,
/*
u16 len
u8[len] formspec
*/
TOCLIENT_NUM_MSG_TYPES = 0x62,
}; };
enum ToServerCommand enum ToServerCommand
@ -930,4 +937,3 @@ enum CSMFlavourLimit : u64 {
CSM_FL_LOOKUP_NODES = 0x00000010, // Limit node lookups CSM_FL_LOOKUP_NODES = 0x00000010, // Limit node lookups
CSM_FL_ALL = 0xFFFFFFFF, CSM_FL_ALL = 0xFFFFFFFF,
}; };

@ -210,4 +210,5 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] =
null_command_factory, null_command_factory,
null_command_factory, null_command_factory,
{ "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60 { "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60
{ "TOCLIENT_FORMSPEC_PREPEND", 0, true }, // 0x61
}; };

@ -148,6 +148,7 @@ public:
float local_animation_speed; float local_animation_speed;
std::string inventory_formspec; std::string inventory_formspec;
std::string formspec_prepend;
PlayerControl control; PlayerControl control;
const PlayerControl& getPlayerControl() { return control; } const PlayerControl& getPlayerControl() { return control; }

@ -1244,6 +1244,37 @@ int ObjectRef::l_get_inventory_formspec(lua_State *L)
return 1; return 1;
} }
// set_formspec_prepend(self, formspec)
int ObjectRef::l_set_formspec_prepend(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
if (player == NULL)
return 0;
std::string formspec = luaL_checkstring(L, 2);
player->formspec_prepend = formspec;
getServer(L)->reportFormspecPrependModified(player->getName());
lua_pushboolean(L, true);
return 1;
}
// get_formspec_prepend(self) -> formspec
int ObjectRef::l_get_formspec_prepend(lua_State *L)
{
NO_MAP_LOCK_REQUIRED;
ObjectRef *ref = checkobject(L, 1);
RemotePlayer *player = getplayer(ref);
if (player == NULL)
return 0;
std::string formspec = player->formspec_prepend;
lua_pushlstring(L, formspec.c_str(), formspec.size());
return 1;
}
// get_player_control(self) // get_player_control(self)
int ObjectRef::l_get_player_control(lua_State *L) int ObjectRef::l_get_player_control(lua_State *L)
{ {
@ -1817,6 +1848,8 @@ const luaL_Reg ObjectRef::methods[] = {
luamethod(ObjectRef, set_attribute), luamethod(ObjectRef, set_attribute),
luamethod(ObjectRef, set_inventory_formspec), luamethod(ObjectRef, set_inventory_formspec),
luamethod(ObjectRef, get_inventory_formspec), luamethod(ObjectRef, get_inventory_formspec),
luamethod(ObjectRef, set_formspec_prepend),
luamethod(ObjectRef, get_formspec_prepend),
luamethod(ObjectRef, get_player_control), luamethod(ObjectRef, get_player_control),
luamethod(ObjectRef, get_player_control_bits), luamethod(ObjectRef, get_player_control_bits),
luamethod(ObjectRef, set_physics_override), luamethod(ObjectRef, set_physics_override),

@ -253,6 +253,12 @@ private:
// get_inventory_formspec(self) -> formspec // get_inventory_formspec(self) -> formspec
static int l_get_inventory_formspec(lua_State *L); static int l_get_inventory_formspec(lua_State *L);
// set_formspec_prepend(self, formspec)
static int l_set_formspec_prepend(lua_State *L);
// get_formspec_prepend(self) -> formspec
static int l_get_formspec_prepend(lua_State *L);
// get_player_control(self) // get_player_control(self)
static int l_get_player_control(lua_State *L); static int l_get_player_control(lua_State *L);

@ -1870,7 +1870,7 @@ void Server::SendPlayerInventoryFormspec(session_t peer_id)
{ {
RemotePlayer *player = m_env->getPlayer(peer_id); RemotePlayer *player = m_env->getPlayer(peer_id);
assert(player); assert(player);
if(player->getPeerId() == PEER_ID_INEXISTENT) if (player->getPeerId() == PEER_ID_INEXISTENT)
return; return;
NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id); NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
@ -1878,6 +1878,18 @@ void Server::SendPlayerInventoryFormspec(session_t peer_id)
Send(&pkt); Send(&pkt);
} }
void Server::SendPlayerFormspecPrepend(session_t peer_id)
{
RemotePlayer *player = m_env->getPlayer(peer_id);
assert(player);
if (player->getPeerId() == PEER_ID_INEXISTENT)
return;
NetworkPacket pkt(TOCLIENT_FORMSPEC_PREPEND, 0, peer_id);
pkt << FORMSPEC_VERSION_STRING + player->formspec_prepend;
Send(&pkt);
}
u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas) u32 Server::SendActiveObjectRemoveAdd(session_t peer_id, const std::string &datas)
{ {
NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id); NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
@ -2918,6 +2930,14 @@ void Server::reportInventoryFormspecModified(const std::string &name)
SendPlayerInventoryFormspec(player->getPeerId()); SendPlayerInventoryFormspec(player->getPeerId());
} }
void Server::reportFormspecPrependModified(const std::string &name)
{
RemotePlayer *player = m_env->getPlayer(name.c_str());
if (!player)
return;
SendPlayerFormspecPrepend(player->getPeerId());
}
void Server::setIpBanned(const std::string &ip, const std::string &name) void Server::setIpBanned(const std::string &ip, const std::string &name)
{ {
m_banmanager->add(ip, name); m_banmanager->add(ip, name);

@ -216,6 +216,7 @@ public:
bool checkPriv(const std::string &name, const std::string &priv); bool checkPriv(const std::string &name, const std::string &priv);
void reportPrivsModified(const std::string &name=""); // ""=all void reportPrivsModified(const std::string &name=""); // ""=all
void reportInventoryFormspecModified(const std::string &name); void reportInventoryFormspecModified(const std::string &name);
void reportFormspecPrependModified(const std::string &name);
void setIpBanned(const std::string &ip, const std::string &name); void setIpBanned(const std::string &ip, const std::string &name);
void unsetIpBanned(const std::string &ip_or_name); void unsetIpBanned(const std::string &ip_or_name);
@ -376,6 +377,7 @@ private:
void SendEyeOffset(session_t peer_id, v3f first, v3f third); void SendEyeOffset(session_t peer_id, v3f first, v3f third);
void SendPlayerPrivileges(session_t peer_id); void SendPlayerPrivileges(session_t peer_id);
void SendPlayerInventoryFormspec(session_t peer_id); void SendPlayerInventoryFormspec(session_t peer_id);
void SendPlayerFormspecPrepend(session_t peer_id);
void SendShowFormspecMessage(session_t peer_id, const std::string &formspec, void SendShowFormspecMessage(session_t peer_id, const std::string &formspec,
const std::string &formname); const std::string &formname);
void SendHUDAdd(session_t peer_id, u32 id, HudElement *form); void SendHUDAdd(session_t peer_id, u32 id, HudElement *form);