Spaces and newlines can be inserted between the blocks, as is used in the
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
#### Chest
* `position` and `anchor` elements need suitable values to avoid a formspec
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>]`
* Start of a container block, moves all physical elements in the container by
(X, Y).
* Redefine player's inventory form
* Should usually be called in `on_joinplayer`
* `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
* The table consists of fields with boolean value representing the pressed
keys, the fields are jump, right, left, LMB, RMB, sneak, aux1, down, up.
void handleCommand_UpdatePlayerList(NetworkPacket* pkt);
void handleCommand_ModChannelMsg(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 ProcessData(NetworkPacket *pkt);
bool sendModChannelMessage(const std::string &channel, const std::string &message);
ModChannel *getModChannel(const std::string &channel);
+ const std::string &getFormspecPrepend() const
+ {
+ return m_env.getLocalPlayer()->formspec_prepend;
+ }
private:
void loadMods();
bool checkBuiltinIntegrity();
void step(f32 dtime);
virtual void setLocalPlayer(LocalPlayer *player);
- LocalPlayer *getLocalPlayer() { return m_local_player; }
+ LocalPlayer *getLocalPlayer() const { return m_local_player; }
/*
ClientSimpleObjects
|| !client->getScript()->on_inventory_open(fs_src->m_client->getInventory(inventoryloc))) {
TextDest *txt_dst = new TextDestPlayerInventory(client);
GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src,
- txt_dst);
+ txt_dst, client->getFormspecPrepend());
cur_formname = "";
current_formspec->setFormSpec(fs_src->getForm(), inventoryloc);
}
new TextDestPlayerInventory(client, *(event->show_formspec.formname));
GUIFormSpecMenu::create(current_formspec, client, &input->joystick,
- fs_src, txt_dst);
+ fs_src, txt_dst, client->getFormspecPrepend());
cur_formname = *(event->show_formspec.formname);
}
FormspecFormSource *fs_src = new FormspecFormSource(*event->show_formspec.formspec);
LocalFormspecHandler *txt_dst =
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.formname;
TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client);
GUIFormSpecMenu::create(current_formspec, client, &input->joystick, fs_src,
- txt_dst);
+ txt_dst, client->getFormspecPrepend());
cur_formname.clear();
current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
FormspecFormSource *fs_src = new FormspecFormSource(os.str());
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->doPause = true;
}
m_texture_source,
m_formspecgui,
m_buttonhandler,
+ "",
false);
m_menu->allowClose(false);
GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr,
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),
m_invmgr(client),
m_tsrc(tsrc),
m_client(client),
+ m_formspec_prepend(formspecPrepend),
m_form_src(fsrc),
m_text_dst(tdst),
m_joystick(joystick),
}
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) {
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;
/*
*/
} else {
+ cur_formspec->setFormspecPrepend(formspecPrepend);
cur_formspec->setFormSource(fs_src);
cur_formspec->setTextDest(txt_dest);
}
);
}
-
m_slotbg_n = video::SColor(255,128,128,128);
m_slotbg_h = video::SColor(255,192,192,192);
/* try to read version from first element only */
if (!elements.empty()) {
- if ( parseVersionDirect(elements[0]) ) {
+ if (parseVersionDirect(elements[0])) {
i++;
}
}
}
}
+ /* "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) {
// compute scaling for specified form size
// multiplied by gui_scaling, even if this means
// the form doesn't fit the screen.
double prefer_imgsize = mydata.screensize.Y / 15 *
- gui_scaling;
+ gui_scaling;
double fitx_imgsize = mydata.screensize.X /
((5.0/4.0) * (0.5 + mydata.invsize.X));
double fity_imgsize = mydata.screensize.Y /
mydata.basepos = getBasePos();
m_tooltip_element->setOverrideFont(m_font);
- gui::IGUISkin* skin = Environment->getSkin();
+ gui::IGUISkin *skin = Environment->getSkin();
sanity_check(skin);
gui::IGUIFont *old_font = skin->getFont();
skin->setFont(m_font);
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++) {
parseElement(&mydata, elements[i]);
}
ISimpleTextureSource *tsrc,
IFormSource* fs_src,
TextDest* txt_dst,
+ std::string formspecPrepend,
bool remap_dbl_click = true);
~GUIFormSpecMenu();
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,
const InventoryLocation ¤t_inventory_location)
regenerateGui(m_screensize_old);
}
+ void setFormspecPrepend(const std::string &formspecPrepend)
+ {
+ m_formspec_prepend = formspecPrepend;
+ }
+
// form_src is deleted by this GUIFormSpecMenu
void setFormSource(IFormSource *form_src)
{
Client *m_client;
std::string m_formspec_string;
+ std::string m_formspec_prepend;
InventoryLocation m_current_inventory_location;
std::vector<ListDrawSpec> m_inventorylists;
null_command_handler,
null_command_handler,
{ "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 };
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)
{
*pkt >> m_csm_flavour_limits >> m_csm_noderange_limit;
'zoom_fov'.
Nodebox version 5
Add disconnected nodeboxes
+ Add TOCLIENT_FORMSPEC_PREPEND
*/
#define LATEST_PROTOCOL_VERSION 36
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
CSM_FL_LOOKUP_NODES = 0x00000010, // Limit node lookups
CSM_FL_ALL = 0xFFFFFFFF,
};
-
null_command_factory,
null_command_factory,
{ "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60
+ { "TOCLIENT_FORMSPEC_PREPEND", 0, true }, // 0x61
};
float local_animation_speed;
std::string inventory_formspec;
+ std::string formspec_prepend;
PlayerControl control;
const PlayerControl& getPlayerControl() { return control; }
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)
int ObjectRef::l_get_player_control(lua_State *L)
{
luamethod(ObjectRef, set_attribute),
luamethod(ObjectRef, set_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_bits),
luamethod(ObjectRef, set_physics_override),
// get_inventory_formspec(self) -> formspec
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)
static int l_get_player_control(lua_State *L);
{
RemotePlayer *player = m_env->getPlayer(peer_id);
assert(player);
- if(player->getPeerId() == PEER_ID_INEXISTENT)
+ if (player->getPeerId() == PEER_ID_INEXISTENT)
return;
NetworkPacket pkt(TOCLIENT_INVENTORY_FORMSPEC, 0, peer_id);
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)
{
NetworkPacket pkt(TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD, datas.size(), peer_id);
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)
{
m_banmanager->add(ip, name);
bool checkPriv(const std::string &name, const std::string &priv);
void reportPrivsModified(const std::string &name=""); // ""=all
void reportInventoryFormspecModified(const std::string &name);
+ void reportFormspecPrependModified(const std::string &name);
void setIpBanned(const std::string &ip, const std::string &name);
void unsetIpBanned(const std::string &ip_or_name);
void SendEyeOffset(session_t peer_id, v3f first, v3f third);
void SendPlayerPrivileges(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,
const std::string &formname);
void SendHUDAdd(session_t peer_id, u32 id, HudElement *form);