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.
This commit is contained in:
Vladimir Druzenko 2025-02-23 00:48:35 +03:00
parent 7927033436
commit 5578dbccb5
9 changed files with 322 additions and 0 deletions

View File

@ -819,6 +819,7 @@
SUBDIR += qonk
SUBDIR += qqwing
SUBDIR += qstat
SUBDIR += qtv
SUBDIR += quackle
SUBDIR += quadra
SUBDIR += quadrapassel

49
games/qtv/Makefile Normal file
View File

@ -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>

3
games/qtv/distinfo Normal file
View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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;
}

106
games/qtv/files/qtv.in Executable file
View File

@ -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"

8
games/qtv/pkg-descr Normal file
View File

@ -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.

102
games/qtv/pkg-plist Normal file
View File

@ -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