From 040c1c11fdb09f5721f51e9982834d13ac5f19d9 Mon Sep 17 00:00:00 2001
From: bones_was_here <bones_was_here@xonotic.au>
Date: Sat, 18 May 2024 21:38:05 +1000
Subject: [PATCH] Fix GetField_fullspawndata sometimes taking the wrong code
 path

When the optional parameter wasn't passed, uninitialised data was read,
which caused door linking to fail on some Q3 maps.

Signed-off-by: bones_was_here <bones_was_here@xonotic.au>
---
 qcsrc/common/mapinfo.qc                 |  2 +-
 qcsrc/common/mapobjects/func/door.qc    |  2 +-
 qcsrc/common/mapobjects/target/speed.qc |  2 +-
 qcsrc/server/compat/quake3.qc           | 12 ++++++------
 qcsrc/server/items/items.qc             |  2 +-
 qcsrc/server/main.qc                    | 12 ++++++------
 qcsrc/server/main.qh                    |  2 +-
 7 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc
index c1cdd7a90c..707c6826ce 100644
--- a/qcsrc/common/mapinfo.qc
+++ b/qcsrc/common/mapinfo.qc
@@ -1339,7 +1339,7 @@ LABEL(mapinfo_handled)
 			if (world.message != "")
 				MapInfo_Map_title = world.message;
 		if (MapInfo_Map_author == "<AUTHOR>")
-			if ((s = GetField_fullspawndata(world, "author")) != "")
+			if ((s = GetField_fullspawndata(world, "author", false)) != "")
 				MapInfo_Map_author = s;
 	}
 #endif
diff --git a/qcsrc/common/mapobjects/func/door.qc b/qcsrc/common/mapobjects/func/door.qc
index 30909e4c45..1e8cb985f2 100644
--- a/qcsrc/common/mapobjects/func/door.qc
+++ b/qcsrc/common/mapobjects/func/door.qc
@@ -809,7 +809,7 @@ spawnfunc(func_door)
 
 		if (!this.team)
 		{
-			string t = GetField_fullspawndata(this, "team");
+			string t = GetField_fullspawndata(this, "team", false);
 			// bones_was_here: same hack as used to support teamed items on Q3 maps
 			if (t) this.team = crc16(false, t);
 		}
diff --git a/qcsrc/common/mapobjects/target/speed.qc b/qcsrc/common/mapobjects/target/speed.qc
index 4c3618cad6..83f25495f6 100644
--- a/qcsrc/common/mapobjects/target/speed.qc
+++ b/qcsrc/common/mapobjects/target/speed.qc
@@ -160,7 +160,7 @@ spawnfunc(target_speed)
 	this.reset = target_speed_reset;
 
 	// support a 0 speed setting AND a default
-	string s = GetField_fullspawndata(this, "speed");
+	string s = GetField_fullspawndata(this, "speed", false);
 	if (!s || s == "")
 		this.speed = 100;
 
diff --git a/qcsrc/server/compat/quake3.qc b/qcsrc/server/compat/quake3.qc
index a4c55116bc..59c0ebd145 100644
--- a/qcsrc/server/compat/quake3.qc
+++ b/qcsrc/server/compat/quake3.qc
@@ -346,29 +346,29 @@ bool DoesQ3ARemoveThisEntity(entity this)
 	// Xonotic is usually played with a CPM-based physics so we default to CPM mode
 	if(cvar_string("g_mod_physics") == "Q3")
 	{
-		if(stof(GetField_fullspawndata(this, "notvq3")))
+		if(stof(GetField_fullspawndata(this, "notvq3", false)))
 			return true;
 	}
-	else if(stof(GetField_fullspawndata(this, "notcpm")))
+	else if(stof(GetField_fullspawndata(this, "notcpm", false)))
 		return true;
 
 	// Q3 mappers use "notq3a" or "notta" to disable an entity in Q3A or Q3TA
 	// Xonotic has ~equivalent features to Team Arena
-	if(stof(GetField_fullspawndata(this, "notta")))
+	if(stof(GetField_fullspawndata(this, "notta", false)))
 		return true;
 
 	// FIXME: singleplayer does not use maxclients 1 as that would prevent bots,
 	// this is the case in Q3 also, it uses another method to block clients.
 	// Only accessible in VQ3, via the `spmap` command.
-	if(stof(GetField_fullspawndata(this, "notsingle")))
+	if(stof(GetField_fullspawndata(this, "notsingle", false)))
 		if(maxclients == 1 && IS_GAMETYPE(DEATHMATCH))
 			return true;
 
-	if(stof(GetField_fullspawndata(this, "notteam")))
+	if(stof(GetField_fullspawndata(this, "notteam", false)))
 		if(teamplay)
 			return true;
 
-	if(stof(GetField_fullspawndata(this, "notfree")))
+	if(stof(GetField_fullspawndata(this, "notfree", false)))
 		if(!teamplay)
 			return true;
 
diff --git a/qcsrc/server/items/items.qc b/qcsrc/server/items/items.qc
index 825f003a5a..f43fa3b3dc 100644
--- a/qcsrc/server/items/items.qc
+++ b/qcsrc/server/items/items.qc
@@ -1104,7 +1104,7 @@ void StartItem(entity this, entity def)
 		{
 			if (!this.team)
 			{
-				string t = GetField_fullspawndata(this, "team");
+				string t = GetField_fullspawndata(this, "team", false);
 				// bones_was_here: this hack is cheaper than changing to a .string strcmp()
 				if(t) this.team = crc16(false, t);
 			}
diff --git a/qcsrc/server/main.qc b/qcsrc/server/main.qc
index 1948081779..b0433b9ea5 100644
--- a/qcsrc/server/main.qc
+++ b/qcsrc/server/main.qc
@@ -434,7 +434,7 @@ void SV_OnEntityPreSpawnFunction(entity this)
 	}
 }
 
-/** Retrieves the value of a map entity field from fullspawndata
+/** Retrieves the value of a map entity field from fullspawndata.
  * This bypasses field value changes made by the engine,
  * eg string-to-float and escape sequence substitution.
  *
@@ -442,12 +442,12 @@ void SV_OnEntityPreSpawnFunction(entity this)
  *
  * Returns the last instance of the field to match DarkPlaces behaviour.
  *
- * Path support: converts \ to / and tests the file if a third (bool, true) arg is passed.
+ * Path support: converts \ to / and checks the file exists, if vfspath is true.
  * Returns string_null if the entity does not have the field, or the file is not in the VFS.
  *
  * FIXME: entities with //comments are not supported.
  */
-string GetField_fullspawndata(entity e, string f, ...)
+string GetField_fullspawndata(entity e, string fieldname, bool vfspath)
 {
 	string v = string_null;
 
@@ -462,14 +462,14 @@ string GetField_fullspawndata(entity e, string f, ...)
 		return v;
 	}
 
-	//print(sprintf("%s(EDICT %s, FIELD %s)\n", __FUNC__, ftos(num_for_edict(e)), f));
+	//print(sprintf("%s(EDICT %s, FIELD %s)\n", __FUNC__, ftos(num_for_edict(e)), fieldname));
 	//print(strcat("FULLSPAWNDATA:", e.fullspawndata, "\n"));
 
 	// tokenize treats \ as an escape, but tokenize_console returns the required literal
 	for (int t = tokenize_console(e.fullspawndata) - 3; t > 0; t -= 2)
 	{
 		//print(sprintf("\tTOKEN %s:%s\t%s:%s\n", ftos(t), ftos(t + 1), argv(t), argv(t + 1)));
-		if (argv(t) == f)
+		if (argv(t) == fieldname)
 		{
 			v = argv(t + 1);
 			break;
@@ -478,7 +478,7 @@ string GetField_fullspawndata(entity e, string f, ...)
 
 	//print(strcat("RESULT: ", v, "\n\n"));
 
-	if (v && ...(0, bool) == true)
+	if (v && vfspath)
 	{
 		v = strreplace("\\", "/", v);
 		if (whichpack(v) == "")
diff --git a/qcsrc/server/main.qh b/qcsrc/server/main.qh
index 30b6864716..8fa9b357e7 100644
--- a/qcsrc/server/main.qh
+++ b/qcsrc/server/main.qh
@@ -48,7 +48,7 @@ float servertime, serverprevtime, serverframetime;
 
 .float contents_damagetime;
 
-string GetField_fullspawndata(entity e, string f, ...);
+string GetField_fullspawndata(entity e, string fieldname, bool vfspath);
 
 /*
 ==================
-- 
2.39.5