aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Druzenko <vvd@FreeBSD.org>2025-02-22 21:48:35 +0000
committerVladimir Druzenko <vvd@FreeBSD.org>2025-02-22 21:48:35 +0000
commit5578dbccb5f6a2d1bda001b4e8a78f626c491918 (patch)
tree8489d031e491e19c5262d094cd3cab2fda0408bf
parent7927033436e985b385fe58a83c5341954a505b50 (diff)
games/qtv: New port: QuakeWorld match broadcasting tool
The maximum number of players and spectators in QuakeWorld is 32. QTV allows hundreds and even thousands of spectators to watch the game at the same time. Also, a delay of about 10 seconds has been added so that spectators cannot prompt players with information about opponents during the game via voice chats. In such a dynamic game as QuakeWorld, a lot can change in a few seconds and the information will be out of date.
-rw-r--r--games/Makefile1
-rw-r--r--games/qtv/Makefile49
-rw-r--r--games/qtv/distinfo3
-rw-r--r--games/qtv/files/patch-src_fs.c20
-rw-r--r--games/qtv/files/patch-src_main.c15
-rw-r--r--games/qtv/files/patch-src_net__utils.c18
-rwxr-xr-xgames/qtv/files/qtv.in106
-rw-r--r--games/qtv/pkg-descr8
-rw-r--r--games/qtv/pkg-plist102
9 files changed, 322 insertions, 0 deletions
diff --git a/games/Makefile b/games/Makefile
index a4086e1704cd..a18ec83e29c7 100644
--- a/games/Makefile
+++ b/games/Makefile
@@ -819,6 +819,7 @@
SUBDIR += qonk
SUBDIR += qqwing
SUBDIR += qstat
+ SUBDIR += qtv
SUBDIR += quackle
SUBDIR += quadra
SUBDIR += quadrapassel
diff --git a/games/qtv/Makefile b/games/qtv/Makefile
new file mode 100644
index 000000000000..b6a6739600a6
--- /dev/null
+++ b/games/qtv/Makefile
@@ -0,0 +1,49 @@
+PORTNAME= qtv
+DISTVERSION= 1.15
+CATEGORIES= games
+
+MAINTAINER= vvd@FreeBSD.org
+COMMENT= QuakeWorld match broadcasting tool
+WWW= https://github.com/QW-Group/qtv/
+
+LICENSE= GPLv2
+LICENSE_FILE= ${WRKSRC}/LICENSE.md
+
+USES= cmake
+
+USE_GITHUB= yes
+GH_ACCOUNT= QW-Group
+USE_RC_SUBR= ${PORTNAME}
+
+EXTRACT_AFTER_ARGS= --exclude .git* \
+ --exclude */.git* \
+ --exclude build_cmake.sh \
+ --exclude resources/example-configs/"PLACE YOUR QTV.BIN HERE" \
+ --exclude resources/metaqtv* \
+ --exclude tools*
+
+QWDIR= ${PREFIX}/quake
+SUB_LIST= QWDIR=${QWDIR} QWUSER=${USERS}
+USERS= qw
+GROUPS= qw
+PLIST_SUB= QWGROUP=${GROUPS}
+PORTDOCS= README.md
+
+OPTIONS_DEFINE= DOCS
+
+do-install:
+ ${INSTALL_PROGRAM} ${BUILD_WRKSRC}/${PORTNAME} ${STAGEDIR}${PREFIX}/bin
+ cd ${WRKSRC}/resources/example-configs && \
+ ${COPYTREE_SHARE} ${PORTNAME} ${STAGEDIR}${QWDIR}
+ ${MV} ${STAGEDIR}${QWDIR}/${PORTNAME}/listip.cfg \
+ ${STAGEDIR}${QWDIR}/${PORTNAME}/listip.cfg.sample
+ ${INSTALL_DATA} ${WRKSRC}/resources/example-configs/${PORTNAME}.cfg \
+ ${STAGEDIR}${QWDIR}/${PORTNAME}/${PORTNAME}.cfg.sample
+
+do-install-DOCS-on:
+ ${MKDIR} ${STAGEDIR}${DOCSDIR}
+ ${INSTALL_DATA} ${PORTDOCS:S,^,${WRKSRC}/,} ${STAGEDIR}${DOCSDIR}
+ ${INSTALL_DATA} ${WRKSRC}/resources/example-configs/README.1ST \
+ ${STAGEDIR}${DOCSDIR}
+
+.include <bsd.port.mk>
diff --git a/games/qtv/distinfo b/games/qtv/distinfo
new file mode 100644
index 000000000000..e44a8b3b80f7
--- /dev/null
+++ b/games/qtv/distinfo
@@ -0,0 +1,3 @@
+TIMESTAMP = 1739923712
+SHA256 (QW-Group-qtv-1.15_GH0.tar.gz) = ab9e699bb0894dd8274c8c7c56d7bba44e75b2a123baf17212aa2f60aef6111c
+SIZE (QW-Group-qtv-1.15_GH0.tar.gz) = 1000884
diff --git a/games/qtv/files/patch-src_fs.c b/games/qtv/files/patch-src_fs.c
new file mode 100644
index 000000000000..53efa85406cb
--- /dev/null
+++ b/games/qtv/files/patch-src_fs.c
@@ -0,0 +1,20 @@
+--- src/fs.c.orig 2025-02-16 14:10:58 UTC
++++ src/fs.c
+@@ -119,6 +119,7 @@ void FS_StripPathAndExtension(char *filepath)
+ size_t lastslash = (size_t) -1;
+ size_t lastdot = (size_t) -1;
+ size_t i = 0;
++ char temp_filepath[MAX_QPATH];
+
+ for ( ; filepath[i]; i++)
+ {
+@@ -131,7 +132,8 @@ void FS_StripPathAndExtension(char *filepath)
+ if (lastdot == (size_t) -1 || lastdot < lastslash)
+ lastdot = i;
+
+- strlcpy(filepath, filepath + lastslash + 1, lastdot - lastslash);
++ strlcpy(temp_filepath, filepath + lastslash + 1, lastdot - lastslash);
++ strlcpy(filepath, temp_filepath, sizeof(temp_filepath));
+ }
+
+ // return file extension with dot, or empty string if dot not found at all
diff --git a/games/qtv/files/patch-src_main.c b/games/qtv/files/patch-src_main.c
new file mode 100644
index 000000000000..42aa78f7e007
--- /dev/null
+++ b/games/qtv/files/patch-src_main.c
@@ -0,0 +1,15 @@
+--- src/main.c.orig 2025-02-16 14:10:58 UTC
++++ src/main.c
+@@ -274,6 +274,12 @@ int main(int argc, char **argv)
+ }
+ #endif // _WIN32
+
++ // Disable buffering for stdout and stderr to avoid issues when output
++ // is redirected to a file or pipe instead of being displayed in a
++ // terminal.
++ setbuf(stdout, NULL);
++ setbuf(stderr, NULL);
++
+ memset(&g_cluster, 0, sizeof(g_cluster));
+
+ g_cluster.tcpsocket = INVALID_SOCKET;
diff --git a/games/qtv/files/patch-src_net__utils.c b/games/qtv/files/patch-src_net__utils.c
new file mode 100644
index 000000000000..f87e702b97db
--- /dev/null
+++ b/games/qtv/files/patch-src_net__utils.c
@@ -0,0 +1,18 @@
+--- src/net_utils.c.orig 2025-02-16 14:10:58 UTC
++++ src/net_utils.c
+@@ -60,11 +60,13 @@ char *Net_BaseAdrToString (struct sockaddr_in *a, char
+ // Windows have inet_ntop only starting from Vista. Sigh.
+ #ifdef _WIN32
+ const char *result = inet_ntoa(a->sin_addr);
++ strlcpy(buf, result ? result : "", bufsize);
+ #else
+ const char *result = inet_ntop(a->sin_family, &a->sin_addr, buf, bufsize);
++ if (!result) {
++ strlcpy(buf, "", bufsize);
++ }
+ #endif
+-
+- strlcpy(buf, result ? result : "", bufsize);
+
+ return buf;
+ }
diff --git a/games/qtv/files/qtv.in b/games/qtv/files/qtv.in
new file mode 100755
index 000000000000..7be83aed5b0f
--- /dev/null
+++ b/games/qtv/files/qtv.in
@@ -0,0 +1,106 @@
+#!/bin/sh
+
+# PROVIDE: qtv
+# REQUIRE: LOGIN
+# KEYWORD: shutdown
+#
+# Add the following lines to /etc/rc.conf or /etc/rc.conf.local to
+# enable qtv:
+# qtv_(instance_)?enable (bool): Set to "NO" by default.
+# Set it to "YES" to enable qtv.
+# qtv_(instance_)?args (str): Custom additional arguments to be passed
+# to qtv (default empty).
+# qtv_(instance_)?user (str): User to run qtv as. Default to
+# "%%QWUSER%%" created by the port.
+# qtv_(instance_)?log (path): Console log file (default
+# /var/log/${name}(_instance)?.log).
+# qtv_(instance_)?qwdir (path): QuakeWorld root directory
+# (default %%QWDIR%%).
+# qtv_instances (str): Set to "" by default.
+# If defined, list of instances to enable.
+
+. /etc/rc.subr
+
+case $0 in
+/etc/rc*)
+ # during boot (shutdown) $0 is /etc/rc (/etc/rc.shutdown),
+ # so get the name of the script from $_file
+ name=$_file
+ ;;
+*)
+ name=$0
+ ;;
+esac
+
+name=${name##*/}
+rcvar="${name}_enable"
+
+load_rc_config "${name}"
+
+eval "${rcvar}=\${${rcvar}:-'NO'}"
+eval "__args=\${${name}_args:-''}"
+eval "__user=\${${name}_user:-'%%QWUSER%%'}"
+eval "__log=\${${name}_log:-/var/log/${name}.log}"
+eval "__qwdir=\${${name}_qwdir:-'%%QWDIR%%'}"
+eval "${name}_chdir=${__qwdir}"
+eval "__instances=\${${name}_instances:-''}"
+
+pidfiledir="/var/run"
+pidfile="${pidfiledir}/${name}.pid"
+
+if [ -n "$2" ]; then
+ instance="$2"
+ load_rc_config ${name}_${instance}
+ case "${__instances}" in
+ "$2 "*|*" $2 "*|*" $2"|"$2")
+ eval "__args=\${${name}_${instance}_args:-${__args}}"
+ eval "__user=\${${name}_${instance}_user:-${__user}}"
+ eval "__log=\${${name}_${instance}_log:-/var/log/${name}_${instance}.log}"
+ eval "__qwdir=\${${name}_${instance}_qwdir:-${__qwdir}}"
+ eval "${name}_chdir=${__qwdir}"
+ pidfile="${pidfiledir}/${name}_${instance}.pid"
+ ;;
+ *)
+ err 1 "$2 not found in ${name}_instances" ;;
+ esac
+else
+ if [ -n "${__instances}" -a -n "$1" ]; then
+ for instance in ${__instances}; do
+ eval "_enable=\${${name}_${instance}_enable}"
+ eval "__enable=\${_enable:-\${${name}_enable}}"
+ case "${__enable}" in
+ [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0)
+ continue
+ ;;
+ [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1)
+ ;;
+ *)
+ if [ -z "${_enable}" ]; then
+ _var=${name}_enable
+ else
+ _var=${name}_${instance}_enable
+ fi
+ warn "Bad value '${__enable}' for ${_var}. " \
+ "Instance ${instance} skipped."
+ continue
+ ;;
+ esac
+ echo "===> ${name} instance: ${instance}"
+ %%PREFIX%%/etc/rc.d/${name} $1 ${instance}
+ retcode="$?"
+ if [ "0${retcode}" -eq 0 ]; then
+ success="${instance} ${success}"
+ else
+ failed="${instance} (retcode=${retcode}) ${failed}"
+ fi
+ done
+ echo "===> ${name} instances success: ${success}"
+ echo "===> ${name} instances failed: ${failed}"
+ exit 0
+ fi
+fi
+
+command="/usr/sbin/daemon"
+command_args="-P ${pidfile} -u ${__user} -R 5 -f -H -o ${__log} -m 3 %%PREFIX%%/bin/qtv ${__args}"
+
+run_rc_command "$1"
diff --git a/games/qtv/pkg-descr b/games/qtv/pkg-descr
new file mode 100644
index 000000000000..867a84856823
--- /dev/null
+++ b/games/qtv/pkg-descr
@@ -0,0 +1,8 @@
+QTV is a QuakeWorld match broadcasting tool.
+
+The maximum number of players and spectators in QuakeWorld is 32. QTV allows
+hundreds and even thousands of spectators to watch the game at the same time.
+Also, a delay of about 10 seconds has been added so that spectators cannot
+prompt players with information about opponents during the game via voice chats.
+In such a dynamic game as QuakeWorld, a lot can change in a few seconds and the
+information will be out of date.
diff --git a/games/qtv/pkg-plist b/games/qtv/pkg-plist
new file mode 100644
index 000000000000..60d2429c3db9
--- /dev/null
+++ b/games/qtv/pkg-plist
@@ -0,0 +1,102 @@
+bin/qtv
+quake/qtv/levelshots/1on1.jpg
+quake/qtv/levelshots/1on1r.jpg
+quake/qtv/levelshots/2fort5.jpg
+quake/qtv/levelshots/2fort5l.jpg
+quake/qtv/levelshots/2fort5r.jpg
+quake/qtv/levelshots/2tech4.jpg
+quake/qtv/levelshots/32seq.jpg
+quake/qtv/levelshots/32smooth.jpg
+quake/qtv/levelshots/_notfound.jpg
+quake/qtv/levelshots/a2.jpg
+quake/qtv/levelshots/aerowalk.jpg
+quake/qtv/levelshots/amphi.jpg
+quake/qtv/levelshots/bam4.jpg
+quake/qtv/levelshots/bases.jpg
+quake/qtv/levelshots/battle.jpg
+quake/qtv/levelshots/blitzkrieg2.jpg
+quake/qtv/levelshots/border1.jpg
+quake/qtv/levelshots/castle.jpg
+quake/qtv/levelshots/cmt1b.jpg
+quake/qtv/levelshots/cmt2.jpg
+quake/qtv/levelshots/cmt3.jpg
+quake/qtv/levelshots/cmt4.jpg
+quake/qtv/levelshots/cmt5b.jpg
+quake/qtv/levelshots/death32c.jpg
+quake/qtv/levelshots/dm1.jpg
+quake/qtv/levelshots/dm2.jpg
+quake/qtv/levelshots/dm3.jpg
+quake/qtv/levelshots/dm4.jpg
+quake/qtv/levelshots/dm5.jpg
+quake/qtv/levelshots/dm6.jpg
+quake/qtv/levelshots/e1m1.jpg
+quake/qtv/levelshots/e1m2.jpg
+quake/qtv/levelshots/e1m3.jpg
+quake/qtv/levelshots/e1m4.jpg
+quake/qtv/levelshots/e1m5.jpg
+quake/qtv/levelshots/e1m6.jpg
+quake/qtv/levelshots/e1m7.jpg
+quake/qtv/levelshots/e1m8.jpg
+quake/qtv/levelshots/e2m1.jpg
+quake/qtv/levelshots/e2m2.jpg
+quake/qtv/levelshots/e2m3.jpg
+quake/qtv/levelshots/e2m4.jpg
+quake/qtv/levelshots/e2m5.jpg
+quake/qtv/levelshots/e2m6.jpg
+quake/qtv/levelshots/e2m7.jpg
+quake/qtv/levelshots/e3m1.jpg
+quake/qtv/levelshots/e3m2.jpg
+quake/qtv/levelshots/e3m3.jpg
+quake/qtv/levelshots/e3m4.jpg
+quake/qtv/levelshots/e3m5.jpg
+quake/qtv/levelshots/e3m6.jpg
+quake/qtv/levelshots/e3m7.jpg
+quake/qtv/levelshots/e4m1.jpg
+quake/qtv/levelshots/e4m2.jpg
+quake/qtv/levelshots/e4m3.jpg
+quake/qtv/levelshots/e4m4.jpg
+quake/qtv/levelshots/e4m5.jpg
+quake/qtv/levelshots/e4m6.jpg
+quake/qtv/levelshots/e4m7.jpg
+quake/qtv/levelshots/e4m8.jpg
+quake/qtv/levelshots/end.jpg
+quake/qtv/levelshots/endif.jpg
+quake/qtv/levelshots/engbat.jpg
+quake/qtv/levelshots/genders2.jpg
+quake/qtv/levelshots/hammer.jpg
+quake/qtv/levelshots/hammerv2.jpg
+quake/qtv/levelshots/hammerv3.jpg
+quake/qtv/levelshots/hippos.jpg
+quake/qtv/levelshots/hohoho.jpg
+quake/qtv/levelshots/hunted.jpg
+quake/qtv/levelshots/mbases.jpg
+quake/qtv/levelshots/pkeg1.jpg
+quake/qtv/levelshots/povdmm4.jpg
+quake/qtv/levelshots/qffldm2.jpg
+quake/qtv/levelshots/qffldm5.jpg
+quake/qtv/levelshots/rock1.jpg
+quake/qtv/levelshots/rs_zz1.jpg
+quake/qtv/levelshots/schloss.jpg
+quake/qtv/levelshots/skull.jpg
+quake/qtv/levelshots/sniprwar.jpg
+quake/qtv/levelshots/spinev2.jpg
+quake/qtv/levelshots/start.jpg
+quake/qtv/levelshots/ukcldm2.jpg
+quake/qtv/levelshots/vote40.jpg
+quake/qtv/levelshots/well6.jpg
+quake/qtv/levelshots/xmastree.jpg
+quake/qtv/levelshots/xpress3.jpg
+quake/qtv/levelshots/ztndm1.jpg
+quake/qtv/levelshots/ztndm2.jpg
+quake/qtv/levelshots/ztndm3.jpg
+quake/qtv/levelshots/ztndm4.jpg
+quake/qtv/levelshots/ztndm5.jpg
+quake/qtv/levelshots/ztndm6.jpg
+@sample(,%%QWGROUP%%,0640) quake/qtv/listip.cfg.sample
+@sample(,%%QWGROUP%%,0640) quake/qtv/qtv.cfg.sample
+quake/qtv/qtvbg01.png
+quake/qtv/save.png
+quake/qtv/script.js
+quake/qtv/stream.png
+quake/qtv/style.css
+%%PORTDOCS%%%%DOCSDIR%%/README.1ST