Jelajahi Sumber

fix(details): read whole values in all info_game config parsers (#5231)

Most config parsers in info_game.sh used a greedy sed that read from the
last delimiter on a line, and some custom parsers kept carriage returns.

- keyvalue_pairs_space, quakec, valve_keyvalues: keep multi-word values
  and drop trailing comments on unquoted values.
- ini, keyvalue_pairs_equals, java_properties, lua, pc_config: split on
  the first delimiter, so values containing = or : are kept whole.
- sqf: works on CRLF files, fixing arma3 and dayz details showing
  NOT SET.
- samp, prism3d (ats, ets2), wmc: no carriage returns in values; wmc
  shows UNLIMITED for a -1 player limit.
- fivem: set/sets/setr prefixes, quoted numbers, IPv6 endpoints.
- rwr: whole attribute names, so mode= no longer matches game_mode=.
- bfv: read game.serverIP, the key the config uses.
Daniel Gibbs 12 jam lalu
induk
melakukan
437371554c
1 mengubah file dengan 83 tambahan dan 102 penghapusan
  1. 83 102
      lgsm/modules/info_game.sh

+ 83 - 102
lgsm/modules/info_game.sh

@@ -15,14 +15,11 @@ moduleselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
 fn_info_game_ini() {
 	# sed is used to process the file.
 	# -n: Suppresses automatic printing of pattern space.
-	# /^\<'"${2}"'\>/: Matches lines starting with the word provided as the second argument ($2), considering it as a whole word.
-	# { s/.*= *"\?\([^"]*\)"\?/\1/p;q }: Command block executed for lines that match the pattern.
-	#   - s/.*= *"\?\([^"]*\)"\?/\1/: Matches and captures the value after an equals sign (=), possibly surrounded by optional double quotes.
-	#     - .*: Matches any characters before the equals sign.
-	#     - = *"\?: Matches the equals sign and any optional spaces before an optional double quote.
-	#     - \([^"]*\): Captures any characters that are not double quotes.
-	#     - "\?: Matches an optional double quote.
-	#     - /1: Replaces the entire matched pattern with the captured value.
+	# /^\<'"${2}"'\>[^=]*=/: Matches lines starting with the word provided as the second argument ($2), considering it as a whole word, followed by an equals sign (=).
+	# { ...;p;q }: Command block executed for the first line that matches the pattern.
+	#   - s/^[^=]*=[[:space:]]*//: Removes everything up to the first equals sign and the whitespace after it, leaving the whole value.
+	#   - s/[[:space:]]*$//: Removes trailing whitespace.
+	#   - s/^"\([^"]*\)".*$/\1/: Keeps only the text inside the quotes when the value is quoted.
 	#   - p: Prints the modified line.
 	#   - q: Quits processing after modifying and printing the line.
 
@@ -31,7 +28,7 @@ fn_info_game_ini() {
 	else
 		servercfgparse="${servercfgfullpath}"
 	fi
-	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>/ { s/.*= *\"\?\([^"]*\)\"\?/\1/p;q }' "${servercfgparse}" | tr -d '\r')"
+	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>[^=]*=/ { s/^[^=]*=[[:space:]]*//; s/[[:space:]]*$//; s/^"\([^"]*\)".*$/\1/; p; q }' "${servercfgparse}" | tr -d '\r')"
 	configtype="ini"
 }
 
@@ -40,14 +37,11 @@ fn_info_game_ini() {
 fn_info_game_keyvalue_pairs_equals() {
 	# sed is used to process the file.
 	# -n: Suppresses automatic printing of pattern space.
-	# /^\<'"${2}"'\>/: Matches lines starting with the word provided as the second argument ($2), considering it as a whole word.
-	# { s/.*= *"\?\([^"]*\)"\?/\1/p;q }: Command block executed for lines that match the pattern.
-	#   - s/.*= *"\?\([^"]*\)"\?/\1/: Matches and captures the value after an equals sign (=), possibly surrounded by optional double quotes.
-	#     - .*: Matches any characters before the equals sign.
-	#     - = *"\?: Matches the equals sign and any optional spaces before an optional double quote.
-	#     - \([^"]*\): Captures any characters that are not double quotes.
-	#     - "\?: Matches an optional double quote.
-	#     - /1: Replaces the entire matched pattern with the captured value.
+	# /^\<'"${2}"'\>[^=]*=/: Matches lines starting with the word provided as the second argument ($2), considering it as a whole word, followed by an equals sign (=).
+	# { ...;p;q }: Command block executed for the first line that matches the pattern.
+	#   - s/^[^=]*=[[:space:]]*//: Removes everything up to the first equals sign and the whitespace after it, leaving the whole value.
+	#   - s/[[:space:]]*$//: Removes trailing whitespace.
+	#   - s/^"\([^"]*\)".*$/\1/: Keeps only the text inside the quotes when the value is quoted.
 	#   - p: Prints the modified line.
 	#   - q: Quits processing after modifying and printing the line.
 
@@ -56,7 +50,7 @@ fn_info_game_keyvalue_pairs_equals() {
 	else
 		servercfgparse="${servercfgfullpath}"
 	fi
-	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>/ { s/.*= *\"\?\([^"]*\)\"\?/\1/p;q }' "${servercfgparse}" | tr -d '\r')"
+	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>[^=]*=/ { s/^[^=]*=[[:space:]]*//; s/[[:space:]]*$//; s/^"\([^"]*\)".*$/\1/; p; q }' "${servercfgparse}" | tr -d '\r')"
 	configtype="keyvalue_pairs"
 }
 
@@ -66,13 +60,11 @@ fn_info_game_keyvalue_pairs_space() {
 	# sed is used to process the file.
 	# -n: Suppresses automatic printing of pattern space.
 	# /^\<'"${2}"'\>/: Matches lines starting with the word provided as the second argument ($2), considering it as a whole word.
-	# { s/.*  *"\?\([^"]*\)"\?/\1/p;q }: Command block executed for lines that match the pattern.
-	#   - s/.*  *"\?\([^"]*\)"\?/\1/: Matches and captures the value after an space ( ), possibly surrounded by optional double quotes.
-	#     - .*: Matches any characters before the space.
-	#     - = *"\?: Matches the space and any optional spaces before an optional double quote.
-	#     - \([^"]*\): Captures any characters that are not double quotes.
-	#     - "\?: Matches an optional double quote.
-	#     - /1: Replaces the entire matched pattern with the captured value.
+	# { ...;p;q }: Command block executed for the first line that matches the pattern.
+	#   - s/^[^[:space:]]*[[:space:]]*//: Removes the key and the whitespace after it, leaving the whole value.
+	#   - /^"/! s/[[:space:]]\+#.*$//: Removes a trailing # comment when the value is not quoted.
+	#   - s/[[:space:]]*$//: Removes trailing whitespace.
+	#   - s/^"\([^"]*\)".*$/\1/: Keeps only the text inside the quotes when the value is quoted.
 	#   - p: Prints the modified line.
 	#   - q: Quits processing after modifying and printing the line.
 
@@ -81,7 +73,7 @@ fn_info_game_keyvalue_pairs_space() {
 	else
 		servercfgparse="${servercfgfullpath}"
 	fi
-	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>/ { s/.*  *\"\?\([^"]*\)\"\?/\1/p;q }' "${servercfgparse}" | tr -d '\r')"
+	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>/ { s/^[^[:space:]]*[[:space:]]*//; /^"/! s/[[:space:]]\+#.*$//; s/[[:space:]]*$//; s/^"\([^"]*\)".*$/\1/; p; q }' "${servercfgparse}" | tr -d '\r')"
 	configtype="keyvalue_pairs"
 }
 
@@ -90,14 +82,11 @@ fn_info_game_keyvalue_pairs_space() {
 fn_info_game_quakec() {
 	# -n: Suppresses automatic printing of pattern space.
 	# /^[[:space:]]*\(set[asu]\?[[:space:]]\+\)\?\<'"${2}"'\>/: Matches lines starting with optional leading whitespace, an optional set, seta, sets or setu, and the word provided as the second argument ($2), considering it as a whole word.
-	# { s/.*  *"\?\([^"]*\)"\?/\1/p;q }: Command block executed for lines that match the pattern.
-	#   - s/.*  *"\?\([^"]*\)"\?/\1/: Matches and captures the value after any number of spaces, possibly surrounded by optional double quotes.
-	#     - .*: Matches any characters before the spaces.
-	#     -   *: Matches any number of spaces.
-	#     - "\?: Matches an optional double quote.
-	#     - \([^"]*\): Captures any characters that are not double quotes.
-	#     - "\?: Matches an optional double quote.
-	#     - /1: Replaces the entire matched pattern with the captured value.
+	# { ...;p;q }: Command block executed for the first line that matches the pattern.
+	#   - s/^[[:space:]]*\(set[asu]\?[[:space:]]\+\)\?[^[:space:]]*[[:space:]]*//: Removes the optional set command, the key and the whitespace after it, leaving the whole value.
+	#   - /^"/! s/[[:space:]]\+\/\/.*$//: Removes a trailing // comment when the value is not quoted.
+	#   - s/[[:space:]]*$//: Removes trailing whitespace.
+	#   - s/^"\([^"]*\)".*$/\1/: Keeps only the text inside the quotes when the value is quoted.
 	#   - p: Prints the modified line.
 	#   - q: Quits processing after modifying and printing the line.
 
@@ -106,7 +95,7 @@ fn_info_game_quakec() {
 	else
 		servercfgparse="${servercfgfullpath}"
 	fi
-	printf -v "${1}" '%s' "$(sed -n '/^[[:space:]]*\(set[asu]\?[[:space:]]\+\)\?\<'"${2}"'\>/ { s/.*  *\"\?\([^"]*\)\"\?/\1/p;q }' "${servercfgparse}" | tr -d '\r')"
+	printf -v "${1}" '%s' "$(sed -n '/^[[:space:]]*\(set[asu]\?[[:space:]]\+\)\?\<'"${2}"'\>/ { s/^[[:space:]]*\(set[asu]\?[[:space:]]\+\)\?[^[:space:]]*[[:space:]]*//; /^"/! s/[[:space:]]\+\/\/.*$//; s/[[:space:]]*$//; s/^"\([^"]*\)".*$/\1/; p; q }' "${servercfgparse}" | tr -d '\r')"
 	configtype="quakec"
 }
 
@@ -127,15 +116,12 @@ fn_info_game_json() {
 fn_info_game_sqf() {
 	# sed is the command itself, indicating that we want to use the sed utility.
 	# -n: Suppresses automatic printing of pattern space.
-	# /^\<'"${2}"'\>/: Matches lines starting with the word provided as the second argument ($2), considering it as a whole word.
-	# { s/.*= *"\?\([^"]*\)"\?/\1/;s/;$//p;q }: Command block executed for lines that match the pattern.
-	#   - s/.*= *"\?\([^"]*\)"\?/\1/: Matches and captures the value after an equals sign (=), possibly surrounded by optional double quotes.
-	#     - .*: Matches any characters before the equals sign.
-	#     - = *"\?: Matches the equals sign and any optional spaces before an optional double quote.
-	#     - \([^"]*\): Captures any characters that are not double quotes.
-	#     - "\?: Matches an optional double quote.
-	#     - /1: Replaces the entire matched pattern with the captured value.
-	#   - s/;$//: Removes a semicolon (;) at the end of the line, if present.
+	# /^\<'"${2}"'\>[^=]*=/: Matches lines starting with the word provided as the second argument ($2), considering it as a whole word, followed by an equals sign (=).
+	# { ...;p;q }: Command block executed for the first line that matches the pattern.
+	#   - s/^[^=]*=[[:space:]]*//: Removes everything up to the first equals sign and the whitespace after it, leaving the whole value.
+	#   - s/[[:space:]]*$//: Removes trailing whitespace.
+	#   - /^"/! s/[[:space:]]*;.*$//: Removes the semicolon (;) and anything after it when the value is not quoted.
+	#   - s/^"\([^"]*\)".*$/\1/: Keeps only the text inside the quotes when the value is quoted.
 	#   - p: Prints the modified line.
 	#   - q: Quits processing after modifying and printing the line.
 
@@ -144,7 +130,7 @@ fn_info_game_sqf() {
 	else
 		servercfgparse="${servercfgfullpath}"
 	fi
-	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>/ { s/.*= *"\?\([^"]*\)"\?/\1/;s/;$//p;q }' "${servercfgparse}" | tr -d '\r')"
+	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>[^=]*=/ { s/^[^=]*=[[:space:]]*//; s/[[:space:]]*$//; /^"/! s/[[:space:]]*;.*$//; s/^"\([^"]*\)".*$/\1/; p; q }' "${servercfgparse}" | tr -d '\r')"
 	configtype="sqf"
 }
 
@@ -166,14 +152,11 @@ fn_info_game_valve_keyvalues() {
 	# sed is used to process the file.
 	# -n: Suppresses automatic printing of pattern space.
 	# /^[[:space:]]*\<'"${2}"'\>/: Matches lines starting with optional leading whitespace and the word provided as the second argument ($2), considering it as a whole word.
-	# { s/.*  *"\?\([^"]*\)"\?/\1/p;q }: Command block executed for lines that match the pattern.
-	#   - s/.*  *"\?\([^"]*\)"\?/\1/: Matches and captures the value after any number of spaces, possibly surrounded by optional double quotes.
-	#     - .*: Matches any characters before the spaces.
-	#     -   *: Matches any number of spaces.
-	#     - "\?: Matches an optional double quote.
-	#     - \([^"]*\): Captures any characters that are not double quotes.
-	#     - "\?: Matches an optional double quote.
-	#     - /1: Replaces the entire matched pattern with the captured value.
+	# { ...;p;q }: Command block executed for the first line that matches the pattern.
+	#   - s/^[[:space:]]*[^[:space:]]*[[:space:]]*//: Removes the key and the whitespace after it, leaving the whole value.
+	#   - /^"/! s/[[:space:]]\+\/\/.*$//: Removes a trailing // comment when the value is not quoted.
+	#   - s/[[:space:]]*$//: Removes trailing whitespace.
+	#   - s/^"\([^"]*\)".*$/\1/: Keeps only the text inside the quotes when the value is quoted.
 	#   - p: Prints the modified line.
 	#   - q: Quits processing after modifying and printing the line.
 
@@ -182,7 +165,7 @@ fn_info_game_valve_keyvalues() {
 	else
 		servercfgparse="${servercfgfullpath}"
 	fi
-	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>/ { s/.*  *"\([^"]*\)".*/\1/p;q }' "${servercfgparse}" | tr -d '\r')"
+	printf -v "${1}" '%s' "$(sed -n '/^[[:space:]]*\<'"${2}"'\>/ { s/^[[:space:]]*[^[:space:]]*[[:space:]]*//; /^"/! s/[[:space:]]\+\/\/.*$//; s/[[:space:]]*$//; s/^"\([^"]*\)".*$/\1/; p; q }' "${servercfgparse}" | tr -d '\r')"
 	configtype="valve_keyvalues"
 }
 
@@ -191,14 +174,11 @@ fn_info_game_valve_keyvalues() {
 fn_info_game_java_properties() {
 	# sed is used to process the file.
 	# -n: Suppresses automatic printing of pattern space.
-	# /^\<'"${2}"'\>/: Matches lines starting with the word provided as the second argument ($2), considering it as a whole word.
-	# { s/.*= *"\?\([^"]*\)"\?/\1/p;q }: Command block executed for lines that match the pattern.
-	#   - s/.*= *"\?\([^"]*\)"\?/\1/: Matches and captures the value after an equals sign (=), possibly surrounded by optional double quotes.
-	#     - .*: Matches any characters before the equals sign.
-	#     - = *"\?: Matches the equals sign and any optional spaces before an optional double quote.
-	#     - \([^"]*\): Captures any characters that are not double quotes.
-	#     - "\?: Matches an optional double quote.
-	#     - /1: Replaces the entire matched pattern with the captured value.
+	# /^\<'"${2}"'\>[^=]*=/: Matches lines starting with the word provided as the second argument ($2), considering it as a whole word, followed by an equals sign (=).
+	# { ...;p;q }: Command block executed for the first line that matches the pattern.
+	#   - s/^[^=]*=[[:space:]]*//: Removes everything up to the first equals sign and the whitespace after it, leaving the whole value.
+	#   - s/[[:space:]]*$//: Removes trailing whitespace.
+	#   - s/^"\([^"]*\)".*$/\1/: Keeps only the text inside the quotes when the value is quoted.
 	#   - p: Prints the modified line.
 	#   - q: Quits processing after modifying and printing the line.
 
@@ -207,7 +187,7 @@ fn_info_game_java_properties() {
 	else
 		servercfgparse="${servercfgfullpath}"
 	fi
-	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>/ { s/.*= *\"\?\([^"]*\)\"\?/\1/p;q }' "${servercfgparse}" | tr -d '\r')"
+	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>[^=]*=/ { s/^[^=]*=[[:space:]]*//; s/[[:space:]]*$//; s/^"\([^"]*\)".*$/\1/; p; q }' "${servercfgparse}" | tr -d '\r')"
 	configtype="java"
 }
 
@@ -216,12 +196,13 @@ fn_info_game_java_properties() {
 # Note: this ini filter does not filter by section. Can cause issues with some games that have multiple sections with the same variable name.
 fn_info_game_lua() {
 	# - The '-n' option suppresses automatic printing of pattern space.
-	# - The pattern '/^[[:space:]]*\<'"${2}"'\>/' matches lines that begin with optional whitespace characters,
-	#   followed by the exact word specified by the second argument.
+	# - The pattern '/^[[:space:]]*\<'"${2}"'\>[^=]*=/' matches lines that begin with optional whitespace characters,
+	#   followed by the exact word specified by the second argument and an equal sign.
 	# - If the pattern matches, the following actions are performed within the curly braces:
-	#   - 's/.*= *"\?\([^"]*\)"\?/\1/' extracts the value within double quotes after an equal sign (if present),
-	#     removing any leading or trailing spaces.
-	#   - 's#,.*##' removes everything after the first comma encountered.
+	#   - 's/^[^=]*=[[:space:]]*//' removes everything up to the first equal sign and the whitespace after it.
+	#   - 's/[[:space:]]*$//' removes trailing whitespace.
+	#   - '/^"/! s/[[:space:]]*\(,\|--\).*$//' removes a trailing comma or -- comment when the value is not quoted.
+	#   - 's/^"\([^"]*\)".*$/\1/' keeps only the text inside the quotes when the value is quoted.
 	#   - 'p' prints the modified pattern space.
 	#   - 'q' quits processing after printing the modified pattern space.
 
@@ -230,7 +211,7 @@ fn_info_game_lua() {
 	else
 		servercfgparse="${servercfgfullpath}"
 	fi
-	printf -v "${1}" '%s' "$(sed -n '/^[[:space:]]*\<'"${2}"'\>/ { s/.*= *"\?\([^"]*\)"\?/\1/;s#,.*##;p;q }' "${servercfgparse}" | tr -d '\r')"
+	printf -v "${1}" '%s' "$(sed -n '/^[[:space:]]*\<'"${2}"'\>[^=]*=/ { s/^[^=]*=[[:space:]]*//; s/[[:space:]]*$//; /^"/! s/[[:space:]]*\(,\|--\).*$//; s/^"\([^"]*\)".*$/\1/; p; q }' "${servercfgparse}" | tr -d '\r')"
 	configtype="lua"
 }
 
@@ -239,14 +220,12 @@ fn_info_game_lua() {
 fn_info_game_pc_config() {
 	# sed is used to process the file.
 	# -n: Suppresses automatic printing of pattern space.
-	# /^\<'"${2}"'\>/: Matches lines starting with the word provided as the second argument ($2), considering it as a whole word.
-	# { s/.*: *"\?\([^"]*\)"\?/\1/p;q }: Command block executed for lines that match the pattern.
-	#   - s/.*: *"\?\([^"]*\)"\?/\1/: Matches and captures the value after an equals sign (=), possibly surrounded by optional double quotes.
-	#     - .*: Matches any characters before the equals sign.
-	#     - : *"\?: Matches the : sign and any optional spaces before an optional double quote.
-	#     - \([^"]*\): Captures any characters that are not double quotes.
-	#     - "\?: Matches an optional double quote.
-	#     - /1: Replaces the entire matched pattern with the captured value.
+	# /^\<'"${2}"'\>[^:]*:/: Matches lines starting with the word provided as the second argument ($2), considering it as a whole word, followed by a colon (:).
+	# { ...;p;q }: Command block executed for the first line that matches the pattern.
+	#   - s/^[^:]*:[[:space:]]*//: Removes everything up to the first colon and the whitespace after it, leaving the whole value.
+	#   - /^"/! s/[[:space:]]\+\/\/.*$//: Removes a trailing // comment when the value is not quoted.
+	#   - s/[[:space:]]*$//: Removes trailing whitespace.
+	#   - s/^"\([^"]*\)".*$/\1/: Keeps only the text inside the quotes when the value is quoted.
 	#   - p: Prints the modified line.
 	#   - q: Quits processing after modifying and printing the line.
 
@@ -255,7 +234,7 @@ fn_info_game_pc_config() {
 	else
 		servercfgparse="${servercfgfullpath}"
 	fi
-	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>/ { s/.*: *\"\?\([^"]*\)\"\?/\1/p;q }' "${servercfgparse}" | tr -d '\r')"
+	printf -v "${1}" '%s' "$(sed -n '/^\<'"${2}"'\>[^:]*:/ { s/^[^:]*:[[:space:]]*//; /^"/! s/[[:space:]]\+\/\/.*$//; s/[[:space:]]*$//; s/^"\([^"]*\)".*$/\1/; p; q }' "${servercfgparse}" | tr -d '\r')"
 	configtype="pc_config"
 }
 
@@ -993,7 +972,7 @@ fn_info_game_bfe() {
 # Filetype: con
 fn_info_game_bfv() {
 	if [ -f "${servercfgfullpath}" ]; then
-		fn_info_game_keyvalue_pairs_space "configip" "game.serverIp"
+		fn_info_game_keyvalue_pairs_space "configip" "game.serverIP"
 		fn_info_game_keyvalue_pairs_space "maxplayers" "game.serverMaxPlayers"
 		fn_info_game_keyvalue_pairs_space "port" "game.serverPort"
 		fn_info_game_keyvalue_pairs_space "servername" "game.serverName"
@@ -1343,10 +1322,12 @@ fn_info_game_etl() {
 # Filetype: cfg
 fn_info_game_fivem() {
 	if [ -f "${servercfgfullpath}" ]; then
-		servername="$(sed -n 's/^sv_hostname[[:space:]]\+"\?\([^"]*\)"\?.*/\1/p' "${servercfgfullpath}" | head -1 | tr -d '\r')"
-		maxplayers="$(sed -n 's/^sv_maxclients[[:space:]]\+\([0-9]*\).*/\1/p' "${servercfgfullpath}" | head -1)"
-		rconpassword="$(sed -n 's/^rcon_password[[:space:]]\+"\?\([^"]*\)"\?.*/\1/p' "${servercfgfullpath}" | head -1 | tr -d '\r')"
-		port="$(sed -n 's/^endpoint_add_udp[[:space:]]\+"\?[^:"]*:\([0-9]*\)"\?.*/\1/p' "${servercfgfullpath}" | head -1)"
+		local cfgvalue='s/^[[:space:]]*\(set[rs]\?[[:space:]]\+\)\?[^[:space:]]*[[:space:]]*//; /^"/! s/[[:space:]]\+\(#\|\/\/\).*$//; s/[[:space:]]*$//; s/^"\([^"]*\)".*$/\1/; p'
+		servername="$(sed -n "/^[[:space:]]*\(set[rs]\?[[:space:]]\+\)\?sv_hostname[[:space:]]/ { ${cfgvalue} }" "${servercfgfullpath}" | tail -n 1)"
+		maxplayers="$(sed -n "/^[[:space:]]*\(set[rs]\?[[:space:]]\+\)\?sv_maxclients[[:space:]]/ { ${cfgvalue} }" "${servercfgfullpath}" | tail -n 1)"
+		rconpassword="$(sed -n "/^[[:space:]]*\(set[rs]\?[[:space:]]\+\)\?rcon_password[[:space:]]/ { ${cfgvalue} }" "${servercfgfullpath}" | tail -n 1)"
+		port="$(sed -n "/^[[:space:]]*endpoint_add_udp[[:space:]]/ { ${cfgvalue} }" "${servercfgfullpath}" | head -n 1)"
+		port="${port##*:}"
 	fi
 	maxplayers="${maxplayers:-"0"}"
 	port="${port:-"0"}"
@@ -1750,11 +1731,11 @@ fn_info_game_prism3d() {
 		servername="${unavailable}"
 		serverpassword="${unavailable}"
 	else
-		maxplayers=$(sed -nr 's/^\s*max_players\s*:\s*([0-9]+)/\1/p' "${servercfgfullpath}")
-		port=$(sed -nr 's/^\s*connection_dedicated_port\s*:\s*([0-9]+)/\1/p' "${servercfgfullpath}")
-		queryport=$(sed -nr 's/^\s*query_dedicated_port\s*:\s*([0-9]+)/\1/p' "${servercfgfullpath}")
-		servername=$(sed -nr 's/^\s*lobby_name\s*:\s*"?([^"\r\n]+)"?/\1/p' "${servercfgfullpath}")
-		serverpassword=$(sed -nr 's/^\s*password\s*:\s*"(.*)"/\1/p' "${servercfgfullpath}")
+		maxplayers=$(sed -nr 's/^\s*max_players\s*:\s*([0-9]+).*/\1/p' "${servercfgfullpath}")
+		port=$(sed -nr 's/^\s*connection_dedicated_port\s*:\s*([0-9]+).*/\1/p' "${servercfgfullpath}")
+		queryport=$(sed -nr 's/^\s*query_dedicated_port\s*:\s*([0-9]+).*/\1/p' "${servercfgfullpath}")
+		servername=$(sed -nr 's/^\s*lobby_name\s*:\s*"([^"]*)".*/\1/p' "${servercfgfullpath}")
+		serverpassword=$(sed -nr 's/^\s*password\s*:\s*"([^"]*)".*/\1/p' "${servercfgfullpath}")
 
 		# Not set
 		maxplayers="${maxplayers:-"0"}"
@@ -1928,10 +1909,10 @@ fn_info_game_rwr() {
 		startscriptfile="${servercfgfullpath}"
 	fi
 	if [ -f "${startscriptfile}" ]; then
-		servername="$(sed -n "s/.*server_name='\([^']*\)'.*/\1/p" "${startscriptfile}" | head -1)"
-		port="$(sed -n "s/.*server_port='\([0-9]*\)'.*/\1/p" "${startscriptfile}" | head -1)"
-		maxplayers="$(sed -n "s/.*max_players='\([0-9]*\)'.*/\1/p" "${startscriptfile}" | head -1)"
-		gamemode="$(sed -n "s/.*mode='\([^']*\)'.*/\1/p" "${startscriptfile}" | head -1)"
+		servername="$(sed -nE "/^[[:space:]]*\/\//! s/^(.*[[:space:]<])?server_name=('([^']*)'|\"([^\"]*)\").*/\3\4/p" "${startscriptfile}" | head -n 1)"
+		port="$(sed -nE "/^[[:space:]]*\/\//! s/^(.*[[:space:]<])?server_port=('([^']*)'|\"([^\"]*)\").*/\3\4/p" "${startscriptfile}" | head -n 1)"
+		maxplayers="$(sed -nE "/^[[:space:]]*\/\//! s/^(.*[[:space:]<])?max_players=('([^']*)'|\"([^\"]*)\").*/\3\4/p" "${startscriptfile}" | head -n 1)"
+		gamemode="$(sed -nE "/^[[:space:]]*\/\//! s/^(.*[[:space:]<])?mode=('([^']*)'|\"([^\"]*)\").*/\3\4/p" "${startscriptfile}" | head -n 1)"
 	fi
 	servername="${servername:-"NOT SET"}"
 	port="${port:-"0"}"
@@ -2013,12 +1994,12 @@ fn_info_game_rw() {
 # filetypes: cfg
 fn_info_game_samp() {
 	if [ -f "${servercfgfullpath}" ]; then
-		servername=$(grep "hostname" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^\//d' -e 's/hostname//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
-		rconpassword=$(grep "rcon_password" "${servercfgfullpath}" | sed -e 's/^[ \t]*//g' -e '/^#/d' -e 's/^rcon_password//g' | tr -d '=\";,:' | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')
-		port=$(grep "port" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		fn_info_game_keyvalue_pairs_space "servername" "hostname"
+		fn_info_game_keyvalue_pairs_space "rconpassword" "rcon_password"
+		fn_info_game_keyvalue_pairs_space "port" "port"
 		queryport="${port}"
 		rconport="${port}"
-		maxplayers=$(grep "maxplayers" "${servercfgfullpath}" | grep -v "#" | tr -cd '[:digit:]')
+		fn_info_game_keyvalue_pairs_space "maxplayers" "maxplayers"
 
 		# Not set
 		servername="${servername:-"NOT SET"}"
@@ -2493,13 +2474,13 @@ fn_info_game_wmc() {
 		queryport="${zero}"
 		queryenabled="${unavailable}"
 	else
-		servername=$(sed -e '/^listeners:/,/^[a-z]/!d' "${servercfgfullpath}" | sed -nr 's/^[ ]+motd: (.*)$/\1/p' | tr -d "'" | sed 's/&1//')
-		queryport=$(sed -nr 's/^[ -]+query_port: ([0-9]+)/\1/p' "${servercfgfullpath}")
-		queryenabled=$(sed -nr 's/^[ ]+query_enabled: (.*)$/\1/p' "${servercfgfullpath}")
-		port=$(sed -nr 's/^[ ]+host: [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:([0-9]+)/\1/p' "${servercfgfullpath}")
+		servername=$(sed -e '/^listeners:/,/^[a-z]/!d' "${servercfgfullpath}" | sed -nr 's/^[ ]+motd: (.*)$/\1/p' | tr -d "'\r" | sed 's/&1//')
+		queryport=$(sed -nr 's/^[ -]+query_port: ([0-9]+).*/\1/p' "${servercfgfullpath}")
+		queryenabled=$(sed -nr 's/^[ ]+query_enabled: ([a-z]+).*/\1/p' "${servercfgfullpath}")
+		port=$(sed -nr 's/^[ ]+host: [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+:([0-9]+).*/\1/p' "${servercfgfullpath}")
 		# the normal max_players does only show in on the client side and has no effect how many players can connect.
-		maxplayers=$(sed -nr 's/^player_limit: ([-]*[0-9])/\1/p' "${servercfgfullpath}")
-		configip=$(sed -nr 's/^[ ]+host: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):[0-9]+/\1/p' "${servercfgfullpath}")
+		maxplayers=$(sed -nr 's/^player_limit: (-?[0-9]+).*/\1/p' "${servercfgfullpath}")
+		configip=$(sed -nr 's/^[ ]+host: ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+):[0-9]+.*/\1/p' "${servercfgfullpath}")
 
 		if [ "${maxplayers}" == "-1" ] || [ "${maxplayers}" == "0" ]; then
 			maxplayers="UNLIMITED"