From: sapier <Sapier at GMX dot net>
Date: Wed, 25 Jun 2014 17:04:47 +0000 (+0200)
Subject: Add formspec api versioning
X-Git-Tag: 0.4.10~27
X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=ebf7ea50193afe5a3d0968b87b4743eb1ee84025;p=oweals%2Fminetest.git

Add formspec api versioning
---

diff --git a/src/clientserver.h b/src/clientserver.h
index a995d8b99..f12384b18 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -19,6 +19,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 #ifndef CLIENTSERVER_HEADER
 #define CLIENTSERVER_HEADER
+#include "util/string.h"
 
 /*
 	changes by PROTOCOL_VERSION:
@@ -122,6 +123,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define PASSWORD_SIZE 28       // Maximum password length. Allows for
                                // base64-encoded SHA-1 (27+\0).
 
+#define FORMSPEC_API_VERSION 1
+#define FORMSPEC_VERSION_STRING "formspec_version[" TOSTRING(FORMSPEC_API_VERSION) "]"
+
 #define TEXTURENAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.-"
 
 enum ToClientCommand
diff --git a/src/game.cpp b/src/game.cpp
index 3acc93828..e74e4697b 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -948,6 +948,7 @@ static void show_chat_menu(GUIFormSpecMenu** cur_formspec,
 		Client* client, std::string text)
 {
 	std::string formspec =
+			FORMSPEC_VERSION_STRING
 		"size[11,5.5,true]"
 		"field[3,2.35;6,0.5;f_text;;" + text + "]"
 		"button_exit[4,3;3,0.5;btn_send;" + wide_to_narrow(wstrgettext("Proceed")) + "]"
@@ -967,7 +968,7 @@ static void show_deathscreen(GUIFormSpecMenu** cur_formspec,
 		IWritableTextureSource* tsrc, IrrlichtDevice * device, Client* client)
 {
 	std::string formspec =
-		std::string("") +
+		std::string(FORMSPEC_VERSION_STRING) +
 		"size[11,5.5,true]"
 		"bgcolor[#320000b4;true]"
 		"label[4.85,1.35;You died.]"
@@ -1005,7 +1006,7 @@ static void show_pause_menu(GUIFormSpecMenu** cur_formspec,
 	float ypos = singleplayermode ? 1.0 : 0.5;
 	std::ostringstream os;
 
-	os << "size[11,5.5,true]"
+	os << FORMSPEC_VERSION_STRING << "size[11,5.5,true]"
 			<< "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
 					<< wide_to_narrow(wstrgettext("Continue"))     << "]";
 
diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp
index 2f4c2f5f3..fd12c4d4d 100644
--- a/src/guiFormSpecMenu.cpp
+++ b/src/guiFormSpecMenu.cpp
@@ -86,7 +86,8 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
 	m_form_src(fsrc),
 	m_text_dst(tdst),
 	m_ext_ptr(ext_ptr),
-	m_font(dev->getGUIEnvironment()->getSkin()->getFont())
+	m_font(dev->getGUIEnvironment()->getSkin()->getFont()),
+	m_formspec_version(0)
 {
 	current_keys_pending.key_down = false;
 	current_keys_pending.key_up = false;
@@ -265,7 +266,9 @@ void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,',');
 
-	if ((parts.size() == 2) || parts.size() == 3) {
+	if (((parts.size() == 2) || parts.size() == 3) ||
+		((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		v2f invsize;
 
 		if (parts[1].find(';') != std::string::npos)
@@ -359,7 +362,9 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element)
 
 	std::vector<std::string> parts = split(element,';');
 
-	if ((parts.size() == 4) || (parts.size() == 5)) {
+	if (((parts.size() == 4) || (parts.size() == 5)) ||
+		((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::string location = parts[0];
 		std::string listname = parts[1];
 		std::vector<std::string> v_pos  = split(parts[2],',');
@@ -407,7 +412,9 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if ((parts.size() >= 3) || (parts.size() <= 4)) {
+	if (((parts.size() >= 3) || (parts.size() <= 4)) ||
+		((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::string name = parts[1];
 		std::string label = parts[2];
@@ -460,7 +467,9 @@ void GUIFormSpecMenu::parseImage(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if (parts.size() == 3) {
+	if ((parts.size() == 3) ||
+		((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::vector<std::string> v_geom = split(parts[1],',');
 		std::string name = unescape_string(parts[2]);
@@ -504,7 +513,9 @@ void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if (parts.size() == 3) {
+	if ((parts.size() == 3) ||
+		((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::vector<std::string> v_geom = split(parts[1],',');
 		std::string name = parts[2];
@@ -533,7 +544,9 @@ void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if (parts.size() == 4) {
+	if ((parts.size() == 4) ||
+		((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::vector<std::string> v_geom = split(parts[1],',');
 		std::string name = parts[2];
@@ -587,7 +600,9 @@ void GUIFormSpecMenu::parseBackground(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if ((parts.size() == 3) || (parts.size() == 4)) {
+	if (((parts.size() == 3) || (parts.size() == 4)) ||
+		((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::vector<std::string> v_geom = split(parts[1],',');
 		std::string name = unescape_string(parts[2]);
@@ -655,7 +670,9 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if ((parts.size() == 4) || (parts.size() == 5)) {
+	if (((parts.size() == 4) || (parts.size() == 5)) ||
+		((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::vector<std::string> v_geom = split(parts[1],',');
 		std::string name = parts[2];
@@ -724,7 +741,9 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if ((parts.size() == 4) || (parts.size() == 5) || (parts.size() == 6)) {
+	if (((parts.size() == 4) || (parts.size() == 5) || (parts.size() == 6)) ||
+		((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::vector<std::string> v_geom = split(parts[1],',');
 		std::string name = parts[2];
@@ -797,7 +816,9 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if (parts.size() == 5) {
+	if ((parts.size() == 5) ||
+		((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::string name = parts[2];
 		std::vector<std::string> items = split(parts[3],',');
@@ -852,7 +873,9 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if (parts.size() == 4) {
+	if ((parts.size() == 4) ||
+		((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::vector<std::string> v_geom = split(parts[1],',');
 		std::string name = parts[2];
@@ -1103,7 +1126,9 @@ void GUIFormSpecMenu::parseField(parserData* data,std::string element,
 		return;
 	}
 
-	if (parts.size() == 5) {
+	if ((parts.size() == 5) ||
+		((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		parseTextArea(data,parts,type);
 		return;
 	}
@@ -1114,7 +1139,9 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if (parts.size() == 2) {
+	if ((parts.size() == 2) ||
+		((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::string text = parts[1];
 
@@ -1153,7 +1180,9 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if (parts.size() == 2) {
+	if ((parts.size() == 2) ||
+		((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::wstring text = narrow_to_wide(unescape_string(parts[1]));
 
@@ -1201,7 +1230,9 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if (((parts.size() >= 5) && (parts.size() <= 8)) && (parts.size() != 6)) {
+	if ((((parts.size() >= 5) && (parts.size() <= 8)) && (parts.size() != 6)) ||
+		((parts.size() > 8) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::vector<std::string> v_geom = split(parts[1],',');
 		std::string image_name = parts[2];
@@ -1286,7 +1317,9 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if ((parts.size() == 4) || (parts.size() == 6)) {
+	if (((parts.size() == 4) || (parts.size() == 6)) ||
+		((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::string name = parts[1];
 		std::vector<std::string> buttons = split(parts[2],',');
@@ -1363,7 +1396,9 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
 
 	std::vector<std::string> parts = split(element,';');
 
-	if (parts.size() == 5) {
+	if ((parts.size() == 5) ||
+		((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::vector<std::string> v_geom = split(parts[1],',');
 		std::string item_name = parts[2];
@@ -1426,7 +1461,9 @@ void GUIFormSpecMenu::parseBox(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if (parts.size() == 3) {
+	if ((parts.size() == 3) ||
+		((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		std::vector<std::string> v_pos = split(parts[0],',');
 		std::vector<std::string> v_geom = split(parts[1],',');
 
@@ -1460,7 +1497,9 @@ void GUIFormSpecMenu::parseBackgroundColor(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if ((parts.size() == 1) || (parts.size() == 2)) {
+	if (((parts.size() == 1) || (parts.size() == 2)) ||
+		((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		parseColor(parts[0],m_bgcolor,false);
 
 		if (parts.size() == 2) {
@@ -1476,7 +1515,9 @@ void GUIFormSpecMenu::parseListColors(parserData* data,std::string element)
 {
 	std::vector<std::string> parts = split(element,';');
 
-	if ((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) {
+	if (((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) ||
+		((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+	{
 		parseColor(parts[0], m_slotbg_n, false);
 		parseColor(parts[1], m_slotbg_h, false);
 
@@ -1503,12 +1544,12 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element)
 	std::vector<std::string> parts = split(element,';');
 	if (parts.size() == 2) {
 		std::string name = parts[0];
-		m_tooltips[narrow_to_wide(name.c_str())] = TooltipSpec (parts[1], m_default_tooltip_bgcolor, m_default_tooltip_color);	
+		m_tooltips[narrow_to_wide(name.c_str())] = TooltipSpec (parts[1], m_default_tooltip_bgcolor, m_default_tooltip_color);
 		return;
 	} else if (parts.size() == 4) {
 		std::string name = parts[0];
 		video::SColor tmp_color1, tmp_color2;
-		if ( parseColor(parts[2], tmp_color1, false) && parseColor(parts[3], tmp_color2, false) ) {	
+		if ( parseColor(parts[2], tmp_color1, false) && parseColor(parts[3], tmp_color2, false) ) {
 			m_tooltips[narrow_to_wide(name.c_str())] = TooltipSpec (parts[1], tmp_color1, tmp_color2);
 			return;
 		}
@@ -1516,7 +1557,31 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element)
 	errorstream<< "Invalid tooltip element(" << parts.size() << "): '" << element << "'"  << std::endl;
 }
 
-void GUIFormSpecMenu::parseElement(parserData* data,std::string element)
+bool GUIFormSpecMenu::parseVersionDirect(std::string data)
+{
+	//some prechecks
+	if (data == "")
+		return false;
+
+	std::vector<std::string> parts = split(data,'[');
+
+	if (parts.size() < 2) {
+		return false;
+	}
+
+	if (parts[0] != "formspec_version") {
+		return false;
+	}
+
+	if (is_number(parts[1])) {
+		m_formspec_version = mystoi(parts[1]);
+		return true;
+	}
+
+	return false;
+}
+
+void GUIFormSpecMenu::parseElement(parserData* data, std::string element)
 {
 	//some prechecks
 	if (element == "")
@@ -1763,10 +1828,18 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		m_tooltip_element->grab();
 	}
 
-
 	std::vector<std::string> elements = split(m_formspec_string,']');
-	for (unsigned int i=0; i< elements.size(); i++) {
-		parseElement(&mydata,elements[i]);
+	unsigned int i = 0;
+
+	/* try to read version from first element only */
+	if (elements.size() >= 1) {
+		if ( parseVersionDirect(elements[0]) ) {
+			i++;
+		}
+	}
+
+	for (; i< elements.size(); i++) {
+		parseElement(&mydata, elements[i]);
 	}
 
 	// If there's fields, add a Proceed button
@@ -2167,12 +2240,12 @@ void GUIFormSpecMenu::drawMenu()
 					m_tooltip_element->setVisible(true);
 					this->bringToFront(m_tooltip_element);
 					break;
-				}		
+				}
 			}
 		}
 	}
 
-	skip_tooltip:	
+	skip_tooltip:
 	/*
 		Draw dragged item stack
 	*/
diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h
index 3368bb82d..5d74978a9 100644
--- a/src/guiFormSpecMenu.h
+++ b/src/guiFormSpecMenu.h
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "inventorymanager.h"
 #include "modalMenu.h"
 #include "guiTable.h"
+#include "clientserver.h"
 
 class IGameDef;
 class InventoryManager;
@@ -339,6 +340,7 @@ private:
 	TextDest         *m_text_dst;
 	GUIFormSpecMenu **m_ext_ptr;
 	gui::IGUIFont    *m_font;
+	unsigned int      m_formspec_version;
 
 	typedef struct {
 		v2s32 size;
@@ -390,6 +392,7 @@ private:
 	void parseBackgroundColor(parserData* data,std::string element);
 	void parseListColors(parserData* data,std::string element);
 	void parseTooltip(parserData* data,std::string element);
+	bool parseVersionDirect(std::string data);
 
 	/**
 	 * check if event is part of a double click
@@ -420,7 +423,7 @@ public:
 	{}
 
 	void setForm(std::string formspec) {
-		m_formspec = formspec;
+		m_formspec = FORMSPEC_VERSION_STRING + formspec;
 	}
 
 	std::string getForm()
diff --git a/src/server.cpp b/src/server.cpp
index 58ecb23b4..122ea993d 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -3132,10 +3132,11 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string &formspec,
 	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);
+	os<<serializeLongString(FORMSPEC_VERSION_STRING + formspec);
 	os<<serializeString(formname);
 
 	// Make data buffer
@@ -3538,7 +3539,7 @@ void Server::SendPlayerInventoryFormspec(u16 peer_id)
 
 	std::ostringstream os(std::ios_base::binary);
 	writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
-	os<<serializeLongString(player->inventory_formspec);
+	os<<serializeLongString(FORMSPEC_VERSION_STRING + player->inventory_formspec);
 
 	// Make data buffer
 	std::string s = os.str();
diff --git a/src/util/string.h b/src/util/string.h
index bed66417e..4aeea17db 100644
--- a/src/util/string.h
+++ b/src/util/string.h
@@ -27,6 +27,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <vector>
 #include <sstream>
 
+#define STRINGIFY(x) #x
+#define TOSTRING(x) STRINGIFY(x)
+
 struct FlagDesc {
 	const char *name;
 	u32 flag;
@@ -316,6 +319,17 @@ inline std::string unescape_string(std::string &s)
 	return res;
 }
 
+inline bool is_number(const std::string& tocheck)
+{
+	std::string::const_iterator iter = tocheck.begin();
+
+	while (iter != tocheck.end() && std::isdigit(*iter)) {
+		++iter;
+	}
+
+	return ((!tocheck.empty()) && (iter == tocheck.end()));
+}
+
 std::string translatePassword(std::string playername, std::wstring password);
 std::string urlencode(std::string str);
 std::string urldecode(std::string str);