diff --git a/src/client/game.cpp b/src/client/game.cpp
index 907f94faa..75a29b131 100644
--- a/src/client/game.cpp
+++ b/src/client/game.cpp
@@ -3627,10 +3627,10 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
 	// Compare core.item_place_node() for what the server does with param2
 	MapNode predicted_node(id, 0, 0);
 
-	const u8 place_param2 = selected_def.place_param2;
+	const auto place_param2 = selected_def.place_param2;
 
 	if (place_param2) {
-		predicted_node.setParam2(place_param2);
+		predicted_node.setParam2(*place_param2);
 	} else if (predicted_f.param_type_2 == CPT2_WALLMOUNTED ||
 			predicted_f.param_type_2 == CPT2_COLORED_WALLMOUNTED) {
 		v3s16 dir = nodepos - neighborpos;
diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp
index 60c303c78..d6d2468f5 100644
--- a/src/client/wieldmesh.cpp
+++ b/src/client/wieldmesh.cpp
@@ -443,7 +443,8 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che
 		default: {
 			// Render non-trivial drawtypes like the actual node
 			MapNode n(id);
-			n.setParam2(def.place_param2);
+			if (def.place_param2)
+				n.setParam2(*def.place_param2);
 
 			mesh = createSpecialNodeMesh(client, n, &m_colors, f);
 			changeToMesh(mesh);
@@ -638,7 +639,8 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result)
 		default: {
 			// Render non-trivial drawtypes like the actual node
 			MapNode n(id);
-			n.setParam2(def.place_param2);
+			if (def.place_param2)
+				n.setParam2(*def.place_param2);
 
 			mesh = createSpecialNodeMesh(client, n, &result->buffer_colors, f);
 			scaleMesh(mesh, v3f(0.12, 0.12, 0.12));
diff --git a/src/itemdef.cpp b/src/itemdef.cpp
index ae252c4a0..daf2295a5 100644
--- a/src/itemdef.cpp
+++ b/src/itemdef.cpp
@@ -123,7 +123,7 @@ void ItemDefinition::reset()
 	sound_use_air = SoundSpec();
 	range = -1;
 	node_placement_prediction.clear();
-	place_param2 = 0;
+	place_param2.reset();
 }
 
 void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
@@ -169,10 +169,20 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
 
 	os << serializeString16(short_description);
 
-	os << place_param2;
+	if (protocol_version <= 43) {
+		// Uncertainity whether 0 is the specified prediction or means disabled
+		if (place_param2)
+			os << *place_param2;
+		else
+			os << (u8)0;
+	}
 
 	sound_use.serializeSimple(os, protocol_version);
 	sound_use_air.serializeSimple(os, protocol_version);
+
+	os << (u8)place_param2.has_value(); // protocol_version >= 43
+	if (place_param2)
+		os << *place_param2;
 }
 
 void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
@@ -226,10 +236,21 @@ void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
 	try {
 		short_description = deSerializeString16(is);
 
-		place_param2 = readU8(is); // 0 if missing
+		if (protocol_version <= 43) {
+			place_param2 = readU8(is);
+			// assume disabled prediction
+			if (place_param2 == 0)
+				place_param2.reset();
+		}
 
 		sound_use.deSerializeSimple(is, protocol_version);
 		sound_use_air.deSerializeSimple(is, protocol_version);
+
+		if (is.eof())
+			throw SerializationError("");
+
+		if (readU8(is)) // protocol_version >= 43
+			place_param2 = readU8(is);
 	} catch(SerializationError &e) {};
 }
 
diff --git a/src/itemdef.h b/src/itemdef.h
index bad3a3e24..8ce6a15cd 100644
--- a/src/itemdef.h
+++ b/src/itemdef.h
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlichttypes_extrabloated.h"
 #include <string>
 #include <iostream>
+#include <optional>
 #include <set>
 #include "itemgroup.h"
 #include "sound.h"
@@ -87,7 +88,7 @@ struct ItemDefinition
 	// Server will update the precise end result a moment later.
 	// "" = no prediction
 	std::string node_placement_prediction;
-	u8 place_param2;
+	std::optional<u8> place_param2;
 
 	/*
 		Some helpful methods
diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h
index c9e29cf12..e012fc06c 100644
--- a/src/network/networkprotocol.h
+++ b/src/network/networkprotocol.h
@@ -217,6 +217,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 		[scheduled bump for 5.7.0]
 	PROTOCOL VERSION 43:
 		"start_time" added to TOCLIENT_PLAY_SOUND
+		place_param2 type change u8 -> optional<u8>
 		[scheduled bump for 5.8.0]
 */
 
diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp
index 94f8dcabe..1756c49c5 100644
--- a/src/script/common/c_content.cpp
+++ b/src/script/common/c_content.cpp
@@ -133,7 +133,9 @@ void read_item_definition(lua_State* L, int index,
 	getstringfield(L, index, "node_placement_prediction",
 			def.node_placement_prediction);
 
-	getintfield(L, index, "place_param2", def.place_param2);
+	int place_param2;
+	if (getintfield(L, index, "place_param2", place_param2))
+		def.place_param2 = rangelim(place_param2, 0, U8_MAX);
 }
 
 /******************************************************************************/